@@ -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
@@ -1083,3 +1099,292 @@ fn do_test_splice_reestablish(reload: bool, async_monitor_update: bool) {
10831099 . chain_source
10841100 . remove_watched_txn_and_outputs ( prev_funding_outpoint, prev_funding_script) ;
10851101}
1102+
1103+ #[ test]
1104+ fn test_propose_splice_while_disconnected ( ) {
1105+ do_test_propose_splice_while_disconnected ( false , false ) ;
1106+ do_test_propose_splice_while_disconnected ( false , true ) ;
1107+ do_test_propose_splice_while_disconnected ( true , false ) ;
1108+ do_test_propose_splice_while_disconnected ( true , true ) ;
1109+ }
1110+
1111+ fn do_test_propose_splice_while_disconnected ( reload : bool , use_0conf : bool ) {
1112+ // Test that both nodes are able to propose a splice while the counterparty is disconnected, and
1113+ // whoever doesn't go first due to the quiescence tie-breaker, will retry their splice after the
1114+ // first one becomes locked.
1115+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
1116+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
1117+ let ( persister_0a, persister_0b, persister_1a, persister_1b) ;
1118+ let ( chain_monitor_0a, chain_monitor_0b, chain_monitor_1a, chain_monitor_1b) ;
1119+ let mut config = test_default_channel_config ( ) ;
1120+ if use_0conf {
1121+ config. manually_accept_inbound_channels = true ;
1122+ config. channel_handshake_limits . trust_own_funding_0conf = true ;
1123+ }
1124+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ Some ( config. clone ( ) ) , Some ( config) ] ) ;
1125+ let ( node_0a, node_0b, node_1a, node_1b) ;
1126+ let mut nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
1127+
1128+ let node_id_0 = nodes[ 0 ] . node . get_our_node_id ( ) ;
1129+ let node_id_1 = nodes[ 1 ] . node . get_our_node_id ( ) ;
1130+
1131+ let initial_channel_value_sat = 1_000_000 ;
1132+ let push_msat = initial_channel_value_sat / 2 * 1000 ;
1133+ let channel_id = if use_0conf {
1134+ let ( funding_tx, channel_id) = open_zero_conf_channel_with_value (
1135+ & nodes[ 0 ] ,
1136+ & nodes[ 1 ] ,
1137+ None ,
1138+ initial_channel_value_sat,
1139+ push_msat,
1140+ ) ;
1141+ mine_transaction ( & nodes[ 0 ] , & funding_tx) ;
1142+ mine_transaction ( & nodes[ 1 ] , & funding_tx) ;
1143+ channel_id
1144+ } else {
1145+ let ( _, _, channel_id, _) = create_announced_chan_between_nodes_with_value (
1146+ & nodes,
1147+ 0 ,
1148+ 1 ,
1149+ initial_channel_value_sat,
1150+ push_msat,
1151+ ) ;
1152+ channel_id
1153+ } ;
1154+
1155+ // Start with the nodes disconnected, and have each one attempt a splice.
1156+ nodes[ 0 ] . node . peer_disconnected ( node_id_1) ;
1157+ nodes[ 1 ] . node . peer_disconnected ( node_id_0) ;
1158+
1159+ let splice_out_sat = initial_channel_value_sat / 4 ;
1160+ let node_0_contribution = SpliceContribution :: SpliceOut {
1161+ outputs : vec ! [ TxOut {
1162+ value: Amount :: from_sat( splice_out_sat) ,
1163+ script_pubkey: nodes[ 0 ] . wallet_source. get_change_script( ) . unwrap( ) ,
1164+ } ] ,
1165+ } ;
1166+ nodes[ 0 ]
1167+ . node
1168+ . splice_channel (
1169+ & channel_id,
1170+ & node_id_1,
1171+ node_0_contribution. clone ( ) ,
1172+ FEERATE_FLOOR_SATS_PER_KW ,
1173+ None ,
1174+ )
1175+ . unwrap ( ) ;
1176+ assert ! ( nodes[ 0 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
1177+
1178+ let node_1_contribution = SpliceContribution :: SpliceOut {
1179+ outputs : vec ! [ TxOut {
1180+ value: Amount :: from_sat( splice_out_sat) ,
1181+ script_pubkey: nodes[ 1 ] . wallet_source. get_change_script( ) . unwrap( ) ,
1182+ } ] ,
1183+ } ;
1184+ nodes[ 1 ]
1185+ . node
1186+ . splice_channel (
1187+ & channel_id,
1188+ & node_id_0,
1189+ node_1_contribution. clone ( ) ,
1190+ FEERATE_FLOOR_SATS_PER_KW ,
1191+ None ,
1192+ )
1193+ . unwrap ( ) ;
1194+ assert ! ( nodes[ 1 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
1195+
1196+ if reload {
1197+ let encoded_monitor_0 = get_monitor ! ( nodes[ 0 ] , channel_id) . encode ( ) ;
1198+ reload_node ! (
1199+ nodes[ 0 ] ,
1200+ nodes[ 0 ] . node. encode( ) ,
1201+ & [ & encoded_monitor_0] ,
1202+ persister_0a,
1203+ chain_monitor_0a,
1204+ node_0a
1205+ ) ;
1206+ let encoded_monitor_1 = get_monitor ! ( nodes[ 1 ] , channel_id) . encode ( ) ;
1207+ reload_node ! (
1208+ nodes[ 1 ] ,
1209+ nodes[ 1 ] . node. encode( ) ,
1210+ & [ & encoded_monitor_1] ,
1211+ persister_1a,
1212+ chain_monitor_1a,
1213+ node_1a
1214+ ) ;
1215+ }
1216+
1217+ // Reconnect the nodes. Both nodes should attempt quiescence as the initiator, but only one will
1218+ // be it via the tie-breaker.
1219+ let mut reconnect_args = ReconnectArgs :: new ( & nodes[ 0 ] , & nodes[ 1 ] ) ;
1220+ reconnect_args. send_channel_ready = ( true , true ) ;
1221+ if !use_0conf {
1222+ reconnect_args. send_announcement_sigs = ( true , true ) ;
1223+ }
1224+ reconnect_args. send_stfu = ( true , true ) ;
1225+ reconnect_nodes ( reconnect_args) ;
1226+ let splice_init = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendSpliceInit , node_id_1) ;
1227+ assert ! ( nodes[ 1 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
1228+
1229+ let ( prev_funding_outpoint, prev_funding_script) = nodes[ 0 ]
1230+ . chain_monitor
1231+ . chain_monitor
1232+ . get_monitor ( channel_id)
1233+ . map ( |monitor| ( monitor. get_funding_txo ( ) , monitor. get_funding_script ( ) ) )
1234+ . unwrap ( ) ;
1235+
1236+ // Negotiate the first splice to completion.
1237+ let initial_commit_sig = negotiate_splice_tx_with_init (
1238+ & nodes[ 0 ] ,
1239+ & nodes[ 1 ] ,
1240+ channel_id,
1241+ node_0_contribution,
1242+ & splice_init,
1243+ ) ;
1244+ let ( splice_tx, splice_locked) =
1245+ sign_interactive_funding_tx ( & nodes[ 0 ] , & nodes[ 1 ] , initial_commit_sig, use_0conf) ;
1246+
1247+ let splice_locked = if use_0conf {
1248+ let ( splice_locked, for_node_id) = splice_locked. unwrap ( ) ;
1249+ assert_eq ! ( for_node_id, node_id_1) ;
1250+ splice_locked
1251+ } else {
1252+ assert ! ( splice_locked. is_none( ) ) ;
1253+
1254+ mine_transaction ( & nodes[ 0 ] , & splice_tx) ;
1255+ mine_transaction ( & nodes[ 1 ] , & splice_tx) ;
1256+
1257+ // Mine enough blocks for the first splice to become locked.
1258+ connect_blocks ( & nodes[ 0 ] , ANTI_REORG_DELAY - 1 ) ;
1259+ connect_blocks ( & nodes[ 1 ] , ANTI_REORG_DELAY - 1 ) ;
1260+
1261+ get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendSpliceLocked , node_id_1)
1262+ } ;
1263+ nodes[ 1 ] . node . handle_splice_locked ( node_id_0, & splice_locked) ;
1264+
1265+ // We should see the node which lost the tie-breaker attempt their splice now by first
1266+ // negotiating quiescence, but their `stfu` won't be sent until after another reconnection.
1267+ let msg_events = nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
1268+ assert_eq ! ( msg_events. len( ) , if use_0conf { 2 } else { 3 } , "{msg_events:?}" ) ;
1269+ if let MessageSendEvent :: SendSpliceLocked { ref msg, .. } = & msg_events[ 0 ] {
1270+ nodes[ 0 ] . node . handle_splice_locked ( node_id_1, msg) ;
1271+ if use_0conf {
1272+ // TODO(splicing): Revisit splice transaction rebroadcasts.
1273+ let txn_0 = nodes[ 0 ] . tx_broadcaster . txn_broadcast ( ) ;
1274+ assert_eq ! ( txn_0. len( ) , 1 ) ;
1275+ assert_eq ! ( & txn_0[ 0 ] , & splice_tx) ;
1276+ mine_transaction ( & nodes[ 0 ] , & splice_tx) ;
1277+ mine_transaction ( & nodes[ 1 ] , & splice_tx) ;
1278+ }
1279+ } else {
1280+ panic ! ( "Unexpected event {:?}" , & msg_events[ 0 ] ) ;
1281+ }
1282+ if !use_0conf {
1283+ if let MessageSendEvent :: SendAnnouncementSignatures { ref msg, .. } = & msg_events[ 1 ] {
1284+ nodes[ 0 ] . node . handle_announcement_signatures ( node_id_1, msg) ;
1285+ } else {
1286+ panic ! ( "Unexpected event {:?}" , & msg_events[ 1 ] ) ;
1287+ }
1288+ }
1289+ assert ! ( matches!(
1290+ & msg_events[ if use_0conf { 1 } else { 2 } ] ,
1291+ MessageSendEvent :: SendStfu { .. }
1292+ ) ) ;
1293+
1294+ let msg_events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
1295+ assert_eq ! ( msg_events. len( ) , if use_0conf { 0 } else { 2 } , "{msg_events:?}" ) ;
1296+ if !use_0conf {
1297+ if let MessageSendEvent :: SendAnnouncementSignatures { ref msg, .. } = & msg_events[ 0 ] {
1298+ nodes[ 1 ] . node . handle_announcement_signatures ( node_id_0, msg) ;
1299+ } else {
1300+ panic ! ( "Unexpected event {:?}" , & msg_events[ 1 ] ) ;
1301+ }
1302+ assert ! ( matches!( & msg_events[ 1 ] , MessageSendEvent :: BroadcastChannelAnnouncement { .. } ) ) ;
1303+ }
1304+
1305+ let msg_events = nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
1306+ assert_eq ! ( msg_events. len( ) , if use_0conf { 0 } else { 1 } , "{msg_events:?}" ) ;
1307+ if !use_0conf {
1308+ assert ! ( matches!( & msg_events[ 0 ] , MessageSendEvent :: BroadcastChannelAnnouncement { .. } ) ) ;
1309+ }
1310+
1311+ expect_channel_ready_event ( & nodes[ 0 ] , & node_id_1) ;
1312+ check_added_monitors ( & nodes[ 0 ] , 1 ) ;
1313+ expect_channel_ready_event ( & nodes[ 1 ] , & node_id_0) ;
1314+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
1315+
1316+ // Remove the corresponding outputs and transactions the chain source is watching for the
1317+ // old funding as it is no longer being tracked.
1318+ nodes[ 0 ]
1319+ . chain_source
1320+ . remove_watched_txn_and_outputs ( prev_funding_outpoint, prev_funding_script. clone ( ) ) ;
1321+ nodes[ 1 ]
1322+ . chain_source
1323+ . remove_watched_txn_and_outputs ( prev_funding_outpoint, prev_funding_script) ;
1324+
1325+ // Reconnect the nodes. This should trigger the node which lost the tie-breaker to resend `stfu`
1326+ // for their splice attempt.
1327+ if reload {
1328+ let encoded_monitor_0 = get_monitor ! ( nodes[ 0 ] , channel_id) . encode ( ) ;
1329+ reload_node ! (
1330+ nodes[ 0 ] ,
1331+ nodes[ 0 ] . node. encode( ) ,
1332+ & [ & encoded_monitor_0] ,
1333+ persister_0b,
1334+ chain_monitor_0b,
1335+ node_0b
1336+ ) ;
1337+ let encoded_monitor_1 = get_monitor ! ( nodes[ 1 ] , channel_id) . encode ( ) ;
1338+ reload_node ! (
1339+ nodes[ 1 ] ,
1340+ nodes[ 1 ] . node. encode( ) ,
1341+ & [ & encoded_monitor_1] ,
1342+ persister_1b,
1343+ chain_monitor_1b,
1344+ node_1b
1345+ ) ;
1346+ } else {
1347+ nodes[ 0 ] . node . peer_disconnected ( node_id_1) ;
1348+ nodes[ 1 ] . node . peer_disconnected ( node_id_0) ;
1349+ }
1350+ let mut reconnect_args = ReconnectArgs :: new ( & nodes[ 0 ] , & nodes[ 1 ] ) ;
1351+ if !use_0conf {
1352+ reconnect_args. send_announcement_sigs = ( true , true ) ;
1353+ }
1354+ reconnect_args. send_stfu = ( true , false ) ;
1355+ reconnect_nodes ( reconnect_args) ;
1356+
1357+ // Drive the second splice to completion.
1358+ let msg_events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
1359+ assert_eq ! ( msg_events. len( ) , 1 , "{msg_events:?}" ) ;
1360+ if let MessageSendEvent :: SendStfu { ref msg, .. } = msg_events[ 0 ] {
1361+ nodes[ 1 ] . node . handle_stfu ( node_id_0, msg) ;
1362+ } else {
1363+ panic ! ( "Unexpected event {:?}" , & msg_events[ 0 ] ) ;
1364+ }
1365+
1366+ let splice_init = get_event_msg ! ( nodes[ 1 ] , MessageSendEvent :: SendSpliceInit , node_id_0) ;
1367+ let initial_commit_sig = negotiate_splice_tx_with_init (
1368+ & nodes[ 1 ] ,
1369+ & nodes[ 0 ] ,
1370+ channel_id,
1371+ node_1_contribution,
1372+ & splice_init,
1373+ ) ;
1374+ let ( splice_tx, splice_locked) =
1375+ sign_interactive_funding_tx ( & nodes[ 1 ] , & nodes[ 0 ] , initial_commit_sig, use_0conf) ;
1376+
1377+ if use_0conf {
1378+ let ( splice_locked, for_node_id) = splice_locked. unwrap ( ) ;
1379+ assert_eq ! ( for_node_id, node_id_0) ;
1380+ lock_splice ( & nodes[ 1 ] , & nodes[ 0 ] , & splice_locked, true ) ;
1381+ } else {
1382+ assert ! ( splice_locked. is_none( ) ) ;
1383+ mine_transaction ( & nodes[ 0 ] , & splice_tx) ;
1384+ mine_transaction ( & nodes[ 1 ] , & splice_tx) ;
1385+ lock_splice_after_blocks ( & nodes[ 1 ] , & nodes[ 0 ] , ANTI_REORG_DELAY - 1 ) ;
1386+ }
1387+
1388+ // Sanity check that we can still make a test payment.
1389+ send_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] ] , 1_000_000 ) ;
1390+ }
0 commit comments