BIP460: CISA for Taproot key path spends #36122

pull fjahr wants to merge 22 commits into bitcoin:master from fjahr:bip460 changing 133 files +11288 −488
  1. fjahr commented at 7:51 PM on August 29, 2026: contributor

    Reference implementation of BIP460 Cross-Input Signature Aggregation for Taproot key path spends. Implements witness v2 outputs whose key path spends can be aggregated with BIP458 half-aggregation or BIP459 full-aggregation, or opt out with a plain BIP341 signature.

    Draft to accompany the BIPs, not intended for merge:

    • BIP458: bitcoin/bips#2205
    • BIP459: bitcoin/bips#2210
    • BIP460: bitcoin/bips#2212

    Further notes:

    • Depends on the unmerged secp256k1 modules from bitcoin-core/secp256k1#1566 (halfagg) and bitcoin-core/secp256k1#1754 (fullagg). The updated subtree here adds both.
    • Activation is regtest-only and there are no parameters set for other chains
    • BIP460 test vectors are vendored in src/test/data/ and run in src/test/cisa_tests.cpp. Additional functional tests cover relay and block validation.
  2. Squashed 'src/secp256k1/' changes from 687155df6b7..a4aca78581b
    a4aca78581b Merge bitcoin-core/secp256k1#1566: BIP458: Schnorr (Incremental) Half Aggregation
    a548265ddce halfagg: Add docs
    d959734fd5e halfagg: Add example
    fa2a660f771 halfagg: Add benchmarks
    3e67c93ea5e Experimental: Add BIP 458 half-aggregation for Schnorr signatures
    65e57481c4c TBD: Sharing common code between fullagg and musig
    cfbb70d3880 fullagg: Add ctime tests
    f1f6604d3ce fullagg: Add benchmarks
    3af59d5bac2 fullagg: Add docs
    07776ea7f0a fullagg: Add to build system
    22099f77de3 fullagg: Add to CI
    5060640320d fullagg: Add example
    a43d452401b fullagg: Add include file
    8722f9ea4fa fullagg: Add module
    bae6b89a203 Merge bitcoin-core/secp256k1#1908: refactor: replace `_get_hash_context` with direct `->hash_ctx` access
    c8745f6b3ad refactor: replace `_get_hash_context` with direct `->hash_ctx` access
    3397f3cb281 Merge bitcoin-core/secp256k1#1878: field: correct `_fe_half` docs (output is not normalized, input requires magnitude <= 31)
    86c7cbaf5af Merge bitcoin-core/secp256k1#1915: refactor: Move (de)ser helpers from musig and eckey to group
    bfd40bd0ce0 refactor: Rename privkey to seckey in eckey helpers
    d8ee6e6cf67 refactor: Move parsing helpers from musig to group
    a37d7cc064a Merge bitcoin-core/secp256k1#1916: ecdh/ellswift: simplify seckey loading with `_scalar_set_b32_seckey`
    3d69f4ce1c7 Merge bitcoin-core/secp256k1#1911: refactor: rename `ctx` param to `ecmult_gen_ctx` where applicable
    c00dc818105 ecdh/ellswift: simplify seckey loading with `_scalar_set_b32_seckey`
    81a5a7567d1 refactor: rename `ctx` param to `ecmult_gen_ctx` where applicable
    2577fb1888c refactor: Move pubkey parsing and serialization from eckey to group
    439278a649d Merge bitcoin-core/secp256k1#1910: scratch: reject sizes that overflow when added to header
    3d4340d1732 scratch: reject sizes that overflow when added to header
    56e031a1482 field: correct `_fe_half` docs (output is not normalized, input requires magnitude <= 31)
    
    git-subtree-dir: src/secp256k1
    git-subtree-split: a4aca78581b69f7bc277fdf856b41d1c9377207d
    b45f40c03b
  3. DrahtBot commented at 7:51 PM on August 29, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/36122.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process. A summary of reviews will appear here.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #36188 (crypto: plug hardware optimized SHA256 into libsecp256k1's context by furszy)
    • #36167 ([RFC] Enable -Wunused by fanquake)
    • #36154 (wallet: fill PSBT_GLOBAL_XPUB for descriptors with more than one key by jeanpablojp)
    • #36133 (wallet: store multipath descriptor by Sjors)
    • #36114 (wallet: harden external signer psbt processing, revamp mock by Sjors)
    • #36091 (test: Add debug output to common tested types by rustaceanrob)
    • #36076 (psbt: preserve sighash type when merging inputs by thomasbuilds)
    • #36039 (psbt: classify missing Taproot script-path signatures as signer by btcpavao)
    • #35793 (Implement BIP 54 (Consensus Cleanup) without mainnet activation by darosior)
    • #35742 (descriptors: check duplicate keys in all multipath Miniscript branches by yashbhutwala)
    • #35713 (Remove boost as a unit test runner by rustaceanrob)
    • #35662 (script: prevent stale sighash caches across transactions by l0rinc)
    • #35569 (Encapsulation for CTransaction by purpleKarrot)
    • #35370 (rpc: add key-origin modes to PSBT processing RPCs by junbyjun1238)
    • #35301 (Silent Payments: Implement bip352 (take 2) by Eunovo)
    • #34520 (refactor: Add [[nodiscard]] to functions returning bool+mutable ref by maflcko)
    • #32857 (wallet: allow skipping script paths by Sjors)
    • #32575 (consensus: Remove special treatment for single threaded script checking by fjahr)
    • #30342 (kernel, logging: Pass Logger instances to kernel objects by ryanofsky)
    • #29843 (policy: Allow non-standard scripts with -acceptnonstdtxn=1 (test nets only) by ajtowns)
    • #29491 ([EXPERIMENTAL] Schnorr batch verification for blocks by fjahr)
    • #29247 (CAT in Tapscript (BIP-347) by arminsabouri)
    • #28690 (build: Introduce internal kernel library by sedited)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. DrahtBot added the label CI failed on Aug 29, 2026
  5. fjahr force-pushed on Sep 7, 2026
  6. fjahr force-pushed on Sep 7, 2026
  7. fjahr commented at 7:54 PM on September 7, 2026: contributor

    The latest commits, prefixed with draft-bip: go further than BIP460 itself. They implement two companion BIPs necessary for adoption that have not been reviewed and I don't think they are worth sharing on the mailing list yet either:

    They are included so that wallets and protocols, like payjoin or coinjoin for example, can build proofs of concept on top of this branch and exercise CISA end-to-end through the their already common descriptor, wallet and PSBT RPCs flows. Expect these commits to change as these drafts evolve. Review of the consensus and policy commits does not depend on them and I would prefer if review is focused on those unless you are building a PoC and depend on these later changes.

  8. fjahr force-pushed on Sep 7, 2026
  9. DrahtBot added the label Needs rebase on Sep 7, 2026
  10. DrahtBot removed the label CI failed on Sep 7, 2026
  11. Merge commit 'b45f40c03b0effe0ad53e49053b7a81923352afe' into bip460 239dba9b84
  12. build: Enable secp halfagg and fullagg modules 067f8511dc
  13. consensus: Add CISA deployment and script flag 7b9d10e289
  14. script: Add witness v2 keypath signature message e99d6e105a
  15. script: Add per-input witness v2 validation 7f9f8627b9
  16. script: Add transaction-level CISA verification d74b4bec9c
  17. validation: Add CISA check to the check queue 002355961c
  18. policy: Make witness v2 transactions standard 639a2a7e6d
  19. test: Add CISA unit tests with BIP460 test vectors 1b531fff1e
  20. test: Add CISA functional test 4761d18cd5
  21. draft-bip: descriptor: Add cisa() output script descriptor d25e2daba2
  22. draft-bip: test: Add cisa() descriptor tests 01b437ae5c
  23. draft-bip: cisa: Add half and full aggregation signing helpers 40d69a788b
  24. draft-bip: psbt: Add CISA input fields 650c4df0ad
  25. fjahr force-pushed on Sep 8, 2026
  26. draft-bip: sign: Sign witness v2 inputs by aggregation mode 2c437f4967
  27. draft-bip: psbt: Finalize CISA aggregation groups 695c09cdea
  28. draft-bip: rpc: Add cisa_mode option to PSBT signing RPCs 1cfa826a7b
  29. draft-bip: test: Add CISA PSBT tests 34eace4d37
  30. draft-bip: cisa: Decouple full aggregation nonces from the signed message 421657c01b
  31. draft-bip: wallet: Add reservecisanonce for pre-shared full aggregation nonces 41ea27e7ff
  32. draft-bip: test: Test full aggregation with reserved nonces 224e15fd89
  33. fjahr force-pushed on Sep 8, 2026
  34. DrahtBot added the label CI failed on Sep 8, 2026
  35. DrahtBot removed the label Needs rebase on Sep 8, 2026
  36. DrahtBot removed the label CI failed on Sep 8, 2026
  37. DrahtBot commented at 2:35 AM on September 9, 2026: contributor

    <!--cf906140f33d8803c4a75a2196329ecb-->

    🐙 This pull request conflicts with the target branch and needs rebase.

  38. DrahtBot added the label Needs rebase on Sep 9, 2026
  39. BarneyChambers commented at 11:39 AM on September 15, 2026: none

    Hey Fabian, Hope you don't mind if I add a review here. I built this locally and ran feature_cisa.py and wallet_cisa.py from commit 224e15f. Both passed, and the consensus/policy test coverage looks great. I did hit three wallet/PSBT issues while reviewing the changes.

  40. in src/script/sign.cpp:824 in 224e15fd89
     818 | @@ -717,7 +819,10 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
     819 |          return false;
     820 |  
     821 |      case TxoutType::WITNESS_V1_TAPROOT:
     822 | -        return SignTaproot(provider, creator, WitnessV1Taproot(XOnlyPubKey{vSolutions[0]}), sigdata, ret);
     823 | +        return SignTaproot(provider, creator, XOnlyPubKey{vSolutions[0]}, /*cisa=*/false, sigdata, ret);
     824 | +
     825 | +    case TxoutType::WITNESS_V2_CISA:
    


    BarneyChambers commented at 11:42 AM on September 15, 2026:

    For a marked v2 keypath, VerifyScript returning success after ParseCISAWitness seems correct for a per-input structure check. ProduceSignature and DataFromTransaction then treat that success as complete, so a dummy member looks fully signed.

    I made a 2-input v2 spend and put 32 zero bytes on both witnesses (half-agg members, no final). signrawtransactionwithwallet came back complete=true with no errors. testmempoolaccept then failed with mempool-script-verify-flag-failed (Invalid CISA aggregation group structure). So CISACheck is doing its job. Anyone who only looks at VerifyScript / complete will think the input is done.

    Would it be reasonable to run VerifyCISATransaction on the full tx before setting complete when the parse yields a marker?

  41. in src/psbt.cpp:482 in 224e15fd89
     477 |      if (sequence == std::nullopt && input.sequence != std::nullopt) sequence = input.sequence;
     478 |      if (time_locktime == std::nullopt && input.time_locktime != std::nullopt) time_locktime = input.time_locktime;
     479 |      if (height_locktime == std::nullopt && input.height_locktime != std::nullopt) height_locktime = input.height_locktime;
     480 |  }
     481 |  
     482 | +bool PSBTInput::CISACompatible(const PSBTInput& input) const
    


    BarneyChambers commented at 11:46 AM on September 15, 2026:

    You already reject conflicting full-agg nonces and partial sigs. wallet_cisa.py covers that (PSBTs not compatible). Half-agg signatures do not get checked the same way. Merge keeps the first m_cisa_halfagg_sig it sees.

    I took two otherwise identical half-agg PSBTs and flipped one bit in input 0’s sig. combinepsbt([A, B]) succeeded and kept A. Swapping the order kept B.

    We can fix this issue if you treat m_cisa_halfagg_sig like the full-agg partial sig (both present and unequal means incompatible). I only mention it because the full-agg side already works

  42. in src/psbt.cpp:844 in 224e15fd89
     840 | @@ -793,6 +841,77 @@ void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx)
     841 |      }
     842 |  }
     843 |  
     844 | +void FinalizeCISAInputs(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata)
    


    BarneyChambers commented at 11:50 AM on September 15, 2026:

    FinalizeCISAInputs skips any input that is already signed. 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, because once it has seen a group’s final it will not accept another member.

    The problem is basically when input 0 already has a final, and you then put input 1 into the same mode with a signature. The loop walks past input 0 and builds a brand new 1-input group on input 1.

    Working example: I signed a 2-input half-agg PSBT without finalizing, cleared input 1’s mode and sig, and ran finalizepsbt. That put a 65-byte final on input 0 and reported complete=false. I put input 1’s mode and sig back and finalized again. That time it reported complete=true, both witnesses were 65 bytes, and testmempoolaccept failed with a Invalid CISA aggregation group structure error.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-09-17 00:51 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me