Skip to content

channeld: verify the peer's input witnesses before signing a splice - #45

Merged
kwsantiago merged 3 commits into
blake2b-unifiedfrom
splice-verify-peer-witnesses
Oct 18, 2026
Merged

kwsantiago merged 3 commits into
blake2b-unifiedfrom
splice-verify-peer-witnesses

Conversation

@kwsantiago

@kwsantiago kwsantiago commented Oct 17, 2026 •

Copy link
Copy Markdown

channeld now checks the witness the peer sends for each of its own splice inputs as its tx_signatures arrive, before any of ours go out, and fails the channel if one is invalid. This covers every path that receives them: the accepter, the initiator (whose signatures normally go second, in a later call), reconnection and RBF.

Without this, a peer that signs first can send a valid shared_input_signature but a bad witness for its own input. We accept it, send our signatures and broadcast a transaction bitcoind rejects, while the peer holds a valid copy with the same txid that it can broadcast whenever it likes, or never. Our wallet inputs stay committed in the meantime.

A peer witness is accepted if it is a valid spend of:

  • a P2WPKH output,
  • a P2TR output by its key path, with a 64-byte (SIGHASH_DEFAULT) or 0x01 signature, or 0x21 where option_unified_sigs applies,
  • a P2WSH output whose witness script is a 2-of-2 funding output (as when the peer splices another channel in at the same time), with both signatures checked in key order, or an anchor channel's to_remote, whose input's sequence must also satisfy its CSV.

Anything else fails the channel. ECDSA signatures may use 0x01, or 0x21 where option_unified_sigs applies.

The transaction is built once for all of the peer's inputs, with each spent output attached, so a peer adding many inputs with large prevtxs does not make the check clone the PSBT or rehash its prevtxs per input. The check is against the spent outputs in the PSBT: whether they exist, are confirmed and unspent, and whether a relative lock is already satisfied, is not known here.

A new unit test, common/test/run-psbt_input_witness.c, checks the legacy taproot digest against the BIP341 key path test vectors, and signs and checks P2WPKH (0x01, and 0x21 only where option_unified_sigs applies), P2WSH 2-of-2 (key order, NULLDUMMY), P2WSH to_remote with CSVs from 1 to 32767 (sequences below the CSV, or with the disable or type flag, are refused) and an unrecognized P2WSH script.

Two dev options support the tests: --dev-splice-corrupt-input-witness sends tx_signatures first with a corrupted witness for each of our inputs, and --dev-splice-sign-second never sends tx_signatures first. Neither changes the order of commitment_signed.

New tests in tests/test_splicing.py:

  • test_splice_bad_input_witness: the bad witness is caught, and when we sign second our tx_signatures never go out; when we signed first, the channel still resolves on chain.
  • test_splice_peer_p2wsh_to_remote_input: a to_remote input is accepted, and a corrupted one caught before we sign.
  • test_splice_peer_p2tr_input: a P2TR key path input is accepted.

Without the check in channeld, the three negative cases fail. A CLN accepter adds no splice inputs, so two CLN nodes only reach the check on the accepter's side; the initiator runs the same check, at the same point, when its peer does add inputs.

Changelog-None

@kwsantiago
kwsantiago merged commit 38e10a8 into blake2b-unified Oct 18, 2026
175 of 180 checks passed
@kwsantiago
kwsantiago deleted the splice-verify-peer-witnesses branch October 18, 2026 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant