diff --git a/smite-scenarios/src/executor/tests.rs b/smite-scenarios/src/executor/tests.rs index 84a9bd87..9341f31d 100644 --- a/smite-scenarios/src/executor/tests.rs +++ b/smite-scenarios/src/executor/tests.rs @@ -1469,50 +1469,12 @@ fn execute_recv_channel_ready_invalid_signature_is_noop() { #[test] fn execute_recv_channel_ready_resend_with_new_alias() { - // We will carry forward two funding flows with different temporary channel - // IDs and funding amounts and hence different funded channel IDs. - let first_negotiation = sample_funding_negotiation(); let target_pcp = sample_pubkey(9); - let second_id = TemporaryChannelId::new([0xcc; 32]); - // A second UTXO so the program can build a second funding transaction. - let mut second_utxo = sample_utxo(); - second_utxo.amount = Amount::from_sat(20_010_000); - second_utxo.outpoint.vout = 1; - // A second negotiation with a different temporary channel ID and funding - // amount, resulting in a different channel ID and allowing the second UTXO - // to fund it. - let mut second_negotiation = sample_funding_negotiation(); - second_negotiation.open_channel.temporary_channel_id = second_id; - second_negotiation.open_channel.funding_satoshis = 20_000_000; - second_negotiation - .accept_channel - .as_mut() - .unwrap() - .temporary_channel_id = second_id; - - // Create the funding transactions and send funding_created for both - // negotiations. Do not wait for funding_signed, since that is not the goal - // of this test. Once the funding transactions have enough confirmations, - // receive channel_ready for both negotiations. The target, instead of - // sending channel_ready for the second funding flow, sends a duplicate - // channel_ready for the first funding flow with a different alias but - // otherwise identical contents. - let mut b = ProgramBuilder::new(); - send_funding_created(&mut b); - let tx = create_funding_tx_with(&mut b, 20_000_000, 15_000); - let second_temp_chan_id = b.append(Operation::LoadChannelId(second_id.0), &[]); - b.append( - Operation::SendFundingCreated, - &[tx.tx, tx.opener_privkey, second_temp_chan_id], - ); - b.append(Operation::MineBlocks(6), &[]); - b.append(Operation::RecvChannelReady, &[]); - b.append(Operation::RecvChannelReady, &[]); - let mut fx = Fixture::new() - .with_utxos(vec![sample_utxo(), second_utxo]) - .with_negotiation(first_negotiation) - .with_negotiation(second_negotiation) + // Instead of a `channel_ready` for the second channel, the target resends + // the first channel's with a different alias but otherwise identical + // contents. + let mut fx = two_funding_negotiations_fixture() .queue(&channel_ready_reply(target_pcp)) .queue(&Message::ChannelReady(ChannelReady { channel_id: funding_channel_id(), @@ -1521,7 +1483,7 @@ fn execute_recv_channel_ready_resend_with_new_alias() { short_channel_id: Some(ShortChannelId::from_u64(0)), }, })); - fx.run(&b.build()); + fx.run(&recv_two_channel_ready_program()); let state = fx.channel_state(&funding_channel_id()); assert_eq!( @@ -1534,48 +1496,10 @@ fn execute_recv_channel_ready_resend_with_new_alias() { #[test] fn execute_recv_channel_ready_rejects_alias_reused_by_another_channel() { - // A second negotiation with a different temporary channel ID and funding - // amount, funded by a second UTXO, resulting in a different channel ID. - let second_id = TemporaryChannelId::new([0xcc; 32]); - let mut second_utxo = sample_utxo(); - second_utxo.amount = Amount::from_sat(20_010_000); - second_utxo.outpoint.vout = 1; - let mut second_negotiation = sample_funding_negotiation(); - second_negotiation.open_channel.temporary_channel_id = second_id; - second_negotiation.open_channel.funding_satoshis = 20_000_000; - second_negotiation - .accept_channel - .as_mut() - .unwrap() - .temporary_channel_id = second_id; - let second_channel_id = ChannelId::v1_from_funding_outpoint(OutPoint { - txid: "cb6f11974ce6265f81037dc7c0e3f054462f645361bc1e10c208f132f4acbe8d" - .parse() - .expect("valid txid"), - vout: 0, - }); - - // Fund both channels and receive a `channel_ready` for each once the - // funding transactions reach `minimum_depth`. The second channel's - // `channel_ready` advertises the alias the first channel already - // advertised. - let mut b = ProgramBuilder::new(); - send_funding_created(&mut b); - let tx = create_funding_tx_with(&mut b, 20_000_000, 15_000); - let second_temp_chan_id = b.append(Operation::LoadChannelId(second_id.0), &[]); - b.append( - Operation::SendFundingCreated, - &[tx.tx, tx.opener_privkey, second_temp_chan_id], - ); - b.append(Operation::MineBlocks(6), &[]); - b.append(Operation::RecvChannelReady, &[]); - b.append(Operation::RecvChannelReady, &[]); - + // The second channel's `channel_ready` advertises the alias the first + // channel already advertised. let alias = ShortChannelId::from_u64(42); - let err = Fixture::new() - .with_utxos(vec![sample_utxo(), second_utxo]) - .with_negotiation(sample_funding_negotiation()) - .with_negotiation(second_negotiation) + let err = two_funding_negotiations_fixture() .queue(&Message::ChannelReady(ChannelReady { channel_id: funding_channel_id(), second_per_commitment_point: sample_pubkey(7), @@ -1584,18 +1508,18 @@ fn execute_recv_channel_ready_rejects_alias_reused_by_another_channel() { }, })) .queue(&Message::ChannelReady(ChannelReady { - channel_id: second_channel_id, + channel_id: second_funding_channel_id(), second_per_commitment_point: sample_pubkey(8), tlvs: ChannelReadyTlvs { short_channel_id: Some(alias), }, })) - .run_err(&b.build()); + .run_err(&recv_two_channel_ready_program()); let ExecuteError::Violation(Violation::InvalidChannelReady(id, reason)) = &err else { panic!("unexpected error: {err:?}"); }; - assert_eq!(*id, second_channel_id); + assert_eq!(*id, second_funding_channel_id()); assert!(reason.contains(&format!( "short_channel_id alias {alias} reuses the short_channel_id of channel {}", funding_channel_id(), @@ -1604,65 +1528,28 @@ fn execute_recv_channel_ready_rejects_alias_reused_by_another_channel() { #[test] fn execute_recv_channel_ready_rejects_alias_matching_another_channels_scid() { - // A second negotiation with a different temporary channel ID and funding - // amount, funded by a second UTXO, resulting in a different channel ID. - let second_id = TemporaryChannelId::new([0xcc; 32]); - let mut second_utxo = sample_utxo(); - second_utxo.amount = Amount::from_sat(20_010_000); - second_utxo.outpoint.vout = 1; - let mut second_negotiation = sample_funding_negotiation(); - second_negotiation.open_channel.temporary_channel_id = second_id; - second_negotiation.open_channel.funding_satoshis = 20_000_000; - second_negotiation - .accept_channel - .as_mut() - .unwrap() - .temporary_channel_id = second_id; - let second_channel_id = ChannelId::v1_from_funding_outpoint(OutPoint { - txid: "cb6f11974ce6265f81037dc7c0e3f054462f645361bc1e10c208f132f4acbe8d" - .parse() - .expect("valid txid"), - vout: 0, - }); - - // Fund both channels and receive a `channel_ready` for each once the - // funding transactions reach `minimum_depth`. The second channel advertises - // an alias matching the first channel's real short channel ID. - let mut b = ProgramBuilder::new(); - send_funding_created(&mut b); - let tx = create_funding_tx_with(&mut b, 20_000_000, 15_000); - let second_temp_chan_id = b.append(Operation::LoadChannelId(second_id.0), &[]); - b.append( - Operation::SendFundingCreated, - &[tx.tx, tx.opener_privkey, second_temp_chan_id], - ); - b.append(Operation::MineBlocks(6), &[]); - b.append(Operation::RecvChannelReady, &[]); - b.append(Operation::RecvChannelReady, &[]); - + // The second channel advertises an alias matching the first channel's real + // short channel ID. let alias = ShortChannelId::new(800_042, 7, 0); - let err = Fixture::new() - .with_utxos(vec![sample_utxo(), second_utxo]) - .with_negotiation(sample_funding_negotiation()) - .with_negotiation(second_negotiation) + let err = two_funding_negotiations_fixture() .queue(&Message::ChannelReady(ChannelReady { channel_id: funding_channel_id(), second_per_commitment_point: sample_pubkey(7), tlvs: ChannelReadyTlvs::default(), })) .queue(&Message::ChannelReady(ChannelReady { - channel_id: second_channel_id, + channel_id: second_funding_channel_id(), second_per_commitment_point: sample_pubkey(8), tlvs: ChannelReadyTlvs { short_channel_id: Some(alias), }, })) - .run_err(&b.build()); + .run_err(&recv_two_channel_ready_program()); let ExecuteError::Violation(Violation::InvalidChannelReady(id, reason)) = &err else { panic!("unexpected error: {err:?}"); }; - assert_eq!(*id, second_channel_id); + assert_eq!(*id, second_funding_channel_id()); assert!(reason.contains(&format!( "short_channel_id alias {alias} reuses the short_channel_id of channel {}", funding_channel_id(), diff --git a/smite-scenarios/src/executor/tests/harness.rs b/smite-scenarios/src/executor/tests/harness.rs index 6ef8fe6e..304ab311 100644 --- a/smite-scenarios/src/executor/tests/harness.rs +++ b/smite-scenarios/src/executor/tests/harness.rs @@ -507,3 +507,43 @@ pub fn sample_funding_negotiation() -> PendingChannel { funding_built: false, } } + +/// A [`sample_funding_negotiation`] with a different `temporary_channel_id` and +/// funding amount, so that it funds a different channel. +pub fn second_funding_negotiation() -> PendingChannel { + let second_id = TemporaryChannelId::new([0xcc; 32]); + let mut negotiation = sample_funding_negotiation(); + negotiation.open_channel.temporary_channel_id = second_id; + negotiation.open_channel.funding_satoshis = 20_000_000; + negotiation + .accept_channel + .as_mut() + .expect("accept_channel present") + .temporary_channel_id = second_id; + + negotiation +} + +/// The channel id [`second_funding_negotiation`]'s funding transaction +/// produces. +pub fn second_funding_channel_id() -> ChannelId { + ChannelId::v1_from_funding_outpoint(OutPoint { + txid: "cb6f11974ce6265f81037dc7c0e3f054462f645361bc1e10c208f132f4acbe8d" + .parse() + .expect("valid txid"), + vout: 0, + }) +} + +/// A fixture with both [`sample_funding_negotiation`] and +/// [`second_funding_negotiation`] seeded, and a UTXO to fund each. +pub fn two_funding_negotiations_fixture() -> Fixture { + let mut second_utxo = sample_utxo(); + second_utxo.amount = Amount::from_sat(20_010_000); + second_utxo.outpoint.vout = 1; + + Fixture::new() + .with_utxos(vec![sample_utxo(), second_utxo]) + .with_negotiation(sample_funding_negotiation()) + .with_negotiation(second_funding_negotiation()) +} diff --git a/smite-scenarios/src/executor/tests/programs.rs b/smite-scenarios/src/executor/tests/programs.rs index 362ab313..c4a5d45e 100644 --- a/smite-scenarios/src/executor/tests/programs.rs +++ b/smite-scenarios/src/executor/tests/programs.rs @@ -7,7 +7,10 @@ //! `raw_program` is an exception and doesn't use [`ProgramBuilder`] since its //! purpose is to create malformed program. -use super::harness::{PointSource, SampleOpenChannel, acceptor_funding_sk, opener_funding_sk}; +use super::harness::{ + PointSource, SampleOpenChannel, acceptor_funding_sk, opener_funding_sk, + second_funding_negotiation, +}; use crate::executor::*; use smite_ir::Instruction; use smite_ir::builder::ProgramBuilder; @@ -317,6 +320,28 @@ pub fn recv_channel_ready_program(confirmations: u8) -> Program { b.build() } +/// A program that sends `funding_created` for both negotiations of +/// `two_funding_negotiations_fixture` without awaiting `funding_signed`, mines +/// to their `minimum_depth`, and receives a `channel_ready` for each. +pub fn recv_two_channel_ready_program() -> Program { + let second = second_funding_negotiation().open_channel; + + let mut b = ProgramBuilder::new(); + send_funding_created(&mut b); + let tx = create_funding_tx_with(&mut b, second.funding_satoshis, FUNDING_FEERATE_PER_KW); + let temporary_channel_id = + b.append(Operation::LoadChannelId(second.temporary_channel_id.0), &[]); + b.append( + Operation::SendFundingCreated, + &[tx.tx, tx.opener_privkey, temporary_channel_id], + ); + b.append(Operation::MineBlocks(6), &[]); + b.append(Operation::RecvChannelReady, &[]); + b.append(Operation::RecvChannelReady, &[]); + + b.build() +} + // -- Gossip -- /// Emits a `channel_announcement` carrying the `ShortChannelId` variable