What changed, and why it matters
This commit adds a new internal-only message format called CommitSigBatch so that Eclair nodes running in cluster mode can send groups of commit_sig messages between internal machines. It is not a change to the public Lightning network protocol; peers still receive commit_sig messages one at a time. The change is a straightforward codec addition with a matching unit test.
No immediate action required. Treat as a routine feature addition. If reviewing the broader cluster-mode design, verify that CommitSigBatch messages are only accepted over authenticated internal channels and that the uint16 length limit and length-delimited framing are consistent with the cluster transport's maximum message size.
Security signals we found
New internal message codec added to existing protocol dispatch table
Length-prefixed list codec (uint16 count, length-delimited items) reuses existing commitSigCodec
No wire protocol change for external peers; cluster-mode-only serialization
No validation, authorization, or rate-limiting logic visible in the diff
Evidence from the diff
The patch introduces commitSigBatchCodec, which encodes a CommitSigBatch as a uint16 length-prefixed list of length-delimited CommitSig records. It registers the codec under message type 53011 in the internal lightningMessageCodec dispatch table. The test verifies a round-trip encode/decode of a batch containing three CommitSig messages. No parsing bounds, length checks, or cryptographic handling beyond the existing CommitSig codec are changed.
Changed components
eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scalaeclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scalaInspect captured patch +18 / −2
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala
index f506a3c..d8d5a32 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala
@@ -278,6 +278,10 @@ object LightningMessageCodecs {
("htlcSignatures" | listofsignatures) ::
("tlvStream" | CommitSigTlv.commitSigTlvCodec)).as[CommitSig]
+ // This isn't a "real" lightning codec, as we send each commit_sig individually to our peers.
+ // But it's necessary to send CommitSigBatch objects to front machines when the cluster mode is used.
+ val commitSigBatchCodec: Codec[CommitSigBatch] = listOfN(uint16, lengthDelimited(commitSigCodec)).xmap(sigs => CommitSigBatch(sigs.toSeq), batch => batch.messages.toList)
+
val revokeAndAckCodec: Codec[RevokeAndAck] = (
("channelId" | bytes32) ::
("perCommitmentSecret" | privateKey) ::
@@ -567,7 +571,7 @@ object LightningMessageCodecs {
//
.typecase(39409, recommendedFeeratesCodec)
//
-
+ .typecase(53011, commitSigBatchCodec)
//
//
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala
index 589ca88..4a3e13b 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala
@@ -27,7 +27,7 @@ import fr.acinq.eclair._
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.channel.ChannelSpendSignature.{IndividualSignature, PartialSignatureWithNonce}
import fr.acinq.eclair.channel.ChannelTypes.SimpleTaprootChannelsPhoenix
-import fr.acinq.eclair.channel.{ChannelFlags, ChannelTypes}
+import fr.acinq.eclair.channel.{ChannelFlags, ChannelSpendSignature, ChannelTypes}
import fr.acinq.eclair.json.JsonSerializers
import fr.acinq.eclair.reputation.Reputation
import fr.acinq.eclair.router.Announcements
@@ -696,6 +696,18 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
}
}
+ test("encode/decode commit_sig batch") {
+ val channelId = randomBytes32()
+ val batch = CommitSigBatch(Seq(
+ CommitSig(channelId, ChannelSpendSignature.IndividualSignature(randomBytes64()), Nil, batchSize = 3),
+ CommitSig(channelId, ChannelSpendSignature.IndividualSignature(randomBytes64()), Nil, batchSize = 3),
+ CommitSig(channelId, ChannelSpendSignature.IndividualSignature(randomBytes64()), Nil, batchSize = 3),
+ ))
+ val encoded = lightningMessageCodec.encode(batch).require
+ val decoded = lightningMessageCodec.decode(encoded).require.value
+ assert(decoded == batch)
+ }
+
test("unknown messages") {
// Non-standard tag number so this message can only be handled by a codec with a fallback
val unknown = UnknownMessage(tag = 47282, data = ByteVector32.Zeroes.bytes)
Why this scored 21/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.