splice: Fail earlier on too-few-funds
What changed, and why it matters
This change adds an early sanity check during a Bitcoin Lightning channel 'splice' operation. It makes the software reject a splice immediately if one party tries to remove more funds than they actually have in the channel, instead of failing later with a confusing error. It is primarily a user-experience and robustness improvement; the underlying limit was already enforced elsewhere.
Treat as a normal robustness/UX improvement. No urgent security action required. Review whether the error path correctly cleans up any partially-initialized splice state.
Security signals we found
Adds defensive input validation for splice-out amounts
Uses existing amount_msat_can_add_sat_s64() overflow/bounds helper
Calls splice_abort() to terminate the splice cleanly on bad input
Evidence from the diff
In channeld/channeld.c, the check_balances() function now validates, before counting external splice inputs, that each side’s relative splice-out amount (opener_relative/accepter_relative) can be added to their current channel balance without 64-bit msat overflow. If not, it calls splice_abort() with a descriptive message. This is a fail-early guard against an invalid splice request that would eventually be rejected by protocol rules anyway.
Changed components
channeld/channeld.cLightning channel splicing logicInspect captured patch +22 / −0
diff --git a/channeld/channeld.c b/channeld/channeld.c
index b5855d9a..c89ea49e 100644
--- a/channeld/channeld.c
+++ b/channeld/channeld.c
@@ -3430,6 +3430,28 @@ static struct amount_sat check_balances(struct peer *peer,
fmt_amount_m_as_sat(tmpctx, in[TX_INITIATOR]),
fmt_amount_m_as_sat(tmpctx, in[TX_ACCEPTER]));
+ /* Here in[*] only contains the amounts from this channel.
+ * This is a great opportunity to check their splice out amount does
+ * not exceed their channel funds as this is never allowed even if
+ * additional funds are otherwise contributed. */
+ if (!amount_msat_can_add_sat_s64(in[TX_INITIATOR],
+ peer->splicing->opener_relative)) {
+ splice_abort(peer, NULL, "Intiator is attempting to splice out"
+ " %"PRId64"sat funds out of channel while only "
+ "having %s funds attributable to them.",
+ peer->splicing->opener_relative,
+ fmt_amount_m_as_sat(tmpctx, in[TX_INITIATOR]));
+ }
+ if (!amount_msat_can_add_sat_s64(in[TX_ACCEPTER],
+ peer->splicing->accepter_relative)) {
+ splice_abort(peer, NULL, "Accepter is attempting to splice out"
+ " %"PRId64"sat funds out of channel while only "
+ "having %s funds attributable to them.",
+ peer->splicing->accepter_relative,
+ fmt_amount_m_as_sat(tmpctx, in[TX_ACCEPTER]));
+ }
+
+ /* Now add values from the other outputs */
for (size_t i = 0; i < psbt->num_inputs; i++)
if (i != chan_input_index)
add_amount_to_side(peer, in,
Why this scored 25/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.