Add `counterparty_node_id` to `Event::SpendableOutputs`
What changed, and why it matters
This commit adds an optional extra piece of information—the counterparty's node ID—to an existing event that tells users when they have spendable on-chain outputs. It is a backward-compatible API enhancement using an odd TLV type, so older versions ignore the new field. There is no security fix or vulnerability here.
No security action required. Treat as a normal feature/API improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change extends Event::SpendableOutputs with an optional counterparty_node_id: Option
Changed components
lightning/src/events/mod.rslightning/src/chain/channelmonitor.rslightning-background-processor/src/lib.rslightning-tests/src/upgrade_downgrade_tests.rslightning/src/ln/functional_tests.rslightning/src/ln/monitor_tests.rslightning/src/ln/reorg_tests.rsInspect captured patch +23 / −9
diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs
index bb99d65..d34ef7d 100644
--- a/lightning-background-processor/src/lib.rs
+++ b/lightning-background-processor/src/lib.rs
@@ -3110,7 +3110,7 @@ mod tests {
let event =
receiver.recv_timeout(EVENT_DEADLINE).expect("Events not handled within deadline");
match event {
- Event::SpendableOutputs { outputs, channel_id } => {
+ Event::SpendableOutputs { outputs, channel_id, counterparty_node_id: _ } => {
nodes[0]
.sweeper
.track_spendable_outputs(outputs, channel_id, false, Some(153))
diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs
index 8df6703..14b0a5c 100644
--- a/lightning-tests/src/upgrade_downgrade_tests.rs
+++ b/lightning-tests/src/upgrade_downgrade_tests.rs
@@ -308,7 +308,9 @@ fn test_0_1_legacy_remote_key_derivation() {
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
let mut spendable_event = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(spendable_event.len(), 1);
- if let Event::SpendableOutputs { outputs, channel_id: ev_id } = spendable_event.pop().unwrap() {
+ if let Event::SpendableOutputs { outputs, channel_id: ev_id, counterparty_node_id: _ } =
+ spendable_event.pop().unwrap()
+ {
assert_eq!(ev_id.unwrap().0, channel_id);
assert_eq!(outputs.len(), 1);
let spk = Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script();
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index a537ff5..b109466 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -5619,6 +5619,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
self.pending_events.push(Event::SpendableOutputs {
outputs: vec![descriptor],
channel_id: Some(self.channel_id()),
+ counterparty_node_id: Some(self.counterparty_node_id),
});
self.spendable_txids_confirmed.push(entry.txid);
},
diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs
index 3d860e9..a5ac08b 100644
--- a/lightning/src/events/mod.rs
+++ b/lightning/src/events/mod.rs
@@ -1318,6 +1318,10 @@ pub enum Event {
///
/// This will always be `Some` for events generated by LDK versions 0.0.117 and above.
channel_id: Option<ChannelId>,
+ /// The `node_id` of the channel counterparty.
+ ///
+ /// This will always be `Some` for events generated by LDK versions 0.3 and above.
+ counterparty_node_id: Option<PublicKey>,
},
/// This event is generated when a payment has been successfully forwarded through us and a
/// forwarding fee earned.
@@ -2012,11 +2016,12 @@ impl Writeable for Event {
});
},
// 4u8 used to be `PendingHTLCsForwardable`
- &Event::SpendableOutputs { ref outputs, channel_id } => {
+ &Event::SpendableOutputs { ref outputs, channel_id, counterparty_node_id } => {
5u8.write(writer)?;
write_tlv_fields!(writer, {
(0, WithoutLength(outputs), required),
(1, channel_id, option),
+ (3, counterparty_node_id, option),
});
},
&Event::HTLCIntercepted {
@@ -2521,11 +2526,17 @@ impl MaybeReadable for Event {
let mut f = || {
let mut outputs = WithoutLength(Vec::new());
let mut channel_id: Option<ChannelId> = None;
+ let mut counterparty_node_id: Option<PublicKey> = None;
read_tlv_fields!(reader, {
(0, outputs, required),
(1, channel_id, option),
+ (3, counterparty_node_id, option),
});
- Ok(Some(Event::SpendableOutputs { outputs: outputs.0, channel_id }))
+ Ok(Some(Event::SpendableOutputs {
+ outputs: outputs.0,
+ channel_id,
+ counterparty_node_id,
+ }))
};
f()
},
diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs
index 8e854b3..a7a062a 100644
--- a/lightning/src/ln/functional_tests.rs
+++ b/lightning/src/ln/functional_tests.rs
@@ -3430,7 +3430,7 @@ macro_rules! check_spendable_outputs {
let secp_ctx = Secp256k1::new();
for event in events.drain(..) {
match event {
- Event::SpendableOutputs { mut outputs, channel_id: _ } => {
+ Event::SpendableOutputs { mut outputs, channel_id: _, counterparty_node_id: _ } => {
for outp in outputs.drain(..) {
let script =
Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script();
diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs
index 097266c..aebd83a 100644
--- a/lightning/src/ln/monitor_tests.rs
+++ b/lightning/src/ln/monitor_tests.rs
@@ -2209,7 +2209,7 @@ fn do_test_revoked_counterparty_aggregated_claims(keyed_anchors: bool, p2a_ancho
let spendable_output_events = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(spendable_output_events.len(), 2);
for event in spendable_output_events {
- if let Event::SpendableOutputs { outputs, channel_id: _ } = event {
+ if let Event::SpendableOutputs { outputs, channel_id: _, counterparty_node_id: _ } = event {
assert_eq!(outputs.len(), 1);
let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(
&[&outputs[0]], Vec::new(), ScriptBuf::new_op_return(&[]), 253, None, &Secp256k1::new(),
@@ -2992,7 +2992,7 @@ fn do_test_anchors_aggregated_revoked_htlc_tx(p2a_anchor: bool) {
// - 1 static to_remote output.
assert_eq!(spendable_output_events.len(), 4);
for event in spendable_output_events {
- if let Event::SpendableOutputs { outputs, channel_id } = event {
+ if let Event::SpendableOutputs { outputs, channel_id, counterparty_node_id: _ } = event {
assert_eq!(outputs.len(), 1);
assert!([chan_b.2, chan_a.2].contains(&channel_id.unwrap()));
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(
diff --git a/lightning/src/ln/reorg_tests.rs b/lightning/src/ln/reorg_tests.rs
index b56caf9..25ad031 100644
--- a/lightning/src/ln/reorg_tests.rs
+++ b/lightning/src/ln/reorg_tests.rs
@@ -622,7 +622,7 @@ fn do_test_to_remote_after_local_detection(style: ConnectStyle) {
let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(node_a_spendable.len(), 1);
- if let Event::SpendableOutputs { outputs, channel_id } = node_a_spendable.pop().unwrap() {
+ if let Event::SpendableOutputs { outputs, channel_id, counterparty_node_id: _ } = node_a_spendable.pop().unwrap() {
assert_eq!(outputs.len(), 1);
assert_eq!(channel_id, Some(chan_id));
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
@@ -643,7 +643,7 @@ fn do_test_to_remote_after_local_detection(style: ConnectStyle) {
let mut node_b_spendable = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
assert_eq!(node_b_spendable.len(), 1);
- if let Event::SpendableOutputs { outputs, channel_id } = node_b_spendable.pop().unwrap() {
+ if let Event::SpendableOutputs { outputs, channel_id, counterparty_node_id: _ } = node_b_spendable.pop().unwrap() {
assert_eq!(outputs.len(), 1);
assert_eq!(channel_id, Some(chan_id));
let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
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.