Defer transaction signing until user clicks Send #915

pull 151henry151 wants to merge 1 commits into bitcoin-core:master from 151henry151:fix-30070-defer-signing changing 2 files +32 −2
  1. 151henry151 commented at 7:19 pm on November 22, 2025: contributor

    Fixes #30070

    When creating an unsigned PSBT from the GUI, the transaction was already signed during preparation, causing legacy inputs to have non-empty scriptSig fields. The PSBT parser then rejects them.

    This defers signing until the user clicks “Send” instead of signing during preparation. Fee calculation still works since transactions can be created without signing.

    Follows the approach suggested by @achow101 in the issue comments.

  2. DrahtBot commented at 7:19 pm on November 22, 2025: contributor

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

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK achow101

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    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.

    LLM Linter (✨ experimental)

    Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • tr("%1 kvB (unsigned)", "PSBT transaction creation").arg((double)m_current_transaction->getTransactionSize() / 1000, 0, 'g', 3) in src/qt/sendcoinsdialog.cpp
    • model->wallet().fillPSBT(std::nullopt, /*sign=*/true, /*bip32derivs=*/false, /*n_signed=*/nullptr, psbtx, complete) in src/qt/sendcoinsdialog.cpp
    • m_wallet->createTransaction(vecSend, coinControl, /*sign=*/false, /*change_pos=*/std::nullopt) in src/qt/walletmodel.cpp

    2026-03-31 14:19:06

  3. hebasto renamed this:
    qt: Defer transaction signing until user clicks Send
    Defer transaction signing until user clicks Send
    on Nov 22, 2025
  4. DrahtBot added the label CI failed on Nov 22, 2025
  5. 151henry151 commented at 6:02 pm on November 23, 2025: contributor
    It looks like the one failing check is a CI issue with disk space too low. I’m not sure how to address that. I think the code of the PR is correct.
  6. 151henry151 commented at 9:51 pm on November 29, 2025: contributor
    @achow101 would you like to check this out and let me know if it looks OK?
  7. maflcko closed this on Dec 17, 2025

  8. maflcko reopened this on Dec 17, 2025

  9. 151henry151 commented at 6:40 pm on January 18, 2026: contributor
    Should I push an empty commit to trigger a re-run of the CI here, or is there some other action needed?
  10. hebasto commented at 3:59 pm on January 19, 2026: member

    Should I push an empty commit to trigger a re-run of the CI here, or is there some other action needed?

    No action needed.

  11. hebasto commented at 3:59 pm on January 19, 2026: member
  12. hebasto removed the label CI failed on Jan 19, 2026
  13. hebasto added the label Wallet on Jan 19, 2026
  14. in src/qt/walletmodel.cpp:150 in 58adbcf88a
    146@@ -147,7 +147,7 @@ bool WalletModel::validateAddress(const QString& address) const
    147     return IsValidDestinationString(address.toStdString());
    148 }
    149 
    150-WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
    151+WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl, bool sign)
    


    achow101 commented at 8:51 pm on January 19, 2026:
    prepareTransaction has only a single caller that already sets sign = false. I don’t think this needs to take a parameter, it can just always not sign.

    151henry151 commented at 5:26 pm on January 20, 2026:
    suggestion implemented

    johnny9 commented at 3:03 am on March 18, 2026:
    did you mean to remove the sign parameter?
  15. in src/qt/sendcoinsdialog.cpp:551 in 58adbcf88a
    546+            // Sign the transaction now that the user has confirmed they want to send.
    547+            CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
    548+            PartiallySignedTransaction psbtx(mtx);
    549+            bool complete = false;
    550+            // Fill and sign the PSBT
    551+            const auto err{model->wallet().fillPSBT(std::nullopt, /*sign=*/true, /*bip32derivs=*/false, /*n_signed=*/nullptr, psbtx, complete)};
    


    achow101 commented at 8:53 pm on January 19, 2026:
    Creating the PSBT and calling fillPSBT on it is being done for all of the other cases in this function, and hence is now duplicated 3 times. This can be refactored so that the PSBT is always created and filled at the top of the function, and each action can do whatever specific thing it needs to do with that PSBT.

    151henry151 commented at 5:26 pm on January 20, 2026:
    refactored as suggested
  16. achow101 commented at 8:54 pm on January 19, 2026: member
    Concept ACK
  17. 151henry151 force-pushed on Jan 20, 2026
  18. DrahtBot added the label CI failed on Jan 20, 2026
  19. DrahtBot added the label Needs rebase on Feb 10, 2026
  20. 151henry151 force-pushed on Feb 11, 2026
  21. DrahtBot removed the label Needs rebase on Feb 11, 2026
  22. 151henry151 force-pushed on Feb 11, 2026
  23. 151henry151 commented at 4:56 am on February 11, 2026: contributor
    For the rebase, I kept master’s new createTransaction usage and error flow, wired in the defer-signing logic (sign parameter), and re-added the AmountExceedsBalance check after a successful result. I initially missed updating that to AmountExceedsBalance, but I’ve corrected it. If anyone wants to check this out and let me know if it looks correct, I’d appreciate any feedback. Thanks
  24. DrahtBot removed the label CI failed on Feb 11, 2026
  25. 151henry151 commented at 9:44 pm on March 1, 2026: contributor
    Would anyone like to take another look at this? Any review or feedback welcomed
  26. in src/qt/sendcoinsdialog.cpp:275 in 9b65413cef


    johnny9 commented at 4:08 pm on March 4, 2026:
    You might need to validate that this works with encrypted wallets. You likely need this UnlockContext to sign later on.

    151henry151 commented at 10:09 pm on March 5, 2026:
    @johnny9 I think I’ve addressed this correctly with 39a5fed – it passed my local tests; do you want to check it out and see if it looks good to you?

    johnny9 commented at 3:01 pm on March 8, 2026:
    You should go ahead and delete this unlock context now as I don’t think you need it anymore

    johnny9 commented at 3:09 pm on March 8, 2026:
    This is a little different now that the current transaction isn’t signed yet so the kvB is different now than before. Can either change the dialog to make this clear or adjust this size to be the same as the estimated size the fee calculation uses.

    johnny9 commented at 3:34 pm on March 8, 2026:

    For this example vsize should equal the fee (208 kvB and 208 sat fee). Without your changes they were same and now the size of the transaction doesnt include the signed portion so it isn’t matching the fee.

  27. johnny9 commented at 4:09 pm on March 4, 2026: contributor
    Double check this works with locked encrypted wallets
  28. johnny9 changes_requested
  29. johnny9 commented at 4:35 pm on March 4, 2026: contributor

    Need to move the UnlockContext when you sign the transaction otherwise it fails to send now.

    https://github.com/user-attachments/assets/6b7e6894-b803-4753-9f20-66541af63633

  30. 151henry151 commented at 4:42 pm on March 4, 2026: contributor
    Thanks @johnny9 , I’ll take a closer look this evening
  31. 151henry151 force-pushed on Mar 5, 2026
  32. DrahtBot added the label CI failed on Mar 6, 2026
  33. johnny9 commented at 3:14 pm on March 8, 2026: contributor

    Moving the UnlockContext in the “Send” click flow resolved the previous issue. I think the previous UnlockContext is no longer needed.

    I also see that the send confirmation dialog isn’t as accurate as it was previously so should adjust what the dialog says or expose the correct fee estimated size through the model.

  34. 151henry151 force-pushed on Mar 16, 2026
  35. 151henry151 commented at 1:03 am on March 16, 2026: contributor
    @johnny9 I think I’ve fixed it correctly; I tested locally (built the gui and tested manually) and it seems like the issues are resolved. Could you test it again to confirm, and take a look at the changes to see if there are any mistakes or problems you can see?
  36. DrahtBot removed the label CI failed on Mar 16, 2026
  37. 151henry151 requested review from johnny9 on Mar 16, 2026
  38. in src/qt/sendcoinsdialog.cpp:275 in 6a999c2912
    271@@ -269,23 +272,18 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
    272     }
    273 
    274     fNewRecipientAllowed = false;
    275-    WalletModel::UnlockContext ctx(model->requestUnlock());
    276-    if(!ctx.isValid())
    277-    {
    278-        // Unlock wallet was cancelled
    


    johnny9 commented at 3:02 am on March 18, 2026:
    I think I was wrong about removing this. I think you will need unlock here too to be sure you can get a change address.
  39. in src/qt/walletmodel.h:98 in 6a999c2912
    94@@ -95,7 +95,7 @@ class WalletModel : public QObject
    95     };
    96 
    97     // prepare transaction for getting txfee before sending coins
    98-    SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const wallet::CCoinControl& coinControl);
    99+    SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const wallet::CCoinControl& coinControl, bool sign = false);
    


    johnny9 commented at 3:05 am on March 18, 2026:
    sign parameter can be removed
  40. johnny9 commented at 3:06 am on March 18, 2026: contributor
    I actually think I was wrong and we need both UnlockContexts. Sorry to flp-flop on that. Also take a look at achow’s comment again. Seems like you didnt remove the parameter he mentioned. Other than those things I think this looks good now
  41. qt: Defer transaction signing until user clicks Send
    This fixes issue #30070 where creating unsigned PSBTs from the GUI
    would fail because the transaction was already signed during preparation,
    causing legacy inputs to have non-empty scriptSig fields.
    
    The fix defers signing until the user explicitly clicks 'Send', allowing
    truly unsigned PSBTs to be created while still supporting fee calculation.
    5b79f271ef
  42. Ari4ka approved
  43. 151henry151 force-pushed on Mar 31, 2026
  44. 151henry151 commented at 2:20 pm on March 31, 2026: contributor
    Thanks for the additional feedback; I’ve made the changes and I tested locally, I think it is all correct. Please check it out for me and see if you find any other errors or problems with it.

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin-core/gui. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-07 19:20 UTC

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