gui: create PSBT with watch-only wallet #16944

pull Sjors wants to merge 4 commits into bitcoin:master from Sjors:2019/08/gui-send-psbt changing 6 files +85 −23
  1. Sjors commented at 7:08 pm on September 23, 2019: member

    For wallets with WALLET_FLAG_DISABLE_PRIVATE_KEYS this makes the watch-only balance available on the send screen (including coin selection). Instead of sending a transaction it generates a PSBT.

    The user can take this PSBT and process it with HWI or put it an SD card for hardware wallets that support that.

    The PSBT is copied to the clipboard. This was the easiest approach; we can add a dialog later to display it, as well as an option to save to disk.

  2. jb55 commented at 7:14 pm on September 23, 2019: member
    Concept ACK
  3. nvk commented at 7:18 pm on September 23, 2019: none
    Would love to see this happen +1
  4. DrahtBot commented at 8:01 pm on September 23, 2019: member

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #17518 (refactor, wallet: Nuke coincontrol circular dependency by hebasto)
    • #17463 (Bugfix: GUI: Restore SendConfirmationDialog button default to “Yes” by luke-jr)
    • #17457 (gui: Fix manual coin control with multiple wallets loaded by promag)

    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.

  5. instagibbs commented at 8:28 pm on September 23, 2019: member

    concept ACK.

    I do however think a proper dialogue will be required to let the user know what’s going on at a minimum rather than magic resulting in a clipboard result.

    very light testing also shows it appears it does what’s on the tin. I’ll do lots more testing later once approach is settled.

  6. DrahtBot added the label GUI on Sep 23, 2019
  7. DrahtBot added the label Wallet on Sep 23, 2019
  8. gwillen commented at 8:58 pm on September 23, 2019: contributor

    So as some of you saw, I did some related work quite awhile ago. I have a dialog with support for creating/saving/loading/signing/sending these. It’s somewhat modeled after the offline signing UI in Armory. I was never happy with the UI, and I think designing this to be intuitive is really hard, but I think it may already be an improvement over clipboard-only.

    Let me spend a few minutes figuring out what the most recent version is that builds cleanly and works, and I will link it here so people can try it.

  9. promag commented at 9:42 pm on September 23, 2019: member
    Concept ACK.
  10. gwillen commented at 10:20 pm on September 23, 2019: contributor

    Ok, if you’d like to see my work on this, please have a look at https://github.com/gwillen/bitcoin/tree/feature-offline-v2 .

    You will need to check preferences->wallet->enable offline transaction features. Then you’ll get an ‘Offline’ menu, a new offline transactions dialog box, and a new pair of checkboxes in the ‘send’ dialog: “Include watchonly coins” and “create offline transaction” (which is forced on if you include watchonly). If you select the latter, the ‘send’ button becomes “Create unsigned” and will take you to the new dialog.

    There is still some amount of jank to be ironed out, there are some XXX comments in the source and some missing error handling.

    I’ve had this vaguely shelved for awhile, but I’d be happy to bring it out and work aggressively on it now if desired; I’d also be happy to bring it out less aggressively and start prepping it as a possible sequel to the PR under discussion here.

  11. fanquake renamed this:
    UI: create PSBT with watch-only wallet
    gui: create PSBT with watch-only wallet
    on Sep 24, 2019
  12. fanquake requested review from achow101 on Sep 24, 2019
  13. Sjors commented at 12:36 pm on September 24, 2019: member

    @gwillen can you make a (draft) PR with that branch? If you rebase on top of this, you can probably drop the “Enable offline transaction features” checkbox as well as the “include watchonly coins” and “create unsigned “checkboxes, in favor of only allowing the functionality with watch-only wallets.

    I like how the send button text changes, so I’ll steal that for this PR :-)

    Once we have descriptor wallets and better HWI integration, the simple distinction between watch-only and wallets with private keys will no longer work. For example you might be part of a 2 of 3 multisig, with the keys in the wallet. Or maybe you don’t have keys in the Core wallet, but your hardware wallet can sign its part via USB.

    So in terms of your wizard, I would split into two wizards: one for composing & exporting, and one for loading.

    There’s probably a step 0 where you attempt to (partially) sign with your own keys and with any connected devices. If that’s not enough, then you go to step 1 where you can export the PSBT via clipboard or file system.

    I would introduce a fresh dialog for loading, maybe co-signing and then broadcasting a transaction.

  14. Sjors commented at 1:34 pm on September 24, 2019: member
    Let’s continue the UX brainstom here: #16954
  15. practicalswift commented at 11:23 am on September 25, 2019: contributor

    I’m afraid the current version of this PR introduces a bug in CWallet::ListCoins.

    First, note that == has higher precedence than the ternary conditional:

    0[cling]$ 1 == 2 ? 3 : 4
    1(int) 4
    2[cling]$ (1 == 2) ? 3 : 4
    3(int) 4
    4[cling]$ 1 == (2 ? 3 : 4)
    5(bool) false
    

    Second, some context:

    0unsigned int IsMine(...);
    1bool IsWalletFlagSet(...);
    2int ISMINE_WATCH_ONLY = 1;
    3int ISMINE_SPENDABLE = 2;
    

    This is being suggested in this PR:

    https://github.com/bitcoin/bitcoin/blob/41c5165aa30d65d40ef3921bf4760c13f5d42b21/src/wallet/wallet.cpp#L2592-L2593

    Shortened to highlight the issue:

    0if (... &&
    1    IsMine(it->second.tx->vout[output.n]) ==
    2      IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ?
    3      ISMINE_WATCH_ONLY : ISMINE_SPENDABLE) {
    

    Which – given the precedence rules – is equivalent to:

    0if (... &&
    1    (IsMine(it->second.tx->vout[output.n]) ==
    2      IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) ?
    3      ISMINE_WATCH_ONLY : ISMINE_SPENDABLE) {
    

    Which – given that ISMINE_WATCH_ONLY and ISMINE_SPENDABLE are both non-zero integer constants (1 and 2) that will be evaluated in a boolean context – is equivalent to:

    0if (... && true) {
    

    Or simply:

    0if (...) {
    

    :)

    I think the intention was:

    0if (... &&
    1    IsMine(it->second.tx->vout[output.n]) ==
    2      (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ?
    3      ISMINE_WATCH_ONLY : ISMINE_SPENDABLE)) {
    

    Could that be the case? :)

  16. in src/wallet/wallet.cpp:2593 in 0e88841b33 outdated
    2589@@ -2590,7 +2590,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch
    2590         if (it != mapWallet.end()) {
    2591             int depth = it->second.GetDepthInMainChain(locked_chain);
    2592             if (depth >= 0 && output.n < it->second.tx->vout.size() &&
    2593-                IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
    2594+                IsMine(it->second.tx->vout[output.n]) == IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE) {
    


    instagibbs commented at 1:19 pm on September 25, 2019:
    As @practicalswift notes you likely wanted parenths around this new clause
  17. in src/qt/sendcoinsdialog.cpp:395 in 078583e2c5 outdated
    394+    if (model->privateKeysDisabled()) {
    395+        done = true;
    396+        CMutableTransaction mtx = CMutableTransaction{*(currentTransaction.getWtx())};
    397+        PartiallySignedTransaction psbtx(mtx);
    398+        bool complete = false;
    399+        const TransactionError err = model->wallet().fillPSBT(psbtx, complete, SIGHASH_ALL, false, true);
    


    instagibbs commented at 1:22 pm on September 25, 2019:
    nit: annotate the optional bool args for clarity
  18. in src/qt/sendcoinsdialog.cpp:389 in 078583e2c5 outdated
    385@@ -383,17 +386,36 @@ void SendCoinsDialog::on_sendButton_clicked()
    386         return;
    387     }
    388 
    389-    // now send the prepared transaction
    390-    WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
    391-    // process sendStatus and on error generate message shown to user
    392-    processSendCoinsReturn(sendStatus);
    393+    bool done = false;
    


    instagibbs commented at 1:27 pm on September 25, 2019:
    done isn’t really helping me parse what is going on here, maybe call it send_failure and reverse the meaning?
  19. instagibbs commented at 1:36 pm on September 25, 2019: member

    The final dialogue should have the “Yes” text replace with “Copy unsigned tx to clipboard” or something self-explanatory.

    Really “Yes” should be “Send” in the average case anyways(think I might PR that change separately)

  20. in src/qt/sendcoinsdialog.cpp:408 in 41c5165aa3 outdated
    407+        assert(err == TransactionError::OK);
    408+        // Serialize the PSBT
    409+        CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
    410+        ssTx << psbtx;
    411+        GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
    412+        Q_EMIT message(tr("PSBT copied"), "Copied to clipbboard", CClientUIInterface::MSG_INFORMATION);
    


    practicalswift commented at 9:56 pm on September 25, 2019:
    Should be “clipboard” :)
  21. Sjors force-pushed on Sep 26, 2019
  22. Sjors commented at 9:46 am on September 26, 2019: member

    Fixed the precedence bug @practicalswift found.

    I added some additional copy:

    With that I don’t think it’s necessary to show a dialog with the PSBT text. See also #16966 for a more rigorous approach (I’m trying to keep that an easy rebase), where it will be easier to integrate @gwillen’s PSBT management changes (save to disk, check progress, etc).

  23. instagibbs commented at 2:12 pm on September 26, 2019: member

    “e.g., and offline Bitcoin Core wallet, or a PSBT-compatible hardware wallet”

    I think that change would make me happy.

  24. Sjors force-pushed on Sep 26, 2019
  25. Sjors commented at 2:27 pm on September 26, 2019: member
    @instagibbs done (I didn’t update the screenshots)
  26. instagibbs commented at 3:02 pm on September 26, 2019: member

    tACK https://github.com/bitcoin/bitcoin/pull/16944/commits/da869592ecf82f8a03eb4ab2339d6bffa3dcea58

    Ran both watch-only and non-watch-only modes, tested coin control in watch-only mode, tested a Core -> HWI -> Core round-trip and successfully finalized a PSBT with the desired properties.

  27. laanwj added the label Feature on Sep 30, 2019
  28. in src/qt/sendcoinsdialog.cpp:414 in da869592ec outdated
    437+            Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
    438+        } else {
    439+            send_failure = true;
    440+        }
    441+    }
    442+    if (!send_failure) {
    


    achow101 commented at 9:04 pm on September 30, 2019:
    This doesn’t need to be outside of the else block since it isn’t used in the no private keys case.

    Sjors commented at 3:26 pm on October 1, 2019:
    In the no private keys case we also clear the form. We just assume (with an assert) that PSBT creation can never fail, which is why send_failure is always false.
  29. achow101 commented at 9:05 pm on September 30, 2019: member

    Conecept ACK

    It would be preferable if clicking “Copy to Clipboard” wouldn’t close the dialog. Also, maybe show the psbt in a text box in the dialog too?

  30. Sjors commented at 3:28 pm on October 1, 2019: member
    The confirmation dialog is a QMessageBox, which isn’t very flexible. #16966 is a better place to tweak things (and I want to avoid rebase hell there).
  31. promag commented at 10:33 pm on October 1, 2019: member

    What am I missing?

     0getbalances
     1{
     2  "mine": {
     3    "trusted": 0.00000000,
     4    "untrusted_pending": 0.00000000,
     5    "immature": 0.00000000
     6  },
     7  "watchonly": {
     8    "trusted": 11202.50000000,
     9    "untrusted_pending": 0.00000000,
    10    "immature": 1250.00003320
    11  }
    12}
    

    And then I get “The amount exceeds your balance.”

  32. gwillen commented at 3:41 am on October 2, 2019: contributor

    My experience was that coin selection refused to select watchonly coins when I wanted it to, even if I set watchonly to true in the coincontrol object passed to preparetransaction (and hence createtransaction), unless I made some more-invasive changes, linked below. I’m not sure if that’s what you’re seeing, @promag.

    In particular, see my changes to CWallet::ListCoins: https://github.com/gwillen/bitcoin/compare/35991b162fd142d76d83e672002846c8a40eb794...gwillen:feature-offline-v2#diff-b2bb174788c7409b671c46ccc86034bdR2362

    which aren’t quite the same as the ones in this PR. When I call AvailableCoins, I force on fWatchOnly in the CCoinControl object that I hand it, which the code did not previously do even if it was called for (I had to add a flag to ListCoins.) Curiously, it seems like it worked for me without the spendability-related changes that this PR makes. Not sure what’s up there.

  33. Sjors commented at 10:13 am on October 2, 2019: member

    @promag how did you insert the watch-only keys into that wallet? I’ve only tested with HWI which imports as follows:

    [{"desc": "wpkh([8...4P/0/*)#36sal9a4", "internal": false, "range": [0, 1000], "timestamp": "now", "keypool": true, "watchonly": true}, {"desc": "wpkh([80..P/1/*)#nl2rc26w", "internal": true, "range": [0, 1000], "timestamp": "now", "keypool": true, "watchonly": true}]

    What does getaddressinfo tell you about the coins you’re trying to spend?

    Does walletcreatefundedpsbt fail the same way?

  34. MarcoFalke referenced this in commit 19ba43ae2d on Oct 2, 2019
  35. instagibbs commented at 2:53 pm on October 2, 2019: member
    needs rebase
  36. DrahtBot added the label Needs rebase on Oct 2, 2019
  37. Sjors force-pushed on Oct 2, 2019
  38. promag commented at 3:43 pm on October 2, 2019: member

    @Sjors result of getaddressinfo:

     0{
     1  "address": "2N9wHFC4dSkZMu3BCspQkApJ9Pf4coHQWUF",
     2  "scriptPubKey": "a914b7155eadf2015c0fe4d1e17c4e29d5d58659a9d987",
     3  "ismine": false,
     4  "solvable": true,
     5  "desc": "sh(wpkh([528979b9]03dce9cd688fd0a9f6dac92627691eb609b8096123455676b5b57db6abaa32f666))#w8dysyn3",
     6  "iswatchonly": true,
     7  "isscript": true,
     8  "iswitness": false,
     9  "script": "witness_v0_keyhash",
    10  "hex": "0014528979b9bff424bdefb6e7d6cf98f692eb5a9d05",
    11  "pubkey": "03dce9cd688fd0a9f6dac92627691eb609b8096123455676b5b57db6abaa32f666",
    12  "embedded": {
    13    "isscript": false,
    14    "iswitness": true,
    15    "witness_version": 0,
    16    "witness_program": "528979b9bff424bdefb6e7d6cf98f692eb5a9d05",
    17    "pubkey": "03dce9cd688fd0a9f6dac92627691eb609b8096123455676b5b57db6abaa32f666",
    18    "address": "bcrt1q22yhnwdl7sjtmmakultvlx8kjt4448g9nrg59w",
    19    "scriptPubKey": "0014528979b9bff424bdefb6e7d6cf98f692eb5a9d05"
    20  },
    21  "label": "",
    22  "ischange": false,
    23  "timestamp": 1,
    24  "labels": [
    25    {
    26      "name": "",
    27      "purpose": "receive"
    28    }
    29  ]
    30}
    

    I’ve used importpubkey.

    Does walletcreatefundedpsbt fail the same way?

    I’ll try it.

  39. DrahtBot removed the label Needs rebase on Oct 2, 2019
  40. sidhujag referenced this in commit da4ce75ee1 on Oct 2, 2019
  41. instagibbs commented at 3:29 pm on October 8, 2019: member
    @promag able to reproduce issue? I’m willing to help debug.
  42. promag commented at 5:40 pm on October 8, 2019: member

    @promag able to reproduce issue? @instagibbs no, currently it’s working 👀

  43. instagibbs commented at 8:50 pm on October 8, 2019: member

    code review and tACK 428bdc8041e2c1479b650207110de081a91a19eb

    Tested:

    1. wallet with privkeys, button is “Send”, coin control works as intended
    2. wallet with imported descriptors, button lets you make psbt(that I am able to sign remote, and finalize), coin control works as expected
    3. wallet with imported addresses only, button also “Create unsigned” but no funds available, nothing shows up in coin control either
  44. harding commented at 8:46 pm on October 9, 2019: contributor

    @Sjors and @gwillen thanks for working on this! I use both the GUI and a Bitcoin Core-based cold wallet, so I’m very appreciative of this type of improvement.

    Nit-ish. If I try to create an unsigned spend by clicking “Use available balance” and checking “Subtract fee from amount”, my transaction shouldn’t have a change output. However, on a wallet where I haven’t created a keypool, I get an error message about not having a change address.

    2019-10-09-10_09_12_440747090

    I also get a second error after that error (“Transaction creation failed”) which seems like error message overkill.

    After importing a key to the keypool, this generated the prompt as expected. However, I hit cancel, tried again, and it failed with the above error message. It seems it marked the change address it allocated as used even though (1) the transaction didn’t include a change address and (2) the PSBT was never sent to the clipboard (so, even if there had been a change address, it would never have been user-accessible).

    After importing another single key into the keypool and this time not cancelling, it worked as expected. I was able to take the PSBT from the clipboard, run it through my other wallet’s walletprocesspsbt, copy it back, run finalizepsbt, run sendrawtransaction, and verify it entered the mempool of my connected regtest node.

    I also tried this on a mixed wallet with some private keys and some watch-only keys, and I can confirm that it doesn’t work there (as it shouldn’t, per the OP). For reference, I ran everything manually (no HWI) and only tested single-sig on regtest.

    Thanks again for working on this!

  45. Sjors commented at 1:53 pm on October 10, 2019: member
    The wallet seems a bit overzealous on trying to top up the keypool. I would prefer to punt this particular bug until after #16341, unless @achow101 thinks a fix won’t create rebase hell for him.
  46. promag commented at 1:59 pm on October 10, 2019: member

    I get an error message about not having a change address

    Yeah also got that one. @Sjors I think that, regardless of #16341, a solution would be nice.

  47. laanwj added this to the "Blockers" column in a project

  48. Sjors commented at 10:49 am on October 11, 2019: member
    I reproduced the issue by creating a watch-only wallet without a keypool (using HWI’s --nokeypool feature). Added a commit to address this.
  49. in src/wallet/wallet.cpp:2972 in 1cb1381ca5 outdated
    2968@@ -2969,7 +2969,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
    2969             // Create change script that will be used if we need change
    2970             // TODO: pass in scriptChange instead of reservedest so
    2971             // change transaction isn't always pay-to-bitcoin-address
    2972-            CScript scriptChange;
    2973+            CScript scriptChange = CScript();
    


    promag commented at 8:45 am on October 12, 2019:

    1cb1381ca51d1967146a0a75fd96c1b4800ec378

    Why was this changed?


    Sjors commented at 10:00 am on October 12, 2019:
    So it’s not undefined when passed into coin selection.

    promag commented at 10:06 am on October 12, 2019:
    The empty constructor was already being called right?

    Sjors commented at 10:43 am on October 12, 2019:
    Ah yes, it’s a class, so it’s initialized… Fixed.
  50. in src/wallet/wallet.cpp:2973 in 1cb1381ca5 outdated
    2968@@ -2969,7 +2969,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
    2969             // Create change script that will be used if we need change
    2970             // TODO: pass in scriptChange instead of reservedest so
    2971             // change transaction isn't always pay-to-bitcoin-address
    2972-            CScript scriptChange;
    2973+            CScript scriptChange = CScript();
    2974+            bool failed_to_get_change_address = false;
    


    promag commented at 8:53 am on October 12, 2019:

    1cb1381ca51d1967146a0a75fd96c1b4800ec378

    nit bool failed_to_get_change_address{false}.

  51. in src/wallet/wallet.cpp:2974 in 1cb1381ca5 outdated
    2968@@ -2969,7 +2969,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
    2969             // Create change script that will be used if we need change
    2970             // TODO: pass in scriptChange instead of reservedest so
    2971             // change transaction isn't always pay-to-bitcoin-address
    2972-            CScript scriptChange;
    2973+            CScript scriptChange = CScript();
    2974+            bool failed_to_get_change_address = false;
    2975+            std::string failed_to_get_change_address_reason = "";
    


    promag commented at 9:04 am on October 12, 2019:

    1cb1381ca51d1967146a0a75fd96c1b4800ec378

    I think you don’t need this, you can write the reason in strFailReason.

  52. in test/functional/wallet_keypool.py:105 in 1cb1381ca5 outdated
    100+
    101+        # creating a transaction with change should not be possible
    102+        assert_raises_rpc_error(-4, "Can't generate a change-address key. No keys in the internal keypool and can't generate any keys.", w2.walletcreatefundedpsbt, inputs=[], outputs=[{addr.pop(): 0.05}], options={"subtractFeeFromOutputs": [0]})
    103+
    104+        # creating a transaction without change should still be possible
    105+        res = w2.walletcreatefundedpsbt(inputs=[], outputs=[{addr.pop(): 0.1}], options={"subtractFeeFromOutputs": [0]})
    


    promag commented at 9:10 am on October 12, 2019:

    1cb1381ca51d1967146a0a75fd96c1b4800ec378

    Could also test it works with changeAddress option?


    Sjors commented at 10:17 am on October 12, 2019:
    Added
  53. in src/wallet/wallet.cpp:3000 in 1cb1381ca5 outdated
    3007+                    bool ret = reservedest.GetReservedDestination(change_type, dest, true);
    3008+                    if (ret) {
    3009+                        scriptChange = GetScriptForDestination(dest);
    3010+                    } else {
    3011+                        failed_to_get_change_address = true;
    3012+                        failed_to_get_change_address_reason = "Keypool ran out, please call keypoolrefill first";
    


    promag commented at 9:11 am on October 12, 2019:

    1cb1381ca51d1967146a0a75fd96c1b4800ec378

    unrelated, I wonder why this is not translated.


    Sjors commented at 10:30 am on October 12, 2019:
    Added translation
  54. promag commented at 9:12 am on October 12, 2019: member
    Code review ACK 1cb1381ca51d1967146a0a75fd96c1b4800ec378, I’ll try it out.
  55. Sjors force-pushed on Oct 12, 2019
  56. Sjors force-pushed on Oct 12, 2019
  57. in src/qt/sendcoinsdialog.cpp:192 in 428bdc8041 outdated
    186@@ -186,6 +187,13 @@ void SendCoinsDialog::setModel(WalletModel *_model)
    187         // set default rbf checkbox state
    188         ui->optInRBF->setCheckState(Qt::Checked);
    189 
    190+        if (model->privateKeysDisabled()) {
    191+            ui->sendButton->setText(tr("Create Unsigned"));
    192+            ui->sendButton->setToolTip(tr("Creates a Partially Unsigned Bitcoin Transaction (PSBT) for use with e.g. an offline Bitcoin Core wallet, or a PSBT-compatible hardware wallet."));
    


    achow101 commented at 7:25 pm on October 16, 2019:
    nit: s/Unsigned/Signed
  58. in src/qt/sendcoinsdialog.h:58 in 428bdc8041 outdated
    54@@ -55,6 +55,7 @@ public Q_SLOTS:
    55 
    56 Q_SIGNALS:
    57     void coinsSent(const uint256& txid);
    58+    void psbtCopied(const std::string& psbt);
    


    achow101 commented at 7:37 pm on October 16, 2019:
    This is unused.
  59. in src/wallet/wallet.cpp:3215 in ca1a9d54d6 outdated
    3209@@ -3207,6 +3210,11 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
    3210                 coin_selection_params.use_bnb = false;
    3211                 continue;
    3212             }
    3213+
    3214+            // Give up if change keypool ran out and coin selection failed to find a solution without change:
    3215+            if (failed_to_get_change_address && nChangePosInOut != -1) {
    


    achow101 commented at 7:45 pm on October 16, 2019:
    IMO this should be up higher at the BnB failure check. If BnB fails to find an exact match, then it is highly unlikely that KnapsackSolver will find an exact match. It is better to fail earlier, so I would prefer that this check and return occurred in https://github.com/bitcoin/bitcoin/blob/master/src/wallet/wallet.cpp#L3081-L3083

    Sjors commented at 9:10 am on October 21, 2019:
    If moved this check to the start of the loop.

    Sjors commented at 11:35 am on October 21, 2019:
    That didn’t work. I don’t like putting this deep inside hard to reason about loop & if/else branches; don’t want to accidentally send change to nowhere. So I added coin_selection_params.use_knapsack which is set to false if failed_to_get_change_address , in order to skip knapsack.

    Sjors commented at 1:35 pm on October 21, 2019:

    This seems hopeless. There’s a few scenarios where BNB isn’t used:

    1. When you use manual coin selection in QT

    https://github.com/bitcoin/bitcoin/blob/a22b62481aae95747830bd3c0db3227860b12d8e/src/wallet/wallet.cpp#L2674-L2682

    This is the scenario @gwillen ran into: #16944 (comment)

    1. When we subtract fee from amount:

    https://github.com/bitcoin/bitcoin/blob/a22b62481aae95747830bd3c0db3227860b12d8e/src/wallet/wallet.cpp#L3011-L3013

    Reverted to just letting the knapsack solver do its thing.


    instagibbs commented at 2:01 pm on October 21, 2019:
    I think the reduced efficiency is fine in that it’s easier to read at least.

    achow101 commented at 10:31 pm on October 23, 2019:
    Seems like we need to enable bnb for those things. Will try.

    Sjors commented at 3:06 pm on October 24, 2019:
    I got part of the way there in #17219
  60. achow101 commented at 8:06 pm on October 16, 2019: member

    Code Review ACK 6429af6e4a41d93a632770e050effc8e3e74b023

    Looked over the code and did a manual test.

    @promag able to reproduce issue?

    @instagibbs no, currently it’s working eyes

    I ran into this issue too, and in the past with other watch only wallet things. I think it’s just due to balance caching and it should go away after closing and reopening the wallet. I’ll investigate as it is not caused by this PR. edit: maybe not. I was not able to reliably reproduce afterwards.

  61. Sjors force-pushed on Oct 21, 2019
  62. Sjors force-pushed on Oct 21, 2019
  63. Sjors force-pushed on Oct 21, 2019
  64. in src/wallet/wallet.cpp:2987 in acb5891cf1 outdated
    2982@@ -2982,21 +2983,23 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
    2983                 //  rediscover unknown transactions that were written with keys of ours to recover
    2984                 //  post-backup change.
    2985 
    2986-                // Reserve a new key pair from key pool
    2987+                // Reserve a new key pair from key pool. If it fails, provide a dummy
    2988+                // destination in case we don't need change.
    


    instagibbs commented at 1:56 pm on October 21, 2019:
    At the risk of making the error message less precise, I think this whole block is easier to read if it just relies on GetReservedDestination to check if CanGetAddress is true or not. You also can just not store ret and use it directly in the conditional.
  65. instagibbs commented at 2:01 pm on October 21, 2019: member
  66. Sjors force-pushed on Oct 22, 2019
  67. Sjors commented at 7:12 pm on October 22, 2019: member
    I extracted the keypool stuff into #17219
  68. instagibbs commented at 7:11 pm on October 25, 2019: member
    Will re-review, but the diff looks good.
  69. instagibbs commented at 7:48 pm on October 25, 2019: member

    re-code-review ACK https://github.com/bitcoin/bitcoin/pull/16944/commits/9808f7867d9c557ba4bc253104f47fbd7f02b668

    only changes from last tested review:

     0e081a91a19eb 9808f7867d9c557ba4bc253104f47fbd7f02b668
     1diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
     2index de701dbdd..3fd63a1b7 100644
     3--- a/src/qt/sendcoinsdialog.cpp
     4+++ b/src/qt/sendcoinsdialog.cpp
     5@@ -189,7 +189,7 @@ void SendCoinsDialog::setModel(WalletModel *_model)
     6 
     7         if (model->privateKeysDisabled()) {
     8             ui->sendButton->setText(tr("Create Unsigned"));
     9-            ui->sendButton->setToolTip(tr("Creates a Partially Unsigned Bitcoin Transaction (PSBT) for use with e.g. an offline Bitcoin Core wallet, or a PSBT-compatible hardware wallet."));
    10+            ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline Bitcoin Core wallet, or a PSBT-compatible hardware wallet."));
    11         } else {
    12             ui->sendButton->setText(tr("Send"));
    13         }
    14diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
    15index df1d96576..ccd849461 100644
    16--- a/src/qt/sendcoinsdialog.h
    17+++ b/src/qt/sendcoinsdialog.h
    18@@ -55,7 +55,6 @@ public Q_SLOTS:
    19 
    20 Q_SIGNALS:
    21     void coinsSent(const uint256& txid);
    22-    void psbtCopied(const std::string& psbt);
    23 
    24 private:
    25     Ui::SendCoinsDialog *ui;
    
  70. Sjors requested review from promag on Oct 29, 2019
  71. meshcollider commented at 6:24 am on November 6, 2019: contributor

    Code review ACK 9808f7867d9c557ba4bc253104f47fbd7f02b668

    I’ll wait for one more tested ACK before merging, just to make sure.

  72. promag commented at 8:35 am on November 6, 2019: member
    Sorry @Sjors, missed the notification, will review.
  73. in src/wallet/wallet.cpp:2593 in a16d2eb0d5 outdated
    2589@@ -2590,7 +2590,9 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch
    2590         if (it != mapWallet.end()) {
    2591             int depth = it->second.GetDepthInMainChain(locked_chain);
    2592             if (depth >= 0 && output.n < it->second.tx->vout.size() &&
    2593-                IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
    2594+                IsMine(it->second.tx->vout[output.n]) ==
    


    promag commented at 9:37 am on November 6, 2019:

    a16d2eb0d593633d6ba733d761c56c835b0e30dc

    Does it make sense to do something like:

    0    // some comment here
    1    isminetype is_mine_filter = IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)
    2                              ? ISMINE_WATCH_ONLY
    3                              : ISMINE_SPENDABLE;
    4
    5
    6                IsMine(it->second.tx->vout[output.n]) == is_mine_filter) {
    

    Sjors commented at 4:18 pm on November 6, 2019:
    Done
  74. in src/qt/sendcoinsdialog.cpp:650 in eef2b63506 outdated
    629@@ -630,6 +630,9 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
    630         coin_control = *CoinControlDialog::coinControl();
    631     }
    632 
    633+    // Include watch-only for wallets without private key
    634+    coin_control.fAllowWatchOnly = model->privateKeysDisabled();
    


    promag commented at 9:42 am on November 6, 2019:

    eef2b6350684f2675989b48a2d7f79c0d8a7be6e

    Does this have to be here? Couldn’t this be checked in CWallet::AvailableCoins?


    Sjors commented at 4:18 pm on November 6, 2019:
    I don’t think that’s a good idea. The RPC sets this based on includeWatching, so then we’d have to make it an Optional.

    promag commented at 0:21 am on November 18, 2019:
    Good point.
  75. in src/interfaces/wallet.h:201 in 2bc1012287 outdated
    195@@ -195,6 +196,13 @@ class Wallet
    196         bool& in_mempool,
    197         int& num_blocks) = 0;
    198 
    199+    //! Fill PSBT
    200+    virtual TransactionError fillPSBT(PartiallySignedTransaction& psbtx,
    201+        bool& complete,
    202+        int sighash_type = 1 /* SIGHASH_ALL */,
    


    promag commented at 9:47 am on November 6, 2019:

    2bc10122874421ba7ce379325ebc2420955b1b9b

    Looks like default arguments aren’t used in these interfaces, @ryanofsky coincidence or is there a reason for that?


    promag commented at 9:54 am on November 6, 2019:
    Maybe for now move the defaults to interfaces/wallet.cpp and just have psbtx and complete arguments for now.

    promag commented at 9:56 am on November 6, 2019:
    Or even just make this unsignedFilllPSBT(psbtx) which assert(complete == false) and makes the GUI code simpler?

    Sjors commented at 4:22 pm on November 6, 2019:
    I think refactoring the interface can be done some other time. External signers #16546 will set sign to true, and the result may or may not be complete (e.g. multisig).
  76. in src/interfaces/wallet.h:199 in 2bc1012287 outdated
    195@@ -195,6 +196,13 @@ class Wallet
    196         bool& in_mempool,
    197         int& num_blocks) = 0;
    198 
    199+    //! Fill PSBT
    


    promag commented at 9:47 am on November 6, 2019:

    2bc10122874421ba7ce379325ebc2420955b1b9b

    nit, comments have period here.

  77. in src/interfaces/wallet.h:17 in 2bc1012287 outdated
    13@@ -14,6 +14,7 @@
    14 #include <functional>
    15 #include <map>
    16 #include <memory>
    17+#include <psbt.h>
    


    promag commented at 9:48 am on November 6, 2019:

    2bc10122874421ba7ce379325ebc2420955b1b9b

    I think you should forward declare what’s necessary.


    Sjors commented at 4:37 pm on November 6, 2019:

    I tried, but it results in having to import stuff all over the place, and errors like this:

    0interfaces/wallet.cpp:354:20: error: no matching function for call to 'FillPSBT'
    1            return FillPSBT(m_wallet.get(), psbtx, complete, sighash_type, sign, bip32derivs);
    2                   ^~~~~~~~
    3./wallet/psbtwallet.h:27:28: note: candidate function not viable: cannot convert argument of incomplete type 'interfaces::PartiallySignedTransaction' to 'PartiallySignedTransaction &' for 2nd argument
    4NODISCARD TransactionError FillPSBT(const CWallet* pwallet,
    5                           ^
    61 error generated.
    
  78. in src/qt/sendcoinsdialog.cpp:24 in 9808f7867d outdated
    20@@ -21,6 +21,7 @@
    21 #include <chainparams.h>
    22 #include <interfaces/node.h>
    23 #include <key_io.h>
    24+#include <wallet/psbtwallet.h>
    


    promag commented at 9:48 am on November 6, 2019:

    9808f7867d9c557ba4bc253104f47fbd7f02b668

    nit sort.

  79. in src/qt/sendcoinsdialog.cpp:340 in 9808f7867d outdated
    337+
    338     questionString.append("<br /><span style='font-size:10pt;'>");
    339-    questionString.append(tr("Please, review your transaction."));
    340+    QString pleaseReview;
    341+    if (!model->privateKeysDisabled()) {
    342+        pleaseReview = tr("Please, review your transaction.");
    


    promag commented at 9:49 am on November 6, 2019:

    9808f7867d9c557ba4bc253104f47fbd7f02b668

    Avoid pleaseReview by calling questionString.append here and below? - like L332 above.

  80. in src/qt/sendcoinsdialog.cpp:397 in 9808f7867d outdated
    392@@ -373,7 +393,12 @@ void SendCoinsDialog::on_sendButton_clicked()
    393         questionString = questionString.arg("<br /><br />" + formatted.at(0));
    394     }
    395 
    396-    SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, this);
    397+    const QString confirmation = model->privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
    398+    QString confirmButtonText = tr("Send");
    


    promag commented at 9:51 am on November 6, 2019:

    9808f7867d9c557ba4bc253104f47fbd7f02b668

    Use ternary like above or just one if (model->privateKeysDisabled()) for these.

  81. promag commented at 9:58 am on November 6, 2019: member
    @Sjors Some comments for your consideration.
  82. Sjors force-pushed on Nov 6, 2019
  83. in src/interfaces/wallet.cpp:354 in d474156f24 outdated
    349+        bool& complete,
    350+        int sighash_type = 1 /* SIGHASH_ALL */,
    351+        bool sign = true,
    352+        bool bip32derivs = false) override
    353+    {
    354+            return FillPSBT(m_wallet.get(), psbtx, complete, sighash_type, sign, bip32derivs);
    


    promag commented at 4:50 pm on November 6, 2019:

    d474156f242dc900c221924765210ac4f7e97f7a

    Wrong indentation.


    Sjors commented at 5:53 pm on November 6, 2019:
    Fixed
  84. Sjors force-pushed on Nov 6, 2019
  85. Sjors commented at 5:54 pm on November 6, 2019: member

    Mmm, this in an odd Travis error:

     02019-11-06T17:49:26.056000Z TestFramework (INFO): sendrawtransaction with missing input
     1198162019-11-06T17:49:42.869000Z TestFramework (ERROR): JSONRPC error
     219817Traceback (most recent call last):
     319818  File "/home/travis/build/bitcoin/bitcoin/build/bitcoin-i686-pc-linux-gnu/test/functional/test_framework/test_framework.py", line 111, in main
     419819    self.run_test()
     519820  File "/home/travis/build/bitcoin/bitcoin/build/bitcoin-i686-pc-linux-gnu/test/functional/rpc_rawtransaction.py", line 354, in run_test
     619821    self.nodes[2].sendrawtransaction(rawTxComb)
     719822  File "/home/travis/build/bitcoin/bitcoin/build/bitcoin-i686-pc-linux-gnu/test/functional/test_framework/coverage.py", line 47, in __call__
     819823    return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
     919824  File "/home/travis/build/bitcoin/bitcoin/build/bitcoin-i686-pc-linux-gnu/test/functional/test_framework/authproxy.py", line 141, in __call__
    1019825    raise JSONRPCException(response['error'], status)
    1119826test_framework.authproxy.JSONRPCException: non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation) (-26)
    
  86. Sjors force-pushed on Nov 7, 2019
  87. Sjors force-pushed on Nov 7, 2019
  88. Sjors force-pushed on Nov 7, 2019
  89. DrahtBot added the label Needs rebase on Nov 8, 2019
  90. Sjors force-pushed on Nov 8, 2019
  91. Sjors force-pushed on Nov 8, 2019
  92. DrahtBot removed the label Needs rebase on Nov 8, 2019
  93. in src/qt/sendcoinsdialog.cpp:192 in 33a766fdb6 outdated
    186@@ -186,6 +187,13 @@ void SendCoinsDialog::setModel(WalletModel *_model)
    187         // set default rbf checkbox state
    188         ui->optInRBF->setCheckState(Qt::Checked);
    189 
    190+        if (model->privateKeysDisabled()) {
    191+            ui->sendButton->setText(tr("Create Unsigned"));
    192+            ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline Bitcoin Core wallet, or a PSBT-compatible hardware wallet."));
    


    luke-jr commented at 3:28 am on November 13, 2019:
    “Bitcoin Core” should be replaced with %1 and PACKAGE_NAME

    luke-jr commented at 3:29 am on November 13, 2019:
    Do we want to imply the Bitcoin Core GUI can sign these yet?

    Sjors commented at 4:59 pm on November 13, 2019:
    In reference to “offline Bitcoin Core wallet”? It doesn’t say GUI :-)

    luke-jr commented at 5:53 pm on November 13, 2019:
    Hence “imply”.

    Sjors commented at 5:59 pm on November 13, 2019:
    I only expect people to use this feature (watch only wallets in general) based on some tutorial, so I don’t think this needs to be perfect. Hopefully we can add signing PSBT from the GUI soon too.
  94. in src/qt/sendcoinsdialog.cpp:194 in 33a766fdb6 outdated
    186@@ -186,6 +187,13 @@ void SendCoinsDialog::setModel(WalletModel *_model)
    187         // set default rbf checkbox state
    188         ui->optInRBF->setCheckState(Qt::Checked);
    189 
    190+        if (model->privateKeysDisabled()) {
    191+            ui->sendButton->setText(tr("Create Unsigned"));
    192+            ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline Bitcoin Core wallet, or a PSBT-compatible hardware wallet."));
    193+        } else {
    194+            ui->sendButton->setText(tr("Send"));
    


    luke-jr commented at 3:30 am on November 13, 2019:
    Do we need this? Each wallet gets its own SendCoinsDialog, and can’t change privateKeysDisabled AFAIK…

    luke-jr commented at 3:31 am on November 13, 2019:
    [If we do need it,] remove duplicate from .ui file

    luke-jr commented at 3:31 am on November 13, 2019:
    [If we do need it,] should be “S&end” for the shortcut key.

    Sjors commented at 5:23 pm on November 13, 2019:
    Not sure what you mean here. We set these strings in setModel(WalletModel *_model). That’s the first time we know privateKeysDisabled. The constructor doesn’t know.

    luke-jr commented at 5:54 pm on November 13, 2019:
    I mean there is never a case where we need to change the text back to “Send” (which is set in the .ui)

    Sjors commented at 5:55 pm on November 13, 2019:
    Ok, I removed the tooltip and button text from the UI file. I also added the s keyboard shortcut back, though I can’t test that on macOS.

    Sjors commented at 6:06 pm on November 13, 2019:
    Ah I see. Ok, I dropped the else branch (keeping the original in the .ui file).
  95. in src/qt/sendcoinsdialog.cpp:317 in 33a766fdb6 outdated
    312@@ -305,9 +313,19 @@ void SendCoinsDialog::on_sendButton_clicked()
    313         formatted.append(recipientElement);
    314     }
    315 
    316-    QString questionString = tr("Are you sure you want to send?");
    317+    QString questionString;
    318+    if (!model->privateKeysDisabled()) {
    


    luke-jr commented at 3:32 am on November 13, 2019:
    Please handle the true case first.

    Sjors commented at 5:57 pm on November 13, 2019:
    Done
  96. in src/qt/sendcoinsdialog.cpp:327 in 33a766fdb6 outdated
    324     questionString.append("<br /><span style='font-size:10pt;'>");
    325-    questionString.append(tr("Please, review your transaction."));
    326+    if (!model->privateKeysDisabled()) {
    327+        questionString.append(tr("Please, review your transaction."));
    328+    } else {
    329+        questionString.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline Bitcoin Core wallet, or a PSBT-compatible hardware wallet."));
    


    luke-jr commented at 3:32 am on November 13, 2019:
    Not sure we need to change this message.

    Sjors commented at 5:29 pm on November 13, 2019:
    This is the only place where it’s not in a toolbox.

    luke-jr commented at 5:54 pm on November 13, 2019:
    ???

    Sjors commented at 5:57 pm on November 13, 2019:
    I mean tool tip.
  97. in src/qt/sendcoinsdialog.cpp:951 in 33a766fdb6 outdated
    949@@ -908,11 +950,11 @@ void SendConfirmationDialog::updateYesButton()
    950     if(secDelay > 0)
    951     {
    952         yesButton->setEnabled(false);
    953-        yesButton->setText(tr("Send") + " (" + QString::number(secDelay) + ")");
    954+        yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")");
    


    luke-jr commented at 3:35 am on November 13, 2019:
    Suggest using 4e10bdd433f5d1ba9069f4942cb89e69dd83d816 for this (part of #15987)

    Sjors commented at 6:01 pm on November 13, 2019:
    I’ll rebase if that’s merged first, but that’s not a trivial enough commit.
  98. [wallet] ListCoins: include watch-only for wallets without private keys
    This makes them available in GUI coin selection.
    40537f0909
  99. [gui] send: include watch-only
    For wallets with WALLET_FLAG_DISABLE_PRIVATE_KEYS.
    848f889208
  100. [wallet] add fillPSBT to interface 39465d545d
  101. in src/wallet/wallet.cpp:2163 in 33a766fdb6 outdated
    2159@@ -2160,7 +2160,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch
    2160 
    2161     for (const COutput& coin : availableCoins) {
    2162         CTxDestination address;
    2163-        if (coin.fSpendable &&
    2164+        if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
    


    luke-jr commented at 3:45 am on November 13, 2019:
    I’d make a const bool include_watch_only to check the wallet flag once outside the loops…

    luke-jr commented at 3:46 am on November 13, 2019:

    Something like this above:

    0    // Include watch-only for wallets without private keys
    1    const bool include_watch_only = IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
    2    const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
    

    luke-jr commented at 6:20 pm on November 13, 2019:
    You didn’t resolve this…
  102. Sjors force-pushed on Nov 13, 2019
  103. in src/qt/sendcoinsdialog.cpp:194 in f80b0b9693 outdated
    186@@ -186,6 +187,14 @@ void SendCoinsDialog::setModel(WalletModel *_model)
    187         // set default rbf checkbox state
    188         ui->optInRBF->setCheckState(Qt::Checked);
    189 
    190+        if (model->privateKeysDisabled()) {
    191+            ui->sendButton->setText(tr("&Create Unsigned"));
    192+            ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
    193+        } else {
    194+            ui->sendButton->setText(tr("&Send"));
    


    luke-jr commented at 5:59 pm on November 13, 2019:
    Should be "S&end"
  104. Sjors force-pushed on Nov 13, 2019
  105. Sjors force-pushed on Nov 13, 2019
  106. in src/qt/sendcoinsdialog.cpp:191 in e0053c40e6 outdated
    186@@ -186,6 +187,11 @@ void SendCoinsDialog::setModel(WalletModel *_model)
    187         // set default rbf checkbox state
    188         ui->optInRBF->setCheckState(Qt::Checked);
    189 
    190+        if (model->privateKeysDisabled()) {
    191+            ui->sendButton->setText(tr("C&reate Unsigned"));
    


    luke-jr commented at 6:23 pm on November 13, 2019:
    Perhaps use "Cr&eate Unsigned" so the “E” shortcut matches with "S&end"

    Sjors commented at 7:03 pm on November 13, 2019:
    Done
  107. [gui] watch-only wallet: copy PSBT to clipboard c6dd565c88
  108. Sjors force-pushed on Nov 13, 2019
  109. instagibbs commented at 5:50 pm on November 14, 2019: member
  110. Sjors requested review from promag on Nov 15, 2019
  111. Sjors requested review from achow101 on Nov 15, 2019
  112. jonasschnelli commented at 10:49 pm on November 15, 2019: contributor
    Concept ACK
  113. meshcollider commented at 7:42 pm on November 22, 2019: contributor
    re-ACK c6dd565c8820aa8a98b190621e10c6e2821a9ecc
  114. meshcollider referenced this in commit 0aa72061e5 on Nov 22, 2019
  115. meshcollider merged this on Nov 22, 2019
  116. meshcollider closed this on Nov 22, 2019

  117. sidhujag referenced this in commit 615d7c2daa on Nov 22, 2019
  118. meshcollider removed this from the "Blockers" column in a project

  119. Sjors deleted the branch on Nov 23, 2019
  120. promag commented at 9:40 pm on November 24, 2019: member

    I’ve used the “Use available balance” which filled the amount, but below balance is still zero.

    Something that can be fixed in a follow up - the button could change and/or the balance info on the bottom too.

    Tested ACK c6dd565c8820aa8a98b190621e10c6e2821a9ecc.

  121. Sjors commented at 10:05 am on November 25, 2019: member
    @promag the balance seems unrelated, though obviously a bug
  122. Sjors commented at 10:39 am on November 25, 2019: member
    @promag fixed in #17587
  123. meshcollider referenced this in commit abb30de63f on Nov 29, 2019
  124. sidhujag referenced this in commit a289b8cba8 on Nov 30, 2019
  125. luke-jr referenced this in commit 36e524367c on Jan 5, 2020
  126. luke-jr referenced this in commit c89dccf9b9 on Jan 5, 2020
  127. luke-jr referenced this in commit 49295aec41 on Jan 5, 2020
  128. luke-jr referenced this in commit 2d5d82064f on Jan 5, 2020
  129. meshcollider referenced this in commit cab3859a35 on Jan 7, 2020
  130. sidhujag referenced this in commit 888015ee0b on Jan 8, 2020
  131. meshcollider referenced this in commit bbb1ba1814 on Apr 18, 2020
  132. sidhujag referenced this in commit 7208ebc3e6 on Apr 19, 2020
  133. jasonbcox referenced this in commit ad7f8efeca on Sep 1, 2020
  134. jasonbcox referenced this in commit 19547733f1 on Oct 1, 2020
  135. jasonbcox referenced this in commit 6e86cfea3f on Oct 1, 2020
  136. jasonbcox referenced this in commit 5624afe234 on Oct 1, 2020
  137. jasonbcox referenced this in commit b4b259e248 on Oct 1, 2020
  138. jasonbcox referenced this in commit d46fc5678d on Oct 2, 2020
  139. sidhujag referenced this in commit 9861f21164 on Nov 10, 2020
  140. sidhujag referenced this in commit b9a1d85c52 on Nov 10, 2020
  141. sidhujag referenced this in commit 6e28068d81 on Nov 10, 2020
  142. PastaPastaPasta referenced this in commit a5a13e9f99 on Jun 27, 2021
  143. PastaPastaPasta referenced this in commit c850fec079 on Jun 28, 2021
  144. PastaPastaPasta referenced this in commit 83359cdac6 on Jun 29, 2021
  145. PastaPastaPasta referenced this in commit 4aca3fa00b on Jul 1, 2021
  146. PastaPastaPasta referenced this in commit 1c21116cc2 on Jul 1, 2021
  147. PastaPastaPasta referenced this in commit 7ee36b407a on Jul 12, 2021
  148. PastaPastaPasta referenced this in commit fb3f3747a4 on Jul 13, 2021
  149. PastaPastaPasta referenced this in commit f46f16772d on Jul 13, 2021
  150. DrahtBot locked this on Dec 16, 2021

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: 2024-12-18 06:12 UTC

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