Skip to content

Commit 0095e2a

Browse files
committed
Test propose channel splice while disconnected
1 parent bc44a4e commit 0095e2a

File tree

2 files changed

+327
-14
lines changed

2 files changed

+327
-14
lines changed

lightning/src/ln/functional_test_utils.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,14 @@ pub fn sign_funding_transaction<'a, 'b, 'c>(
15491549
pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(
15501550
initiator: &'a Node<'b, 'c, 'd>, receiver: &'a Node<'b, 'c, 'd>,
15511551
initiator_config: Option<UserConfig>,
1552+
) -> (bitcoin::Transaction, ChannelId) {
1553+
open_zero_conf_channel_with_value(initiator, receiver, initiator_config, 100_000, 10_001)
1554+
}
1555+
1556+
// Receiver must have been initialized with manually_accept_inbound_channels set to true.
1557+
pub fn open_zero_conf_channel_with_value<'a, 'b, 'c, 'd>(
1558+
initiator: &'a Node<'b, 'c, 'd>, receiver: &'a Node<'b, 'c, 'd>,
1559+
initiator_config: Option<UserConfig>, channel_value_sat: u64, push_msat: u64,
15521560
) -> (bitcoin::Transaction, ChannelId) {
15531561
let initiator_channels = initiator.node.list_usable_channels().len();
15541562
let receiver_channels = receiver.node.list_usable_channels().len();
@@ -1558,7 +1566,7 @@ pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(
15581566

15591567
initiator
15601568
.node
1561-
.create_channel(receiver_node_id, 100_000, 10_001, 42, None, initiator_config)
1569+
.create_channel(receiver_node_id, channel_value_sat, push_msat, 42, None, initiator_config)
15621570
.unwrap();
15631571
let open_channel =
15641572
get_event_msg!(initiator, MessageSendEvent::SendOpenChannel, receiver_node_id);
@@ -1587,7 +1595,7 @@ pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(
15871595
initiator.node.handle_accept_channel(receiver_node_id, &accept_channel);
15881596

15891597
let (temporary_channel_id, tx, _) =
1590-
create_funding_transaction(&initiator, &receiver_node_id, 100_000, 42);
1598+
create_funding_transaction(&initiator, &receiver_node_id, channel_value_sat, 42);
15911599
initiator
15921600
.node
15931601
.funding_transaction_generated(temporary_channel_id, receiver_node_id, tx.clone())

lightning/src/ln/splicing_tests.rs

Lines changed: 317 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
6894
fn 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

Comments
 (0)