diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 621bae134cf..4277c81f12c 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -56,8 +56,13 @@ use crate::prelude::*; /// 483 for non-zero-fee-commitment channels and 114 for zero-fee-commitment channels. /// /// Actual maximums can be set equal to or below this value by each channel participant. -pub fn max_htlcs(_channel_type: &ChannelTypeFeatures) -> u16 { - 483 +pub fn max_htlcs(channel_type: &ChannelTypeFeatures) -> u16 { + if channel_type.supports_anchor_zero_fee_commitments() { + // TRUC restricts the size of our commitment transactions to 10K vB rather than 100K vB + 114 + } else { + 483 + } } /// The weight of a BIP141 witnessScript for a BOLT3's "offered HTLC output" on a commitment transaction, non-anchor variant. pub const OFFERED_HTLC_SCRIPT_WEIGHT: usize = 133; diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index bc4d38beeea..2c723e3e5bd 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -3036,12 +3036,18 @@ impl ChannelContext where SP::Target: SignerProvider { debug_assert!(!channel_type.supports_any_optional_bits()); debug_assert!(!channel_type.requires_unknown_bits_from(&channelmanager::provided_channel_type_features(&config))); - let (commitment_conf_target, anchor_outputs_value_msat) = if channel_type.supports_anchors_zero_fee_htlc_tx() { - (ConfirmationTarget::AnchorChannelFee, ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000) - } else { - (ConfirmationTarget::NonAnchorChannelFee, 0) - }; - let commitment_feerate = fee_estimator.bounded_sat_per_1000_weight(commitment_conf_target); + let (commitment_feerate, anchor_outputs_value_msat) = + if channel_type.supports_anchor_zero_fee_commitments() { + (0, 0) + } else if channel_type.supports_anchors_zero_fee_htlc_tx() { + let feerate = fee_estimator + .bounded_sat_per_1000_weight(ConfirmationTarget::AnchorChannelFee); + (feerate, ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000) + } else { + let feerate = fee_estimator + .bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee); + (feerate, 0) + }; let value_to_self_msat = channel_value_satoshis * 1000 - push_msat; let commitment_tx_fee = commit_tx_fee_sat(commitment_feerate, MIN_AFFORDABLE_HTLC_COUNT, &channel_type) * 1000; @@ -4860,7 +4866,16 @@ impl ChannelContext where SP::Target: SignerProvider { // counterparty is advertising the feature, but rejecting channels proposing the feature for // whatever reason. let channel_type = &mut funding.channel_transaction_parameters.channel_type_features; - if channel_type.supports_anchors_zero_fee_htlc_tx() { + if channel_type.supports_anchor_zero_fee_commitments() { + channel_type.clear_anchor_zero_fee_commitments(); + channel_type.set_anchors_zero_fee_htlc_tx_required(); + channel_type.set_static_remote_key_required(); + + self.feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::AnchorChannelFee); + assert!(!channel_type.supports_anchor_zero_fee_commitments()); + assert!(channel_type.supports_anchors_zero_fee_htlc_tx()); + assert!(channel_type.supports_static_remote_key()); + } else if channel_type.supports_anchors_zero_fee_htlc_tx() { channel_type.clear_anchors_zero_fee_htlc_tx(); self.feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee); assert!(!channel_type.supports_anchors_nonzero_fee_htlc_tx()); @@ -5223,6 +5238,15 @@ impl FundedChannel where feerate_per_kw: u32, cur_feerate_per_kw: Option, logger: &L ) -> Result<(), ChannelError> where F::Target: FeeEstimator, L::Target: Logger, { + if channel_type.supports_anchor_zero_fee_commitments() { + if feerate_per_kw != 0 { + let err = "Zero Fee Channels must never attempt to use a fee".to_owned(); + return Err(ChannelError::close(err)); + } else { + return Ok(()); + } + } + let lower_limit_conf_target = if channel_type.supports_anchors_zero_fee_htlc_tx() { ConfirmationTarget::MinAllowedAnchorChannelRemoteFee } else { @@ -9906,8 +9930,9 @@ pub(super) fn channel_type_from_open_channel( // We only support the channel types defined by the `ChannelManager` in // `provided_channel_type_features`. The channel type must always support - // `static_remote_key`. - if !channel_type.requires_static_remote_key() { + // `static_remote_key`, except for `option_zero_fee_commitments` which + // assumes this feature. + if !channel_type.requires_static_remote_key() && !channel_type.requires_anchor_zero_fee_commitments(){ return Err(ChannelError::close("Channel Type was not understood - we require static remote key".to_owned())); } // Make sure we support all of the features behind the channel type. @@ -10492,10 +10517,21 @@ fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures) ret.set_scid_privacy_required(); } - // Optionally, if the user would like to negotiate the `anchors_zero_fee_htlc_tx` option, we - // set it now. If they don't understand it, we'll fall back to our default of - // `only_static_remotekey`. - if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx && + // Optionally, if the user would like to negotiate `option_zero_fee_commitments` we set it now. + // If they don't understand it (or we don't want it), we check the same conditions for + // `option_anchors_zero_fee_htlc_tx`. The counterparty can still refuse the channel and we'll + // try to fall back (all the way to `only_static_remotekey`). + #[cfg(not(test))] + let negotiate_zero_fee_commitments = false; + + #[cfg(test)] + let negotiate_zero_fee_commitments = config.channel_handshake_config.negotiate_anchor_zero_fee_commitments; + + if negotiate_zero_fee_commitments && their_features.supports_anchor_zero_fee_commitments() { + ret.set_anchor_zero_fee_commitments_required(); + // `option_static_remote_key` is assumed by `option_zero_fee_commitments`. + ret.clear_static_remote_key(); + } else if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx && their_features.supports_anchors_zero_fee_htlc_tx() { ret.set_anchors_zero_fee_htlc_tx_required(); } @@ -13104,6 +13140,30 @@ mod tests { 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 mut config = UserConfig::default(); + config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + + 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(); + + do_test_supports_channel_type(config, expected_channel_type) + } + + #[test] + fn test_supports_zero_fee_commitments() { + // Tests that if both sides support and negotiate `anchors_zero_fee_commitments`, it is + // the resulting `channel_type`. + let mut config = UserConfig::default(); + config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = true; + + let mut expected_channel_type = ChannelTypeFeatures::empty(); + expected_channel_type.set_anchor_zero_fee_commitments_required(); + + do_test_supports_channel_type(config, expected_channel_type) + } + + fn do_test_supports_channel_type(config: UserConfig, expected_channel_type: ChannelTypeFeatures) { let secp_ctx = Secp256k1::new(); let fee_estimator = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000}); let network = Network::Testnet; @@ -13113,21 +13173,14 @@ mod tests { 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. + // Assert that we don't get the target channel type when the receiving node does not signal + // support. 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(); + assert!(channel_a.funding.get_channel_type() != &expected_channel_type); let mut channel_a = OutboundV1Channel::<&TestKeysInterface>::new( &fee_estimator, &&keys_provider, &&keys_provider, node_id_b, @@ -13144,6 +13197,14 @@ mod tests { assert_eq!(channel_a.funding.get_channel_type(), &expected_channel_type); assert_eq!(channel_b.funding.get_channel_type(), &expected_channel_type); + + if expected_channel_type.supports_anchor_zero_fee_commitments() { + assert_eq!(channel_a.context.feerate_per_kw, 0); + assert_eq!(channel_b.context.feerate_per_kw, 0); + } else { + assert_ne!(channel_a.context.feerate_per_kw, 0); + assert_ne!(channel_b.context.feerate_per_kw, 0); + } } #[test] diff --git a/lightning/src/ln/channel_acceptance_tests.rs b/lightning/src/ln/channel_acceptance_tests.rs new file mode 100644 index 00000000000..85d2f9731ad --- /dev/null +++ b/lightning/src/ln/channel_acceptance_tests.rs @@ -0,0 +1,189 @@ +//! Various unit tests covering acceptance of incoming channels and negotiation of channel types. + +use crate::events::Event; +use crate::ln::functional_test_utils::*; +use crate::ln::msgs::{ + AcceptChannel, BaseMessageHandler, ChannelMessageHandler, ErrorAction, MessageSendEvent, +}; +use crate::prelude::*; +use crate::util::config::{ChannelConfigOverrides, ChannelHandshakeConfigUpdate, UserConfig}; +use lightning_types::features::ChannelTypeFeatures; + +#[test] +fn test_inbound_anchors_manual_acceptance() { + let mut anchors_cfg = test_default_channel_config(); + anchors_cfg.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + do_test_manual_inbound_accept_with_override(anchors_cfg, None); +} + +#[test] +fn test_inbound_anchors_manual_acceptance_overridden() { + let overrides = ChannelConfigOverrides { + handshake_overrides: Some(ChannelHandshakeConfigUpdate { + max_inbound_htlc_value_in_flight_percent_of_channel: Some(5), + htlc_minimum_msat: Some(1000), + minimum_depth: Some(2), + to_self_delay: Some(200), + max_accepted_htlcs: Some(5), + channel_reserve_proportional_millionths: Some(20000), + }), + update_overrides: None, + }; + + let mut anchors_cfg = test_default_channel_config(); + anchors_cfg.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + + let accept_message = do_test_manual_inbound_accept_with_override(anchors_cfg, Some(overrides)); + assert_eq!(accept_message.common_fields.max_htlc_value_in_flight_msat, 5_000_000); + assert_eq!(accept_message.common_fields.htlc_minimum_msat, 1_000); + assert_eq!(accept_message.common_fields.minimum_depth, 2); + assert_eq!(accept_message.common_fields.to_self_delay, 200); + assert_eq!(accept_message.common_fields.max_accepted_htlcs, 5); + assert_eq!(accept_message.channel_reserve_satoshis, 2_000); +} + +#[test] +fn test_inbound_zero_fee_commitments_acceptance() { + let mut zero_fee_cfg = test_default_channel_config(); + zero_fee_cfg.channel_handshake_config.negotiate_anchor_zero_fee_commitments = true; + do_test_manual_inbound_accept_with_override(zero_fee_cfg, None); +} + +fn do_test_manual_inbound_accept_with_override( + start_cfg: UserConfig, config_overrides: Option, +) -> AcceptChannel { + let mut mannual_accept_cfg = start_cfg.clone(); + mannual_accept_cfg.manually_accept_inbound_channels = true; + + let chanmon_cfgs = create_chanmon_cfgs(3); + let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs( + 3, + &node_cfgs, + &[Some(start_cfg.clone()), Some(start_cfg.clone()), Some(mannual_accept_cfg.clone())], + ); + let nodes = create_network(3, &node_cfgs, &node_chanmgrs); + + nodes[0] + .node + .create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 42, None, None) + .unwrap(); + let open_channel_msg = get_event_msg!( + nodes[0], + MessageSendEvent::SendOpenChannel, + nodes[1].node.get_our_node_id() + ); + + nodes[1].node.handle_open_channel(nodes[0].node.get_our_node_id(), &open_channel_msg); + assert!(nodes[1].node.get_and_clear_pending_events().is_empty()); + let msg_events = nodes[1].node.get_and_clear_pending_msg_events(); + match &msg_events[0] { + MessageSendEvent::HandleError { node_id, action } => { + assert_eq!(*node_id, nodes[0].node.get_our_node_id()); + match action { + ErrorAction::SendErrorMessage { msg } => { + assert_eq!(msg.data, "No channels with anchor outputs accepted".to_owned()) + }, + _ => panic!("Unexpected error action"), + } + }, + _ => panic!("Unexpected event"), + } + + nodes[2].node.handle_open_channel(nodes[0].node.get_our_node_id(), &open_channel_msg); + let events = nodes[2].node.get_and_clear_pending_events(); + match events[0] { + Event::OpenChannelRequest { temporary_channel_id, .. } => nodes[2] + .node + .accept_inbound_channel( + &temporary_channel_id, + &nodes[0].node.get_our_node_id(), + 23, + config_overrides, + ) + .unwrap(), + _ => panic!("Unexpected event"), + } + get_event_msg!(nodes[2], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()) +} + +#[test] +fn test_anchors_zero_fee_htlc_tx_downgrade() { + // Tests that if both nodes support anchors, but the remote node does not want to accept + // anchor channels at the moment, an error it sent to the local node such that it can retry + // the channel without the anchors feature. + let mut anchors_config = test_default_channel_config(); + anchors_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + anchors_config.manually_accept_inbound_channels = true; + + do_test_channel_type_downgrade(anchors_config, |features| { + features.supports_anchors_zero_fee_htlc_tx() + }) +} + +#[test] +fn test_zero_fee_commitments_downgrade() { + // Tests that the local node will retry without zero fee commitments in the case where the + // remote node supports the feature but does not accept it. + let mut zero_fee_config = test_default_channel_config(); + zero_fee_config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = true; + zero_fee_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + zero_fee_config.manually_accept_inbound_channels = true; + + do_test_channel_type_downgrade(zero_fee_config, |features| { + features.supports_anchor_zero_fee_commitments() + }) +} + +fn do_test_channel_type_downgrade(user_cfg: UserConfig, start_type_set: F) +where + F: Fn(&ChannelTypeFeatures) -> bool, +{ + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = + create_node_chanmgrs(2, &node_cfgs, &[Some(user_cfg.clone()), Some(user_cfg.clone())]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + let error_message = "Channel force-closed"; + + nodes[0] + .node + .create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 0, None, None) + .unwrap(); + let open_channel_msg = get_event_msg!( + nodes[0], + MessageSendEvent::SendOpenChannel, + nodes[1].node.get_our_node_id() + ); + assert!(start_type_set(open_channel_msg.common_fields.channel_type.as_ref().unwrap())); + + nodes[1].node.handle_open_channel(nodes[0].node.get_our_node_id(), &open_channel_msg); + let events = nodes[1].node.get_and_clear_pending_events(); + match events[0] { + Event::OpenChannelRequest { temporary_channel_id, .. } => { + nodes[1] + .node + .force_close_broadcasting_latest_txn( + &temporary_channel_id, + &nodes[0].node.get_our_node_id(), + error_message.to_string(), + ) + .unwrap(); + }, + _ => panic!("Unexpected event"), + } + + let error_msg = get_err_msg(&nodes[1], &nodes[0].node.get_our_node_id()); + nodes[0].node.handle_error(nodes[1].node.get_our_node_id(), &error_msg); + + let open_channel_msg = get_event_msg!( + nodes[0], + MessageSendEvent::SendOpenChannel, + nodes[1].node.get_our_node_id() + ); + assert!(!start_type_set(open_channel_msg.common_fields.channel_type.as_ref().unwrap())); + + // Since nodes[1] should not have accepted the channel, it should + // not have generated any events. + assert!(nodes[1].node.get_and_clear_pending_events().is_empty()); +} diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 170d8261d5a..2d3676be2cc 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -8175,7 +8175,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ if channel_type.requires_zero_conf() { return Err(MsgHandleErrInternal::send_err_msg_no_close("No zero confirmation channels accepted".to_owned(), common_fields.temporary_channel_id)); } - if channel_type.requires_anchors_zero_fee_htlc_tx() { + if channel_type.requires_anchors_zero_fee_htlc_tx() || channel_type.requires_anchor_zero_fee_commitments() { return Err(MsgHandleErrInternal::send_err_msg_no_close("No channels with anchor outputs accepted".to_owned(), common_fields.temporary_channel_id)); } @@ -12931,6 +12931,14 @@ pub fn provided_init_features(config: &UserConfig) -> InitFeatures { // quiescent-dependent protocols (e.g., splicing). #[cfg(any(test, fuzzing))] features.set_quiescence_optional(); + + #[cfg(test)] + { + if config.channel_handshake_config.negotiate_anchor_zero_fee_commitments { + features.set_anchor_zero_fee_commitments_optional(); + } + } + features } @@ -15026,6 +15034,7 @@ where mod tests { use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use bitcoin::secp256k1::ecdh::SharedSecret; + use lightning_types::features::ChannelTypeFeatures; use core::sync::atomic::Ordering; use crate::events::{Event, HTLCHandlingFailureType, ClosureReason}; use crate::ln::onion_utils::AttributionData; @@ -15041,7 +15050,7 @@ mod tests { use crate::util::errors::APIError; use crate::util::ser::Writeable; use crate::util::test_utils; - use crate::util::config::{ChannelConfig, ChannelConfigUpdate, ChannelHandshakeConfigUpdate}; + use crate::util::config::{ChannelConfig, ChannelConfigUpdate, ChannelHandshakeConfigUpdate, UserConfig}; use crate::sign::EntropySource; #[test] @@ -16083,7 +16092,9 @@ mod tests { #[test] fn test_inbound_anchors_manual_acceptance() { - test_inbound_anchors_manual_acceptance_with_override(None); + let mut anchors_cfg = test_default_channel_config(); + anchors_cfg.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + do_test_manual_inbound_accept_with_override(anchors_cfg, None); } #[test] @@ -16100,7 +16111,10 @@ mod tests { update_overrides: None, }; - let accept_message = test_inbound_anchors_manual_acceptance_with_override(Some(overrides)); + let mut anchors_cfg = test_default_channel_config(); + anchors_cfg.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + + let accept_message = do_test_manual_inbound_accept_with_override(anchors_cfg, Some(overrides)); assert_eq!(accept_message.common_fields.max_htlc_value_in_flight_msat, 5_000_000); assert_eq!(accept_message.common_fields.htlc_minimum_msat, 1_000); assert_eq!(accept_message.common_fields.minimum_depth, 2); @@ -16109,19 +16123,23 @@ mod tests { assert_eq!(accept_message.channel_reserve_satoshis, 2_000); } - fn test_inbound_anchors_manual_acceptance_with_override(config_overrides: Option) -> AcceptChannel { - // Tests that we properly limit inbound channels when we have the manual-channel-acceptance - // flag set and (sometimes) accept channels as 0conf. - let mut anchors_cfg = test_default_channel_config(); - anchors_cfg.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + #[test] + fn test_inbound_zero_fee_commitments_acceptance() { + let mut zero_fee_cfg = test_default_channel_config(); + zero_fee_cfg.channel_handshake_config.negotiate_anchor_zero_fee_commitments = true; + do_test_manual_inbound_accept_with_override(zero_fee_cfg, None); + } - let mut anchors_manual_accept_cfg = anchors_cfg.clone(); - anchors_manual_accept_cfg.manually_accept_inbound_channels = true; + fn do_test_manual_inbound_accept_with_override(start_cfg: UserConfig, + config_overrides: Option) -> AcceptChannel { + + let mut mannual_accept_cfg = start_cfg.clone(); + mannual_accept_cfg.manually_accept_inbound_channels = true; let chanmon_cfgs = create_chanmon_cfgs(3); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, - &[Some(anchors_cfg.clone()), Some(anchors_cfg.clone()), Some(anchors_manual_accept_cfg.clone())]); + &[Some(start_cfg.clone()), Some(start_cfg.clone()), Some(mannual_accept_cfg.clone())]); let nodes = create_network(3, &node_cfgs, &node_chanmgrs); nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 42, None, None).unwrap(); @@ -16153,22 +16171,40 @@ mod tests { } #[test] - fn test_anchors_zero_fee_htlc_tx_fallback() { + fn test_anchors_zero_fee_htlc_tx_downgrade() { // Tests that if both nodes support anchors, but the remote node does not want to accept // anchor channels at the moment, an error it sent to the local node such that it can retry // the channel without the anchors feature. - let chanmon_cfgs = create_chanmon_cfgs(2); - let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); let mut anchors_config = test_default_channel_config(); anchors_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; anchors_config.manually_accept_inbound_channels = true; - let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(anchors_config.clone()), Some(anchors_config.clone())]); + + do_test_channel_type_downgrade(anchors_config, |features| features.supports_anchors_zero_fee_htlc_tx()) + } + + #[test] + fn test_zero_fee_commitments_downgrade() { + // Tests that the local node will retry without zero fee commitments in the case where the + // remote node supports the feature but does not accept it. + let mut zero_fee_config = test_default_channel_config(); + zero_fee_config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = true; + zero_fee_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + zero_fee_config.manually_accept_inbound_channels = true; + + do_test_channel_type_downgrade(zero_fee_config, |features| features.supports_anchor_zero_fee_commitments()) + } + + fn do_test_channel_type_downgrade(user_cfg: UserConfig, start_type_set: F) + where F: Fn(&ChannelTypeFeatures) -> bool { + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(user_cfg.clone()), Some(user_cfg.clone())]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); let error_message = "Channel force-closed"; nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 0, None, None).unwrap(); let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id()); - assert!(open_channel_msg.common_fields.channel_type.as_ref().unwrap().supports_anchors_zero_fee_htlc_tx()); + assert!(start_type_set(open_channel_msg.common_fields.channel_type.as_ref().unwrap())); nodes[1].node.handle_open_channel(nodes[0].node.get_our_node_id(), &open_channel_msg); let events = nodes[1].node.get_and_clear_pending_events(); @@ -16183,7 +16219,7 @@ mod tests { nodes[0].node.handle_error(nodes[1].node.get_our_node_id(), &error_msg); let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id()); - assert!(!open_channel_msg.common_fields.channel_type.unwrap().supports_anchors_zero_fee_htlc_tx()); + assert!(!start_type_set(open_channel_msg.common_fields.channel_type.as_ref().unwrap())); // Since nodes[1] should not have accepted the channel, it should // not have generated any events. diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 0c0bb0713cb..038bd1da431 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -256,6 +256,66 @@ pub fn test_insane_channel_opens() { }); } +#[test] +fn test_insane_zero_fee_channel_open() { + let mut cfg = UserConfig::default(); + cfg.manually_accept_inbound_channels = true; + cfg.channel_handshake_config.negotiate_anchor_zero_fee_commitments = true; + + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = + create_node_chanmgrs(2, &node_cfgs, &[Some(cfg.clone()), Some(cfg.clone())]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let node_a_id = nodes[0].node.get_our_node_id(); + let node_b_id = nodes[1].node.get_our_node_id(); + + nodes[0].node.create_channel(node_b_id, 100_000, 0, 42, None, None).unwrap(); + + let open_channel_message = + get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, node_b_id); + + let insane_open_helper = + |expected_error_str: &str, message_mutator: fn(msgs::OpenChannel) -> msgs::OpenChannel| { + let open_channel_mutated = message_mutator(open_channel_message.clone()); + nodes[1].node.handle_open_channel(node_a_id, &open_channel_mutated); + + let events = nodes[1].node.get_and_clear_pending_events(); + match events[0] { + Event::OpenChannelRequest { temporary_channel_id, .. } => { + match nodes[1].node.accept_inbound_channel( + &temporary_channel_id, + &nodes[0].node.get_our_node_id(), + 23, + None, + ) { + Ok(_) => panic!("Unexpected successful channel accept"), + Err(e) => assert!(format!("{:?}", e).contains(expected_error_str)), + } + }, + _ => panic!("Unexpected event"), + } + + let events = nodes[1].node.get_and_clear_pending_msg_events(); + assert_eq!(events.len(), 1); + assert!(matches!(events[0], MessageSendEvent::HandleError { .. })); + }; + + insane_open_helper( + "max_accepted_htlcs was 115. It must not be larger than 114".into(), + |mut msg| { + msg.common_fields.max_accepted_htlcs = 115; + msg + }, + ); + + insane_open_helper("Zero Fee Channels must never attempt to use a fee".into(), |mut msg| { + msg.common_fields.commitment_feerate_sat_per_1000_weight = 123; + msg + }); +} + #[xtest(feature = "_externalize_tests")] pub fn test_funding_exceeds_no_wumbo_limit() { // Test that if a peer does not support wumbo channels, we'll refuse to open a wumbo channel to diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index 7b4795962b5..4ca38f5776e 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -116,5 +116,8 @@ mod offers_tests; #[cfg(test)] #[allow(unused_mut)] mod dual_funding_tests; +#[cfg(test)] +#[allow(unused_mut)] +mod channel_acceptance_tests; pub use self::peer_channel_encryptor::LN_MAX_MSG_LEN; diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs index 152a6a62eb6..96dd3ebee89 100644 --- a/lightning/src/util/config.rs +++ b/lightning/src/util/config.rs @@ -183,6 +183,41 @@ pub struct ChannelHandshakeConfig { /// [`DecodeError::InvalidValue`]: crate::ln::msgs::DecodeError::InvalidValue pub negotiate_anchors_zero_fee_htlc_tx: bool, + /// If set, we attempt to negotiate the `zero_fee_commitments` option for all future channels. + /// + /// These channels operate very similarly to the `anchors_zero_fee_htlc` channels but rely on + /// [TRUC] to assign zero fee to the commitment transactions themselves, avoiding many protocol + /// edge-cases involving fee updates and greatly simplifying the concept of your "balance" in + /// lightning. + /// + /// Like `anchors_zero_fee_htlc` channels, this feature requires having a reserve of onchain + /// funds readily available to bump transactions in the event of a channel force close to avoid + /// the possibility of losing funds. + /// + /// Note that if you wish accept inbound channels with anchor outputs, you must enable + /// [`UserConfig::manually_accept_inbound_channels`] and manually accept them with + /// [`ChannelManager::accept_inbound_channel`]. This is done to give you the chance to check + /// whether your reserve of onchain funds is enough to cover the fees for all existing and new + /// channels featuring anchor outputs in the event of a force close. + /// + /// If this option is set, channels may be created that will not be readable by LDK versions + /// prior to 0.2, causing [`ChannelManager`]'s read method to return a + /// [`DecodeError::InvalidValue`]. + /// + /// Note that setting this to true does *not* prevent us from opening channels with + /// counterparties that do not support the `zero_fee_commitments` option; we will simply fall + /// back to a `anchors_zero_fee_htlc` (if [`Self::negotiate_anchors_zero_fee_htlc_tx`] + /// is set) or `static_remote_key` channel. + /// + /// Default value: `false` (This value is likely to change to `true` in the future.) + /// + /// [TRUC]: (https://bitcoinops.org/en/topics/version-3-transaction-relay/) + /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager + /// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel + /// [`DecodeError::InvalidValue`]: crate::ln::msgs::DecodeError::InvalidValue + #[cfg(test)] + pub negotiate_anchor_zero_fee_commitments: bool, + /// The maximum number of HTLCs in-flight from our counterparty towards us at the same time. /// /// Increasing the value can help improve liquidity and stability in @@ -212,6 +247,8 @@ impl Default for ChannelHandshakeConfig { commit_upfront_shutdown_pubkey: true, their_channel_reserve_proportional_millionths: 10_000, negotiate_anchors_zero_fee_htlc_tx: false, + #[cfg(test)] + negotiate_anchor_zero_fee_commitments: false, our_max_accepted_htlcs: 50, } } @@ -233,6 +270,8 @@ impl Readable for ChannelHandshakeConfig { commit_upfront_shutdown_pubkey: Readable::read(reader)?, their_channel_reserve_proportional_millionths: Readable::read(reader)?, negotiate_anchors_zero_fee_htlc_tx: Readable::read(reader)?, + #[cfg(test)] + negotiate_anchor_zero_fee_commitments: Readable::read(reader)?, our_max_accepted_htlcs: Readable::read(reader)?, }) }