@@ -16,7 +16,8 @@ use crate::chain::ChannelMonitorUpdateStatus;
1616use crate :: events:: { ClosureReason , Event , FundingInfo , HTLCHandlingFailureType } ;
1717use crate :: ln:: chan_utils;
1818use 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} ;
2122use crate :: ln:: channelmanager:: { provided_init_features, PaymentId , BREAKDOWN_TIMEOUT } ;
2223use 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