From 98ba1fb72c1ef5494934565c42ecf67887d70e3f Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Tue, 3 Jun 2025 15:11:57 -0400 Subject: [PATCH 1/4] ln/refactor: remove channel test fee estimator and clean up imports --- lightning/src/ln/channel.rs | 116 ++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index d68eac8e159..efb5638f23a 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -11784,15 +11784,14 @@ mod tests { use crate::ln::script::ShutdownScript; use crate::ln::chan_utils::{self, htlc_success_tx_weight, htlc_timeout_tx_weight}; use crate::chain::BestBlock; - use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator, ConfirmationTarget}; + use crate::chain::chaininterface::LowerBoundedFeeEstimator; use crate::sign::{ChannelSigner, InMemorySigner, EntropySource, SignerProvider}; use crate::chain::transaction::OutPoint; use crate::routing::router::{Path, RouteHop}; use crate::util::config::UserConfig; use crate::util::errors::APIError; use crate::util::ser::{ReadableArgs, Writeable}; - use crate::util::test_utils; - use crate::util::test_utils::{OnGetShutdownScriptpubkey, TestKeysInterface}; + use crate::util::test_utils::{self, OnGetShutdownScriptpubkey, TestKeysInterface, TestFeeEstimator, TestLogger}; use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature}; use bitcoin::secp256k1::ffi::Signature as FFISignature; use bitcoin::secp256k1::{SecretKey,PublicKey}; @@ -11816,15 +11815,6 @@ mod tests { assert!(ChannelState::ChannelReady(ChannelReadyFlags::new()) < ChannelState::ShutdownComplete); } - struct TestFeeEstimator { - fee_est: u32 - } - impl FeeEstimator for TestFeeEstimator { - fn get_est_sat_per_1000_weight(&self, _: ConfirmationTarget) -> u32 { - self.fee_est - } - } - #[test] fn test_max_funding_satoshis_no_wumbo() { assert_eq!(TOTAL_BITCOIN_SUPPLY_SATOSHIS, 21_000_000 * 100_000_000); @@ -11883,16 +11873,16 @@ mod tests { let seed = [42; 32]; let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + let keys_provider = TestKeysInterface::new(&seed, network); keys_provider.expect(OnGetShutdownScriptpubkey { returns: non_v0_segwit_shutdown_script.clone(), }); - let logger = test_utils::TestLogger::new(); + let logger = TestLogger::new(); let secp_ctx = Secp256k1::new(); let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - match OutboundV1Channel::<&TestKeysInterface>::new(&LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 253 }), &&keys_provider, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42, None, &logger) { + match OutboundV1Channel::<&TestKeysInterface>::new(&LowerBoundedFeeEstimator::new(&TestFeeEstimator::new(253)), &&keys_provider, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42, None, &logger) { Err(APIError::IncompatibleShutdownScript { script }) => { assert_eq!(script.into_inner(), non_v0_segwit_shutdown_script.into_inner()); }, @@ -11906,13 +11896,13 @@ mod tests { #[test] fn test_open_channel_msg_fee() { let original_fee = 253; - let mut fee_est = TestFeeEstimator{fee_est: original_fee }; + let fee_est = TestFeeEstimator::new(original_fee); let bounded_fee_estimator = LowerBoundedFeeEstimator::new(&fee_est); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); - let logger = test_utils::TestLogger::new(); + let keys_provider = TestKeysInterface::new(&seed, network); + let logger = TestLogger::new(); let node_a_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); @@ -11920,7 +11910,7 @@ mod tests { // Now change the fee so we can check that the fee in the open_channel message is the // same as the old fee. - fee_est.fee_est = 500; + *fee_est.sat_per_kw.lock().unwrap() = 500; let open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); assert_eq!(open_channel_msg.common_fields.commitment_feerate_sat_per_1000_weight, original_fee); } @@ -11929,12 +11919,13 @@ mod tests { fn test_holder_vs_counterparty_dust_limit() { // Test that when calculating the local and remote commitment transaction fees, the correct // dust limits are used. - let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); + let test_est = TestFeeEstimator::new(15000); + let feeest = LowerBoundedFeeEstimator::new(&test_est); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); - let logger = test_utils::TestLogger::new(); + let keys_provider = TestKeysInterface::new(&seed, network); + let logger = TestLogger::new(); let best_block = BestBlock::from_network(network); // Go through the flow of opening a channel between two nodes, making sure @@ -12020,12 +12011,13 @@ mod tests { // calculate the real dust limits for HTLCs (i.e. the dust limit given by the counterparty // *plus* the fees paid for the HTLC) they don't swap `HTLC_SUCCESS_TX_WEIGHT` for // `HTLC_TIMEOUT_TX_WEIGHT`, and vice versa. - let fee_est = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 253 }); + let test_est = TestFeeEstimator::new(253); + let fee_est = LowerBoundedFeeEstimator::new(&test_est); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); - let logger = test_utils::TestLogger::new(); + let keys_provider = TestKeysInterface::new(&seed, network); + let logger = TestLogger::new(); let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); @@ -12064,14 +12056,15 @@ mod tests { #[test] fn channel_reestablish_no_updates() { - let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); - let logger = test_utils::TestLogger::new(); + let test_est = TestFeeEstimator::new(15000); + let feeest = LowerBoundedFeeEstimator::new(&test_est); + let logger = TestLogger::new(); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; let best_block = BestBlock::from_network(network); let chain_hash = ChainHash::using_genesis_block(network); - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + let keys_provider = TestKeysInterface::new(&seed, network); // Go through the flow of opening a channel between two nodes. @@ -12121,12 +12114,13 @@ mod tests { #[test] fn test_configured_holder_max_htlc_value_in_flight() { - let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); - let logger = test_utils::TestLogger::new(); + let test_est = TestFeeEstimator::new(15000); + let feeest = LowerBoundedFeeEstimator::new(&test_est); + let logger = TestLogger::new(); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + let keys_provider = TestKeysInterface::new(&seed, network); let outbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let inbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); @@ -12215,12 +12209,13 @@ mod tests { } fn test_self_and_counterparty_channel_reserve(channel_value_satoshis: u64, outbound_selected_channel_reserve_perc: f64, inbound_selected_channel_reserve_perc: f64) { - let fee_est = LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 15_000 }); - let logger = test_utils::TestLogger::new(); + let test_est = TestFeeEstimator::new(15000); + let fee_est = LowerBoundedFeeEstimator::new(&test_est); + let logger = TestLogger::new(); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + let keys_provider = TestKeysInterface::new(&seed, network); let outbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let inbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); @@ -12252,14 +12247,15 @@ mod tests { #[test] fn channel_update() { - let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); - let logger = test_utils::TestLogger::new(); + let test_est = TestFeeEstimator::new(15000); + let feeest = LowerBoundedFeeEstimator::new(&test_est); + let logger = TestLogger::new(); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; let best_block = BestBlock::from_network(network); let chain_hash = ChainHash::using_genesis_block(network); - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + let keys_provider = TestKeysInterface::new(&seed, network); // Create Node A's channel pointing to Node B's pubkey let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); @@ -12329,13 +12325,14 @@ mod tests { fn blinding_point_skimmed_fee_malformed_ser() { // Ensure that channel blinding points, skimmed fees, and malformed HTLCs are (de)serialized // properly. - let logger = test_utils::TestLogger::new(); - let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); + let logger = TestLogger::new(); + let test_est = TestFeeEstimator::new(15000); + let feeest = LowerBoundedFeeEstimator::new(&test_est); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; let best_block = BestBlock::from_network(network); - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + let keys_provider = TestKeysInterface::new(&seed, network); let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); @@ -12474,8 +12471,8 @@ mod tests { use core::str::FromStr; // Test vectors from BOLT 3 Appendices C and F (anchors): - let feeest = TestFeeEstimator{fee_est: 15000}; - let logger : Arc = Arc::new(test_utils::TestLogger::new()); + let feeest = TestFeeEstimator::new(15000); + let logger : Arc = Arc::new(TestLogger::new()); let secp_ctx = Secp256k1::new(); let signer = InMemorySigner::new( @@ -13235,12 +13232,13 @@ mod tests { #[test] fn test_zero_conf_channel_type_support() { - let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); + let test_est = TestFeeEstimator::new(15000); + let feeest = LowerBoundedFeeEstimator::new(&test_est); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); - let logger = test_utils::TestLogger::new(); + let keys_provider = TestKeysInterface::new(&seed, network); + let logger = TestLogger::new(); let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); @@ -13264,10 +13262,11 @@ mod tests { // Tests that if both sides support and negotiate `anchors_zero_fee_htlc_tx`, it is the // resulting `channel_type`. let secp_ctx = Secp256k1::new(); - let fee_estimator = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); + let test_est = TestFeeEstimator::new(15000); + let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&[42; 32], network); - let logger = test_utils::TestLogger::new(); + let keys_provider = TestKeysInterface::new(&[42; 32], network); + let logger = TestLogger::new(); let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); @@ -13310,10 +13309,11 @@ mod tests { // Tests that if `option_anchors` is being negotiated implicitly through the intersection of // each side's `InitFeatures`, it is rejected. let secp_ctx = Secp256k1::new(); - let fee_estimator = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); + let test_est = TestFeeEstimator::new(15000); + let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&[42; 32], network); - let logger = test_utils::TestLogger::new(); + let keys_provider = TestKeysInterface::new(&[42; 32], network); + let logger = TestLogger::new(); let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); @@ -13351,10 +13351,11 @@ mod tests { // Tests that if `option_anchors` is being negotiated through the `channel_type` feature, // it is rejected. let secp_ctx = Secp256k1::new(); - let fee_estimator = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); + let test_est = TestFeeEstimator::new(15000); + let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); let network = Network::Testnet; - let keys_provider = test_utils::TestKeysInterface::new(&[42; 32], network); - let logger = test_utils::TestLogger::new(); + let keys_provider = TestKeysInterface::new(&[42; 32], network); + let logger = TestLogger::new(); let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); @@ -13417,14 +13418,15 @@ mod tests { #[test] fn test_waiting_for_batch() { - let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); - let logger = test_utils::TestLogger::new(); + let test_est = TestFeeEstimator::new(15000); + let feeest = LowerBoundedFeeEstimator::new(&test_est); + let logger = TestLogger::new(); let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; let best_block = BestBlock::from_network(network); let chain_hash = ChainHash::using_genesis_block(network); - let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + let keys_provider = TestKeysInterface::new(&seed, network); let mut config = UserConfig::default(); // Set trust_own_funding_0conf while ensuring we don't send channel_ready for a From 4fd7eb2df4a487790a28c7ae241edf0a1f5a476e Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Wed, 4 Jun 2025 09:44:02 -0400 Subject: [PATCH 2/4] ln/refactor: move channel type tests into own test file --- lightning/src/ln/channel.rs | 191 +----------------------- lightning/src/ln/channel_type_tests.rs | 199 +++++++++++++++++++++++++ lightning/src/ln/mod.rs | 3 + 3 files changed, 205 insertions(+), 188 deletions(-) create mode 100644 lightning/src/ln/channel_type_tests.rs diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index efb5638f23a..8049fe8a09e 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -11775,10 +11775,11 @@ mod tests { use crate::types::payment::{PaymentHash, PaymentPreimage}; use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint}; use crate::ln::channelmanager::{self, HTLCSource, PaymentId}; - use crate::ln::channel::InitFeatures; use crate::ln::channel::{AwaitingChannelReadyFlags, ChannelState, FundedChannel, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, HTLCUpdateAwaitingACK, commit_tx_fee_sat}; use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS}; - use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures}; + use crate::types::features::{ChannelFeatures, NodeFeatures}; + #[cfg(ldk_test_vectors)] + use crate::types::features::ChannelTypeFeatures; use crate::ln::msgs; use crate::ln::msgs::{ChannelUpdate, UnsignedChannelUpdate, MAX_VALUE_MSAT}; use crate::ln::script::ShutdownScript; @@ -13230,192 +13231,6 @@ mod tests { SecretKey::from_slice(&>::from_hex("d09ffff62ddb2297ab000cc85bcb4283fdeb6aa052affbc9dddcf33b61078110").unwrap()[..]).unwrap()); } - #[test] - fn test_zero_conf_channel_type_support() { - let test_est = TestFeeEstimator::new(15000); - let feeest = LowerBoundedFeeEstimator::new(&test_est); - let secp_ctx = Secp256k1::new(); - let seed = [42; 32]; - let network = Network::Testnet; - let keys_provider = TestKeysInterface::new(&seed, network); - let logger = TestLogger::new(); - - let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); - let config = UserConfig::default(); - let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, - node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, None, &logger).unwrap(); - - let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key(); - channel_type_features.set_zero_conf_required(); - - let mut open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); - open_channel_msg.common_fields.channel_type = Some(channel_type_features); - let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); - let res = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, - node_b_node_id, &channelmanager::provided_channel_type_features(&config), - &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false); - assert!(res.is_ok()); - } - - #[test] - fn test_supports_anchors_zero_htlc_tx_fee() { - // Tests that if both sides support and negotiate `anchors_zero_fee_htlc_tx`, it is the - // resulting `channel_type`. - let secp_ctx = Secp256k1::new(); - let test_est = TestFeeEstimator::new(15000); - let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); - let network = Network::Testnet; - let keys_provider = TestKeysInterface::new(&[42; 32], network); - let logger = TestLogger::new(); - - let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); - let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); - - let mut config = UserConfig::default(); - config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; - - // It is not enough for just the initiator to signal `option_anchors_zero_fee_htlc_tx`, both - // need to signal it. - let channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, - &channelmanager::provided_init_features(&UserConfig::default()), 10000000, 100000, 42, - &config, 0, 42, None, &logger - ).unwrap(); - assert!(!channel_a.funding.get_channel_type().supports_anchors_zero_fee_htlc_tx()); - - let mut expected_channel_type = ChannelTypeFeatures::empty(); - expected_channel_type.set_static_remote_key_required(); - expected_channel_type.set_anchors_zero_fee_htlc_tx_required(); - - let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, - &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, - None, &logger - ).unwrap(); - - let open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); - let channel_b = InboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, - &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), - &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false - ).unwrap(); - - assert_eq!(channel_a.funding.get_channel_type(), &expected_channel_type); - assert_eq!(channel_b.funding.get_channel_type(), &expected_channel_type); - } - - #[test] - fn test_rejects_implicit_simple_anchors() { - // Tests that if `option_anchors` is being negotiated implicitly through the intersection of - // each side's `InitFeatures`, it is rejected. - let secp_ctx = Secp256k1::new(); - let test_est = TestFeeEstimator::new(15000); - let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); - let network = Network::Testnet; - let keys_provider = TestKeysInterface::new(&[42; 32], network); - let logger = TestLogger::new(); - - let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); - let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); - - let config = UserConfig::default(); - - // See feature bit assignments: https://github.com/lightning/bolts/blob/master/09-features.md - let static_remote_key_required: u64 = 1 << 12; - let simple_anchors_required: u64 = 1 << 20; - let raw_init_features = static_remote_key_required | simple_anchors_required; - let init_features_with_simple_anchors = InitFeatures::from_le_bytes(raw_init_features.to_le_bytes().to_vec()); - - let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, - &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, - None, &logger - ).unwrap(); - - // Set `channel_type` to `None` to force the implicit feature negotiation. - let mut open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); - open_channel_msg.common_fields.channel_type = None; - - // Since A supports both `static_remote_key` and `option_anchors`, but B only accepts - // `static_remote_key`, it will fail the channel. - let channel_b = InboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, - &channelmanager::provided_channel_type_features(&config), &init_features_with_simple_anchors, - &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false - ); - assert!(channel_b.is_err()); - } - - #[test] - fn test_rejects_simple_anchors_channel_type() { - // Tests that if `option_anchors` is being negotiated through the `channel_type` feature, - // it is rejected. - let secp_ctx = Secp256k1::new(); - let test_est = TestFeeEstimator::new(15000); - let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); - let network = Network::Testnet; - let keys_provider = TestKeysInterface::new(&[42; 32], network); - let logger = TestLogger::new(); - - let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); - let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); - - let config = UserConfig::default(); - - // See feature bit assignments: https://github.com/lightning/bolts/blob/master/09-features.md - let static_remote_key_required: u64 = 1 << 12; - let simple_anchors_required: u64 = 1 << 20; - let simple_anchors_raw_features = static_remote_key_required | simple_anchors_required; - let simple_anchors_init = InitFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec()); - let simple_anchors_channel_type = ChannelTypeFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec()); - assert!(!simple_anchors_init.requires_unknown_bits()); - assert!(!simple_anchors_channel_type.requires_unknown_bits()); - - // First, we'll try to open a channel between A and B where A requests a channel type for - // the original `option_anchors` feature (non zero fee htlc tx). This should be rejected by - // B as it's not supported by LDK. - let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, - &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, - None, &logger - ).unwrap(); - - let mut open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); - open_channel_msg.common_fields.channel_type = Some(simple_anchors_channel_type.clone()); - - let res = InboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, - &channelmanager::provided_channel_type_features(&config), &simple_anchors_init, - &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false - ); - assert!(res.is_err()); - - // Then, we'll try to open another channel where A requests a channel type for - // `anchors_zero_fee_htlc_tx`. B is malicious and tries to downgrade the channel type to the - // original `option_anchors` feature, which should be rejected by A as it's not supported by - // LDK. - let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, &simple_anchors_init, - 10000000, 100000, 42, &config, 0, 42, None, &logger - ).unwrap(); - - let open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); - - let mut channel_b = InboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, - &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), - &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false - ).unwrap(); - - let mut accept_channel_msg = channel_b.get_accept_channel_message(&&logger).unwrap(); - accept_channel_msg.common_fields.channel_type = Some(simple_anchors_channel_type.clone()); - - let res = channel_a.accept_channel( - &accept_channel_msg, &config.channel_handshake_limits, &simple_anchors_init - ); - assert!(res.is_err()); - } - #[test] fn test_waiting_for_batch() { let test_est = TestFeeEstimator::new(15000); diff --git a/lightning/src/ln/channel_type_tests.rs b/lightning/src/ln/channel_type_tests.rs new file mode 100644 index 00000000000..f679b55c3da --- /dev/null +++ b/lightning/src/ln/channel_type_tests.rs @@ -0,0 +1,199 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +use bitcoin::constants::ChainHash; +use bitcoin::network::Network; +use lightning_types::features::{ChannelTypeFeatures, InitFeatures}; +use crate::ln::channel::{OutboundV1Channel, InboundV1Channel}; +use crate::chain::chaininterface::LowerBoundedFeeEstimator; +use bitcoin::secp256k1::{SecretKey, PublicKey}; +use crate::ln::channelmanager; +use crate::util::config::UserConfig; +use crate::util::test_utils::{TestFeeEstimator, TestKeysInterface, TestLogger}; +use bitcoin::secp256k1::Secp256k1; +use crate::prelude::*; + +#[test] +fn test_zero_conf_channel_type_support() { + let test_est = TestFeeEstimator::new(15000); + let feeest = LowerBoundedFeeEstimator::new(&test_est); + let secp_ctx = Secp256k1::new(); + let seed = [42; 32]; + let network = Network::Testnet; + let keys_provider = TestKeysInterface::new(&seed, network); + let logger = TestLogger::new(); + + let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); + let config = UserConfig::default(); + let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, + node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, None, &logger).unwrap(); + + let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key(); + channel_type_features.set_zero_conf_required(); + + let mut open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + open_channel_msg.common_fields.channel_type = Some(channel_type_features); + let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); + let res = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, + node_b_node_id, &channelmanager::provided_channel_type_features(&config), + &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false); + assert!(res.is_ok()); +} + +#[test] +fn test_supports_anchors_zero_htlc_tx_fee() { + // Tests that if both sides support and negotiate `anchors_zero_fee_htlc_tx`, it is the + // resulting `channel_type`. + let secp_ctx = Secp256k1::new(); + let test_est = TestFeeEstimator::new(15000); + let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); + let network = Network::Testnet; + let keys_provider = TestKeysInterface::new(&[42; 32], network); + let logger = TestLogger::new(); + + let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); + let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); + + let mut config = UserConfig::default(); + config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + + // It is not enough for just the initiator to signal `option_anchors_zero_fee_htlc_tx`, both + // need to signal it. + let channel_a = OutboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, + &channelmanager::provided_init_features(&UserConfig::default()), 10000000, 100000, 42, + &config, 0, 42, None, &logger + ).unwrap(); + assert!(!channel_a.funding.get_channel_type().supports_anchors_zero_fee_htlc_tx()); + + let mut expected_channel_type = ChannelTypeFeatures::empty(); + expected_channel_type.set_static_remote_key_required(); + expected_channel_type.set_anchors_zero_fee_htlc_tx_required(); + + let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, + &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, + None, &logger + ).unwrap(); + + let open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + let channel_b = InboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, + &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), + &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false + ).unwrap(); + + assert_eq!(channel_a.funding.get_channel_type(), &expected_channel_type); + assert_eq!(channel_b.funding.get_channel_type(), &expected_channel_type); +} + +#[test] +fn test_rejects_implicit_simple_anchors() { + // Tests that if `option_anchors` is being negotiated implicitly through the intersection of + // each side's `InitFeatures`, it is rejected. + let secp_ctx = Secp256k1::new(); + let test_est = TestFeeEstimator::new(15000); + let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); + let network = Network::Testnet; + let keys_provider = TestKeysInterface::new(&[42; 32], network); + let logger = TestLogger::new(); + + let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); + let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); + + let config = UserConfig::default(); + + // See feature bit assignments: https://github.com/lightning/bolts/blob/master/09-features.md + let static_remote_key_required: u64 = 1 << 12; + let simple_anchors_required: u64 = 1 << 20; + let raw_init_features = static_remote_key_required | simple_anchors_required; + let init_features_with_simple_anchors = InitFeatures::from_le_bytes(raw_init_features.to_le_bytes().to_vec()); + + let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, + &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, + None, &logger + ).unwrap(); + + // Set `channel_type` to `None` to force the implicit feature negotiation. + let mut open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + open_channel_msg.common_fields.channel_type = None; + + // Since A supports both `static_remote_key` and `option_anchors`, but B only accepts + // `static_remote_key`, it will fail the channel. + let channel_b = InboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, + &channelmanager::provided_channel_type_features(&config), &init_features_with_simple_anchors, + &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false + ); + assert!(channel_b.is_err()); +} + +#[test] +fn test_rejects_simple_anchors_channel_type() { + // Tests that if `option_anchors` is being negotiated through the `channel_type` feature, + // it is rejected. + let secp_ctx = Secp256k1::new(); + let test_est = TestFeeEstimator::new(15000); + let fee_estimator = LowerBoundedFeeEstimator::new(&test_est); + let network = Network::Testnet; + let keys_provider = TestKeysInterface::new(&[42; 32], network); + let logger = TestLogger::new(); + + let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); + let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); + + let config = UserConfig::default(); + + // See feature bit assignments: https://github.com/lightning/bolts/blob/master/09-features.md + let static_remote_key_required: u64 = 1 << 12; + let simple_anchors_required: u64 = 1 << 20; + let simple_anchors_raw_features = static_remote_key_required | simple_anchors_required; + let simple_anchors_init = InitFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec()); + let simple_anchors_channel_type = ChannelTypeFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec()); + assert!(!simple_anchors_init.requires_unknown_bits()); + assert!(!simple_anchors_channel_type.requires_unknown_bits()); + + // First, we'll try to open a channel between A and B where A requests a channel type for + // the original `option_anchors` feature (non zero fee htlc tx). This should be rejected by + // B as it's not supported by LDK. + let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, + &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, + None, &logger + ).unwrap(); + + let mut open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + open_channel_msg.common_fields.channel_type = Some(simple_anchors_channel_type.clone()); + + let res = InboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, + &channelmanager::provided_channel_type_features(&config), &simple_anchors_init, + &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false + ); + assert!(res.is_err()); + + // Then, we'll try to open another channel where A requests a channel type for + // `anchors_zero_fee_htlc_tx`. B is malicious and tries to downgrade the channel type to the + // original `option_anchors` feature, which should be rejected by A as it's not supported by + // LDK. + let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, &simple_anchors_init, + 10000000, 100000, 42, &config, 0, 42, None, &logger + ).unwrap(); + + let open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + + let mut channel_b = InboundV1Channel::<&TestKeysInterface>::new( + &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, + &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), + &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false + ).unwrap(); + + let mut accept_channel_msg = channel_b.get_accept_channel_message(&&logger).unwrap(); + accept_channel_msg.common_fields.channel_type = Some(simple_anchors_channel_type.clone()); + + let res = channel_a.accept_channel( + &accept_channel_msg, &config.channel_handshake_limits, &simple_anchors_init + ); + assert!(res.is_err()); +} diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index 8d741f2954d..997a3b02e1a 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -74,6 +74,9 @@ pub mod bolt11_payment_tests; mod chanmon_update_fail_tests; #[cfg(test)] #[allow(unused_mut)] +mod channel_type_tests; +#[cfg(test)] +#[allow(unused_mut)] mod dual_funding_tests; #[cfg(any(test, feature = "_externalize_tests"))] #[allow(unused_mut)] From 1c8291f668a1ac820fc0dca5ece45928ea954b90 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Tue, 3 Jun 2025 14:09:00 -0400 Subject: [PATCH 3/4] ln/format: run rust fmt on channel_type_tests --- lightning/src/ln/channel_type_tests.rs | 268 ++++++++++++++++++------- 1 file changed, 201 insertions(+), 67 deletions(-) diff --git a/lightning/src/ln/channel_type_tests.rs b/lightning/src/ln/channel_type_tests.rs index f679b55c3da..5253959850a 100644 --- a/lightning/src/ln/channel_type_tests.rs +++ b/lightning/src/ln/channel_type_tests.rs @@ -1,16 +1,13 @@ -#![cfg_attr(rustfmt, rustfmt_skip)] - -use bitcoin::constants::ChainHash; -use bitcoin::network::Network; -use lightning_types::features::{ChannelTypeFeatures, InitFeatures}; -use crate::ln::channel::{OutboundV1Channel, InboundV1Channel}; use crate::chain::chaininterface::LowerBoundedFeeEstimator; -use bitcoin::secp256k1::{SecretKey, PublicKey}; +use crate::ln::channel::{InboundV1Channel, OutboundV1Channel}; use crate::ln::channelmanager; +use crate::prelude::*; use crate::util::config::UserConfig; use crate::util::test_utils::{TestFeeEstimator, TestKeysInterface, TestLogger}; -use bitcoin::secp256k1::Secp256k1; -use crate::prelude::*; +use bitcoin::constants::ChainHash; +use bitcoin::network::Network; +use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use lightning_types::features::{ChannelTypeFeatures, InitFeatures}; #[test] fn test_zero_conf_channel_type_support() { @@ -22,20 +19,48 @@ fn test_zero_conf_channel_type_support() { let keys_provider = TestKeysInterface::new(&seed, network); let logger = TestLogger::new(); - let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); + let node_b_node_id = + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, - node_b_node_id, &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, None, &logger).unwrap(); + let mut node_a_chan = OutboundV1Channel::<&TestKeysInterface>::new( + &feeest, + &&keys_provider, + &&keys_provider, + node_b_node_id, + &channelmanager::provided_init_features(&config), + 10000000, + 100000, + 42, + &config, + 0, + 42, + None, + &logger, + ) + .unwrap(); let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key(); channel_type_features.set_zero_conf_required(); - let mut open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + let mut open_channel_msg = + node_a_chan.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); open_channel_msg.common_fields.channel_type = Some(channel_type_features); - let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); - let res = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, - node_b_node_id, &channelmanager::provided_channel_type_features(&config), - &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false); + let node_b_node_id = + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); + let res = InboundV1Channel::<&TestKeysInterface>::new( + &feeest, + &&keys_provider, + &&keys_provider, + node_b_node_id, + &channelmanager::provided_channel_type_features(&config), + &channelmanager::provided_init_features(&config), + &open_channel_msg, + 7, + &config, + 0, + &&logger, + /*is_0conf=*/ false, + ); assert!(res.is_ok()); } @@ -50,8 +75,10 @@ fn test_supports_anchors_zero_htlc_tx_fee() { let keys_provider = TestKeysInterface::new(&[42; 32], network); let logger = TestLogger::new(); - let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); - let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); + let node_id_a = + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); + let node_id_b = + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); let mut config = UserConfig::default(); config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; @@ -59,10 +86,21 @@ fn test_supports_anchors_zero_htlc_tx_fee() { // It is not enough for just the initiator to signal `option_anchors_zero_fee_htlc_tx`, both // need to signal it. let channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, - &channelmanager::provided_init_features(&UserConfig::default()), 10000000, 100000, 42, - &config, 0, 42, None, &logger - ).unwrap(); + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_b, + &channelmanager::provided_init_features(&UserConfig::default()), + 10000000, + 100000, + 42, + &config, + 0, + 42, + None, + &logger, + ) + .unwrap(); assert!(!channel_a.funding.get_channel_type().supports_anchors_zero_fee_htlc_tx()); let mut expected_channel_type = ChannelTypeFeatures::empty(); @@ -70,17 +108,39 @@ fn test_supports_anchors_zero_htlc_tx_fee() { expected_channel_type.set_anchors_zero_fee_htlc_tx_required(); let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, - &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, - None, &logger - ).unwrap(); - - let open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_b, + &channelmanager::provided_init_features(&config), + 10000000, + 100000, + 42, + &config, + 0, + 42, + None, + &logger, + ) + .unwrap(); + + let open_channel_msg = + channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); let channel_b = InboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, - &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), - &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false - ).unwrap(); + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_a, + &channelmanager::provided_channel_type_features(&config), + &channelmanager::provided_init_features(&config), + &open_channel_msg, + 7, + &config, + 0, + &&logger, + /*is_0conf=*/ false, + ) + .unwrap(); assert_eq!(channel_a.funding.get_channel_type(), &expected_channel_type); assert_eq!(channel_b.funding.get_channel_type(), &expected_channel_type); @@ -97,8 +157,10 @@ fn test_rejects_implicit_simple_anchors() { let keys_provider = TestKeysInterface::new(&[42; 32], network); let logger = TestLogger::new(); - let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); - let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); + let node_id_a = + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); + let node_id_b = + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); let config = UserConfig::default(); @@ -106,24 +168,46 @@ fn test_rejects_implicit_simple_anchors() { let static_remote_key_required: u64 = 1 << 12; let simple_anchors_required: u64 = 1 << 20; let raw_init_features = static_remote_key_required | simple_anchors_required; - let init_features_with_simple_anchors = InitFeatures::from_le_bytes(raw_init_features.to_le_bytes().to_vec()); + let init_features_with_simple_anchors = + InitFeatures::from_le_bytes(raw_init_features.to_le_bytes().to_vec()); let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, - &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, - None, &logger - ).unwrap(); + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_b, + &channelmanager::provided_init_features(&config), + 10000000, + 100000, + 42, + &config, + 0, + 42, + None, + &logger, + ) + .unwrap(); // Set `channel_type` to `None` to force the implicit feature negotiation. - let mut open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + let mut open_channel_msg = + channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); open_channel_msg.common_fields.channel_type = None; // Since A supports both `static_remote_key` and `option_anchors`, but B only accepts // `static_remote_key`, it will fail the channel. let channel_b = InboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, - &channelmanager::provided_channel_type_features(&config), &init_features_with_simple_anchors, - &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_a, + &channelmanager::provided_channel_type_features(&config), + &init_features_with_simple_anchors, + &open_channel_msg, + 7, + &config, + 0, + &&logger, + /*is_0conf=*/ false, ); assert!(channel_b.is_err()); } @@ -139,8 +223,10 @@ fn test_rejects_simple_anchors_channel_type() { let keys_provider = TestKeysInterface::new(&[42; 32], network); let logger = TestLogger::new(); - let node_id_a = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); - let node_id_b = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); + let node_id_a = + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[1; 32]).unwrap()); + let node_id_b = + PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[2; 32]).unwrap()); let config = UserConfig::default(); @@ -148,8 +234,10 @@ fn test_rejects_simple_anchors_channel_type() { let static_remote_key_required: u64 = 1 << 12; let simple_anchors_required: u64 = 1 << 20; let simple_anchors_raw_features = static_remote_key_required | simple_anchors_required; - let simple_anchors_init = InitFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec()); - let simple_anchors_channel_type = ChannelTypeFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec()); + let simple_anchors_init = + InitFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec()); + let simple_anchors_channel_type = + ChannelTypeFeatures::from_le_bytes(simple_anchors_raw_features.to_le_bytes().to_vec()); assert!(!simple_anchors_init.requires_unknown_bits()); assert!(!simple_anchors_channel_type.requires_unknown_bits()); @@ -157,18 +245,39 @@ fn test_rejects_simple_anchors_channel_type() { // the original `option_anchors` feature (non zero fee htlc tx). This should be rejected by // B as it's not supported by LDK. let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, - &channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42, - None, &logger - ).unwrap(); - - let mut open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_b, + &channelmanager::provided_init_features(&config), + 10000000, + 100000, + 42, + &config, + 0, + 42, + None, + &logger, + ) + .unwrap(); + + let mut open_channel_msg = + channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); open_channel_msg.common_fields.channel_type = Some(simple_anchors_channel_type.clone()); let res = InboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, - &channelmanager::provided_channel_type_features(&config), &simple_anchors_init, - &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_a, + &channelmanager::provided_channel_type_features(&config), + &simple_anchors_init, + &open_channel_msg, + 7, + &config, + 0, + &&logger, + /*is_0conf=*/ false, ); assert!(res.is_err()); @@ -177,23 +286,48 @@ fn test_rejects_simple_anchors_channel_type() { // original `option_anchors` feature, which should be rejected by A as it's not supported by // LDK. let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, &simple_anchors_init, - 10000000, 100000, 42, &config, 0, 42, None, &logger - ).unwrap(); - - let open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_b, + &simple_anchors_init, + 10000000, + 100000, + 42, + &config, + 0, + 42, + None, + &logger, + ) + .unwrap(); + + let open_channel_msg = + channel_a.get_open_channel(ChainHash::using_genesis_block(network), &&logger).unwrap(); let mut channel_b = InboundV1Channel::<&TestKeysInterface>::new( - &fee_estimator, &&keys_provider, &&keys_provider, node_id_a, - &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), - &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false - ).unwrap(); + &fee_estimator, + &&keys_provider, + &&keys_provider, + node_id_a, + &channelmanager::provided_channel_type_features(&config), + &channelmanager::provided_init_features(&config), + &open_channel_msg, + 7, + &config, + 0, + &&logger, + /*is_0conf=*/ false, + ) + .unwrap(); let mut accept_channel_msg = channel_b.get_accept_channel_message(&&logger).unwrap(); accept_channel_msg.common_fields.channel_type = Some(simple_anchors_channel_type.clone()); let res = channel_a.accept_channel( - &accept_channel_msg, &config.channel_handshake_limits, &simple_anchors_init + &accept_channel_msg, + &config.channel_handshake_limits, + &simple_anchors_init, ); assert!(res.is_err()); } From e7e0124466ea811907e950f586b6d195583ea1b6 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Tue, 3 Jun 2025 14:56:10 -0400 Subject: [PATCH 4/4] ln/test: add unit test for get_initial_channel_type Add straightforward unit tests for get_initial_channel_type, which will be expanded when we add zero_fee_commitment type. --- lightning/src/ln/channel.rs | 2 +- lightning/src/ln/channel_type_tests.rs | 62 +++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 8049fe8a09e..80695231d93 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -10647,7 +10647,7 @@ impl PendingV2Channel where SP::Target: SignerProvider { // Unfunded channel utilities -fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> ChannelTypeFeatures { +pub(crate) fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) -> ChannelTypeFeatures { // The default channel type (ie the first one we try) depends on whether the channel is // public - if it is, we just go with `only_static_remotekey` as it's the only option // available. If it's private, we first try `scid_privacy` as it provides better privacy diff --git a/lightning/src/ln/channel_type_tests.rs b/lightning/src/ln/channel_type_tests.rs index 5253959850a..e1c1891a787 100644 --- a/lightning/src/ln/channel_type_tests.rs +++ b/lightning/src/ln/channel_type_tests.rs @@ -1,5 +1,5 @@ use crate::chain::chaininterface::LowerBoundedFeeEstimator; -use crate::ln::channel::{InboundV1Channel, OutboundV1Channel}; +use crate::ln::channel::{get_initial_channel_type, InboundV1Channel, OutboundV1Channel}; use crate::ln::channelmanager; use crate::prelude::*; use crate::util::config::UserConfig; @@ -9,6 +9,66 @@ use bitcoin::network::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use lightning_types::features::{ChannelTypeFeatures, InitFeatures}; +#[test] +fn test_option_scid_privacy_initial() { + let mut expected_type = ChannelTypeFeatures::only_static_remote_key(); + expected_type.set_scid_privacy_required(); + + do_test_get_initial_channel_type( + UserConfig::default(), + InitFeatures::empty(), + ChannelTypeFeatures::only_static_remote_key(), + |cfg: &mut UserConfig| { + // announce_for_forwarding = false is required, but set by UserConfig::default(). + cfg.channel_handshake_config.negotiate_scid_privacy = true; + }, + |their_features: &mut InitFeatures| { + their_features.set_scid_privacy_optional(); + }, + expected_type, + ) +} + +#[test] +fn test_option_anchors_zero_fee_initial() { + let mut expected_type = ChannelTypeFeatures::only_static_remote_key(); + expected_type.set_anchors_zero_fee_htlc_tx_required(); + + do_test_get_initial_channel_type( + UserConfig::default(), + InitFeatures::empty(), + ChannelTypeFeatures::only_static_remote_key(), + |cfg: &mut UserConfig| { + cfg.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + }, + |their_features: &mut InitFeatures| { + their_features.set_anchors_zero_fee_htlc_tx_optional(); + }, + expected_type, + ) +} + +fn do_test_get_initial_channel_type( + start_cfg: UserConfig, start_features: InitFeatures, start_type: ChannelTypeFeatures, + mut local_cfg_mod: F1, mut remote_features_mod: F2, channel_type: ChannelTypeFeatures, +) where + F1: FnOnce(&mut UserConfig), + F2: FnOnce(&mut InitFeatures), +{ + // Local node supports feature, remote does not. + let mut config = start_cfg.clone(); + local_cfg_mod(&mut config); + assert_eq!(get_initial_channel_type(&config, &start_features), start_type); + + // Remote node supports feature, local does not. + let mut their_features = start_features.clone(); + remote_features_mod(&mut their_features); + assert_eq!(get_initial_channel_type(&start_cfg, &their_features), start_type); + + // Both support feature. + assert_eq!(get_initial_channel_type(&config, &their_features), channel_type) +} + #[test] fn test_zero_conf_channel_type_support() { let test_est = TestFeeEstimator::new(15000);