psbt: support output metadata updates before inputs are added #35797

pull l0rinc wants to merge 6 commits into bitcoin:master from l0rinc:l0rinc/psbt-zero-input-output-update changing 5 files +163 −19
  1. l0rinc commented at 4:33 AM on July 25, 2026: contributor

    Problem: PSBTv2 permits outputs to be added before inputs. An authenticated descriptorprocesspsbt request can abort the node while updating metadata for one of those outputs because UpdatePSBTOutput() traverses its script with a signature creator for input 0. ECDSA signing or a miniscript timelock check can then access the missing input.

    Fix: Make MutableTransactionSignatureCreator reject ECDSA and Schnorr signing and MuSig2 operations when its input index does not exist, and return a rejecting checker for transaction-bound conditions. Output metadata traversal still records scripts and key origins, allowing outputs to be updated before inputs are added.

  2. test: characterize PSBT output metadata
    Document the scripts and key origins collected for common output descriptors before changing zero-input handling.
    Use a valid input for ECDSA-backed outputs because their zero-input paths currently abort.
    c261c2c3c9
  3. sign: reject ECDSA signing without input
    PSBTv2 permits outputs to be added before inputs, but `UpdatePSBTOutput()` traverses output scripts with a signature creator for input 0.
    A descriptor with private key material can therefore reach `SignatureHash()` without a corresponding input and abort.
    Reject the signature attempt while preserving the scripts and key origins collected during traversal.
    97e0758ccb
  4. test: characterize P2WSH miniscript output
    Add a relative-timelock case that consults the transaction checker during output metadata traversal.
    Use a valid input because the pre-fix missing-input path is only reliably diagnosed under a sanitizer.
    59f3f9525f
  5. sign: reject checks without input
    A miniscript relative timelock can consult `CheckSequence()` while `UpdatePSBTOutput()` is only collecting metadata.
    Return a rejecting checker when the creator has no corresponding input, so traversal can continue without reading a missing sequence.
    9f0afec37e
  6. test: characterize Schnorr signature creator
    Document direct Taproot signing with a valid input before covering a missing input index.
    The PSBT output-update path has no precomputed transaction data, so it cannot exercise this sighash path.
    54453625dd
  7. sign: reject Schnorr signing without input
    `ComputeSchnorrSignatureHash()` is shared by Schnorr signing and MuSig2 operations and can pass a missing input index to `SignatureHashSchnorr()`.
    Reject the request before computing the sighash, matching the missing-input behavior for ECDSA signing and transaction checks.
    0fa3581395
  8. DrahtBot added the label PSBT on Jul 25, 2026
  9. DrahtBot commented at 4:33 AM on July 25, 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/35797.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Approach ACK vicjuma

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #35848 (test: Cover IsNull() for PSBT, PSBTInput, PSBTOutput by nebula-21)
    • #35747 (wallet: Fix FillPSBT failing to sign owned inputs when UTXOs disagree by nervana21)
    • #35713 (Remove boost as a unit test runner by rustaceanrob)
    • #32857 (wallet: allow skipping script paths by Sjors)

    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-->

  10. in src/script/sign.h:59 in 0fa3581395
      55 | @@ -59,12 +56,14 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator
      56 |      const MutableTransactionSignatureChecker checker;
      57 |      const PrecomputedTransactionData* m_txdata;
      58 |  
      59 | +    bool HasInput() const { return nIn < m_txto.vin.size(); }
    


    vicjuma commented at 4:05 PM on July 29, 2026:

    This appears to be for ECDSA and/or Schnorr inputs check

  11. in src/script/sign.h:66 in 0fa3581395
      62 |  public:
      63 |      MutableTransactionSignatureCreator(const CMutableTransaction& tx LIFETIMEBOUND, unsigned int input_idx, const CAmount& amount, const SignOptions& options);
      64 |      MutableTransactionSignatureCreator(const CMutableTransaction& tx LIFETIMEBOUND, unsigned int input_idx, const CAmount& amount, const PrecomputedTransactionData* txdata, const SignOptions& options);
      65 | -    const BaseSignatureChecker& Checker() const override { return checker; }
      66 | +    /** Returns the transaction checker, or a rejecting checker when input_idx has no corresponding input. */
      67 | +    const BaseSignatureChecker& Checker() const override;
    


    vicjuma commented at 4:12 PM on July 29, 2026:

    This appears to be for the miniscript path checks

  12. in src/script/sign.cpp:53 in 0fa3581395
      49 | @@ -50,10 +50,18 @@ MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMu
      50 |  {
      51 |  }
      52 |  
      53 | +const BaseSignatureChecker& MutableTransactionSignatureCreator::Checker() const
    


    vicjuma commented at 5:58 PM on July 29, 2026:

    Implementation has fixed the miniscript path as indicated in the RPC testing process

    Before change

    ratedg@0xratedg:~/projects/contributions/bitcoin/build/bin$ ./bitcoind
    Bitcoin Core starting
    ratedg@0xratedg:~/projects/contributions/bitcoin/build/bin$ ./bitcoin-cli descriptorprocesspsbt "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQABBQEBAfsEAgAAAAABAwhAQg8AAAAAAAEEIgAgVLmFfPxqejfeM2UcPknlg+zd1E47Aj/mtr5ZMMYDfN4A" "[\"wsh(and_v(v:pk([150541d1/44h/1h/0h/0/0]03c5bf15b5baf73ebd5285746ece7c95d502916f189145a7005248bb755e2c3c5e),older(10)))#qlhqad4l\"]"
    error: Error while attempting to communicate with server 127.0.0.1:18443 (EOF)
    
    Make sure the bitcoind server is running and that you are connecting to the correct RPC port.
    Use "bitcoin-cli -help" for more info.
    ratedg@0xratedg:~/projects/contributions/bitcoin/build/bin$  
    

    After change

    ratedg@0xratedg:~/projects/contributions/bitcoin$ cd build/bin/ && ./bitcoin-cli stop && ./bitcoind
    Bitcoin Core stopping
    Bitcoin Core starting
    ratedg@0xratedg:~/projects/contributions/bitcoin/build/bin$ ./bitcoin-cli descriptorprocesspsbt "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQABBQEBAfsEAgAAAAABAwhAQg8AAAAAAAEEIgAgVLmFfPxqejfeM2UcPknlg+zd1E47Aj/mtr5ZMMYDfN4A" "[\"wsh(and_v(v:pk([150541d1/44h/1h/0h/0/0]03c5bf15b5baf73ebd5285746ece7c95d502916f189145a7005248bb755e2c3c5e),older(10)))#qlhqad4l\"]"
    {
      "psbt": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQABBQEBAfsEAgAAAAABASUhA8W/FbW69z69UoV0bs58ldUCkW8YkUWnAFJIu3VeLDxerVqyIgIDxb8Vtbr3Pr1ShXRuznyV1QKRbxiRRacAUki7dV4sPF4YFQVB0SwAAIABAACAAAAAgAAAAAAAAAAAAQMIQEIPAAAAAAABBCIAIFS5hXz8ano33jNlHD5J5YPs3dROOwI/5ra+WTDGA3zeAA==",
      "complete": true,
      "hex": "02000000000140420f000000000022002054b9857cfc6a7a37de33651c3e49e583ecddd44e3b023fe6b6be5930c6037cde00000000"
    }
    ratedg@0xratedg:~/projects/contributions/bitcoin/build/bin$ 
    
    
  13. in src/script/sign.cpp:63 in 0fa3581395
      58 | +
      59 |  bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
      60 |  {
      61 |      assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
      62 |  
      63 | +    if (!HasInput()) return false;
    


    vicjuma commented at 6:03 PM on July 29, 2026:

    Reasonable to me for ECDSA path

  14. in src/script/sign.cpp:90 in 0fa3581395
      86 | @@ -79,6 +87,8 @@ std::optional<uint256> MutableTransactionSignatureCreator::ComputeSchnorrSignatu
      87 |  {
      88 |      assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
      89 |  
      90 | +    if (!HasInput()) return std::nullopt;
    


    vicjuma commented at 6:04 PM on July 29, 2026:

    Reasonable to me for Schnorr path

  15. vicjuma commented at 6:33 PM on July 29, 2026: none

    Tested the miniscript path and the change seems to fix the initial crush with empty inputs created from the createpsbt RPC

    ./bitcoin-cli createpsbt "[]" "[{\"bcrt1q2juc2l8udfar0h3nv5wruj09s0kdm4zw8vprle4khevnp3sr0n0q6gkweu\":0.01}]"
    
    cHNidP8BAgQCAAAAAQMEAAAAAAEEAQABBQEBAfsEAgAAAAABAwhAQg8AAAAAAAEEIgAgVLmFfPxqejfeM2UcPknlg+zd1E47Aj/mtr5ZMMYDfN4A
    

    N/B: To reproduce the crush, the address used was derived from the miniscript.
    "wsh(and_v(v:pk([150541d1/44h/1h/0h/0/0]03c5bf15b5baf73ebd5285746ece7c95d502916f189145a7005248bb755e2c3c5e),older(10)))#qlhqad4l" [ "bcrt1q2juc2l8udfar0h3nv5wruj09s0kdm4zw8vprle4khevnp3sr0n0q6gkweu" ]

  16. l0rinc commented at 7:12 PM on July 29, 2026: contributor

    Thanks for the excellent before-and-after reproducers @vicjuma. It sounds as though you agree with the approach - if you are comfortable taking responsibility for the review, consider ACK-ing the PR as described in the code review guidelines?

  17. vicjuma commented at 7:14 PM on July 29, 2026: none

    Thanks for the excellent before-and-after reproducers @vicjuma. It sounds as though you agree with the approach - if you are comfortable taking responsibility for the review, consider ACK-ing the PR as described in the code review guidelines?

    Approach ACK

Labels

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-08-08 02:51 UTC

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