Capture announcement signatures resend in reconnection tests
What changed, and why it matters
This commit only changes test code in the Lightning Dev Kit Rust repository. It extends the test helper that simulates two Lightning nodes reconnecting after a disconnection so it can now capture and optionally re-send 'announcement_signatures' messages. The change prepares tests for a future commit that will verify re-sending of these messages after a channel splice becomes locked. No production code is modified, so it cannot directly affect real users or introduce a runtime security vulnerability.
No security action required. Treat as normal test-infrastructure maintenance. Review the follow-up commit that uses these test changes to verify the actual announcement_signatures resend logic for correctness.
Security signals we found
No production code changes
Test-only refactor of reconnection helper
Adds coverage for announcement_signatures resend behavior
No new assertions of unsafe conditions in production code
Evidence from the diff
The diff is confined to Rust test modules under lightning/src/ln/. The handle_chan_reestablish_msgs! macro is updated to return a fifth element, announcement_sigs, by inspecting MessageSendEvent::SendAnnouncementSignatures in the reconnection event stream. ReconnectArgs gains a send_announcement_sigs field, and reconnect_nodes now optionally consumes and handles those announcement_signatures when the test requests it. Existing tests are updated to set the new flag and to ignore the extra tuple element where appropriate. There are no changes to message handling, state machine, cryptography, or networking logic in the library itself.
Changed components
lightning/src/ln/async_signer_tests.rslightning/src/ln/chanmon_update_fail_tests.rslightning/src/ln/functional_test_utils.rslightning/src/ln/functional_tests.rslightning/src/ln/payment_tests.rslightning/src/ln/quiescence_tests.rslightning/src/ln/reorg_tests.rslightning/src/ln/splicing_tests.rsInspect captured patch +95 / −13
diff --git a/lightning/src/ln/async_signer_tests.rs b/lightning/src/ln/async_signer_tests.rs
index de4ee80..ff4ef50 100644
--- a/lightning/src/ln/async_signer_tests.rs
+++ b/lightning/src/ln/async_signer_tests.rs
@@ -596,7 +596,7 @@ fn do_test_async_raa_peer_disconnect(
}
// Expect the RAA
- let (_, revoke_and_ack, commitment_signed, resend_order) =
+ let (_, revoke_and_ack, commitment_signed, resend_order, _) =
handle_chan_reestablish_msgs!(dst, src);
if test_case == UnblockSignerAcrossDisconnectCase::AtEnd {
assert!(revoke_and_ack.is_none());
@@ -612,14 +612,14 @@ fn do_test_async_raa_peer_disconnect(
dst.node.signer_unblocked(Some((src_node_id, chan_id)));
if test_case == UnblockSignerAcrossDisconnectCase::AtEnd {
- let (_, revoke_and_ack, commitment_signed, resend_order) =
+ let (_, revoke_and_ack, commitment_signed, resend_order, _) =
handle_chan_reestablish_msgs!(dst, src);
assert!(revoke_and_ack.is_some());
assert!(commitment_signed.is_some());
assert!(resend_order == RAACommitmentOrder::RevokeAndACKFirst);
} else {
// Make sure we don't double send the RAA.
- let (_, revoke_and_ack, commitment_signed, _) = handle_chan_reestablish_msgs!(dst, src);
+ let (_, revoke_and_ack, commitment_signed, _, _) = handle_chan_reestablish_msgs!(dst, src);
assert!(revoke_and_ack.is_none());
assert!(commitment_signed.is_none());
}
@@ -745,7 +745,7 @@ fn do_test_async_commitment_signature_peer_disconnect(
}
// Expect the RAA
- let (_, revoke_and_ack, commitment_signed, _) = handle_chan_reestablish_msgs!(dst, src);
+ let (_, revoke_and_ack, commitment_signed, _, _) = handle_chan_reestablish_msgs!(dst, src);
assert!(revoke_and_ack.is_some());
if test_case == UnblockSignerAcrossDisconnectCase::AtEnd {
assert!(commitment_signed.is_none());
@@ -758,11 +758,11 @@ fn do_test_async_commitment_signature_peer_disconnect(
dst.node.signer_unblocked(Some((src_node_id, chan_id)));
if test_case == UnblockSignerAcrossDisconnectCase::AtEnd {
- let (_, _, commitment_signed, _) = handle_chan_reestablish_msgs!(dst, src);
+ let (_, _, commitment_signed, _, _) = handle_chan_reestablish_msgs!(dst, src);
assert!(commitment_signed.is_some());
} else {
// Make sure we don't double send the CS.
- let (_, _, commitment_signed, _) = handle_chan_reestablish_msgs!(dst, src);
+ let (_, _, commitment_signed, _, _) = handle_chan_reestablish_msgs!(dst, src);
assert!(commitment_signed.is_none());
}
}
@@ -877,6 +877,7 @@ fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool) {
assert!(as_resp.0.is_none());
assert!(as_resp.1.is_none());
assert!(as_resp.2.is_none());
+ assert!(as_resp.4.is_none());
if monitor_update_failure {
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed);
@@ -896,6 +897,7 @@ fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool) {
assert!(as_resp.0.is_none());
assert!(as_resp.1.is_none());
assert!(as_resp.2.is_none());
+ assert!(as_resp.4.is_none());
nodes[0].enable_channel_signer_op(&node_b_id, &chan_id, SignerOp::SignCounterpartyCommitment);
nodes[0].node.signer_unblocked(Some((node_b_id, chan_id)));
@@ -912,6 +914,9 @@ fn do_test_async_commitment_signature_ordering(monitor_update_failure: bool) {
assert!(as_resp.3 == RAACommitmentOrder::CommitmentFirst);
+ assert!(as_resp.4.is_none());
+ assert!(bs_resp.4.is_none());
+
// Now that everything is restored, get the CS + RAA and handle them.
nodes[1]
.node
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index 1bc1bfb..1a9af4f 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -206,6 +206,7 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) {
nodes[1].node.peer_disconnected(node_a_id);
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
reconnect_args.send_channel_ready = (true, true);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_nodes(reconnect_args);
}
diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs
index 6bde99b..aa2c45c 100644
--- a/lightning/src/ln/functional_test_utils.rs
+++ b/lightning/src/ln/functional_test_utils.rs
@@ -4796,11 +4796,14 @@ macro_rules! handle_chan_reestablish_msgs {
None
};
- if let Some(&MessageSendEvent::SendAnnouncementSignatures { ref node_id, msg: _ }) =
+ let mut announcement_sigs = None; // May be now or later
+ if let Some(&MessageSendEvent::SendAnnouncementSignatures { ref node_id, ref msg }) =
msg_events.get(idx)
{
idx += 1;
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
+ assert!(announcement_sigs.is_none());
+ announcement_sigs = Some(msg.clone());
}
let mut had_channel_update = false; // ChannelUpdate may be now or later, but not both
@@ -4859,6 +4862,15 @@ macro_rules! handle_chan_reestablish_msgs {
}
}
+ if let Some(&MessageSendEvent::SendAnnouncementSignatures { ref node_id, ref msg }) =
+ msg_events.get(idx)
+ {
+ idx += 1;
+ assert_eq!(*node_id, $dst_node.node.get_our_node_id());
+ assert!(announcement_sigs.is_none());
+ announcement_sigs = Some(msg.clone());
+ }
+
if let Some(&MessageSendEvent::SendChannelUpdate { ref node_id, .. }) = msg_events.get(idx)
{
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
@@ -4866,9 +4878,9 @@ macro_rules! handle_chan_reestablish_msgs {
assert!(!had_channel_update);
}
- assert_eq!(msg_events.len(), idx);
+ assert_eq!(msg_events.len(), idx, "{msg_events:?}");
- (channel_ready, revoke_and_ack, commitment_update, order)
+ (channel_ready, revoke_and_ack, commitment_update, order, announcement_sigs)
}};
}
@@ -4876,6 +4888,7 @@ pub struct ReconnectArgs<'a, 'b, 'c, 'd> {
pub node_a: &'a Node<'b, 'c, 'd>,
pub node_b: &'a Node<'b, 'c, 'd>,
pub send_channel_ready: (bool, bool),
+ pub send_announcement_sigs: (bool, bool),
pub pending_responding_commitment_signed: (bool, bool),
/// Indicates that the pending responding commitment signed will be a dup for the recipient,
/// and no monitor update is expected
@@ -4894,6 +4907,7 @@ impl<'a, 'b, 'c, 'd> ReconnectArgs<'a, 'b, 'c, 'd> {
node_a,
node_b,
send_channel_ready: (false, false),
+ send_announcement_sigs: (false, false),
pending_responding_commitment_signed: (false, false),
pending_responding_commitment_signed_dup_monitor: (false, false),
pending_htlc_adds: (0, 0),
@@ -4913,6 +4927,7 @@ pub fn reconnect_nodes<'a, 'b, 'c, 'd>(args: ReconnectArgs<'a, 'b, 'c, 'd>) {
node_a,
node_b,
send_channel_ready,
+ send_announcement_sigs,
pending_htlc_adds,
pending_htlc_claims,
pending_htlc_fails,
@@ -4994,7 +5009,7 @@ pub fn reconnect_nodes<'a, 'b, 'c, 'd>(args: ReconnectArgs<'a, 'b, 'c, 'd>) {
&& pending_cell_htlc_fails.1 == 0)
);
- for chan_msgs in resp_1.drain(..) {
+ for mut chan_msgs in resp_1.drain(..) {
if send_channel_ready.0 {
node_a.node.handle_channel_ready(node_b_id, &chan_msgs.0.unwrap());
let announcement_event = node_a.node.get_and_clear_pending_msg_events();
@@ -5009,6 +5024,18 @@ pub fn reconnect_nodes<'a, 'b, 'c, 'd>(args: ReconnectArgs<'a, 'b, 'c, 'd>) {
} else {
assert!(chan_msgs.0.is_none());
}
+ if send_announcement_sigs.0 {
+ let announcement_sigs = chan_msgs.4.take().unwrap();
+ node_a.node.handle_announcement_signatures(node_b_id, &announcement_sigs);
+ let msg_events = node_a.node.get_and_clear_pending_msg_events();
+ assert_eq!(msg_events.len(), 1, "{msg_events:?}");
+ if let MessageSendEvent::BroadcastChannelAnnouncement { .. } = msg_events[0] {
+ } else {
+ panic!("Unexpected event! {:?}", msg_events[0]);
+ }
+ } else {
+ assert!(chan_msgs.4.is_none());
+ }
if pending_raa.0 {
assert!(chan_msgs.3 == RAACommitmentOrder::RevokeAndACKFirst);
node_a.node.handle_revoke_and_ack(node_b_id, &chan_msgs.1.unwrap());
@@ -5073,7 +5100,7 @@ pub fn reconnect_nodes<'a, 'b, 'c, 'd>(args: ReconnectArgs<'a, 'b, 'c, 'd>) {
}
}
- for chan_msgs in resp_2.drain(..) {
+ for mut chan_msgs in resp_2.drain(..) {
if send_channel_ready.1 {
node_b.node.handle_channel_ready(node_a_id, &chan_msgs.0.unwrap());
let announcement_event = node_b.node.get_and_clear_pending_msg_events();
@@ -5088,6 +5115,18 @@ pub fn reconnect_nodes<'a, 'b, 'c, 'd>(args: ReconnectArgs<'a, 'b, 'c, 'd>) {
} else {
assert!(chan_msgs.0.is_none());
}
+ if send_announcement_sigs.1 {
+ let announcement_sigs = chan_msgs.4.take().unwrap();
+ node_b.node.handle_announcement_signatures(node_a_id, &announcement_sigs);
+ let mut msg_events = node_b.node.get_and_clear_pending_msg_events();
+ assert_eq!(msg_events.len(), 1, "{msg_events:?}");
+ if let MessageSendEvent::BroadcastChannelAnnouncement { .. } = msg_events.remove(0) {
+ } else {
+ panic!();
+ }
+ } else {
+ assert!(chan_msgs.4.is_none());
+ }
if pending_raa.1 {
assert!(chan_msgs.3 == RAACommitmentOrder::RevokeAndACKFirst);
node_b.node.handle_revoke_and_ack(node_a_id, &chan_msgs.1.unwrap());
diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs
index d1c0ac8..d79b307 100644
--- a/lightning/src/ln/functional_tests.rs
+++ b/lightning/src/ln/functional_tests.rs
@@ -2559,6 +2559,7 @@ pub fn test_simple_peer_disconnect() {
nodes[1].node.peer_disconnected(node_a_id);
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
reconnect_args.send_channel_ready = (true, true);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_nodes(reconnect_args);
let payment_preimage_1 = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 1000000).0;
@@ -2716,22 +2717,29 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken
// received on either side, both sides will need to resend them.
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
reconnect_args.send_channel_ready = (true, true);
+ if simulate_broken_lnd || messages_delivered > 0 {
+ reconnect_args.send_announcement_sigs.0 = true;
+ }
+ reconnect_args.send_announcement_sigs.1 = true;
reconnect_args.pending_htlc_adds.1 = 1;
reconnect_nodes(reconnect_args);
} else if messages_delivered == 3 {
// nodes[0] still wants its RAA + commitment_signed
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_args.pending_responding_commitment_signed.0 = true;
reconnect_args.pending_raa.0 = true;
reconnect_nodes(reconnect_args);
} else if messages_delivered == 4 {
// nodes[0] still wants its commitment_signed
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ reconnect_args.send_announcement_sigs.0 = true;
reconnect_args.pending_responding_commitment_signed.0 = true;
reconnect_nodes(reconnect_args);
} else if messages_delivered == 5 {
// nodes[1] still wants its final RAA
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ reconnect_args.send_announcement_sigs.0 = true;
reconnect_args.pending_raa.1 = true;
reconnect_nodes(reconnect_args);
} else if messages_delivered == 6 {
@@ -2752,7 +2760,16 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken
nodes[0].node.peer_disconnected(node_b_id);
nodes[1].node.peer_disconnected(node_a_id);
- reconnect_nodes(ReconnectArgs::new(&nodes[0], &nodes[1]));
+ let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ if !simulate_broken_lnd
+ && (messages_delivered == 0 || (messages_delivered > 2 && messages_delivered < 6))
+ {
+ reconnect_args.send_announcement_sigs.0 = true;
+ }
+ if messages_delivered < 4 {
+ reconnect_args.send_announcement_sigs.1 = true;
+ }
+ reconnect_nodes(reconnect_args);
nodes[1].node.process_pending_htlc_forwards();
@@ -2850,6 +2867,10 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken
nodes[1].node.peer_disconnected(node_a_id);
if messages_delivered < 2 {
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ if !simulate_broken_lnd && messages_delivered == 0 {
+ reconnect_args.send_announcement_sigs.0 = true;
+ }
+ reconnect_args.send_announcement_sigs.1 = true;
reconnect_args.pending_htlc_claims.0 = 1;
reconnect_nodes(reconnect_args);
if messages_delivered < 1 {
@@ -2860,12 +2881,14 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken
} else if messages_delivered == 2 {
// nodes[0] still wants its RAA + commitment_signed
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ reconnect_args.send_announcement_sigs.1 = true;
reconnect_args.pending_responding_commitment_signed.1 = true;
reconnect_args.pending_raa.1 = true;
reconnect_nodes(reconnect_args);
} else if messages_delivered == 3 {
// nodes[0] still wants its commitment_signed
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ reconnect_args.send_announcement_sigs.1 = true;
reconnect_args.pending_responding_commitment_signed.1 = true;
reconnect_nodes(reconnect_args);
} else if messages_delivered == 4 {
@@ -2885,7 +2908,15 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken
nodes[0].node.peer_disconnected(node_b_id);
nodes[1].node.peer_disconnected(node_a_id);
}
- reconnect_nodes(ReconnectArgs::new(&nodes[0], &nodes[1]));
+ let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ if !simulate_broken_lnd {
+ if messages_delivered == 0 {
+ reconnect_args.send_announcement_sigs.0 = true;
+ } else if messages_delivered == 2 || messages_delivered == 3 {
+ reconnect_args.send_announcement_sigs.1 = true;
+ }
+ }
+ reconnect_nodes(reconnect_args);
if messages_delivered > 2 {
expect_payment_path_successful!(nodes[0]);
diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs
index d2479bb..9eb8517 100644
--- a/lightning/src/ln/payment_tests.rs
+++ b/lightning/src/ln/payment_tests.rs
@@ -4858,6 +4858,7 @@ fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) {
}
let mut reconnect_args = ReconnectArgs::new(&nodes[2], &nodes[3]);
reconnect_args.send_channel_ready = (true, true);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_nodes(reconnect_args);
// Create a new channel between C and D as A will refuse to retry on the existing one because
diff --git a/lightning/src/ln/quiescence_tests.rs b/lightning/src/ln/quiescence_tests.rs
index 63c5009..7a5d35b 100644
--- a/lightning/src/ln/quiescence_tests.rs
+++ b/lightning/src/ln/quiescence_tests.rs
@@ -755,6 +755,7 @@ fn do_test_quiescence_termination_on_disconnect(reload: bool) {
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
reconnect_args.send_channel_ready = (true, true);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_nodes(reconnect_args);
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
diff --git a/lightning/src/ln/reorg_tests.rs b/lightning/src/ln/reorg_tests.rs
index 90cd459..b040f45 100644
--- a/lightning/src/ln/reorg_tests.rs
+++ b/lightning/src/ln/reorg_tests.rs
@@ -333,6 +333,7 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool, use_funding_
// generate an error message we can handle below.
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
reconnect_args.send_channel_ready = (true, true);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_nodes(reconnect_args);
}
}
diff --git a/lightning/src/ln/splicing_tests.rs b/lightning/src/ln/splicing_tests.rs
index 1384281..fc062d6 100644
--- a/lightning/src/ln/splicing_tests.rs
+++ b/lightning/src/ln/splicing_tests.rs
@@ -401,6 +401,7 @@ fn do_test_splice_state_reset_on_disconnect(reload: bool) {
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
reconnect_args.send_channel_ready = (true, true);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_nodes(reconnect_args);
nodes[0]
@@ -457,6 +458,7 @@ fn do_test_splice_state_reset_on_disconnect(reload: bool) {
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
reconnect_args.send_channel_ready = (true, true);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_nodes(reconnect_args);
// Attempt a splice negotiation that completes, (i.e. `tx_signatures` are exchanged). Reconnecting
@@ -488,6 +490,7 @@ fn do_test_splice_state_reset_on_disconnect(reload: bool) {
}
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
+ reconnect_args.send_announcement_sigs = (true, true);
reconnect_nodes(reconnect_args);
mine_transaction(&nodes[0], &splice_tx);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.