Skip to content

Commit 6b14dab

Browse files
committed
Add test_0reserve_splice
1 parent 2b38052 commit 6b14dab

File tree

2 files changed

+202
-2
lines changed

2 files changed

+202
-2
lines changed

lightning/src/ln/htlc_reserve_unit_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2581,7 +2581,7 @@ fn test_0reserve_no_outputs() {
25812581
do_test_0reserve_no_outputs_p2a_anchor();
25822582
}
25832583

2584-
fn setup_0reserve_no_outputs_channels<'a, 'b, 'c, 'd>(
2584+
pub(crate) fn setup_0reserve_no_outputs_channels<'a, 'b, 'c, 'd>(
25852585
nodes: &'a Vec<Node<'b, 'c, 'd>>, channel_value_sat: u64, dust_limit_satoshis: u64,
25862586
) -> (ChannelId, Transaction) {
25872587
let node_a_id = nodes[0].node.get_our_node_id();

lightning/src/ln/splicing_tests.rs

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use crate::chain::ChannelMonitorUpdateStatus;
1616
use crate::events::{ClosureReason, Event, FundingInfo, HTLCHandlingFailureType};
1717
use crate::ln::chan_utils;
1818
use crate::ln::channel::{
19-
CHANNEL_ANNOUNCEMENT_PROPAGATION_DELAY, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE,
19+
ANCHOR_OUTPUT_VALUE_SATOSHI, CHANNEL_ANNOUNCEMENT_PROPAGATION_DELAY,
20+
FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE,
2021
};
2122
use crate::ln::channelmanager::{provided_init_features, PaymentId, BREAKDOWN_TIMEOUT};
2223
use crate::ln::functional_test_utils::*;
@@ -6647,3 +6648,202 @@ fn test_splice_rbf_rejects_own_low_feerate_after_several_attempts() {
66476648
other => panic!("Expected SpliceFailed, got {:?}", other),
66486649
}
66496650
}
6651+
6652+
#[test]
6653+
fn test_0reserve_splice() {
6654+
let mut config = test_default_channel_config();
6655+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false;
6656+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
6657+
let a = do_test_0reserve_splice_holder_validation(false, config.clone());
6658+
let b = do_test_0reserve_splice_holder_validation(true, config.clone());
6659+
assert_eq!(a, b);
6660+
assert_eq!(a, ChannelTypeFeatures::only_static_remote_key());
6661+
6662+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
6663+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
6664+
let a = do_test_0reserve_splice_holder_validation(false, config.clone());
6665+
let b = do_test_0reserve_splice_holder_validation(true, config.clone());
6666+
assert_eq!(a, b);
6667+
assert_eq!(a, ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies());
6668+
6669+
let mut config = test_default_channel_config();
6670+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false;
6671+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
6672+
let a = do_test_0reserve_splice_counterparty_validation(false, config.clone());
6673+
let b = do_test_0reserve_splice_counterparty_validation(true, config.clone());
6674+
assert_eq!(a, b);
6675+
assert_eq!(a, ChannelTypeFeatures::only_static_remote_key());
6676+
6677+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
6678+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
6679+
let a = do_test_0reserve_splice_counterparty_validation(false, config.clone());
6680+
let b = do_test_0reserve_splice_counterparty_validation(true, config.clone());
6681+
assert_eq!(a, b);
6682+
assert_eq!(a, ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies());
6683+
6684+
// TODO: Skip 0FC channels for now as these always have an output on the commitment, the P2A
6685+
// output. We will be able to withdraw up to the dust limit of the funding script, which
6686+
// is checked in interactivetx. Still need to double check whether that's what we actually
6687+
// want.
6688+
}
6689+
6690+
#[cfg(test)]
6691+
fn do_test_0reserve_splice_holder_validation(
6692+
splice_passes: bool, config: UserConfig,
6693+
) -> ChannelTypeFeatures {
6694+
use crate::ln::htlc_reserve_unit_tests::setup_0reserve_no_outputs_channels;
6695+
6696+
let chanmon_cfgs = create_chanmon_cfgs(2);
6697+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
6698+
let node_chanmgrs =
6699+
create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config.clone())]);
6700+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
6701+
6702+
let _node_id_0 = nodes[0].node.get_our_node_id();
6703+
let _node_id_1 = nodes[1].node.get_our_node_id();
6704+
6705+
let channel_value_sat = 100_000;
6706+
// Some dust limit, does not matter
6707+
let dust_limit_satoshis = 546;
6708+
6709+
let (channel_id, _tx) =
6710+
setup_0reserve_no_outputs_channels(&nodes, channel_value_sat, dust_limit_satoshis);
6711+
let details = &nodes[0].node.list_channels()[0];
6712+
let channel_type = details.channel_type.clone().unwrap();
6713+
6714+
let feerate = if channel_type == ChannelTypeFeatures::only_static_remote_key() {
6715+
253 * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32
6716+
} else if channel_type == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
6717+
253
6718+
} else {
6719+
panic!("Unexpected channel type");
6720+
};
6721+
let commit_tx_fee_sat = chan_utils::commit_tx_fee_sat(feerate, 0, &channel_type);
6722+
let anchors_sat =
6723+
if channel_type == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
6724+
ANCHOR_OUTPUT_VALUE_SATOSHI * 2
6725+
} else {
6726+
0
6727+
};
6728+
6729+
let estimated_fees = 183;
6730+
let splice_out_max_value = Amount::from_sat(
6731+
channel_value_sat - commit_tx_fee_sat - anchors_sat - estimated_fees - dust_limit_satoshis,
6732+
);
6733+
let outputs = vec![TxOut {
6734+
value: splice_out_max_value + if splice_passes { Amount::ZERO } else { Amount::ONE_SAT },
6735+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
6736+
}];
6737+
6738+
if splice_passes {
6739+
let contribution = initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).unwrap();
6740+
6741+
let (splice_tx, _) = splice_channel(&nodes[0], &nodes[1], channel_id, contribution);
6742+
mine_transaction(&nodes[0], &splice_tx);
6743+
mine_transaction(&nodes[1], &splice_tx);
6744+
lock_splice_after_blocks(&nodes[0], &nodes[1], ANTI_REORG_DELAY - 1);
6745+
} else {
6746+
assert!(initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).is_err());
6747+
let splice_out_value =
6748+
splice_out_max_value + Amount::from_sat(estimated_fees) + Amount::ONE_SAT;
6749+
let splice_out_max_value = splice_out_max_value + Amount::from_sat(estimated_fees);
6750+
let cannot_be_funded = format!(
6751+
"Channel {channel_id} cannot be funded: Our \
6752+
splice-out value of {splice_out_value} is greater than the maximum \
6753+
{splice_out_max_value}"
6754+
);
6755+
nodes[0].logger.assert_log("lightning::ln::channel", cannot_be_funded, 1);
6756+
}
6757+
6758+
channel_type
6759+
}
6760+
6761+
#[cfg(test)]
6762+
fn do_test_0reserve_splice_counterparty_validation(
6763+
splice_passes: bool, config: UserConfig,
6764+
) -> ChannelTypeFeatures {
6765+
use crate::ln::htlc_reserve_unit_tests::setup_0reserve_no_outputs_channels;
6766+
6767+
let chanmon_cfgs = create_chanmon_cfgs(2);
6768+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
6769+
let node_chanmgrs =
6770+
create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config.clone())]);
6771+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
6772+
6773+
let _node_id_0 = nodes[0].node.get_our_node_id();
6774+
let _node_id_1 = nodes[1].node.get_our_node_id();
6775+
6776+
let channel_value_sat = 100_000;
6777+
// Some dust limit, does not matter
6778+
let dust_limit_satoshis = 546;
6779+
6780+
let (channel_id, _tx) =
6781+
setup_0reserve_no_outputs_channels(&nodes, channel_value_sat, dust_limit_satoshis);
6782+
let details = &nodes[0].node.list_channels()[0];
6783+
let channel_type = details.channel_type.clone().unwrap();
6784+
6785+
let feerate = if channel_type == ChannelTypeFeatures::only_static_remote_key() {
6786+
253 * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32
6787+
} else if channel_type == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
6788+
253
6789+
} else {
6790+
panic!("Unexpected channel type");
6791+
};
6792+
let commit_tx_fee_sat = chan_utils::commit_tx_fee_sat(feerate, 0, &channel_type);
6793+
let anchors_sat =
6794+
if channel_type == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
6795+
ANCHOR_OUTPUT_VALUE_SATOSHI * 2
6796+
} else {
6797+
0
6798+
};
6799+
6800+
let splice_out_value_incl_fees =
6801+
Amount::from_sat(channel_value_sat - commit_tx_fee_sat - anchors_sat - dust_limit_satoshis);
6802+
6803+
let funding_contribution_sat =
6804+
-(splice_out_value_incl_fees.to_sat() as i64) - if splice_passes { 0 } else { 1 };
6805+
let outputs = vec![TxOut {
6806+
// Splice out some dummy amount to get past the initiator's validation,
6807+
// we'll modify the message in-flight.
6808+
value: Amount::from_sat(1_000),
6809+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
6810+
}];
6811+
let _contribution = initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).unwrap();
6812+
6813+
let initiator = &nodes[0];
6814+
let acceptor = &nodes[1];
6815+
let node_id_initiator = initiator.node.get_our_node_id();
6816+
let node_id_acceptor = acceptor.node.get_our_node_id();
6817+
6818+
let stfu_init = get_event_msg!(initiator, MessageSendEvent::SendStfu, node_id_acceptor);
6819+
acceptor.node.handle_stfu(node_id_initiator, &stfu_init);
6820+
let stfu_ack = get_event_msg!(acceptor, MessageSendEvent::SendStfu, node_id_initiator);
6821+
initiator.node.handle_stfu(node_id_acceptor, &stfu_ack);
6822+
6823+
let mut splice_init =
6824+
get_event_msg!(initiator, MessageSendEvent::SendSpliceInit, node_id_acceptor);
6825+
// Make the modification here
6826+
splice_init.funding_contribution_satoshis = funding_contribution_sat;
6827+
6828+
if splice_passes {
6829+
acceptor.node.handle_splice_init(node_id_initiator, &splice_init);
6830+
let _splice_ack =
6831+
get_event_msg!(acceptor, MessageSendEvent::SendSpliceAck, node_id_initiator);
6832+
} else {
6833+
acceptor.node.handle_splice_init(node_id_initiator, &splice_init);
6834+
let msg_events = acceptor.node.get_and_clear_pending_msg_events();
6835+
assert_eq!(msg_events.len(), 1);
6836+
if let MessageSendEvent::HandleError { action, .. } = &msg_events[0] {
6837+
assert!(matches!(action, msgs::ErrorAction::DisconnectPeerWithWarning { .. }));
6838+
} else {
6839+
panic!("Expected MessageSendEvent::HandleError");
6840+
}
6841+
let cannot_splice_out = format!(
6842+
"Got non-closing error: Channel {channel_id} cannot \
6843+
be spliced; Balance exhausted on local commitment"
6844+
);
6845+
acceptor.logger.assert_log("lightning::ln::channelmanager", cannot_splice_out, 1);
6846+
}
6847+
6848+
channel_type
6849+
}

0 commit comments

Comments
 (0)