From 05a3777f08430b0bae7b6caa17e7417d2e88db45 Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Thu, 15 Oct 2026 19:27:57 -0500 Subject: [PATCH 1/2] smite-scenarios: derive the funding negotiation from wire fixtures The injected funding negotiation used a hand-built PendingChannel that AcceptChannelOracle would reject: - neither message had a channel_type - the acceptor's first_per_commitment_point matched its static pubkeys As a result, we couldn't actually create the same negotiation without injecting it. Since we want to move away from injecting negotiations, change the problematic parameters to pass the oracle. Additionally, give the PendingChannel messages their own helper functions so that they can be used in programs too. --- smite-scenarios/src/executor/tests/harness.rs | 87 ++++++++++--------- .../src/executor/tests/programs.rs | 12 +-- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/smite-scenarios/src/executor/tests/harness.rs b/smite-scenarios/src/executor/tests/harness.rs index 7171529a..d4f3caa4 100644 --- a/smite-scenarios/src/executor/tests/harness.rs +++ b/smite-scenarios/src/executor/tests/harness.rs @@ -473,51 +473,52 @@ pub fn recv_channel_ready_fixture() -> (Fixture, PublicKey) { (fx, target_pcp) } -#[allow(clippy::similar_names)] -pub fn sample_funding_negotiation() -> PendingChannel { - let secp = Secp256k1::new(); - let opener_pk = PublicKey::from_secret_key(&secp, &opener_funding_sk()); - let acceptor_pk = PublicKey::from_secret_key(&secp, &acceptor_funding_sk()); +/// The `open_channel` of the funding flow, with all six pubkeys derived from +/// [`opener_funding_sk`]. +pub fn funded_open_channel() -> SampleOpenChannel { + let mut oc = SampleOpenChannel::new(PointSource::Secret(opener_funding_sk().secret_bytes())); + oc.message.funding_satoshis = 10_000_000; + oc.message.push_msat = 3_000_000_000; + oc.message.feerate_per_kw = 15_000; + oc.message.tlvs.channel_type = Some(ChannelTypeVariant::StaticRemoteKey.encode()); + + oc +} - PendingChannel { - open_channel: OpenChannel { - chain_hash: REGTEST_CHAIN_HASH, - temporary_channel_id: TEMPORARY_CHANNEL_ID, - funding_satoshis: 10_000_000, - push_msat: 3_000_000_000, - dust_limit_satoshis: 546, - max_htlc_value_in_flight_msat: 100_000_000, - channel_reserve_satoshis: 10_000, - htlc_minimum_msat: 1_000, - feerate_per_kw: 15_000, - to_self_delay: 144, - max_accepted_htlcs: 483, - funding_pubkey: opener_pk, - revocation_basepoint: opener_pk, - payment_basepoint: opener_pk, - delayed_payment_basepoint: opener_pk, - htlc_basepoint: opener_pk, - first_per_commitment_point: opener_pk, - channel_flags: 1, - tlvs: OpenChannelTlvs::default(), +/// The target's `accept_channel` for [`funded_open_channel`], with all five +/// static pubkeys derived from [`acceptor_funding_sk`]. +pub fn funded_accept_channel() -> AcceptChannel { + let acceptor_pk = PublicKey::from_secret_key(&Secp256k1::new(), &acceptor_funding_sk()); + + AcceptChannel { + temporary_channel_id: TEMPORARY_CHANNEL_ID, + dust_limit_satoshis: 546, + max_htlc_value_in_flight_msat: 100_000_000, + channel_reserve_satoshis: 10_000, + htlc_minimum_msat: 1_000, + minimum_depth: 6, + to_self_delay: 144, + max_accepted_htlcs: 483, + funding_pubkey: acceptor_pk, + revocation_basepoint: acceptor_pk, + payment_basepoint: acceptor_pk, + delayed_payment_basepoint: acceptor_pk, + htlc_basepoint: acceptor_pk, + // Distinct from the static pubkeys and every other sample pubkey the + // tests use, as required by `AcceptChannelOracle`. + first_per_commitment_point: sample_pubkey(10), + tlvs: AcceptChannelTlvs { + upfront_shutdown_script: None, + channel_type: Some(ChannelTypeVariant::StaticRemoteKey.encode()), }, - accept_channel: Some(AcceptChannel { - temporary_channel_id: TEMPORARY_CHANNEL_ID, - dust_limit_satoshis: 546, - max_htlc_value_in_flight_msat: 100_000_000, - channel_reserve_satoshis: 10_000, - htlc_minimum_msat: 1_000, - minimum_depth: 6, - to_self_delay: 144, - max_accepted_htlcs: 483, - funding_pubkey: acceptor_pk, - revocation_basepoint: acceptor_pk, - payment_basepoint: acceptor_pk, - delayed_payment_basepoint: acceptor_pk, - htlc_basepoint: acceptor_pk, - first_per_commitment_point: acceptor_pk, - tlvs: AcceptChannelTlvs::default(), - }), + } +} + +/// The negotiation of [`funded_open_channel`] and [`funded_accept_channel`]. +pub fn sample_funding_negotiation() -> PendingChannel { + PendingChannel { + open_channel: funded_open_channel().message, + accept_channel: Some(funded_accept_channel()), funding_built: false, } } diff --git a/smite-scenarios/src/executor/tests/programs.rs b/smite-scenarios/src/executor/tests/programs.rs index 911630d6..883614db 100644 --- a/smite-scenarios/src/executor/tests/programs.rs +++ b/smite-scenarios/src/executor/tests/programs.rs @@ -8,8 +8,8 @@ //! purpose is to create malformed program. use super::harness::{ - PointSource, SampleOpenChannel, TEMPORARY_CHANNEL_ID, acceptor_funding_sk, opener_funding_sk, - second_funding_negotiation, + PointSource, SampleOpenChannel, TEMPORARY_CHANNEL_ID, acceptor_funding_sk, funded_open_channel, + opener_funding_sk, second_funding_negotiation, }; use crate::executor::*; use smite_ir::Instruction; @@ -182,8 +182,7 @@ pub fn negotiate_channel_program(oc: &SampleOpenChannel) -> Program { // -- Funding transaction -- -/// The funding amount and feerate the funding-flow programs negotiate. -const FUNDING_SATOSHIS: u64 = 10_000_000; +/// The funding transaction feerate used by the funding-flow programs. const FUNDING_FEERATE_PER_KW: u32 = 15_000; /// The variables a funding transaction fragment produces. @@ -199,10 +198,11 @@ pub struct FundingTxVars { pub tx: usize, } -/// Emits a `CreateFundingTransaction` for [`FUNDING_SATOSHIS`] at +/// Emits a `CreateFundingTransaction` for [`funded_open_channel`], at /// [`FUNDING_FEERATE_PER_KW`]. pub fn create_funding_tx(b: &mut ProgramBuilder) -> FundingTxVars { - create_funding_tx_with(b, FUNDING_SATOSHIS, FUNDING_FEERATE_PER_KW) + let funding_satoshis = funded_open_channel().message.funding_satoshis; + create_funding_tx_with(b, funding_satoshis, FUNDING_FEERATE_PER_KW) } /// Emits a `CreateFundingTransaction` between the opener and acceptor funding From 714ad7d07521ddc882e8590f35b5a4fe86f0818e Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Thu, 15 Oct 2026 19:47:45 -0500 Subject: [PATCH 2/2] smite-scenarios: simplify channel_ready key-reuse tests Now that we have funded_open_channel() and funded_accept_channel() helpers that can be used in programs, we can drop the unnecessary injected negotiation from the channel_ready key-reuse tests. --- smite-scenarios/src/executor/tests.rs | 30 ++++++++++++--------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/smite-scenarios/src/executor/tests.rs b/smite-scenarios/src/executor/tests.rs index 8a830a15..b6dc9d29 100644 --- a/smite-scenarios/src/executor/tests.rs +++ b/smite-scenarios/src/executor/tests.rs @@ -1662,21 +1662,20 @@ fn execute_recv_channel_ready_unexpected_message() { #[test] fn execute_recv_channel_ready_rejects_reused_our_pubkey() { - // The target's `channel_ready` reuses a pubkey we revealed in an - // `open_channel` of a different negotiation. - let mut oc = SampleOpenChannel::new(PointSource::Secret([0x11; 32])); - oc.message.temporary_channel_id = SECOND_TEMPORARY_CHANNEL_ID; + // The target's `channel_ready` reuses a pubkey we revealed in our + // `open_channel`. + let oc = funded_open_channel(); let reused = oc.message.funding_pubkey; let mut b = ProgramBuilder::new(); - let sent_open_channel = send_open_channel(&mut b, &oc); + let negotiated = negotiate_channel(&mut b, &oc); let funding_created = send_funding_created(&mut b); b.append(Operation::RecvFundingSigned, &[funding_created.sent]); b.append(Operation::MineBlocks(6), &[]); b.append(Operation::RecvChannelReady, &[]); let err = Fixture::new() - .with_negotiation(sample_funding_negotiation()) + .queue(&Message::AcceptChannel(funded_accept_channel())) .queue(&funding_signed_reply(funding_channel_id())) .queue(&channel_ready_reply(reused)) .run_err(&b.build()); @@ -1687,32 +1686,29 @@ fn execute_recv_channel_ready_rejects_reused_our_pubkey() { assert_eq!(*id, funding_channel_id()); assert!(reason.contains(&format!( "second_per_commitment_point {reused} reuses our key from instruction {}", - sent_open_channel.sent, + negotiated.open_channel.sent, ))); } #[test] fn execute_recv_channel_ready_rejects_reused_target_static_pubkey() { - let mut oc = announced_open_channel(); - oc.message.temporary_channel_id = SECOND_TEMPORARY_CHANNEL_ID; - - // The target's `channel_ready` reuses a static pubkey it revealed in an - // `accept_channel` of a different negotiation. + // The target's `channel_ready` reuses a static pubkey it revealed in its + // `accept_channel`. A distinct key in a non-first field shows every static + // pubkey is recorded, not just `funding_pubkey`. let accept_channel = AcceptChannel { - temporary_channel_id: SECOND_TEMPORARY_CHANNEL_ID, - ..sample_accept_channel() + delayed_payment_basepoint: sample_pubkey(11), + ..funded_accept_channel() }; let reused = accept_channel.delayed_payment_basepoint; let mut b = ProgramBuilder::new(); - negotiate_channel(&mut b, &oc); + negotiate_channel(&mut b, &funded_open_channel()); let funding_created = send_funding_created(&mut b); b.append(Operation::RecvFundingSigned, &[funding_created.sent]); b.append(Operation::MineBlocks(6), &[]); b.append(Operation::RecvChannelReady, &[]); let err = Fixture::new() - .with_negotiation(sample_funding_negotiation()) .queue(&Message::AcceptChannel(accept_channel)) .queue(&funding_signed_reply(funding_channel_id())) .queue(&channel_ready_reply(reused)) @@ -1723,7 +1719,7 @@ fn execute_recv_channel_ready_rejects_reused_target_static_pubkey() { }; assert_eq!(*id, funding_channel_id()); assert!(reason.contains(&format!( - "second_per_commitment_point {reused} reuses target's delayed_payment_basepoint on channel {SECOND_TEMPORARY_CHANNEL_ID}" + "second_per_commitment_point {reused} reuses target's delayed_payment_basepoint on channel {TEMPORARY_CHANNEL_ID}" ))); }