@@ -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:: * ;
@@ -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