Skip to content

Commit 0e8cfb5

Browse files
committed
Add test_0reserve_splice
1 parent 13de2bc commit 0e8cfb5

2 files changed

Lines changed: 202 additions & 2 deletions

File tree

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::*;
@@ -6733,3 +6734,202 @@ fn test_splice_rbf_rejects_own_low_feerate_after_several_attempts() {
67336734
other => panic!("Expected SpliceFailed, got {:?}", other),
67346735
}
67356736
}
6737+
6738+
#[test]
6739+
fn test_0reserve_splice() {
6740+
let mut config = test_default_channel_config();
6741+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false;
6742+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
6743+
let a = do_test_0reserve_splice_holder_validation(false, config.clone());
6744+
let b = do_test_0reserve_splice_holder_validation(true, config.clone());
6745+
assert_eq!(a, b);
6746+
assert_eq!(a, ChannelTypeFeatures::only_static_remote_key());
6747+
6748+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
6749+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
6750+
let a = do_test_0reserve_splice_holder_validation(false, config.clone());
6751+
let b = do_test_0reserve_splice_holder_validation(true, config.clone());
6752+
assert_eq!(a, b);
6753+
assert_eq!(a, ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies());
6754+
6755+
let mut config = test_default_channel_config();
6756+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = false;
6757+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
6758+
let a = do_test_0reserve_splice_counterparty_validation(false, config.clone());
6759+
let b = do_test_0reserve_splice_counterparty_validation(true, config.clone());
6760+
assert_eq!(a, b);
6761+
assert_eq!(a, ChannelTypeFeatures::only_static_remote_key());
6762+
6763+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
6764+
config.channel_handshake_config.negotiate_anchor_zero_fee_commitments = false;
6765+
let a = do_test_0reserve_splice_counterparty_validation(false, config.clone());
6766+
let b = do_test_0reserve_splice_counterparty_validation(true, config.clone());
6767+
assert_eq!(a, b);
6768+
assert_eq!(a, ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies());
6769+
6770+
// TODO: Skip 0FC channels for now as these always have an output on the commitment, the P2A
6771+
// output. We will be able to withdraw up to the dust limit of the funding script, which
6772+
// is checked in interactivetx. Still need to double check whether that's what we actually
6773+
// want.
6774+
}
6775+
6776+
#[cfg(test)]
6777+
fn do_test_0reserve_splice_holder_validation(
6778+
splice_passes: bool, config: UserConfig,
6779+
) -> ChannelTypeFeatures {
6780+
use crate::ln::htlc_reserve_unit_tests::setup_0reserve_no_outputs_channels;
6781+
6782+
let chanmon_cfgs = create_chanmon_cfgs(2);
6783+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
6784+
let node_chanmgrs =
6785+
create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config.clone())]);
6786+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
6787+
6788+
let _node_id_0 = nodes[0].node.get_our_node_id();
6789+
let _node_id_1 = nodes[1].node.get_our_node_id();
6790+
6791+
let channel_value_sat = 100_000;
6792+
// Some dust limit, does not matter
6793+
let dust_limit_satoshis = 546;
6794+
6795+
let (channel_id, _tx) =
6796+
setup_0reserve_no_outputs_channels(&nodes, channel_value_sat, dust_limit_satoshis);
6797+
let details = &nodes[0].node.list_channels()[0];
6798+
let channel_type = details.channel_type.clone().unwrap();
6799+
6800+
let feerate = if channel_type == ChannelTypeFeatures::only_static_remote_key() {
6801+
253 * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32
6802+
} else if channel_type == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
6803+
253
6804+
} else {
6805+
panic!("Unexpected channel type");
6806+
};
6807+
let commit_tx_fee_sat = chan_utils::commit_tx_fee_sat(feerate, 0, &channel_type);
6808+
let anchors_sat =
6809+
if channel_type == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
6810+
ANCHOR_OUTPUT_VALUE_SATOSHI * 2
6811+
} else {
6812+
0
6813+
};
6814+
6815+
let estimated_fees = 183;
6816+
let splice_out_max_value = Amount::from_sat(
6817+
channel_value_sat - commit_tx_fee_sat - anchors_sat - estimated_fees - dust_limit_satoshis,
6818+
);
6819+
let outputs = vec![TxOut {
6820+
value: splice_out_max_value + if splice_passes { Amount::ZERO } else { Amount::ONE_SAT },
6821+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
6822+
}];
6823+
6824+
if splice_passes {
6825+
let contribution = initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).unwrap();
6826+
6827+
let (splice_tx, _) = splice_channel(&nodes[0], &nodes[1], channel_id, contribution);
6828+
mine_transaction(&nodes[0], &splice_tx);
6829+
mine_transaction(&nodes[1], &splice_tx);
6830+
lock_splice_after_blocks(&nodes[0], &nodes[1], ANTI_REORG_DELAY - 1);
6831+
} else {
6832+
assert!(initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).is_err());
6833+
let splice_out_value =
6834+
splice_out_max_value + Amount::from_sat(estimated_fees) + Amount::ONE_SAT;
6835+
let splice_out_max_value = splice_out_max_value + Amount::from_sat(estimated_fees);
6836+
let cannot_be_funded = format!(
6837+
"Channel {channel_id} cannot be funded: Our \
6838+
splice-out value of {splice_out_value} is greater than the maximum \
6839+
{splice_out_max_value}"
6840+
);
6841+
nodes[0].logger.assert_log("lightning::ln::channel", cannot_be_funded, 1);
6842+
}
6843+
6844+
channel_type
6845+
}
6846+
6847+
#[cfg(test)]
6848+
fn do_test_0reserve_splice_counterparty_validation(
6849+
splice_passes: bool, config: UserConfig,
6850+
) -> ChannelTypeFeatures {
6851+
use crate::ln::htlc_reserve_unit_tests::setup_0reserve_no_outputs_channels;
6852+
6853+
let chanmon_cfgs = create_chanmon_cfgs(2);
6854+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
6855+
let node_chanmgrs =
6856+
create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config.clone())]);
6857+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
6858+
6859+
let _node_id_0 = nodes[0].node.get_our_node_id();
6860+
let _node_id_1 = nodes[1].node.get_our_node_id();
6861+
6862+
let channel_value_sat = 100_000;
6863+
// Some dust limit, does not matter
6864+
let dust_limit_satoshis = 546;
6865+
6866+
let (channel_id, _tx) =
6867+
setup_0reserve_no_outputs_channels(&nodes, channel_value_sat, dust_limit_satoshis);
6868+
let details = &nodes[0].node.list_channels()[0];
6869+
let channel_type = details.channel_type.clone().unwrap();
6870+
6871+
let feerate = if channel_type == ChannelTypeFeatures::only_static_remote_key() {
6872+
253 * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32
6873+
} else if channel_type == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
6874+
253
6875+
} else {
6876+
panic!("Unexpected channel type");
6877+
};
6878+
let commit_tx_fee_sat = chan_utils::commit_tx_fee_sat(feerate, 0, &channel_type);
6879+
let anchors_sat =
6880+
if channel_type == ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies() {
6881+
ANCHOR_OUTPUT_VALUE_SATOSHI * 2
6882+
} else {
6883+
0
6884+
};
6885+
6886+
let splice_out_value_incl_fees =
6887+
Amount::from_sat(channel_value_sat - commit_tx_fee_sat - anchors_sat - dust_limit_satoshis);
6888+
6889+
let funding_contribution_sat =
6890+
-(splice_out_value_incl_fees.to_sat() as i64) - if splice_passes { 0 } else { 1 };
6891+
let outputs = vec![TxOut {
6892+
// Splice out some dummy amount to get past the initiator's validation,
6893+
// we'll modify the message in-flight.
6894+
value: Amount::from_sat(1_000),
6895+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
6896+
}];
6897+
let _contribution = initiate_splice_out(&nodes[0], &nodes[1], channel_id, outputs).unwrap();
6898+
6899+
let initiator = &nodes[0];
6900+
let acceptor = &nodes[1];
6901+
let node_id_initiator = initiator.node.get_our_node_id();
6902+
let node_id_acceptor = acceptor.node.get_our_node_id();
6903+
6904+
let stfu_init = get_event_msg!(initiator, MessageSendEvent::SendStfu, node_id_acceptor);
6905+
acceptor.node.handle_stfu(node_id_initiator, &stfu_init);
6906+
let stfu_ack = get_event_msg!(acceptor, MessageSendEvent::SendStfu, node_id_initiator);
6907+
initiator.node.handle_stfu(node_id_acceptor, &stfu_ack);
6908+
6909+
let mut splice_init =
6910+
get_event_msg!(initiator, MessageSendEvent::SendSpliceInit, node_id_acceptor);
6911+
// Make the modification here
6912+
splice_init.funding_contribution_satoshis = funding_contribution_sat;
6913+
6914+
if splice_passes {
6915+
acceptor.node.handle_splice_init(node_id_initiator, &splice_init);
6916+
let _splice_ack =
6917+
get_event_msg!(acceptor, MessageSendEvent::SendSpliceAck, node_id_initiator);
6918+
} else {
6919+
acceptor.node.handle_splice_init(node_id_initiator, &splice_init);
6920+
let msg_events = acceptor.node.get_and_clear_pending_msg_events();
6921+
assert_eq!(msg_events.len(), 1);
6922+
if let MessageSendEvent::HandleError { action, .. } = &msg_events[0] {
6923+
assert!(matches!(action, msgs::ErrorAction::DisconnectPeerWithWarning { .. }));
6924+
} else {
6925+
panic!("Expected MessageSendEvent::HandleError");
6926+
}
6927+
let cannot_splice_out = format!(
6928+
"Got non-closing error: Channel {channel_id} cannot \
6929+
be spliced; Balance exhausted on local commitment"
6930+
);
6931+
acceptor.logger.assert_log("lightning::ln::channelmanager", cannot_splice_out, 1);
6932+
}
6933+
6934+
channel_type
6935+
}

0 commit comments

Comments
 (0)