#36122 BIP460: CISA for Taproot key path spends
| Reviewability | Reviews | Agreement | Size | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| #36122 BIP460: CISA for Taproot key path spends | Waiting on author | 0 | Strong | 7,680 + 3,565 tests | ||||||||||||
|
|
|
|
| ||||||||||||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | ||||||||||||||||
Goal
Implements a reference implementation of BIP460 Cross-Input Signature Aggregation (CISA) for Taproot key path spends using witness version 2 outputs. Includes consensus script verification, check queue validation, mempool standardness, descriptor support (`cisa()`), PSBT fields, and wallet signing using BIP458 half-aggregation and BIP459 full-aggregation from an updated secp256k1 subtree.
Problem: Every Taproot key path spend requires an individual 64-byte Schnorr signature, increasing transaction size and fees linearly with the number of inputs and disincentivizing multi-party collaborative transactions like CoinJoin or Payjoin.
Evidence of importance:
- Author notes the PR is a reference implementation accompanying proposed BIPs: 'Draft to accompany the BIPs, not intended for merge: BIP458: bitcoin/bips#2205, BIP459: bitcoin/bips#2210, BIP460: bitcoin/bips#2212'
- Depends on unmerged upstream libsecp256k1 PRs: 'Depends on the unmerged secp256k1 modules from bitcoin-core/secp256k1#1566 (halfagg) and bitcoin-core/secp256k1#1754 (fullagg)'
Scope: Contains a large libsecp256k1 subtree update (+11k lines) bringing in experimental halfagg and fullagg modules, consensus changes for witness v2 script validation, and activation restricted to regtest only.
Risk of a mistake: high (consensus, validation, mempool_policy, wallet, rpc_api, build, tests). Introduces new consensus validation rules and script verification logic that, if flawed or inconsistent across nodes, would cause consensus splits.
Changed since the previous assessment: Added commits decoupling full aggregation nonces from the signed message, introducing the `reservecisanonce` wallet RPC to pre-share BIP459 nonces, and adding corresponding unit and functional tests.
Reviewability
Paused · Waiting on author
Open objection from fametrano with no author reply for 7 days; review would repeat what has been asked.
Waiting on (from the thread read):
- author: response to suggestion to update vendored cisa_consensus_vectors.json 2026-10-25
- reviewer: BarneyChambers to check the fixes for wallet and PSBT handling 2026-09-21
- author: update vendored cisa_consensus_vectors.json from BIP repo 2026-10-25
- reviewer: BarneyChambers to check fixes for wallet and PSBT handling 2026-09-21
Agreement
Strong
substantive support, no open objection (fametrano)
Thread notes: Added fametrano's testing support and suggestion to update vendored test vectors. Updated BarneyChambers' code defects to fixed with clears_with=change following the pushed fixes. / Updated existing claims under current schema: rc:4015169298 and rc:4015234807 were fixed in code by the author, so their clears_with is set to change and status to fixed. Added fametrano's Tested ACK and their suggestion to update the vendored test vectors.
Objections, suggestions, and questions:
| Reviewer | Type | Harm, request, or question | Status | Must be | Author replied | Quote |
|---|---|---|---|---|---|---|
| BarneyChambers (none) | objection, correctness | signrawtransactionwithwallet prematurely reports complete=true on incomplete spends with dummy member witnesses because per-input script verification succeeds on markers | fixed · fix pushed · author added tx-level VerifyCISATransaction check in SignTransaction and a wallet test | changed | yes | 2026-09-15: 'ProduceSignature and DataFromTransaction then treat that success as complete, so a dummy member looks fully signed.' Status from: 2026-09-21: fjahr: 'SignTransaction now runs VerifyCISATransaction once all inputs are processed and reports the error on the aggregated inputs' |
| BarneyChambers (none) | objection, correctness | combinepsbt silently keeps the first half-aggregation signature instead of detecting conflicting signatures across inputs | answered · author explained keeping first is intended per BIP 174/371 and added test showing invalid sigs fail finalization | answered | yes | 2026-09-15: 'Half-agg signatures do not get checked the same way. Merge keeps the first m_cisa_halfagg_sig it sees.' Status from: 2026-09-21: fjahr: 'This is intended, halfagg has no signing session so two different signatures for the same input can both be valid and either one aggregates fine.' |
| BarneyChambers (none) | objection, correctness | FinalizeCISAInputs skipping already-signed inputs can partition inputs into duplicate groups of the same mode that fail consensus | fixed · fix pushed · author updated FinalizeCISAInputs to close group once an input has a final witness | changed | yes | 2026-09-15: 'This skip on already-signed inputs can leave you with two separate groups of the same mode. Consensus then fails the tx with SCRIPT_ERR_CISA_GROUP_INVALID' Status from: 2026-09-21: fjahr: 'The finalizer should treat a group as closed once any input of that mode has a final witness and I have changed FinalizeCISAInputs to do that' |
| fametrano (contributor) | suggestion, maintenance | update vendored cisa_consensus_vectors.json to match the updated BIP copy | open | answered | no | 2026-10-25: 'The vendored cisa_consensus_vectors.json is older than the BIP's copy. Its marker-undefined case is optout-sighash-undefined there, with the same transaction, now an opted-out signature with an undefined sighash type, which is how the BIP text reads 0xbb. Copying the BIP's file over fixes it.' |
Support:
- fametrano (contributor) (2026-10-25) [both: cisa_consensus_vectors.json, cisa_tests, consensus code, feature_cisa.py, wallet vectors, wallet_cisa.py]: the consensus code agrees with BIP460's vectors and with 35 extra edge cases, checked against maintained libraries
Participants:
- BarneyChambers (none, objection, 4 statements 2026-09-15 to 2026-09-15): tested functional tests and raised three wallet and PSBT issues, which the author addressed
- fametrano (contributor, support, 1 statements 2026-10-25 to 2026-10-25): tested against BIP460 vectors and 35 edge cases; suggested updating vendored test vectors
Files
3584 lines under test/bench/ci.
- src/secp256k1/src/modules/schnorrsig_halfagg/vectors.h +1162/-0
- src/secp256k1/src/modules/fullagg/main_impl.h +1144/-0
- src/test/data/cisa_consensus_vectors.json +950/-0
- src/secp256k1/src/modules/fullagg/vectors.h +702/-0
- src/secp256k1/src/modules/fullagg/tests_impl.h +618/-0
- src/test/data/cisa_descriptor_vectors.json +496/-0
- test/functional/feature_cisa.py +484/-0
- src/secp256k1/include/secp256k1_fullagg.h +448/-0
- src/test/cisa_tests.cpp +371/-0
- src/secp256k1/src/modules/schnorrsig_halfagg/tests_impl.h +329/-0
- src/test/data/cisa_psbt_vectors.json +310/-0
- test/functional/wallet_cisa.py +300/-0
- src/secp256k1/tools/test_vectors_halfagg_generate.py +237/-0
- src/test/data/cisa_wallet_vectors.json +222/-0
- src/secp256k1/tools/test_vectors_fullagg_generate.py +214/-0
- src/secp256k1/src/modules/schnorrsig_halfagg/main_impl.h +211/-0
- src/secp256k1/examples/fullagg.c +208/-0
- src/cisa.cpp +192/-0
- src/script/interpreter.cpp +173/-12
- src/test/psbt_tests.cpp +169/-0
- src/script/sign.cpp +138/-21
- src/secp256k1/src/tests.c +108/-46
- src/psbt.cpp +134/-5
- src/secp256k1/examples/halfagg.c +115/-0
- src/secp256k1/include/secp256k1_schnorrsig_halfagg.h +112/-0
- src/secp256k1/src/modules/fullagg/bench_impl.h +106/-0
- test/functional/test_framework/cisa.py +106/-0
- src/secp256k1/src/ctime_tests.c +96/-0
- src/secp256k1/configure.ac +59/-24
- src/script/descriptor.cpp +44/-35
- src/test/descriptor_tests.cpp +75/-0
- src/secp256k1/src/modules/schnorrsig_halfagg/bench_impl.h +74/-0
- src/wallet/rpc/spend.cpp +70/-4
- src/cisa.h +71/-0
- src/psbt.h +70/-0
- src/secp256k1/.github/workflows/ci.yml +47/-15
- src/validation.cpp +56/-4
- src/secp256k1/doc/fullagg.md +54/-0
- src/secp256k1/src/bench.c +52/-1
- src/pubkey.cpp +51/-0
- src/script/interpreter.h +43/-4
- src/validation.h +44/-3
- src/secp256k1/src/group_impl.h +34/-6
- src/script/signingprovider.cpp +38/-0
- src/secp256k1/Makefile.am +33/-1
- doc/psbt.md +27/-0
- src/rpc/rawtransaction.cpp +21/-4
- src/script/sign.h +25/-0
- src/secp256k1/doc/halfagg.md +22/-0
- src/rpc/util.cpp +21/-0
- src/secp256k1/src/CMakeLists.txt +21/-0
- src/core_io.cpp +19/-0
- src/wallet/scriptpubkeyman.cpp +16/-2
- src/secp256k1/src/secp256k1.c +15/-1
- src/test/transaction_tests.cpp +11/-4
- test/functional/rpc_blockchain.py +14/-1
- test/functional/rpc_deriveaddresses.py +15/-0
- src/key_io.cpp +14/-0
- src/wallet/wallet.cpp +12/-0
- src/addresstype.cpp +11/-0
- src/script/signingprovider.h +11/-0
- src/secp256k1/src/group.h +9/-2
- src/test/script_standard_tests.cpp +8/-3
- src/test/txvalidationcache_tests.cpp +5/-5
- src/addresstype.h +8/-1
- test/functional/p2p_segwit.py +5/-4
- src/script/script_error.cpp +8/-0
- src/secp256k1/examples/CMakeLists.txt +8/-0
- src/kernel/chainparams.cpp +7/-0
- src/script/script.cpp +7/-0
- src/secp256k1/CMakeLists.txt +7/-0
- src/wallet/wallet.h +7/-0
- src/common/types.h +6/-0
- src/key.cpp +6/-0
- src/policy/policy.h +4/-2
- src/pubkey.h +6/-0
- src/script/script_error.h +6/-0
- src/wallet/scriptpubkeyman.h +6/-0
- src/policy/policy.cpp +3/-2
- src/script/solver.cpp +5/-0
- src/secp256k1/src/modules/fullagg/Makefile.am.include +5/-0
- src/secp256k1/src/modules/schnorrsig_halfagg/Makefile.am.include +5/-0
- src/test/CMakeLists.txt +5/-0
- src/test/fuzz/util.cpp +4/-1
- doc/descriptors.md +3/-1
- src/deploymentinfo.cpp +4/-0
- src/key.h +4/-0
- src/secp256k1/README.md +4/-0
- src/secp256k1/ci/ci.sh +3/-1
- src/test/versionbits_tests.cpp +4/-0
- test/functional/feature_taproot.py +4/-0
- test/functional/test_framework/psbt.py +4/-0
- cmake/secp256k1.cmake +3/-0
- src/node/psbt.cpp +2/-1
- src/rpc/blockchain.cpp +3/-0
- src/rpc/client.cpp +3/-0
- src/core_io.h +2/-0
- src/secp256k1/.gitignore +2/-0
- src/secp256k1/src/modules/musig/session_impl.h +1/-1
- src/secp256k1/src/modules/silentpayments/main_impl.h +1/-1
- src/wallet/rpc/wallet.cpp +2/-0
- test/functional/mining_basic.py +1/-1
- test/functional/test_runner.py +2/-0
- src/CMakeLists.txt +1/-0
- src/consensus/params.h +1/-0
- src/outputtype.cpp +1/-0
- src/rpc/util.h +1/-0
- src/script/script.h +1/-0
- src/script/solver.h +1/-0
- src/wallet/feebumper.h +1/-0
- src/wallet/rpc/addresses.cpp +1/-0
Card
The short text the ranking pass compares across PRs.
This PR provides a draft reference implementation of BIP460 Cross-Input Signature Aggregation (CISA) for Taproot key path spends as witness version 2 outputs. It addresses linear witness byte bloat across multi-input transactions by allowing inputs to aggregate Schnorr signatures using BIP458 half-aggregation or BIP459 full-aggregation. It includes consensus rules, check queue integration, mempool policy, descriptors, PSBT handling, and wallet signing. The author explicitly notes this is a draft to accompany BIP proposals and not intended for immediate merge, and it vendors unmerged experimental libsecp256k1 PRs.
Data
thread read through 2026-10-25 (9 statements), head 07addd3748
labels: Needs rebase, CI failed · draft
Categories
Mempool and policy
P3 · new feature
Defines policy standardness rules for witness v2 transactions to support cross-input signature aggregation safely before full activation. Policy rules for experimental soft fork proposals provide a clear specification for transaction relay and acceptance.
Membership: Implements mempool standardness and policy rules for witness version 2 CISA spends in src/policy/policy.cpp.
Factors: security/stability 1, bug 0, performance 1, user value 2, leverage 1
Utilities (logging, arguments, libraries)
P3 · new feature (ranking pass: Moved down: an explicitly draft reference implementation accompanying unmerged BIPs and unmerged secp256k1 modules.)
Vendors unmerged upstream libsecp256k1 modules (bitcoin-core/secp256k1#1566 and #1754) to provide signature aggregation primitives. Subtree updates for prospective cryptographic standards are worthwhile for testing and specification validation.
Membership: Updates the vendored libsecp256k1 subtree with experimental BIP458 half-aggregation and BIP459 full-aggregation modules.
Factors: security/stability 1, bug 0, performance 1, user value 2, leverage 1
Validation
P2 · new feature
Provides the consensus reference implementation for BIP460 Cross-Input Signature Aggregation. As a proposed soft fork, evaluating its consensus rules and validation security is strategically important for the project's consensus roadmap.
Membership: Implements consensus script verification for witness version 2 outputs and check-queue batching in src/script/interpreter.cpp and src/validation.cpp.
Factors: security/stability 1, bug 0, performance 1, user value 2, leverage 1
Wallet
P4 · new feature (assessed alone as P3; Lowered from P3: explicitly a draft accompanying unaccepted BIPs and vendoring unmerged libsecp256k1 work; wallet value is speculative until consensus work lands.)
Adds wallet descriptor and PSBT support for Taproot key path spends with aggregated signatures to enable multi-party transaction construction. It demonstrates and exercises end-to-end wallet signing for collaborative transactions under BIP460.
Membership: Adds `cisa()` descriptor parsing and wallet signing integration using half- and full-aggregation across src/wallet/ and src/psbt.cpp.
Factors: security/stability 1, bug 0, performance 1, user value 2, leverage 1