diff --git a/smite-ir/src/mutators/operation_param.rs b/smite-ir/src/mutators/operation_param.rs index b5152f6c..d7cc31d1 100644 --- a/smite-ir/src/mutators/operation_param.rs +++ b/smite-ir/src/mutators/operation_param.rs @@ -109,6 +109,8 @@ fn mutate_operation(op: &mut Operation, rng: &mut impl Rng) -> bool { // match have drifted out of sync. Operation::DerivePoint | Operation::CreateFundingTransaction + | Operation::DeriveTemporaryChannelIdV2 + | Operation::DeriveChannelIdV2 | Operation::LoadTargetPubkeyFromContext | Operation::LoadChainHashFromContext | Operation::BuildOpenChannel diff --git a/smite-ir/src/operation.rs b/smite-ir/src/operation.rs index 2b369251..98e5014c 100644 --- a/smite-ir/src/operation.rs +++ b/smite-ir/src/operation.rs @@ -81,6 +81,18 @@ pub enum Operation { /// 2: `funding_satoshis` (`Amount`) /// 3: `feerate_per_kw` (`FeeratePerKw`) CreateFundingTransaction, + /// Derive the v2 `temporary_channel_id` from our revocation basepoint, + /// using a zeroed basepoint for the not-yet-known peer (BOLT 2). + /// + /// Input: `Point` -- our `revocation_basepoint`. + DeriveTemporaryChannelIdV2, + /// Derive the v2 `channel_id` from both peers' revocation basepoints + /// (BOLT 2). + /// + /// Inputs (2): + /// 0: `revocation_basepoint` (`Point`) -- ours + /// 1: `revocation_basepoint` (`Point`) -- the peer's + DeriveChannelIdV2, // -- Build: construct a BOLT message from inputs -- /// Build an `open_channel` message (BOLT 2, type 32). @@ -537,6 +549,8 @@ impl fmt::Display for Operation { Self::DerivePoint => write!(f, "DerivePoint"), Self::ExtractAcceptChannel(field) => write!(f, "Extract{field}"), Self::CreateFundingTransaction => write!(f, "CreateFundingTransaction"), + Self::DeriveTemporaryChannelIdV2 => write!(f, "DeriveTemporaryChannelIdV2"), + Self::DeriveChannelIdV2 => write!(f, "DeriveChannelIdV2"), Self::BuildOpenChannel => write!(f, "BuildOpenChannel"), Self::BuildChannelAnnouncement => write!(f, "BuildChannelAnnouncement"), Self::BuildNodeAnnouncement { rgb_color, alias } => write!( @@ -583,7 +597,10 @@ impl Operation { Self::LoadBytes(_) | Self::LoadShutdownScript(_) => Some(VariableType::Bytes), Self::LoadFeatures(_) | Self::LoadChannelType(_) => Some(VariableType::Features), Self::LoadPrivateKey(_) => Some(VariableType::PrivateKey), - Self::LoadChannelId(_) | Self::RecvFundingSigned => Some(VariableType::ChannelId), + Self::LoadChannelId(_) + | Self::DeriveTemporaryChannelIdV2 + | Self::DeriveChannelIdV2 + | Self::RecvFundingSigned => Some(VariableType::ChannelId), Self::LoadTargetPubkeyFromContext | Self::DerivePoint => Some(VariableType::Point), Self::LoadChainHashFromContext => Some(VariableType::ChainHash), Self::ExtractAcceptChannel(field) => Some(field.output_type()), @@ -637,6 +654,11 @@ impl Operation { VariableType::Amount, // funding_satoshis VariableType::FeeratePerKw, // feerate_per_kw ], + Self::DeriveTemporaryChannelIdV2 => vec![VariableType::Point], // revocation_basepoint + Self::DeriveChannelIdV2 => vec![ + VariableType::Point, // our revocation_basepoint + VariableType::Point, // the peer's revocation_basepoint + ], Self::BuildOpenChannel => vec![ VariableType::ChainHash, // chain_hash VariableType::ChannelId, // temporary_channel_id @@ -753,6 +775,8 @@ impl Operation { | Self::DerivePoint | Self::ExtractAcceptChannel(_) | Self::CreateFundingTransaction + | Self::DeriveTemporaryChannelIdV2 + | Self::DeriveChannelIdV2 | Self::BuildOpenChannel | Self::BuildChannelAnnouncement | Self::BuildNodeAnnouncement { .. } @@ -799,6 +823,8 @@ impl Operation { | Self::LoadChainHashFromContext | Self::DerivePoint | Self::ExtractAcceptChannel(_) + | Self::DeriveTemporaryChannelIdV2 + | Self::DeriveChannelIdV2 | Self::BuildOpenChannel | Self::BuildChannelAnnouncement | Self::BuildNodeAnnouncement { .. } @@ -847,6 +873,8 @@ impl Operation { | Self::LoadChainHashFromContext | Self::DerivePoint | Self::ExtractAcceptChannel(_) + | Self::DeriveTemporaryChannelIdV2 + | Self::DeriveChannelIdV2 | Self::BuildOpenChannel | Self::BuildChannelAnnouncement | Self::BuildNodeAnnouncement { .. } @@ -910,6 +938,8 @@ impl Operation { | Self::LoadChainHashFromContext | Self::DerivePoint | Self::CreateFundingTransaction + | Self::DeriveTemporaryChannelIdV2 + | Self::DeriveChannelIdV2 | Self::BuildOpenChannel | Self::BuildChannelAnnouncement | Self::BuildChannelUpdate diff --git a/smite-ir/src/tests.rs b/smite-ir/src/tests.rs index 84072fcb..76f7389e 100644 --- a/smite-ir/src/tests.rs +++ b/smite-ir/src/tests.rs @@ -735,6 +735,26 @@ fn lookup_short_channel_id_operation() { assert_eq!(op.to_string(), "LookupShortChannelId"); } +#[test] +fn derive_v2_channel_id_operations() { + let op = Operation::DeriveTemporaryChannelIdV2; + assert_eq!(op.input_types(), vec![VariableType::Point]); + assert_eq!(op.output_type(), Some(VariableType::ChannelId)); + assert!(!op.is_param_mutable()); + assert!(op.is_pure()); + assert_eq!(op.to_string(), "DeriveTemporaryChannelIdV2"); + + let op = Operation::DeriveChannelIdV2; + assert_eq!( + op.input_types(), + vec![VariableType::Point, VariableType::Point], + ); + assert_eq!(op.output_type(), Some(VariableType::ChannelId)); + assert!(!op.is_param_mutable()); + assert!(op.is_pure()); + assert_eq!(op.to_string(), "DeriveChannelIdV2"); +} + fn create_and_broadcast_tx_instructions() -> Vec { vec![ Instruction { diff --git a/smite-scenarios/src/executor.rs b/smite-scenarios/src/executor.rs index fe58d6ae..14ae18b4 100644 --- a/smite-scenarios/src/executor.rs +++ b/smite-scenarios/src/executor.rs @@ -406,6 +406,21 @@ impl Executor { Some(Variable::FundingTransaction(ft)) } + Operation::DeriveTemporaryChannelIdV2 => { + let revocation_basepoint = resolve_pubkey(&variables, instr.inputs[0]); + Some(Variable::ChannelId( + ChannelId::v2_temporary_from_revocation_basepoint(&revocation_basepoint), + )) + } + + Operation::DeriveChannelIdV2 => { + let ours = resolve_pubkey(&variables, instr.inputs[0]); + let theirs = resolve_pubkey(&variables, instr.inputs[1]); + Some(Variable::ChannelId( + ChannelId::v2_from_revocation_basepoints(&ours, &theirs), + )) + } + // -- Build operations -- Operation::BuildOpenChannel => { let oc = build_open_channel(&variables, &instr.inputs); diff --git a/smite-scenarios/src/executor/tests.rs b/smite-scenarios/src/executor/tests.rs index b2236f0f..d747693a 100644 --- a/smite-scenarios/src/executor/tests.rs +++ b/smite-scenarios/src/executor/tests.rs @@ -307,6 +307,57 @@ fn execute_derive_point() { assert_eq!(fx.sent::(0), oc.message); } +#[test] +fn execute_derive_temporary_channel_id_v2() { + let mut b = ProgramBuilder::new(); + let revocation_basepoint = b.append(Operation::LoadTargetPubkeyFromContext, &[]); + let channel_id = b.append( + Operation::DeriveTemporaryChannelIdV2, + &[revocation_basepoint], + ); + let scriptpubkey = b.append( + Operation::LoadShutdownScript(ShutdownScriptVariant::Empty), + &[], + ); + b.append(Operation::SendShutdown, &[channel_id, scriptpubkey]); + + let mut fx = Fixture::new(); + fx.run(&b.build()); + + assert_eq!( + fx.sent::(0).channel_id, + ChannelId::v2_temporary_from_revocation_basepoint(&sample_context().target_pubkey), + ); +} + +#[test] +fn execute_derive_channel_id_v2() { + let mut b = ProgramBuilder::new(); + let our_basepoint = b.append(Operation::LoadTargetPubkeyFromContext, &[]); + let their_privkey = b.append(Operation::LoadPrivateKey([0x22; 32]), &[]); + let their_basepoint = b.append(Operation::DerivePoint, &[their_privkey]); + let channel_id = b.append( + Operation::DeriveChannelIdV2, + &[our_basepoint, their_basepoint], + ); + let scriptpubkey = b.append( + Operation::LoadShutdownScript(ShutdownScriptVariant::Empty), + &[], + ); + b.append(Operation::SendShutdown, &[channel_id, scriptpubkey]); + + let mut fx = Fixture::new(); + fx.run(&b.build()); + + let secp = Secp256k1::new(); + let their_basepoint = + PublicKey::from_secret_key(&secp, &SecretKey::from_slice(&[0x22; 32]).unwrap()); + assert_eq!( + fx.sent::(0).channel_id, + ChannelId::v2_from_revocation_basepoints(&sample_context().target_pubkey, &their_basepoint), + ); +} + #[test] fn execute_recv_and_extract_all_fields() { // Receive accept_channel (v0), then extract all 16 fields (v1..v16).