@@ -65,6 +65,32 @@ fn test_v1_splice_in_negative_insufficient_inputs() {
6565 }
6666}
6767
68+ fn negotiate_splice_tx_with_init < ' a , ' b , ' c , ' d > (
69+ initiator : & ' a Node < ' b , ' c , ' d > , acceptor : & ' a Node < ' b , ' c , ' d > , channel_id : ChannelId ,
70+ initiator_contribution : SpliceContribution , splice_init : & msgs:: SpliceInit ,
71+ ) -> msgs:: CommitmentSigned {
72+ let node_id_initiator = initiator. node . get_our_node_id ( ) ;
73+ let node_id_acceptor = acceptor. node . get_our_node_id ( ) ;
74+
75+ acceptor. node . handle_splice_init ( node_id_initiator, & splice_init) ;
76+ let splice_ack = get_event_msg ! ( acceptor, MessageSendEvent :: SendSpliceAck , node_id_initiator) ;
77+ initiator. node . handle_splice_ack ( node_id_acceptor, & splice_ack) ;
78+
79+ let new_funding_script = chan_utils:: make_funding_redeemscript (
80+ & splice_init. funding_pubkey ,
81+ & splice_ack. funding_pubkey ,
82+ )
83+ . to_p2wsh ( ) ;
84+
85+ complete_interactive_funding_negotiation (
86+ initiator,
87+ acceptor,
88+ channel_id,
89+ initiator_contribution,
90+ new_funding_script,
91+ )
92+ }
93+
6894fn negotiate_splice_tx < ' a , ' b , ' c , ' d > (
6995 initiator : & ' a Node < ' b , ' c , ' d > , acceptor : & ' a Node < ' b , ' c , ' d > , channel_id : ChannelId ,
7096 initiator_contribution : SpliceContribution ,
@@ -89,22 +115,12 @@ fn negotiate_splice_tx<'a, 'b, 'c, 'd>(
89115 initiator. node . handle_stfu ( node_id_acceptor, & stfu_ack) ;
90116
91117 let splice_init = get_event_msg ! ( initiator, MessageSendEvent :: SendSpliceInit , node_id_acceptor) ;
92- acceptor. node . handle_splice_init ( node_id_initiator, & splice_init) ;
93- let splice_ack = get_event_msg ! ( acceptor, MessageSendEvent :: SendSpliceAck , node_id_initiator) ;
94- initiator. node . handle_splice_ack ( node_id_acceptor, & splice_ack) ;
95-
96- let new_funding_script = chan_utils:: make_funding_redeemscript (
97- & splice_init. funding_pubkey ,
98- & splice_ack. funding_pubkey ,
99- )
100- . to_p2wsh ( ) ;
101-
102- complete_interactive_funding_negotiation (
118+ negotiate_splice_tx_with_init (
103119 initiator,
104120 acceptor,
105121 channel_id,
106122 initiator_contribution,
107- new_funding_script ,
123+ & splice_init ,
108124 )
109125}
110126
@@ -901,3 +917,293 @@ fn do_test_splice_reestablish(reload: bool, async_monitor_update: bool) {
901917 . chain_source
902918 . remove_watched_txn_and_outputs ( prev_funding_outpoint, prev_funding_script) ;
903919}
920+
921+ #[ test]
922+ fn test_propose_splice_while_disconnected ( ) {
923+ do_test_propose_splice_while_disconnected ( false , false ) ;
924+ do_test_propose_splice_while_disconnected ( false , true ) ;
925+ do_test_propose_splice_while_disconnected ( true , false ) ;
926+ do_test_propose_splice_while_disconnected ( true , true ) ;
927+ }
928+
929+ fn do_test_propose_splice_while_disconnected ( reload : bool , use_0conf : bool ) {
930+ // Test that both nodes are able to propose a splice while the counterparty is disconnected, and
931+ // whoever doesn't go first due to the quiescence tie-breaker, will retry their splice after the
932+ // first one becomes locked.
933+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
934+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
935+ let ( persister_0a, persister_0b, persister_1a, persister_1b) ;
936+ let ( chain_monitor_0a, chain_monitor_0b, chain_monitor_1a, chain_monitor_1b) ;
937+ let mut config = test_default_channel_config ( ) ;
938+ if use_0conf {
939+ config. manually_accept_inbound_channels = true ;
940+ config. channel_handshake_limits . trust_own_funding_0conf = true ;
941+ }
942+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ Some ( config. clone ( ) ) , Some ( config) ] ) ;
943+ let ( node_0a, node_0b, node_1a, node_1b) ;
944+ let mut nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
945+
946+ let node_id_0 = nodes[ 0 ] . node . get_our_node_id ( ) ;
947+ let node_id_1 = nodes[ 1 ] . node . get_our_node_id ( ) ;
948+
949+ let initial_channel_value_sat = 1_000_000 ;
950+ let push_msat = initial_channel_value_sat / 2 * 1000 ;
951+ let channel_id = if use_0conf {
952+ let ( funding_tx, channel_id) = open_zero_conf_channel_with_value (
953+ & nodes[ 0 ] ,
954+ & nodes[ 1 ] ,
955+ None ,
956+ initial_channel_value_sat,
957+ push_msat,
958+ ) ;
959+ mine_transaction ( & nodes[ 0 ] , & funding_tx) ;
960+ mine_transaction ( & nodes[ 1 ] , & funding_tx) ;
961+ channel_id
962+ } else {
963+ let ( _, _, channel_id, _) = create_announced_chan_between_nodes_with_value (
964+ & nodes,
965+ 0 ,
966+ 1 ,
967+ initial_channel_value_sat,
968+ push_msat,
969+ ) ;
970+ channel_id
971+ } ;
972+
973+ // Start with the nodes disconnected, and have each one attempt a splice.
974+ nodes[ 0 ] . node . peer_disconnected ( node_id_1) ;
975+ nodes[ 1 ] . node . peer_disconnected ( node_id_0) ;
976+
977+ let splice_out_sat = initial_channel_value_sat / 4 ;
978+ let node_0_contribution = SpliceContribution :: SpliceOut {
979+ outputs : vec ! [ TxOut {
980+ value: Amount :: from_sat( splice_out_sat) ,
981+ script_pubkey: nodes[ 0 ] . wallet_source. get_change_script( ) . unwrap( ) ,
982+ } ] ,
983+ } ;
984+ nodes[ 0 ]
985+ . node
986+ . splice_channel (
987+ & channel_id,
988+ & node_id_1,
989+ node_0_contribution. clone ( ) ,
990+ FEERATE_FLOOR_SATS_PER_KW ,
991+ None ,
992+ )
993+ . unwrap ( ) ;
994+ assert ! ( nodes[ 0 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
995+
996+ let node_1_contribution = SpliceContribution :: SpliceOut {
997+ outputs : vec ! [ TxOut {
998+ value: Amount :: from_sat( splice_out_sat) ,
999+ script_pubkey: nodes[ 1 ] . wallet_source. get_change_script( ) . unwrap( ) ,
1000+ } ] ,
1001+ } ;
1002+ nodes[ 1 ]
1003+ . node
1004+ . splice_channel (
1005+ & channel_id,
1006+ & node_id_0,
1007+ node_1_contribution. clone ( ) ,
1008+ FEERATE_FLOOR_SATS_PER_KW ,
1009+ None ,
1010+ )
1011+ . unwrap ( ) ;
1012+ assert ! ( nodes[ 1 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
1013+
1014+ if reload {
1015+ let encoded_monitor_0 = get_monitor ! ( nodes[ 0 ] , channel_id) . encode ( ) ;
1016+ reload_node ! (
1017+ nodes[ 0 ] ,
1018+ nodes[ 0 ] . node. encode( ) ,
1019+ & [ & encoded_monitor_0] ,
1020+ persister_0a,
1021+ chain_monitor_0a,
1022+ node_0a
1023+ ) ;
1024+ let encoded_monitor_1 = get_monitor ! ( nodes[ 1 ] , channel_id) . encode ( ) ;
1025+ reload_node ! (
1026+ nodes[ 1 ] ,
1027+ nodes[ 1 ] . node. encode( ) ,
1028+ & [ & encoded_monitor_1] ,
1029+ persister_1a,
1030+ chain_monitor_1a,
1031+ node_1a
1032+ ) ;
1033+ }
1034+
1035+ // Reconnect the nodes. Both nodes should attempt quiescence as the initiator, but only one will
1036+ // be it via the tie-breaker.
1037+ let mut reconnect_args = ReconnectArgs :: new ( & nodes[ 0 ] , & nodes[ 1 ] ) ;
1038+ reconnect_args. send_channel_ready = ( true , true ) ;
1039+ if !use_0conf {
1040+ reconnect_args. send_announcement_sigs = ( true , true ) ;
1041+ }
1042+ reconnect_args. send_stfu = ( true , true ) ;
1043+ reconnect_nodes ( reconnect_args) ;
1044+ let splice_init = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendSpliceInit , node_id_1) ;
1045+ assert ! ( nodes[ 1 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
1046+
1047+ let ( prev_funding_outpoint, prev_funding_script) = nodes[ 0 ]
1048+ . chain_monitor
1049+ . chain_monitor
1050+ . get_monitor ( channel_id)
1051+ . map ( |monitor| ( monitor. get_funding_txo ( ) , monitor. get_funding_script ( ) ) )
1052+ . unwrap ( ) ;
1053+
1054+ // Negotiate the first splice to completion.
1055+ let initial_commit_sig = negotiate_splice_tx_with_init (
1056+ & nodes[ 0 ] ,
1057+ & nodes[ 1 ] ,
1058+ channel_id,
1059+ node_0_contribution,
1060+ & splice_init,
1061+ ) ;
1062+ let ( splice_tx, splice_locked) =
1063+ sign_interactive_funding_tx ( & nodes[ 0 ] , & nodes[ 1 ] , initial_commit_sig, use_0conf) ;
1064+
1065+ let splice_locked = if use_0conf {
1066+ let ( splice_locked, for_node_id) = splice_locked. unwrap ( ) ;
1067+ assert_eq ! ( for_node_id, node_id_1) ;
1068+ splice_locked
1069+ } else {
1070+ assert ! ( splice_locked. is_none( ) ) ;
1071+
1072+ mine_transaction ( & nodes[ 0 ] , & splice_tx) ;
1073+ mine_transaction ( & nodes[ 1 ] , & splice_tx) ;
1074+
1075+ // Mine enough blocks for the first splice to become locked.
1076+ connect_blocks ( & nodes[ 0 ] , ANTI_REORG_DELAY - 1 ) ;
1077+ connect_blocks ( & nodes[ 1 ] , ANTI_REORG_DELAY - 1 ) ;
1078+
1079+ get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendSpliceLocked , node_id_1)
1080+ } ;
1081+ nodes[ 1 ] . node . handle_splice_locked ( node_id_0, & splice_locked) ;
1082+
1083+ // We should see the node which lost the tie-breaker attempt their splice now by first
1084+ // negotiating quiescence, but their `stfu` won't be sent until after another reconnection.
1085+ let msg_events = nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
1086+ assert_eq ! ( msg_events. len( ) , if use_0conf { 2 } else { 3 } , "{msg_events:?}" ) ;
1087+ if let MessageSendEvent :: SendSpliceLocked { ref msg, .. } = & msg_events[ 0 ] {
1088+ nodes[ 0 ] . node . handle_splice_locked ( node_id_1, msg) ;
1089+ if use_0conf {
1090+ // TODO(splicing): Revisit splice transaction rebroadcasts.
1091+ let txn_0 = nodes[ 0 ] . tx_broadcaster . txn_broadcast ( ) ;
1092+ assert_eq ! ( txn_0. len( ) , 1 ) ;
1093+ assert_eq ! ( & txn_0[ 0 ] , & splice_tx) ;
1094+ mine_transaction ( & nodes[ 0 ] , & splice_tx) ;
1095+ mine_transaction ( & nodes[ 1 ] , & splice_tx) ;
1096+ }
1097+ } else {
1098+ panic ! ( "Unexpected event {:?}" , & msg_events[ 0 ] ) ;
1099+ }
1100+ if !use_0conf {
1101+ if let MessageSendEvent :: SendAnnouncementSignatures { ref msg, .. } = & msg_events[ 1 ] {
1102+ nodes[ 0 ] . node . handle_announcement_signatures ( node_id_1, msg) ;
1103+ } else {
1104+ panic ! ( "Unexpected event {:?}" , & msg_events[ 1 ] ) ;
1105+ }
1106+ }
1107+ assert ! ( matches!(
1108+ & msg_events[ if use_0conf { 1 } else { 2 } ] ,
1109+ MessageSendEvent :: SendStfu { .. }
1110+ ) ) ;
1111+
1112+ let msg_events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
1113+ assert_eq ! ( msg_events. len( ) , if use_0conf { 0 } else { 2 } , "{msg_events:?}" ) ;
1114+ if !use_0conf {
1115+ if let MessageSendEvent :: SendAnnouncementSignatures { ref msg, .. } = & msg_events[ 0 ] {
1116+ nodes[ 1 ] . node . handle_announcement_signatures ( node_id_0, msg) ;
1117+ } else {
1118+ panic ! ( "Unexpected event {:?}" , & msg_events[ 1 ] ) ;
1119+ }
1120+ assert ! ( matches!( & msg_events[ 1 ] , MessageSendEvent :: BroadcastChannelAnnouncement { .. } ) ) ;
1121+ }
1122+
1123+ let msg_events = nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
1124+ assert_eq ! ( msg_events. len( ) , if use_0conf { 0 } else { 1 } , "{msg_events:?}" ) ;
1125+ if !use_0conf {
1126+ assert ! ( matches!( & msg_events[ 0 ] , MessageSendEvent :: BroadcastChannelAnnouncement { .. } ) ) ;
1127+ }
1128+
1129+ expect_channel_ready_event ( & nodes[ 0 ] , & node_id_1) ;
1130+ check_added_monitors ( & nodes[ 0 ] , 1 ) ;
1131+ expect_channel_ready_event ( & nodes[ 1 ] , & node_id_0) ;
1132+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
1133+
1134+ // Remove the corresponding outputs and transactions the chain source is watching for the
1135+ // old funding as it is no longer being tracked.
1136+ nodes[ 0 ]
1137+ . chain_source
1138+ . remove_watched_txn_and_outputs ( prev_funding_outpoint, prev_funding_script. clone ( ) ) ;
1139+ nodes[ 1 ]
1140+ . chain_source
1141+ . remove_watched_txn_and_outputs ( prev_funding_outpoint, prev_funding_script) ;
1142+
1143+ // Reconnect the nodes. This should trigger the node which lost the tie-breaker to resend `stfu`
1144+ // for their splice attempt.
1145+ if reload {
1146+ let encoded_monitor_0 = get_monitor ! ( nodes[ 0 ] , channel_id) . encode ( ) ;
1147+ reload_node ! (
1148+ nodes[ 0 ] ,
1149+ nodes[ 0 ] . node. encode( ) ,
1150+ & [ & encoded_monitor_0] ,
1151+ persister_0b,
1152+ chain_monitor_0b,
1153+ node_0b
1154+ ) ;
1155+ let encoded_monitor_1 = get_monitor ! ( nodes[ 1 ] , channel_id) . encode ( ) ;
1156+ reload_node ! (
1157+ nodes[ 1 ] ,
1158+ nodes[ 1 ] . node. encode( ) ,
1159+ & [ & encoded_monitor_1] ,
1160+ persister_1b,
1161+ chain_monitor_1b,
1162+ node_1b
1163+ ) ;
1164+ } else {
1165+ nodes[ 0 ] . node . peer_disconnected ( node_id_1) ;
1166+ nodes[ 1 ] . node . peer_disconnected ( node_id_0) ;
1167+ }
1168+ let mut reconnect_args = ReconnectArgs :: new ( & nodes[ 0 ] , & nodes[ 1 ] ) ;
1169+ reconnect_args. send_channel_ready = ( true , true ) ;
1170+ if !use_0conf {
1171+ reconnect_args. send_announcement_sigs = ( true , true ) ;
1172+ }
1173+ reconnect_args. send_stfu = ( true , false ) ;
1174+ reconnect_nodes ( reconnect_args) ;
1175+
1176+ // Drive the second splice to completion.
1177+ let msg_events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
1178+ assert_eq ! ( msg_events. len( ) , 1 , "{msg_events:?}" ) ;
1179+ if let MessageSendEvent :: SendStfu { ref msg, .. } = msg_events[ 0 ] {
1180+ nodes[ 1 ] . node . handle_stfu ( node_id_0, msg) ;
1181+ } else {
1182+ panic ! ( "Unexpected event {:?}" , & msg_events[ 0 ] ) ;
1183+ }
1184+
1185+ let splice_init = get_event_msg ! ( nodes[ 1 ] , MessageSendEvent :: SendSpliceInit , node_id_0) ;
1186+ let initial_commit_sig = negotiate_splice_tx_with_init (
1187+ & nodes[ 1 ] ,
1188+ & nodes[ 0 ] ,
1189+ channel_id,
1190+ node_1_contribution,
1191+ & splice_init,
1192+ ) ;
1193+ let ( splice_tx, splice_locked) =
1194+ sign_interactive_funding_tx ( & nodes[ 1 ] , & nodes[ 0 ] , initial_commit_sig, use_0conf) ;
1195+
1196+ if use_0conf {
1197+ let ( splice_locked, for_node_id) = splice_locked. unwrap ( ) ;
1198+ assert_eq ! ( for_node_id, node_id_0) ;
1199+ lock_splice ( & nodes[ 1 ] , & nodes[ 0 ] , & splice_locked, true ) ;
1200+ } else {
1201+ assert ! ( splice_locked. is_none( ) ) ;
1202+ mine_transaction ( & nodes[ 0 ] , & splice_tx) ;
1203+ mine_transaction ( & nodes[ 1 ] , & splice_tx) ;
1204+ lock_splice_after_blocks ( & nodes[ 1 ] , & nodes[ 0 ] , ANTI_REORG_DELAY - 1 ) ;
1205+ }
1206+
1207+ // Sanity check that we can still make a test payment.
1208+ send_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] ] , 1_000_000 ) ;
1209+ }
0 commit comments