splice: Add `start_batch` and an internal wire type
What changed, and why it matters
This commit adds support for two new Lightning protocol messages used in splicing: `start_batch` and an internal-only `protocol_batch_element`. The code mostly ignores or routes these messages; it does not appear to fix a security bug. The change is a feature addition matching a draft specification, with no clear security relevance in the diff itself.
Treat as a routine feature commit. Reviewers should verify that `WIRE_PROTOCOL_BATCH_ELEMENT` type 0 cannot be injected by untrusted peers, and that `start_batch` fields are properly validated when full splicing batch logic is later implemented. No immediate security action required.
Security signals we found
New peer wire message type with type number 0 (`protocol_batch_element`) added; type 0 is reserved/internal and could conflict with future protocol allocations if mishandled.
Messages are accepted/ignored without validation of sender, channel state, or batch parameters, which is consistent with a stub implementation but increases attack surface.
No changelog or commit message indicates a security fix; framed as a feature addition.
No explicit bounds or malformed-message handling added for the new `batch_size` or `element_size` fields.
Evidence from the diff
The patch introduces WIRE_START_BATCH (type 127) and WIRE_PROTOCOL_BATCH_ELEMENT (type 0) to Core Lightning’s wire protocol definitions and updates message dispatch in channeld, openingd, connectd, gossipd, and common helpers. WIRE_START_BATCH is accepted and ignored in openingd and channeld. WIRE_PROTOCOL_BATCH_ELEMENT is treated as non-gossip/non-public and swallowed by connectd. The new wire types are added to various switch statements so existing code compiles and routes them. There is no parsing hardening, authorization check, or memory-safety fix visible in the diff.
Changed components
channeld/channeld.copeningd/dualopend.cconnectd/multiplex.cconnectd/gossip_store.cconnectd/gossip_rcvd_filter.cgossipd/gossipd.ccommon/interactivetx.ccommon/gossmap.cwire/peer_wire.cwire/peer_wire.csvInspect captured patch +68 / −0
diff --git a/channeld/channeld.c b/channeld/channeld.c
index a94cd887..287cda70 100644
--- a/channeld/channeld.c
+++ b/channeld/channeld.c
@@ -4906,6 +4906,8 @@ static void peer_in(struct peer *peer, const u8 *msg)
case WIRE_UPDATE_ADD_HTLC:
handle_peer_add_htlc(peer, msg);
return;
+ case WIRE_START_BATCH:
+ return;
case WIRE_COMMITMENT_SIGNED:
handle_peer_commit_sig_batch(peer, msg, 0,
peer->channel->funding_pubkey[REMOTE],
@@ -4976,6 +4978,7 @@ static void peer_in(struct peer *peer, const u8 *msg)
return;
/* These are all swallowed by connectd */
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
case WIRE_CHANNEL_ANNOUNCEMENT:
case WIRE_CHANNEL_UPDATE:
case WIRE_NODE_ANNOUNCEMENT:
diff --git a/common/gossmap.c b/common/gossmap.c
index 576acd0e..1fbd72f9 100644
--- a/common/gossmap.c
+++ b/common/gossmap.c
@@ -1799,6 +1799,8 @@ const void *gossmap_stream_next(const tal_t *ctx,
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
diff --git a/common/interactivetx.c b/common/interactivetx.c
index 03ceb9d1..6e1aa1c9 100644
--- a/common/interactivetx.c
+++ b/common/interactivetx.c
@@ -167,6 +167,8 @@ static u8 *read_next_msg(const tal_t *ctx,
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
@@ -821,6 +823,8 @@ char *process_interactivetx_updates(const tal_t *ctx,
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
diff --git a/connectd/gossip_rcvd_filter.c b/connectd/gossip_rcvd_filter.c
index 2337b48d..c8097a4d 100644
--- a/connectd/gossip_rcvd_filter.c
+++ b/connectd/gossip_rcvd_filter.c
@@ -73,6 +73,8 @@ static bool is_msg_gossip_broadcast(const u8 *cursor)
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
diff --git a/connectd/gossip_store.c b/connectd/gossip_store.c
index 561387ed..7a6bb61d 100644
--- a/connectd/gossip_store.c
+++ b/connectd/gossip_store.c
@@ -82,6 +82,8 @@ static bool public_msg_type(enum peer_wire type)
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
diff --git a/connectd/multiplex.c b/connectd/multiplex.c
index 49eccbb3..f493b6a9 100644
--- a/connectd/multiplex.c
+++ b/connectd/multiplex.c
@@ -382,6 +382,8 @@ static bool is_urgent(enum peer_wire type)
/* These are time-sensitive, and so send without delay. */
case WIRE_PING:
case WIRE_PONG:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
return true;
diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c
index 4f773658..41807050 100644
--- a/gossipd/gossipd.c
+++ b/gossipd/gossipd.c
@@ -244,6 +244,8 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg)
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
diff --git a/openingd/dualopend.c b/openingd/dualopend.c
index f6c183a9..357ba939 100644
--- a/openingd/dualopend.c
+++ b/openingd/dualopend.c
@@ -1673,6 +1673,8 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state)
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
@@ -2057,6 +2059,8 @@ static bool run_tx_interactive(struct state *state,
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
@@ -4196,6 +4200,9 @@ static u8 *handle_peer_in(struct state *state)
}
accepter_start(state, msg);
return NULL;
+ case WIRE_START_BATCH:
+ /* We ignore batch messages as we dont need them */
+ return NULL;
case WIRE_COMMITMENT_SIGNED:
handle_commit_signed(state, msg);
return NULL;
@@ -4257,6 +4264,7 @@ static u8 *handle_peer_in(struct state *state)
case WIRE_SPLICE:
case WIRE_SPLICE_ACK:
case WIRE_SPLICE_LOCKED:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
break;
}
diff --git a/wire/extracted_peer_12_splice_update.patch b/wire/extracted_peer_12_splice_update.patch
new file mode 100644
index 00000000..e47ba33f
--- /dev/null
+++ b/wire/extracted_peer_12_splice_update.patch
@@ -0,0 +1,24 @@
+diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv
+index 9abcb0e64..e2aae8efb 100644
+--- a/wire/peer_wire.csv
++++ b/wire/peer_wire.csv
+@@ -1,3 +1,6 @@
++msgtype,protocol_batch_element,0
++msgdata,protocol_batch_element,channel_id,channel_id,
++msgdata,protocol_batch_element,element_size,u16,
+ msgtype,init,16
+ msgdata,init,gflen,u16,
+ msgdata,init,globalfeatures,byte,gflen
+@@ -293,6 +296,12 @@ msgdata,update_fail_malformed_htlc,channel_id,channel_id,
+ msgdata,update_fail_malformed_htlc,id,u64,
+ msgdata,update_fail_malformed_htlc,sha256_of_onion,sha256,
+ msgdata,update_fail_malformed_htlc,failure_code,u16,
++msgtype,start_batch,127
++msgdata,start_batch,channel_id,channel_id,
++msgdata,start_batch,batch_size,u16,
++msgdata,start_batch,batch_info,start_batch_tlvs,
++tlvtype,start_batch_tlvs,batch_info,1
++tlvdata,start_batch_tlvs,batch_info,message_type,u16,
+ msgtype,commitment_signed,132
+ msgdata,commitment_signed,channel_id,channel_id,
+ msgdata,commitment_signed,signature,signature,
diff --git a/wire/peer_wire.c b/wire/peer_wire.c
index fc2e1c07..12c019b3 100644
--- a/wire/peer_wire.c
+++ b/wire/peer_wire.c
@@ -21,6 +21,8 @@ static bool unknown_type(enum peer_wire t)
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
@@ -89,6 +91,8 @@ bool is_msg_for_gossipd(const u8 *cursor)
case WIRE_UPDATE_FULFILL_HTLC:
case WIRE_UPDATE_FAIL_HTLC:
case WIRE_UPDATE_FAIL_MALFORMED_HTLC:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
+ case WIRE_START_BATCH:
case WIRE_COMMITMENT_SIGNED:
case WIRE_REVOKE_AND_ACK:
case WIRE_UPDATE_FEE:
@@ -166,6 +170,7 @@ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id)
case WIRE_ONION_MESSAGE:
case WIRE_PEER_STORAGE:
case WIRE_PEER_STORAGE_RETRIEVAL:
+ case WIRE_PROTOCOL_BATCH_ELEMENT:
return false;
/* Special cases: */
@@ -345,6 +350,11 @@ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id)
* 2. data:
* * [`channel_id`:`channel_id`]
*/
+ case WIRE_START_BATCH:
+ /* 1. type: 127 (`start_batch`)
+ * 2. data:
+ * * [`channel_id`:`channel_id`]
+ */
case WIRE_COMMITMENT_SIGNED:
/* BOLT #2:
* 1. type: 132 (`commitment_signed`)
diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv
index 9abcb0e6..2b7d17c3 100644
--- a/wire/peer_wire.csv
+++ b/wire/peer_wire.csv
@@ -1,3 +1,6 @@
+msgtype,protocol_batch_element,0
+msgdata,protocol_batch_element,channel_id,channel_id,
+msgdata,protocol_batch_element,element_size,u16,
msgtype,init,16
msgdata,init,gflen,u16,
msgdata,init,globalfeatures,byte,gflen
@@ -293,6 +296,12 @@ msgdata,update_fail_malformed_htlc,channel_id,channel_id,
msgdata,update_fail_malformed_htlc,id,u64,
msgdata,update_fail_malformed_htlc,sha256_of_onion,sha256,
msgdata,update_fail_malformed_htlc,failure_code,u16,
+msgtype,start_batch,127
+msgdata,start_batch,channel_id,channel_id,
+msgdata,start_batch,batch_size,u16,
+msgdata,start_batch,batch_info,start_batch_tlvs,
+tlvtype,start_batch_tlvs,batch_info,1
+tlvdata,start_batch_tlvs,batch_info,message_type,u16,
msgtype,commitment_signed,132
msgdata,commitment_signed,channel_id,channel_id,
msgdata,commitment_signed,signature,signature,
Why this scored 23/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.