Skip to content

Commit 1cbf91c

Browse files
committed
Test propose channel splice while disconnected
1 parent 4bf68fd commit 1cbf91c

File tree

2 files changed

+323
-2
lines changed

2 files changed

+323
-2
lines changed

lightning/src/ln/functional_test_utils.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,14 @@ pub fn sign_funding_transaction<'a, 'b, 'c>(
15721572
pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(
15731573
initiator: &'a Node<'b, 'c, 'd>, receiver: &'a Node<'b, 'c, 'd>,
15741574
initiator_config: Option<UserConfig>,
1575+
) -> (bitcoin::Transaction, ChannelId) {
1576+
open_zero_conf_channel_with_value(initiator, receiver, initiator_config, 100_000, 10_001)
1577+
}
1578+
1579+
// Receiver must have been initialized with manually_accept_inbound_channels set to true.
1580+
pub fn open_zero_conf_channel_with_value<'a, 'b, 'c, 'd>(
1581+
initiator: &'a Node<'b, 'c, 'd>, receiver: &'a Node<'b, 'c, 'd>,
1582+
initiator_config: Option<UserConfig>, channel_value_sat: u64, push_msat: u64,
15751583
) -> (bitcoin::Transaction, ChannelId) {
15761584
let initiator_channels = initiator.node.list_usable_channels().len();
15771585
let receiver_channels = receiver.node.list_usable_channels().len();
@@ -1581,7 +1589,7 @@ pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(
15811589

15821590
initiator
15831591
.node
1584-
.create_channel(receiver_node_id, 100_000, 10_001, 42, None, initiator_config)
1592+
.create_channel(receiver_node_id, channel_value_sat, push_msat, 42, None, initiator_config)
15851593
.unwrap();
15861594
let open_channel =
15871595
get_event_msg!(initiator, MessageSendEvent::SendOpenChannel, receiver_node_id);
@@ -1610,7 +1618,7 @@ pub fn open_zero_conf_channel<'a, 'b, 'c, 'd>(
16101618
initiator.node.handle_accept_channel(receiver_node_id, &accept_channel);
16111619

16121620
let (temporary_channel_id, tx, _) =
1613-
create_funding_transaction(&initiator, &receiver_node_id, 100_000, 42);
1621+
create_funding_transaction(&initiator, &receiver_node_id, channel_value_sat, 42);
16141622
initiator
16151623
.node
16161624
.funding_transaction_generated(temporary_channel_id, receiver_node_id, tx.clone())

lightning/src/ln/splicing_tests.rs

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,319 @@ fn do_test_splice_reestablish(reload: bool, async_monitor_update: bool) {
11781178
.remove_watched_txn_and_outputs(prev_funding_outpoint, prev_funding_script);
11791179
}
11801180

1181+
#[test]
1182+
fn test_propose_splice_while_disconnected() {
1183+
do_test_propose_splice_while_disconnected(false, false);
1184+
do_test_propose_splice_while_disconnected(false, true);
1185+
do_test_propose_splice_while_disconnected(true, false);
1186+
do_test_propose_splice_while_disconnected(true, true);
1187+
}
1188+
1189+
fn do_test_propose_splice_while_disconnected(reload: bool, use_0conf: bool) {
1190+
// Test that both nodes are able to propose a splice while the counterparty is disconnected, and
1191+
// whoever doesn't go first due to the quiescence tie-breaker, will retry their splice after the
1192+
// first one becomes locked.
1193+
let chanmon_cfgs = create_chanmon_cfgs(2);
1194+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1195+
let (persister_0a, persister_0b, persister_1a, persister_1b);
1196+
let (chain_monitor_0a, chain_monitor_0b, chain_monitor_1a, chain_monitor_1b);
1197+
let mut config = test_default_channel_config();
1198+
if use_0conf {
1199+
config.manually_accept_inbound_channels = true;
1200+
config.channel_handshake_limits.trust_own_funding_0conf = true;
1201+
}
1202+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]);
1203+
let (node_0a, node_0b, node_1a, node_1b);
1204+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1205+
1206+
let node_id_0 = nodes[0].node.get_our_node_id();
1207+
let node_id_1 = nodes[1].node.get_our_node_id();
1208+
1209+
let initial_channel_value_sat = 1_000_000;
1210+
let push_msat = initial_channel_value_sat / 2 * 1000;
1211+
let channel_id = if use_0conf {
1212+
let (funding_tx, channel_id) = open_zero_conf_channel_with_value(
1213+
&nodes[0],
1214+
&nodes[1],
1215+
None,
1216+
initial_channel_value_sat,
1217+
push_msat,
1218+
);
1219+
mine_transaction(&nodes[0], &funding_tx);
1220+
mine_transaction(&nodes[1], &funding_tx);
1221+
channel_id
1222+
} else {
1223+
let (_, _, channel_id, _) = create_announced_chan_between_nodes_with_value(
1224+
&nodes,
1225+
0,
1226+
1,
1227+
initial_channel_value_sat,
1228+
push_msat,
1229+
);
1230+
channel_id
1231+
};
1232+
1233+
// Start with the nodes disconnected, and have each one attempt a splice.
1234+
nodes[0].node.peer_disconnected(node_id_1);
1235+
nodes[1].node.peer_disconnected(node_id_0);
1236+
1237+
let splice_out_sat = initial_channel_value_sat / 4;
1238+
let node_0_contribution = SpliceContribution::SpliceOut {
1239+
outputs: vec![TxOut {
1240+
value: Amount::from_sat(splice_out_sat),
1241+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
1242+
}],
1243+
};
1244+
nodes[0]
1245+
.node
1246+
.splice_channel(
1247+
&channel_id,
1248+
&node_id_1,
1249+
node_0_contribution.clone(),
1250+
FEERATE_FLOOR_SATS_PER_KW,
1251+
None,
1252+
)
1253+
.unwrap();
1254+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
1255+
1256+
let node_1_contribution = SpliceContribution::SpliceOut {
1257+
outputs: vec![TxOut {
1258+
value: Amount::from_sat(splice_out_sat),
1259+
script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(),
1260+
}],
1261+
};
1262+
nodes[1]
1263+
.node
1264+
.splice_channel(
1265+
&channel_id,
1266+
&node_id_0,
1267+
node_1_contribution.clone(),
1268+
FEERATE_FLOOR_SATS_PER_KW,
1269+
None,
1270+
)
1271+
.unwrap();
1272+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
1273+
1274+
if reload {
1275+
let encoded_monitor_0 = get_monitor!(nodes[0], channel_id).encode();
1276+
reload_node!(
1277+
nodes[0],
1278+
nodes[0].node.encode(),
1279+
&[&encoded_monitor_0],
1280+
persister_0a,
1281+
chain_monitor_0a,
1282+
node_0a
1283+
);
1284+
let encoded_monitor_1 = get_monitor!(nodes[1], channel_id).encode();
1285+
reload_node!(
1286+
nodes[1],
1287+
nodes[1].node.encode(),
1288+
&[&encoded_monitor_1],
1289+
persister_1a,
1290+
chain_monitor_1a,
1291+
node_1a
1292+
);
1293+
}
1294+
1295+
// Reconnect the nodes. Both nodes should attempt quiescence as the initiator, but only one will
1296+
// be it via the tie-breaker.
1297+
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
1298+
reconnect_args.send_channel_ready = (true, true);
1299+
if !use_0conf {
1300+
reconnect_args.send_announcement_sigs = (true, true);
1301+
}
1302+
reconnect_args.send_stfu = (true, true);
1303+
reconnect_nodes(reconnect_args);
1304+
let splice_init = get_event_msg!(nodes[0], MessageSendEvent::SendSpliceInit, node_id_1);
1305+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
1306+
1307+
let (prev_funding_outpoint, prev_funding_script) = nodes[0]
1308+
.chain_monitor
1309+
.chain_monitor
1310+
.get_monitor(channel_id)
1311+
.map(|monitor| (monitor.get_funding_txo(), monitor.get_funding_script()))
1312+
.unwrap();
1313+
1314+
// Negotiate the first splice to completion.
1315+
let initial_commit_sig = {
1316+
nodes[1].node.handle_splice_init(node_id_0, &splice_init);
1317+
let splice_ack = get_event_msg!(nodes[1], MessageSendEvent::SendSpliceAck, node_id_0);
1318+
nodes[0].node.handle_splice_ack(node_id_1, &splice_ack);
1319+
let new_funding_script = chan_utils::make_funding_redeemscript(
1320+
&splice_init.funding_pubkey,
1321+
&splice_ack.funding_pubkey,
1322+
)
1323+
.to_p2wsh();
1324+
complete_interactive_funding_negotiation(
1325+
&nodes[0],
1326+
&nodes[1],
1327+
channel_id,
1328+
node_0_contribution,
1329+
new_funding_script,
1330+
)
1331+
};
1332+
let (splice_tx, splice_locked) =
1333+
sign_interactive_funding_tx(&nodes[0], &nodes[1], initial_commit_sig, use_0conf);
1334+
expect_splice_pending_event(&nodes[0], &node_id_1);
1335+
expect_splice_pending_event(&nodes[1], &node_id_0);
1336+
1337+
let splice_locked = if use_0conf {
1338+
let (splice_locked, for_node_id) = splice_locked.unwrap();
1339+
assert_eq!(for_node_id, node_id_1);
1340+
splice_locked
1341+
} else {
1342+
assert!(splice_locked.is_none());
1343+
1344+
mine_transaction(&nodes[0], &splice_tx);
1345+
mine_transaction(&nodes[1], &splice_tx);
1346+
1347+
// Mine enough blocks for the first splice to become locked.
1348+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
1349+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
1350+
1351+
get_event_msg!(nodes[0], MessageSendEvent::SendSpliceLocked, node_id_1)
1352+
};
1353+
nodes[1].node.handle_splice_locked(node_id_0, &splice_locked);
1354+
1355+
// We should see the node which lost the tie-breaker attempt their splice now by first
1356+
// negotiating quiescence, but their `stfu` won't be sent until after another reconnection.
1357+
let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
1358+
assert_eq!(msg_events.len(), if use_0conf { 2 } else { 3 }, "{msg_events:?}");
1359+
if let MessageSendEvent::SendSpliceLocked { ref msg, .. } = &msg_events[0] {
1360+
nodes[0].node.handle_splice_locked(node_id_1, msg);
1361+
if use_0conf {
1362+
// TODO(splicing): Revisit splice transaction rebroadcasts.
1363+
let txn_0 = nodes[0].tx_broadcaster.txn_broadcast();
1364+
assert_eq!(txn_0.len(), 1);
1365+
assert_eq!(&txn_0[0], &splice_tx);
1366+
mine_transaction(&nodes[0], &splice_tx);
1367+
mine_transaction(&nodes[1], &splice_tx);
1368+
}
1369+
} else {
1370+
panic!("Unexpected event {:?}", &msg_events[0]);
1371+
}
1372+
if !use_0conf {
1373+
if let MessageSendEvent::SendAnnouncementSignatures { ref msg, .. } = &msg_events[1] {
1374+
nodes[0].node.handle_announcement_signatures(node_id_1, msg);
1375+
} else {
1376+
panic!("Unexpected event {:?}", &msg_events[1]);
1377+
}
1378+
}
1379+
assert!(matches!(
1380+
&msg_events[if use_0conf { 1 } else { 2 }],
1381+
MessageSendEvent::SendStfu { .. }
1382+
));
1383+
1384+
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
1385+
assert_eq!(msg_events.len(), if use_0conf { 0 } else { 2 }, "{msg_events:?}");
1386+
if !use_0conf {
1387+
if let MessageSendEvent::SendAnnouncementSignatures { ref msg, .. } = &msg_events[0] {
1388+
nodes[1].node.handle_announcement_signatures(node_id_0, msg);
1389+
} else {
1390+
panic!("Unexpected event {:?}", &msg_events[1]);
1391+
}
1392+
assert!(matches!(&msg_events[1], MessageSendEvent::BroadcastChannelAnnouncement { .. }));
1393+
}
1394+
1395+
let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
1396+
assert_eq!(msg_events.len(), if use_0conf { 0 } else { 1 }, "{msg_events:?}");
1397+
if !use_0conf {
1398+
assert!(matches!(&msg_events[0], MessageSendEvent::BroadcastChannelAnnouncement { .. }));
1399+
}
1400+
1401+
expect_channel_ready_event(&nodes[0], &node_id_1);
1402+
check_added_monitors(&nodes[0], 1);
1403+
expect_channel_ready_event(&nodes[1], &node_id_0);
1404+
check_added_monitors(&nodes[1], 1);
1405+
1406+
// Remove the corresponding outputs and transactions the chain source is watching for the
1407+
// old funding as it is no longer being tracked.
1408+
nodes[0]
1409+
.chain_source
1410+
.remove_watched_txn_and_outputs(prev_funding_outpoint, prev_funding_script.clone());
1411+
nodes[1]
1412+
.chain_source
1413+
.remove_watched_txn_and_outputs(prev_funding_outpoint, prev_funding_script);
1414+
1415+
// Reconnect the nodes. This should trigger the node which lost the tie-breaker to resend `stfu`
1416+
// for their splice attempt.
1417+
if reload {
1418+
let encoded_monitor_0 = get_monitor!(nodes[0], channel_id).encode();
1419+
reload_node!(
1420+
nodes[0],
1421+
nodes[0].node.encode(),
1422+
&[&encoded_monitor_0],
1423+
persister_0b,
1424+
chain_monitor_0b,
1425+
node_0b
1426+
);
1427+
let encoded_monitor_1 = get_monitor!(nodes[1], channel_id).encode();
1428+
reload_node!(
1429+
nodes[1],
1430+
nodes[1].node.encode(),
1431+
&[&encoded_monitor_1],
1432+
persister_1b,
1433+
chain_monitor_1b,
1434+
node_1b
1435+
);
1436+
} else {
1437+
nodes[0].node.peer_disconnected(node_id_1);
1438+
nodes[1].node.peer_disconnected(node_id_0);
1439+
}
1440+
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
1441+
if !use_0conf {
1442+
reconnect_args.send_announcement_sigs = (true, true);
1443+
}
1444+
reconnect_args.send_stfu = (true, false);
1445+
reconnect_nodes(reconnect_args);
1446+
1447+
// Drive the second splice to completion.
1448+
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
1449+
assert_eq!(msg_events.len(), 1, "{msg_events:?}");
1450+
if let MessageSendEvent::SendStfu { ref msg, .. } = msg_events[0] {
1451+
nodes[1].node.handle_stfu(node_id_0, msg);
1452+
} else {
1453+
panic!("Unexpected event {:?}", &msg_events[0]);
1454+
}
1455+
1456+
let splice_init = get_event_msg!(nodes[1], MessageSendEvent::SendSpliceInit, node_id_0);
1457+
let initial_commit_sig = {
1458+
nodes[0].node.handle_splice_init(node_id_1, &splice_init);
1459+
let splice_ack = get_event_msg!(nodes[0], MessageSendEvent::SendSpliceAck, node_id_1);
1460+
nodes[1].node.handle_splice_ack(node_id_0, &splice_ack);
1461+
let new_funding_script = chan_utils::make_funding_redeemscript(
1462+
&splice_init.funding_pubkey,
1463+
&splice_ack.funding_pubkey,
1464+
)
1465+
.to_p2wsh();
1466+
complete_interactive_funding_negotiation(
1467+
&nodes[1],
1468+
&nodes[0],
1469+
channel_id,
1470+
node_1_contribution,
1471+
new_funding_script,
1472+
)
1473+
};
1474+
let (splice_tx, splice_locked) =
1475+
sign_interactive_funding_tx(&nodes[1], &nodes[0], initial_commit_sig, use_0conf);
1476+
expect_splice_pending_event(&nodes[0], &node_id_1);
1477+
expect_splice_pending_event(&nodes[1], &node_id_0);
1478+
1479+
if use_0conf {
1480+
let (splice_locked, for_node_id) = splice_locked.unwrap();
1481+
assert_eq!(for_node_id, node_id_0);
1482+
lock_splice(&nodes[1], &nodes[0], &splice_locked, true);
1483+
} else {
1484+
assert!(splice_locked.is_none());
1485+
mine_transaction(&nodes[0], &splice_tx);
1486+
mine_transaction(&nodes[1], &splice_tx);
1487+
lock_splice_after_blocks(&nodes[1], &nodes[0], ANTI_REORG_DELAY - 1);
1488+
}
1489+
1490+
// Sanity check that we can still make a test payment.
1491+
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
1492+
}
1493+
11811494
#[test]
11821495
fn disconnect_on_unexpected_interactive_tx_message() {
11831496
let chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)