lnwire: update ChannelReestablish with LocalNonces field
What changed, and why it matters
This commit adds a new optional data field called LocalNonces to a Lightning Network channel-recovery message. It is a protocol-extension change designed to support multiple cryptographic nonces for in-flight channel operations (such as splices). There is no direct evidence in the commit that this fixes or introduces a security vulnerability; it appears to be a feature/backwards-compatible protocol update.
Treat as a routine protocol-extension commit. Review the full definition of LocalNoncesData/OptLocalNonces (not shown in the diff) for map-size limits, duplicate-key behavior, and deserialization bounds to ensure no DoS or memory-exhaustion path is introduced. No immediate security action is warranted based solely on this diff.
Security signals we found
New optional TLV field added to a consensus-adjacent P2P message
Backwards-compatible encoding claimed by commit message
No validation, bounds, or duplicate-key handling changes visible in the diff
No explicit security relevance, CVE, or bug-fix language in commit or title
Evidence from the diff
The patch extends lnwire.ChannelReestablish with an optional TLV field CRLocalNonces (type 22) carrying a map of MuSig2 local nonces keyed by funding TXID. Encode/Decode are updated to serialize/deserialize the new TLV, and property-based tests are extended to randomly populate it. The change preserves the existing single LocalNonce and DynHeight fields and is intended to be backwards compatible.
Changed components
lnwire.ChannelReestablishlnwire/channel_reestablish.golnwire/test_message.goInspect captured patch +44 / −7
diff --git a/lnwire/channel_reestablish.go b/lnwire/channel_reestablish.go
index f26a2fc..b7246ab 100644
--- a/lnwire/channel_reestablish.go
+++ b/lnwire/channel_reestablish.go
@@ -10,7 +10,8 @@ import (
)
const (
- CRDynHeight tlv.Type = 20
+ CRDynHeight tlv.Type = 20
+ CRLocalNonces tlv.Type = 22
)
// DynHeight is a newtype wrapper to get the proper RecordProducer instance
@@ -89,6 +90,12 @@ type ChannelReestablish struct {
// a dynamic commitment negotiation
DynHeight fn.Option[DynHeight]
+ // LocalNonces is an optional field that stores a map of local musig2
+ // nonces, keyed by TXID. This extends the single-nonce LocalNonce
+ // field to support multiple in-flight splices, each of which needs
+ // its own nonce keyed by the relevant funding TXID.
+ LocalNonces OptLocalNonces
+
// ExtraData is the set of data that was appended to this message to
// fill out the full maximum transport message size. These fields can
// be used to specify optional data such as custom TLV fields.
@@ -140,19 +147,21 @@ func (a *ChannelReestablish) Encode(w *bytes.Buffer, pver uint32) error {
return err
}
- recordProducers := make([]tlv.RecordProducer, 0, 1)
+ recordProducers := make([]tlv.RecordProducer, 0, 3)
a.LocalNonce.WhenSome(func(localNonce Musig2NonceTLV) {
recordProducers = append(recordProducers, &localNonce)
})
a.DynHeight.WhenSome(func(h DynHeight) {
recordProducers = append(recordProducers, &h)
})
+ a.LocalNonces.WhenSome(func(ln LocalNoncesData) {
+ recordProducers = append(recordProducers, &ln)
+ })
err := EncodeMessageExtraData(&a.ExtraData, recordProducers...)
if err != nil {
return err
}
-
return WriteBytes(w, a.ExtraData)
}
@@ -207,11 +216,13 @@ func (a *ChannelReestablish) Decode(r io.Reader, pver uint32) error {
}
var (
- dynHeight DynHeight
- localNonce = a.LocalNonce.Zero()
+ dynHeight DynHeight
+ localNonce = a.LocalNonce.Zero()
+ localNoncesData LocalNoncesData
)
+
typeMap, err := tlvRecords.ExtractRecords(
- &localNonce, &dynHeight,
+ &localNonce, &dynHeight, &localNoncesData,
)
if err != nil {
return err
@@ -223,11 +234,13 @@ func (a *ChannelReestablish) Decode(r io.Reader, pver uint32) error {
if val, ok := typeMap[CRDynHeight]; ok && val == nil {
a.DynHeight = fn.Some(dynHeight)
}
+ if val, ok := typeMap[CRLocalNonces]; ok && val == nil {
+ a.LocalNonces = SomeLocalNonces(localNoncesData)
+ }
if len(tlvRecords) != 0 {
a.ExtraData = tlvRecords
}
-
return nil
}
diff --git a/lnwire/test_message.go b/lnwire/test_message.go
index 9af7621..498b591 100644
--- a/lnwire/test_message.go
+++ b/lnwire/test_message.go
@@ -376,6 +376,7 @@ func (a *ChannelReestablish) RandTestMessage(t *rapid.T) Message {
// Randomly decide whether to include optional fields
includeLocalNonce := rapid.Bool().Draw(t, "includeLocalNonce")
includeDynHeight := rapid.Bool().Draw(t, "includeDynHeight")
+ includeLocalNonces := rapid.Bool().Draw(t, "includeLocalNonces")
if includeLocalNonce {
nonce := RandMusig2Nonce(t)
@@ -387,6 +388,29 @@ func (a *ChannelReestablish) RandTestMessage(t *rapid.T) Message {
msg.DynHeight = fn.Some(height)
}
+ if includeLocalNonces {
+ numNonces := rapid.IntRange(0, 3).Draw(t, "numLocalNonces")
+ nonces := make(map[chainhash.Hash]Musig2Nonce)
+ for i := 0; i < numNonces; i++ {
+ txid := RandChainHash(t)
+
+ // Ensure unique txids for the map.
+ for {
+ _, ok := nonces[txid]
+ if !ok {
+ break
+ }
+ txid = RandChainHash(t)
+ }
+
+ nonces[txid] = RandMusig2Nonce(t)
+ }
+
+ msg.LocalNonces = SomeLocalNonces(
+ LocalNoncesData{NoncesMap: nonces},
+ )
+ }
+
return msg
}
Why this scored 17/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.