Add a `counterparty_node_id` to `ClaimedHTLC` in claimed events
What changed, and why it matters
This commit adds extra identifying information (the counterparty's public node ID) to payment claim events in the Lightning Dev Kit. It is a defensive API improvement, not a fix for an active exploit. The change helps downstream applications reliably identify which channel partner sent a payment, especially in edge cases where channel IDs could overlap. No security vulnerability is directly patched here.
Treat as a routine API/observability improvement. Downstream consumers should update to use the new `counterparty_node_id` field when correlating claimed HTLCs with channels, but no urgent security deployment is required. Review whether any internal logic relies solely on `channel_id` uniqueness and should now also check `counterparty_node_id`.
Security signals we found
Adds counterparty_node_id to Event::PaymentClaimed/ClaimedHTLC to disambiguate channel identity
Commit message references non-unique channel_id risk from malicious 0conf counterparties
Backward-compatible TLV serialization for new optional field
No direct memory-safety, cryptographic, or consensus bug is fixed
Evidence from the diff
The patch extends the ClaimedHTLC struct in lightning/src/events/mod.rs with an optional counterparty_node_id: Option<PublicKey> field and populates it in channelmanager.rs from ClaimableHTLC.prev_hop.counterparty_node_id. Serialization uses TLV field index 3 as optional, preserving backward compatibility (None for pre-0.2 serialized objects). The commit message notes that channel IDs are not guaranteed unique in theory (e.g., malicious 0conf counterparties), and many LDK APIs require both node ID and channel ID.
Changed components
lightning/src/events/mod.rslightning/src/ln/channelmanager.rsEvent::PaymentClaimed public APIClaimedHTLC serialization formatInspect captured patch +7 / −0
diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs
index 2b71207..8ac0230 100644
--- a/lightning/src/events/mod.rs
+++ b/lightning/src/events/mod.rs
@@ -233,6 +233,11 @@ impl_writeable_tlv_based_enum_legacy!(PaymentPurpose,
/// Information about an HTLC that is part of a payment that can be claimed.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClaimedHTLC {
+ /// The counterparty of the channel.
+ ///
+ /// This value will always be `None` for objects serialized with LDK versions prior to 0.2 and
+ /// `Some` otherwise.
+ pub counterparty_node_id: Option<PublicKey>,
/// The `channel_id` of the channel over which the HTLC was received.
pub channel_id: ChannelId,
/// The `user_channel_id` of the channel over which the HTLC was received. This is the value
@@ -263,6 +268,7 @@ impl_writeable_tlv_based!(ClaimedHTLC, {
(0, channel_id, required),
(1, counterparty_skimmed_fee_msat, (default_value, 0u64)),
(2, user_channel_id, required),
+ (3, counterparty_node_id, option),
(4, cltv_expiry, required),
(6, value_msat, required),
});
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 0d05dc3..19129fd 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -512,6 +512,7 @@ struct ClaimableHTLC {
impl From<&ClaimableHTLC> for events::ClaimedHTLC {
fn from(val: &ClaimableHTLC) -> Self {
events::ClaimedHTLC {
+ counterparty_node_id: val.prev_hop.counterparty_node_id,
channel_id: val.prev_hop.channel_id,
user_channel_id: val.prev_hop.user_channel_id.unwrap_or(0),
cltv_expiry: val.cltv_expiry,
Why this scored 18/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.