Add funding_locked_txid TLVs to channel_reestablish
What changed, and why it matters
This commit adds new optional message fields to the Lightning channel-reestablish protocol so peers can compare which funding transaction each side believes is active after reconnecting. It is a partial implementation of a protocol extension (splicing). The actual logic that uses these new fields to detect or retransmit lost messages is not included in this commit, so the immediate security risk is very low. It mainly prepares the wire format and data structures for future behavior.
No immediate action required. Treat as a normal feature/protocol-update commit. Review the follow-up commits that implement the spec requirements to ensure lost `splice_locked` retransmission is handled safely and that `retransmit_flags` bits are validated before acting on them.
Security signals we found
New TLV field added to a peer protocol message (channel_reestablish)
Commit message states this is a partial/spec-prep change; behavior not yet implemented
No bounds checks or validation logic for received retransmit_flags in this commit
No memory-unsafe code or obvious parsing overflow introduced
Test coverage added for encoding only, not for retransmission behavior
Evidence from the diff
The change extends ChannelReestablish with a new TLV (type-length-value) field my_current_funding_locked of type Option<FundingLocked>, plus a new FundingLocked struct carrying a txid and an 8-bit retransmit_flags field. It updates serialization (impl_writeable_msg! and impl_writeable!), construction sites in channel.rs and channelmanager.rs, and unit tests for encoding. The commit message explicitly states this is only the wire-format update and that ‘subsequent commits will implement the spec requirements.’ No parsing/validation logic that acts on the received TLV is added here.
Changed components
lightning/src/ln/msgs.rslightning/src/ln/channel.rslightning/src/ln/channelmanager.rsInspect captured patch +91 / −0
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index f12ea13..5a78bd9 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -11013,6 +11013,7 @@ where
your_last_per_commitment_secret: remote_last_secret,
my_current_per_commitment_point: dummy_pubkey,
next_funding_txid: self.maybe_get_next_funding_txid(),
+ my_current_funding_locked: None,
}
}
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index ef2630d..5c2627b 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -11088,6 +11088,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
your_last_per_commitment_secret: [1u8; 32],
my_current_per_commitment_point: PublicKey::from_slice(&[2u8; 33]).unwrap(),
next_funding_txid: None,
+ my_current_funding_locked: None,
},
});
return Err(MsgHandleErrInternal::send_err_msg_no_close(
diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs
index 71f73e0..93107de 100644
--- a/lightning/src/ln/msgs.rs
+++ b/lightning/src/ln/msgs.rs
@@ -926,6 +926,28 @@ pub struct ChannelReestablish {
/// * `channel_reestablish`-sending node: https:///github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L2466-L2470
/// * `channel_reestablish`-receiving node: https:///github.com/lightning/bolts/blob/247e83d/02-peer-protocol.md?plain=1#L2520-L2531
pub next_funding_txid: Option<Txid>,
+ /// The last funding txid sent by the sending node, which may be:
+ /// - the txid of the last `splice_locked` it sent, otherwise
+ /// - the txid of the funding transaction if it sent `channel_ready`, or else
+ /// - `None` if it has never sent `channel_ready` or `splice_locked`
+ ///
+ /// Also contains a bitfield indicating which messages should be retransmitted.
+ pub my_current_funding_locked: Option<FundingLocked>,
+}
+
+/// Information exchanged during channel reestablishment about the last funding locked.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub struct FundingLocked {
+ /// The last txid sent by the sending node, which may be either from the last `splice_locked` or
+ /// for the initial funding transaction if it sent `channel_ready`.
+ pub txid: Txid,
+
+ /// A bitfield indicating which messages should be retransmitted by the receiving node.
+ ///
+ /// | Bit Position | Name |
+ /// | ------------- | --------------------------|
+ /// | 0 | `announcement_signatures` |
+ pub retransmit_flags: u8,
}
/// An [`announcement_signatures`] message to be sent to or received from a peer.
@@ -2852,6 +2874,12 @@ impl_writeable_msg!(ChannelReestablish, {
my_current_per_commitment_point,
}, {
(0, next_funding_txid, option),
+ (5, my_current_funding_locked, option),
+});
+
+impl_writeable!(FundingLocked, {
+ txid,
+ retransmit_flags
});
impl_writeable_msg!(ClosingSigned,
@@ -4321,6 +4349,7 @@ mod tests {
your_last_per_commitment_secret: [9; 32],
my_current_per_commitment_point: public_key,
next_funding_txid: None,
+ my_current_funding_locked: None,
};
let encoded_value = cr.encode();
@@ -4372,6 +4401,7 @@ mod tests {
])
.unwrap(),
)),
+ my_current_funding_locked: None,
};
let encoded_value = cr.encode();
@@ -4395,6 +4425,65 @@ mod tests {
);
}
+ #[test]
+ fn encoding_channel_reestablish_with_funding_locked_txid() {
+ let public_key = {
+ let secp_ctx = Secp256k1::new();
+ PublicKey::from_secret_key(
+ &secp_ctx,
+ &SecretKey::from_slice(
+ &<Vec<u8>>::from_hex(
+ "0101010101010101010101010101010101010101010101010101010101010101",
+ )
+ .unwrap()[..],
+ )
+ .unwrap(),
+ )
+ };
+
+ let cr = msgs::ChannelReestablish {
+ channel_id: ChannelId::from_bytes([
+ 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]),
+ next_local_commitment_number: 3,
+ next_remote_commitment_number: 4,
+ your_last_per_commitment_secret: [9; 32],
+ my_current_per_commitment_point: public_key,
+ next_funding_txid: None,
+ my_current_funding_locked: Some(msgs::FundingLocked {
+ txid: Txid::from_raw_hash(
+ bitcoin::hashes::Hash::from_slice(&[
+ 21, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15,
+ 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124,
+ ])
+ .unwrap(),
+ ),
+ retransmit_flags: 1,
+ }),
+ };
+
+ let encoded_value = cr.encode();
+ assert_eq!(
+ encoded_value,
+ vec![
+ 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, // channel_id
+ 0, 0, 0, 0, 0, 0, 0, 3, // next_local_commitment_number
+ 0, 0, 0, 0, 0, 0, 0, 4, // next_remote_commitment_number
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, // your_last_per_commitment_secret
+ 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30,
+ 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7,
+ 143, // my_current_per_commitment_point
+ 5, // Type (my_current_funding_locked)
+ 33, // Length
+ 21, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4,
+ 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, 1, // Value
+ ]
+ );
+ }
+
macro_rules! get_keys_from {
($slice: expr, $secp_ctx: expr) => {{
let privkey = SecretKey::from_slice(&<Vec<u8>>::from_hex($slice).unwrap()[..]).unwrap();
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.