Skip to content

rustfmt: move channel acceptance tests into own file and format #3797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
9 changes: 7 additions & 2 deletions lightning/src/ln/chan_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
107 changes: 84 additions & 23 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3036,12 +3036,18 @@ impl<SP: Deref> ChannelContext<SP> 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;
Expand Down Expand Up @@ -4860,7 +4866,16 @@ impl<SP: Deref> ChannelContext<SP> 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());
Expand Down Expand Up @@ -5223,6 +5238,15 @@ impl<SP: Deref> FundedChannel<SP> where
feerate_per_kw: u32, cur_feerate_per_kw: Option<u32>, 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 {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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]
Expand Down
189 changes: 189 additions & 0 deletions lightning/src/ln/channel_acceptance_tests.rs
Original file line number Diff line number Diff line change
@@ -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<ChannelConfigOverrides>,
) -> 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<F>(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());
}
Loading
Loading