wallet: allow skipping script paths #32857

pull Sjors wants to merge 7 commits into bitcoin:master from Sjors:2025/07/no_script_path changing 35 files +227 −110
  1. Sjors commented at 2:13 pm on July 2, 2025: member

    While testing MuSig2 integration in #29675 I ran into a Ledger bug https://github.com/LedgerHQ/app-bitcoin-new/issues/329 that was triggered because we signed for a script path in addition to the key path. The bug itself will be fixed, but it seems useful in general to be able to skip script path signing.

    For example, you may have a 2-of-2 musig() descriptor with some sort of fallback after coins are N blocks old. For both on chain privacy and fee reasons, you don’t want to use this path unless absolutely necessary. But our wallet can’t know if our co-signer still has their key, so we sign the script path at all times. This could lead to accidental broadcast.

    This PR introduces a keypath_only option (default false for now) to the send and walletprocesspsbt RPC.

    The resulting PSBT won’t include taproot_script_path_sigs. It does still have taproot_bip32_derivs and musig2_participant_pubkeys.

    The wallet_taproot.py tests are expanded to cover this behavior.

    It can also be tested with https://github.com/Sjors/bitcoin/pull/91 and a slightly modified Moosig. See https://github.com/LedgerHQ/app-bitcoin-new/issues/329 for the rough steps.

    Based on:

    TODO:

    • maybe refactor MutableTransactionSignatureCreator
    • add test coverage for send

    Potential followups:

    • make this default for musig() (after #29675)
  2. DrahtBot added the label Wallet on Jul 2, 2025
  3. DrahtBot commented at 2:13 pm on July 2, 2025: contributor

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

    Code Coverage & Benchmarks

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

    Reviews

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #33008 (wallet: support bip388 policy with external signer by Sjors)
    • #32964 (descriptor: don’t underestimate the size of a Taproot spend (instead, overestimate it) by w0xlt)
    • #32958 (wallet/refactor: change PSBTError to PSBTResult and remove std::optionalcommon::PSBTResult and return common::PSBTResult by kevkevinpal)
    • #32473 (Introduce per-txin sighash midstate cache for legacy/p2sh/segwitv0 scripts by sipa)
    • #29675 (wallet: Be able to receive and spend inputs involving MuSig2 aggregate keys by achow101)
    • #28944 (wallet, rpc: add anti-fee-sniping to send and sendall by ishaanam)
    • #21283 (Implement BIP 370 PSBTv2 by achow101)

    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.

  4. DrahtBot added the label CI failed on Jul 2, 2025
  5. DrahtBot commented at 3:53 pm on July 2, 2025: contributor

    🚧 At least one of the CI tasks failed. Task ARM, unit tests, no functional tests: https://github.com/bitcoin/bitcoin/runs/45219351756 LLM reason (✨ experimental): Compilation error due to passing a bool where an int* is expected in fillPSBT function.

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  6. fanquake commented at 4:04 pm on July 2, 2025: member
  7. Sjors commented at 4:35 pm on July 2, 2025: member
    @fanquake because it’s in the fuzzer code, I incorrectly changed one of the function signatures there.
  8. Sjors force-pushed on Jul 2, 2025
  9. Sjors commented at 4:53 pm on July 2, 2025: member

    From the original description:

    Under the hood it adds m_hide_script_paths to the HidingSigningProvider which triggers an early return in GetTaprootSpendData() when set.

    It’s also missing taproot_scripts which might be a bit too much. At least in initial testing Ledger wouldn’t sign it.

    I probably need to implement this at the SigningProvider level rather than HidingSigningProvider. The early return needs to be inside SignTaproot() right before // Try script path spending rather than in GetTaprootSpendData(). The goal isn’t to hide leaf scripts from co-signers, only to make sure we don’t sign them ourselves.

  10. Sjors force-pushed on Jul 2, 2025
  11. Sjors commented at 6:31 pm on July 2, 2025: member

    Shoehorning this into SigningProvider got ugly real fast. So instead I endup up passing avoid_script_path along the call stack from SignPSBTInput to SignTaproot. Which is also ugly…

    Well, it compiles and works. Thoughts on how to implement this elegantly?

  12. Sjors force-pushed on Jul 3, 2025
  13. Sjors commented at 12:48 pm on July 3, 2025: member
    The walletprocesspsbt RPC now also has this option. Added test coverage by expanding wallet_taproot.py.
  14. Sjors force-pushed on Jul 3, 2025
  15. Sjors commented at 2:01 pm on July 3, 2025: member

    I split off a non-behavior-changing commit that adds avoid_script_path to FillPSBT. I think it makes sense to pass this option here, although in general we may want to refactor FillPSBT to take an options struct instead of this ever expanding argument list. That could be an independent or prerequisite PR.

    I then did the same for SignPSBTInput, where I also think struct would be better.

    Rinse and repeat for ::SignTransaction.

    Will continue later to further split 09334ae0bd7939ed7476eb2b36f3bf8085885fe7. It might make sense to add a property to BaseSignatureCreator or it subclasses?

  16. DrahtBot removed the label CI failed on Jul 3, 2025
  17. Sjors referenced this in commit ea493972d2 on Jul 4, 2025
  18. Sjors referenced this in commit edbb5d8df3 on Jul 4, 2025
  19. Sjors referenced this in commit 2829cb78c5 on Jul 4, 2025
  20. Sjors force-pushed on Jul 4, 2025
  21. Sjors commented at 1:45 pm on July 4, 2025: member
    I opened #32876 and rebased this PR on top of it. That allowed me to drop 1486f05c8eded02d72690bf34d983a03e4ad748b + efc34a514b59cc1bab6e4d0f80c197481561c429.
  22. Sjors force-pushed on Jul 4, 2025
  23. Sjors commented at 4:02 pm on July 4, 2025: member
    I expanded #32876 with SignOptions. This shrinks 93fb607af3141ff1e22693588f498764401fc2c4 to just e2a151c3dc35997ac0f515feb292fc4543093593 which is very small … but very ugly: casting BaseSignatureCreator to MutableTransactionSignatureCreator to access its avoid_script_path option.
  24. Sjors commented at 7:57 am on July 7, 2025: member
    This may also be useful for Silent Payments, cc @josibake.
  25. Sjors commented at 12:59 pm on July 7, 2025: member
    Sidenote: currently it’s unlikely you’ll accidentally spend via an older() or after() script path. This is because you need to manually set the input sequence field in the send (etc.) RPC (and manually adjust the fee rate). Found out the hard way while testing 🤦 (there’s currently no useful feedback when this happens).
  26. DrahtBot added the label Needs rebase on Jul 7, 2025
  27. Sjors referenced this in commit 2923087479 on Jul 8, 2025
  28. Sjors force-pushed on Jul 8, 2025
  29. DrahtBot removed the label Needs rebase on Jul 8, 2025
  30. Sjors referenced this in commit 576da4c7dd on Jul 8, 2025
  31. Sjors force-pushed on Jul 8, 2025
  32. refactor: use PSBTFillOptions for filling and signing
    Replace the sign, finalize , bip32derivs and sighash_type arguments which
    are passed to FillPSBT() and SignPSBTInput() with a PSBTFillOptions struct.
    
    This makes it easier to add additional options later without large code
    churn, such as avoid_script_path proposed in #32857. It also makes the
    use of default boolean options safer compared to positional arguments
    that can easily get mixed up.
    8660b55c88
  33. refactor: use SignOptions for SignTransaction 7b6efb3e98
  34. refactor: use SignOptions for MutableTransactionSignatureCreator 7092541631
  35. wallet: add option to avoid script path spends 37f4d47b2a
  36. rpc: add keypath_only to walletprocesspsbt ac4940c349
  37. test: cover keypath_only in wallet_taproot.py
    Expand taproot tests to cover avoid_script_path in  walletprocesspsbt.
    
    When avoiding script paths, there's no need for the workaround that increases fee_rate to compensate for the wallet's inability to estimate fees for script path spends. We use this to indirectly test that key path was used.
    
    We also check that taproot_script_path_sigs is not set.
    
    Finally, for transactions that can't be signed using their key path, we try again by allowing the script path.
    
    Additional test extended private keys were extracted from other tests.
    5373ecefeb
  38. rpc: add keypath_only to send 50023a918e
  39. Sjors force-pushed on Jul 14, 2025

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: 2025-07-25 18:13 UTC

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