Skip to content

Commit 6b8123d

Browse files
committed
Support async fetching of commitment point during channel reestablish
`HolderCommitmentPoint` currently tracks the current and next point used on counterparty commitments, which are unrevoked. When we reestablish a channel, the counterparty sends us the commitment height, along with the corresponding secret, for the state they believe to be the latest. We compare said secret to the derived point we fetch from the signer to know if the peer is being honest. Since the protocol does not allow peers (assuming no data loss) to be behind the current state by more than one update, we can cache the two latest revoked commitment points alongside `HolderCommitmentPoint`, such that we no longer need to reach the signer asynchronously when handling `channel_reestablish` messages throughout the happy path. By doing so, we avoid complexity in needing to pause the state machine (which may also result in needing to stash any update messages from the counterparty) while the signer response is pending. The only remaining case left to handle is when the counterparty presents a `channel_reestablish` with a state later than what we know. This can only result in two terminal cases: either they provided a valid commitment secret proving we are behind and we need to panic, or they lied and we force close the channel. This is the only case we choose to handle asynchronously as it's relatively trivial to handle.
1 parent 3f96d12 commit 6b8123d

File tree

3 files changed

+231
-36
lines changed

3 files changed

+231
-36
lines changed

lightning/src/ln/async_signer_tests.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//! properly with a signer implementation that asynchronously derives signatures.
1212
1313
use crate::prelude::*;
14+
use crate::util::ser::Writeable;
1415
use bitcoin::secp256k1::Secp256k1;
1516

1617
use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
@@ -1442,3 +1443,97 @@ fn test_no_disconnect_while_async_commitment_signed_expecting_remote_revoke_and_
14421443
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
14431444
assert!(nodes[1].node.get_and_clear_pending_msg_events().into_iter().any(has_disconnect_event));
14441445
}
1446+
1447+
#[test]
1448+
fn test_async_panic_on_stale_state() {
1449+
// Test that we panic if the counterparty sends us a `channel_reestablish` message with a
1450+
// `next_remote_commitment_number` greater than what we know with a valid corresponding secret,
1451+
// proving that we have lost state, when we have an async signer that is not able to immediately
1452+
// fetch the corresponding point to verify.
1453+
let chanmon_cfgs = create_chanmon_cfgs(2);
1454+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1455+
let stale_persister;
1456+
let stale_chain_monitor;
1457+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1458+
let stale_node;
1459+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1460+
1461+
let node_id_0 = nodes[0].node.get_our_node_id();
1462+
let node_id_1 = nodes[1].node.get_our_node_id();
1463+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
1464+
1465+
let encoded_stale_node_1 = nodes[1].node.encode();
1466+
let encoded_stale_monitor_1 = get_monitor!(nodes[1], chan_id).encode();
1467+
1468+
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
1469+
1470+
nodes[0].node.peer_disconnected(node_id_1);
1471+
nodes[1].node.peer_disconnected(node_id_0);
1472+
1473+
reload_node!(
1474+
nodes[1],
1475+
encoded_stale_node_1,
1476+
&[&encoded_stale_monitor_1],
1477+
stale_persister,
1478+
stale_chain_monitor,
1479+
stale_node
1480+
);
1481+
1482+
nodes[1].disable_channel_signer_op(&node_id_0, &chan_id, SignerOp::GetPerCommitmentPoint);
1483+
1484+
connect_nodes(&nodes[0], &nodes[1]);
1485+
let reestablish_0_to_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
1486+
nodes[1].node.handle_channel_reestablish(node_id_0, &reestablish_0_to_1[0]);
1487+
1488+
nodes[1].enable_channel_signer_op(&node_id_0, &chan_id, SignerOp::GetPerCommitmentPoint);
1489+
std::panic::catch_unwind(|| nodes[1].node.signer_unblocked(None)).unwrap_err();
1490+
nodes[1].logger.assert_log_contains(
1491+
"lightning::ln::channel",
1492+
"We have fallen behind - we have received proof that if we broadcast our counterparty is going to claim all our funds.",
1493+
1,
1494+
);
1495+
1496+
std::panic::catch_unwind(|| drop(nodes)).unwrap_err();
1497+
}
1498+
1499+
#[test]
1500+
fn test_async_force_close_on_invalid_secret_for_stale_state() {
1501+
// Test that we force close a channel if the counterparty sends us a `channel_reestablish`
1502+
// message with a `next_remote_commitment_number` greater than what we know with an invalid
1503+
// corresponding secret when we have an async signer that is not able to immediately fetch the
1504+
// corresponding point to verify.
1505+
let chanmon_cfgs = create_chanmon_cfgs(2);
1506+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1507+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1508+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1509+
1510+
let node_id_0 = nodes[0].node.get_our_node_id();
1511+
let node_id_1 = nodes[1].node.get_our_node_id();
1512+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
1513+
1514+
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
1515+
1516+
nodes[0].node.peer_disconnected(node_id_1);
1517+
nodes[1].node.peer_disconnected(node_id_0);
1518+
1519+
nodes[1].disable_channel_signer_op(&node_id_0, &chan_id, SignerOp::GetPerCommitmentPoint);
1520+
1521+
connect_nodes(&nodes[0], &nodes[1]);
1522+
let mut reestablish_0_to_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
1523+
let _ = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
1524+
reestablish_0_to_1[0].next_remote_commitment_number += 1;
1525+
nodes[1].node.handle_channel_reestablish(node_id_0, &reestablish_0_to_1[0]);
1526+
1527+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
1528+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
1529+
1530+
nodes[1].enable_channel_signer_op(&node_id_0, &chan_id, SignerOp::GetPerCommitmentPoint);
1531+
nodes[1].node.signer_unblocked(None);
1532+
1533+
let closure_reason = ClosureReason::ProcessingError {
1534+
err: "Peer sent a channel_reestablish indicating we're stale with an invalid commitment secret".to_owned(),
1535+
};
1536+
check_added_monitors(&nodes[1], 1);
1537+
check_closed_broadcast(&nodes[1], 1, true);
1538+
check_closed_event(&nodes[1], 1, closure_reason, false, &[node_id_0], 100_000);
1539+
}

0 commit comments

Comments
 (0)