splice: Remove `batch_size` from `commitment_signed`
What changed, and why it matters
This commit updates Core Lightning's implementation of the Lightning Network splicing protocol to match a newer version of the specification. It removes a 'batch_size' field from the 'commitment_signed' message and moves that information to a different message ('start_batch'). The code changes are mostly mechanical: updating data structures, removing checks that used batch_size, and adjusting how messages are parsed and compared. There is no direct evidence in the commit or supplied references that this fixes a security vulnerability; it appears to be a protocol compatibility/spec-compliance update.
Treat as a routine protocol update. Reviewers should verify that removing `batch_size` from `commitment_signed` and relying on the caller-supplied value in `handle_peer_commit_sig_batch` does not introduce state desynchronization between peers during batched splicing. No immediate security action is indicated by the available evidence.
Security signals we found
Protocol spec alignment: removes a field that has moved to another message in the updated specification
Reduced TLV complexity: nested splice_info struct collapsed to direct bitcoin_txid
Removed validation logic: batch_size consistency checks between bundled commitment_signed messages are deleted
No explicit security claim in commit message or diff
Evidence from the diff
The commit removes the batch_size field from the commitment_signed_tlvs splice_info TLV, leaving only funding_txid. Because the TLV now contains a single element, the generated wire type changes from a nested struct to a direct bitcoin_txid pointer. The patch updates channeld/channeld.c to stop setting/reading batch_size, changes wire/peer_wire.csv and the extracted splice patch to remove the field, and updates wire/test/run-peer-wire.c equality checks and test construction accordingly. A new batch_size parameter is added to handle_peer_commit_sig_batch and passed explicitly from peer_in with a default of 1, replacing the previous extraction from the TLV.
Changed components
channeld/channeld.cwire/peer_wire.csvwire/extracted_peer_12_splice_update.patchwire/test/run-peer-wire.cInspect captured patch +23 / −46
diff --git a/channeld/channeld.c b/channeld/channeld.c
index 287cda70..2c1697cd 100644
--- a/channeld/channeld.c
+++ b/channeld/channeld.c
@@ -1160,12 +1160,7 @@ static u8 *send_commit_part(const tal_t *ctx,
(int)splice_amnt, (int)remote_splice_amnt,
remote_index);
- if (batch_size > 1) {
- cs_tlv->splice_info = tal(cs_tlv, struct tlv_commitment_signed_tlvs_splice_info);
-
- cs_tlv->splice_info->batch_size = batch_size;
- cs_tlv->splice_info->funding_txid = funding->txid;
- }
+ cs_tlv->splice_info = tal_dup(cs_tlv, struct bitcoin_txid, &funding->txid);
}
txs = channel_txs(tmpctx, funding, funding_sats, &htlc_map,
@@ -1926,11 +1921,11 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer,
if (peer->splice_state->await_commitment_succcess
&& !tal_count(peer->splice_state->inflights) && cs_tlv && cs_tlv->splice_info) {
if (!bitcoin_txid_eq(&peer->channel->funding.txid,
- &cs_tlv->splice_info->funding_txid)) {
+ cs_tlv->splice_info)) {
status_info("Ignoring stale commit_sig for channel_id"
" %s, as %s is locked in now.",
fmt_bitcoin_txid(tmpctx,
- &cs_tlv->splice_info->funding_txid),
+ cs_tlv->splice_info),
fmt_bitcoin_txid(tmpctx,
&peer->channel->funding.txid));
return NULL;
@@ -1980,22 +1975,17 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer,
outpoint = peer->splice_state->inflights[commit_index - 1]->outpoint;
funding_sats = peer->splice_state->inflights[commit_index - 1]->amnt;
- if (cs_tlv && cs_tlv->splice_info
- && cs_tlv->splice_info->batch_size == 1)
- peer_failed_err(peer->pps, &peer->channel_id,
- "batch_size can never be 1");
-
status_debug("handle_peer_commit_sig for inflight outpoint %s",
fmt_bitcoin_txid(tmpctx, &outpoint.txid));
if (cs_tlv->splice_info
&& !bitcoin_txid_eq(&outpoint.txid,
- &cs_tlv->splice_info->funding_txid))
+ cs_tlv->splice_info))
peer_failed_err(peer->pps, &peer->channel_id,
"Expected commit sig message for %s but"
" got %s",
fmt_bitcoin_txid(tmpctx, &outpoint.txid),
- fmt_bitcoin_txid(tmpctx, &cs_tlv->splice_info->funding_txid));
+ fmt_bitcoin_txid(tmpctx, cs_tlv->splice_info));
}
else {
outpoint = peer->channel->funding;
@@ -2052,7 +2042,7 @@ static struct commitsig_info *handle_peer_commit_sig(struct peer *peer,
fmt_amount_sat(tmpctx, funding_sats),
cs_tlv && cs_tlv->splice_info
? fmt_bitcoin_txid(tmpctx,
- &cs_tlv->splice_info->funding_txid)
+ cs_tlv->splice_info)
: "N/A",
peer->splice_state->await_commitment_succcess ? "yes"
: "no",
@@ -2216,7 +2206,7 @@ static int commit_index_from_msg(const u8 *msg, struct peer *peer)
if (!cs_tlv || !cs_tlv->splice_info)
return -1;
- funding_txid = cs_tlv->splice_info->funding_txid;
+ funding_txid = *cs_tlv->splice_info;
if (bitcoin_txid_eq(&funding_txid, &peer->channel->funding.txid))
return 0;
@@ -2269,12 +2259,12 @@ static struct commitsig_info *handle_peer_commit_sig_batch(struct peer *peer,
s64 remote_splice_amnt,
u64 local_index,
const struct pubkey *local_per_commit,
- bool allow_empty_commit)
+ bool allow_empty_commit,
+ u16 batch_size)
{
struct channel_id channel_id;
struct bitcoin_signature commit_sig;
secp256k1_ecdsa_signature *raw_sigs;
- u16 batch_size;
const u8 **msg_batch;
enum peer_wire type;
struct tlv_commitment_signed_tlvs *cs_tlv
@@ -2286,11 +2276,6 @@ static struct commitsig_info *handle_peer_commit_sig_batch(struct peer *peer,
peer_failed_warn(peer->pps, &peer->channel_id,
"Bad commit_sig %s", tal_hex(msg, msg));
- /* Default batch_size is 1 */
- batch_size = 1;
- if (cs_tlv->splice_info && cs_tlv->splice_info->batch_size)
- batch_size = cs_tlv->splice_info->batch_size;
-
msg_batch = tal_arr(tmpctx, const u8*, batch_size);
msg_batch[0] = msg;
status_debug("msg_batch[0]: %p", msg_batch[0]);
@@ -2326,14 +2311,6 @@ static struct commitsig_info *handle_peer_commit_sig_batch(struct peer *peer,
" splice_info",
tal_hex(sub_msg, sub_msg), i, batch_size);
- if (!sub_cs_tlv->splice_info
- || sub_cs_tlv->splice_info->batch_size != batch_size)
- peer_failed_err(peer->pps, &peer->channel_id,
- "batch_size value mismatch in"
- " commit_sig bundle, item [%"PRIu16
- "/%"PRIu16"] %s", i, batch_size,
- tal_hex(sub_msg, sub_msg));
-
msg_batch[i] = sub_msg;
status_debug("msg_batch[%d]: %p", (int)i, msg_batch[i]);
}
@@ -4914,7 +4891,8 @@ static void peer_in(struct peer *peer, const u8 *msg)
NULL, 0, 0,
peer->next_index[LOCAL],
&peer->next_local_per_commit,
- false);
+ false,
+ 1); /* Batch size default is 1 */
return;
case WIRE_UPDATE_FEE:
handle_peer_feechange(peer, msg);
diff --git a/wire/extracted_peer_12_splice_update.patch b/wire/extracted_peer_12_splice_update.patch
index e47ba33f..817acd8c 100644
--- a/wire/extracted_peer_12_splice_update.patch
+++ b/wire/extracted_peer_12_splice_update.patch
@@ -22,3 +22,11 @@ index 9abcb0e64..e2aae8efb 100644
msgtype,commitment_signed,132
msgdata,commitment_signed,channel_id,channel_id,
msgdata,commitment_signed,signature,signature,
+@@ -309,6 +309,5 @@ msgdata,commitment_signed,num_htlcs,u16,
+ msgdata,commitment_signed,htlc_signature,signature,num_htlcs
+ msgdata,commitment_signed,splice_channel_id,commitment_signed_tlvs,
+ tlvtype,commitment_signed_tlvs,splice_info,0
+-tlvdata,commitment_signed_tlvs,splice_info,batch_size,u16,
+ tlvdata,commitment_signed_tlvs,splice_info,funding_txid,sha256,
+ msgtype,revoke_and_ack,133
+ msgdata,revoke_and_ack,channel_id,channel_id,
diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv
index 2b7d17c3..e2aae8ef 100644
--- a/wire/peer_wire.csv
+++ b/wire/peer_wire.csv
@@ -309,7 +309,6 @@ msgdata,commitment_signed,num_htlcs,u16,
msgdata,commitment_signed,htlc_signature,signature,num_htlcs
msgdata,commitment_signed,splice_channel_id,commitment_signed_tlvs,
tlvtype,commitment_signed_tlvs,splice_info,0
-tlvdata,commitment_signed_tlvs,splice_info,batch_size,u16,
tlvdata,commitment_signed_tlvs,splice_info,funding_txid,sha256,
msgtype,revoke_and_ack,133
msgdata,revoke_and_ack,channel_id,channel_id,
diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c
index cbb457ac..89492044 100644
--- a/wire/test/run-peer-wire.c
+++ b/wire/test/run-peer-wire.c
@@ -792,19 +792,12 @@ static bool update_fail_htlc_eq(const struct msg_update_fail_htlc *a,
&& eq_var(a, b, reason);
}
-static bool tlv_splice_info_eq(const struct tlv_commitment_signed_tlvs_splice_info *a,
- const struct tlv_commitment_signed_tlvs_splice_info *b)
-{
- return eq_field(a, b, batch_size)
- && eq_field(a, b, funding_txid);
-}
-
static bool commitment_signed_eq(const struct msg_commitment_signed *a,
- const struct msg_commitment_signed *b)
+ const struct msg_commitment_signed *b)
{
return eq_upto(a, b, htlc_signature)
&& eq_var(a, b, htlc_signature)
- && eq_tlv(a, b, splice_info, tlv_splice_info_eq);
+ && eq_tlv(a, b, splice_info, bitcoin_txid_eq);
}
static bool funding_signed_eq(const struct msg_funding_signed *a,
@@ -1026,9 +1019,8 @@ int main(int argc, char *argv[])
cs.htlc_signature = tal_arr(ctx, secp256k1_ecdsa_signature, 2);
memset(cs.htlc_signature, 2, sizeof(secp256k1_ecdsa_signature)*2);
cs.tlvs = tlv_commitment_signed_tlvs_new(tmpctx);
- cs.tlvs->splice_info = tal(ctx, struct tlv_commitment_signed_tlvs_splice_info);
- cs.tlvs->splice_info->batch_size = 1;
- set_bitcoin_txid(&cs.tlvs->splice_info->funding_txid);
+ cs.tlvs->splice_info = tal(ctx, struct bitcoin_txid);
+ set_bitcoin_txid(cs.tlvs->splice_info);
msg = towire_struct_commitment_signed(ctx, &cs);
cs2 = fromwire_struct_commitment_signed(ctx, msg);
Why this scored 31/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.