[wallet] use P2WPKH change output if any destination is P2WPKH or P2WSH #12119

pull Sjors wants to merge 1 commits into bitcoin:master from Sjors:bech32-change changing 6 files +154 −24
  1. Sjors commented at 7:25 pm on January 8, 2018: member

    If -changetype is not explicitly set, then regardless of -addresstype, the wallet will use a ~bech32 change address~ P2WPKH change output if any destination is P2WPKH or P2WSH.

    This seems more intuitive to me and more in line with the spirit of BIP-69.

    When combined with #11991 a QT user could opt to use bech32 exclusively without having to figure out how to launch with -changetype=bech32, although so would #11937.

  2. fanquake added the label Wallet on Jan 8, 2018
  3. promag commented at 11:44 pm on January 8, 2018: member
    Does it forces bech32 addresses in the wallet without consent?
  4. Sjors commented at 10:05 am on January 9, 2018: member

    @promag for change addresses yes, so that could be an objection.

    More conservative approaches could be:

    1. have another UI check box to send change to a bech32 address; this could be checked by default if all destinations are bech32. This won’t work for RPC users, but those can be expected to use the -changeaddress flag.

    2. add an additional constraint that at least one of the the from addresses is bech32.

  5. sipa commented at 10:07 am on January 9, 2018: member
    Creating a change output AFAIK does not add any address to the wallet.
  6. promag commented at 10:09 am on January 9, 2018: member
    @sipa they are listed in listunspent right?
  7. promag commented at 10:11 am on January 9, 2018: member
    @Sjors IMO the -changetype is enough, eventually it will default to bech32 I guess.
  8. gmaxwell commented at 7:08 am on January 11, 2018: contributor
    General concept ACK– but I would suggest that it only do this if the change type is at least p2sh segwit: There might be some compatibility reason that someone is avoiding creating segwit outputs for some reason. That doesn’t hold if they’re already asking for p2sh output… privacy was the major reason the sw wallet support didn’t go 100% native change from the start… but that is even more an argument for this. In fact, I think it’s an argument that we should use native change if any (or no more strict than ‘most’) outputs are native and not just require all.
  9. Sjors force-pushed on Jan 11, 2018
  10. Sjors force-pushed on Jan 11, 2018
  11. Sjors force-pushed on Jan 11, 2018
  12. Sjors renamed this:
    [wallet] use bech32 change address if all destinations are bech32
    [wallet] use bech32 change address if any destination is bech32
    on Jan 11, 2018
  13. Sjors commented at 9:45 am on January 11, 2018: member
    @gmaxwell it now uses a bech32 change address if change type is p2sh-segwit and any of the outputs are bech32.
  14. Sjors force-pushed on Jan 11, 2018
  15. Sjors force-pushed on Jan 11, 2018
  16. luke-jr commented at 10:25 am on January 11, 2018: member

    Seems to me if the user is explicitly setting a change type, we should respect that.

    Instead, I suggest turning OUTPUT_TYPE_DEFAULT into a real value, and handling it specially for change here. Eg, https://github.com/luke-jr/bitcoin/commits/bech32-change

  17. Sjors commented at 10:41 am on January 11, 2018: member
    @luke-jr I like the idea of having OUTPUT_TYPE_DEFAULT so we can distinguish between -changetype being absent or set to p2sh-segwit. Can you make d4e51351 a PR that I can rebase this on?
  18. luke-jr commented at 10:43 am on January 11, 2018: member

    It seems a bit pointless to stand on its own in a PR. It would be better to just include it as a separate commit here (and in another PR I’m about to open).

    To get it in your own branch as-is, switch to it and do:

    0git fetch git://github.com/luke-jr/bitcoin bech32-change && git reset --hard 5efd54745c34ff329d9c75f2f5ee852930b21231
    
  19. Sjors commented at 12:05 pm on January 11, 2018: member

    @luke-jr I’ll wait a little bit to see if your commit needs more changes.

    I do think it’s worth it’s own PR, but applied to all launch flags where absence or presence can cause ambiguity about user intention. I’ve run into this general problem several times now, e.g. with #9527.

  20. in src/wallet/wallet.cpp:2671 in c3bc3f6a19 outdated
    2666+        // Check if the destination doesn't contain a witness program, in which
    2667+        // case the addresstype for this destination is not bech32:
    2668+        int witnessversion = 0;
    2669+        std::vector<unsigned char> witnessprogram;
    2670+        if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
    2671+          anyDestinationBech32 = true;
    


    promag commented at 3:02 pm on January 11, 2018:
    Wrong indentation. Also any_destination_bech32?

    Sjors commented at 6:32 pm on January 11, 2018:
    Will fix indentation and check variable name, thanks.

    Sjors commented at 3:53 pm on January 12, 2018:
    Fixed.
  21. in src/wallet/wallet.cpp:2755 in c3bc3f6a19 outdated
    2752-                scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, g_change_type));
    2753+                // If g_change_type is p2sh_segwit, use bech32 for the change
    2754+                // address if any destination is bech32.
    2755+                OutputType change_type = g_change_type;
    2756+                if (g_change_type == OUTPUT_TYPE_P2SH_SEGWIT && anyDestinationBech32) {
    2757+                  change_type = OUTPUT_TYPE_BECH32;
    


    promag commented at 3:02 pm on January 11, 2018:
    Wrong indentation.
  22. promag commented at 3:03 pm on January 11, 2018: member
    Why does it matter if any destination is bech32? Should check inputs instead? If there is one p2sh-segwit or bech32 input then use bech32 change?
  23. TheBlueMatt commented at 5:35 pm on January 11, 2018: member
    Concept ACK. Somewhat agree with @luke-jr, though you can do it simpler - just check if the change address type option has been set - if it has, use it, if it hasn’t turn on this new behavior.
  24. Sjors commented at 6:34 pm on January 11, 2018: member

    Should check inputs instead?

    Or even check if any input or any output is bech32?

  25. instagibbs commented at 7:06 pm on January 11, 2018: member
    agreed with @TheBlueMatt just check if it’s set
  26. laanwj added this to the milestone 0.16.0 on Jan 11, 2018
  27. Sjors commented at 7:23 pm on January 11, 2018: member
    I can’t reproduce this Travis failure on MacOS or Ubuntu 14.04 (I didn’t use the depends system).
  28. gmaxwell commented at 8:35 pm on January 11, 2018: contributor
    It seemed like people in the IRC meeting liked: “If the configured change type is not legacy, use p2wpkh if any of the outputs are p2wpkh”.
  29. Sjors commented at 8:49 pm on January 11, 2018: member
    @gmaxwell by “legacy” you mean p2sh-segwit? -changetype=legacy means p2pkh (in which case we also don’t want to use bech32). Maybe we should also rename that legacy option, because the term seems overloaded.
  30. gmaxwell commented at 11:14 pm on January 11, 2018: contributor

    By legacy I mean p2pkh. We should not create native segwit change if the user has specifically asked for non-segwit change. If a user is setting legacy then it’s likely that they really don’t want segwit outputs for some reason (for example, compatibility with older software).

    That argument doesn’t apply for p2sh-segwit. The only reason to ever use p2sh-segwit change is for change distinguishability privacy, which, as you note– doesn’t apply when there are segwit outputs.

  31. gmaxwell commented at 11:16 pm on January 11, 2018: contributor
    As a pedantic aside, it’s a mistake to say “bech32” – there is no address involved with change, so no bech32 involved here at all. The question here is to use p2sh-p2wpkh or p2wpkh, which would exist no less even if bech32 had never been specified.
  32. achow101 commented at 2:43 am on January 12, 2018: member
    Concept ACK
  33. Sjors commented at 12:04 pm on January 12, 2018: member
    @gmaxwell I know there’s no spoon, but block explorers will display a spoon :-)
  34. Sjors renamed this:
    [wallet] use bech32 change address if any destination is bech32
    [wallet] use P2WPKH change output if any destination is P2WPKH or P2SH-P2WSH
    on Jan 12, 2018
  35. Sjors force-pushed on Jan 12, 2018
  36. Sjors force-pushed on Jan 12, 2018
  37. Sjors force-pushed on Jan 12, 2018
  38. Sjors force-pushed on Jan 12, 2018
  39. in src/wallet/wallet.cpp:2669 in 6349a30053 outdated
    2661@@ -2661,6 +2662,13 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
    2662 
    2663         if (recipient.fSubtractFeeFromAmount)
    2664             nSubtractFeeFromAmount++;
    2665+
    2666+        // Check if any destination contains a witness program:
    2667+        int witnessversion = 0;
    2668+        std::vector<unsigned char> witnessprogram;
    2669+        if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
    


    instagibbs commented at 4:10 pm on January 12, 2018:
    Should probably check/track witness version here? It shouldn’t be >0 unless we update, but probably more future-safe.

    Sjors commented at 6:48 pm on January 12, 2018:
    Would new witness versions require different change address behavior?

    instagibbs commented at 7:27 pm on January 12, 2018:
    Let’s say you send to a v1, you wouldn’t want to accidentally make a v0 to “copy” it. Perhaps more appropriate for an assert here.

    Sjors commented at 8:56 pm on January 12, 2018:

    It might be assertophobia, but I’m not sure what you mean by that example. If you send coins to a future v1 segwit address, the change has to go somewhere right? So the change would just go to a v0 witness derived from the next available key in the pool.

    Note that IsWitnessProgram writes to witnessversion, not the other way around. And witnessversion isn’t used when the change address is created.

    It depends on what the intention of v1 is whether change can safely go to a v1 address. But I don’t see how it would be unsafe to send to a v0 address. Of course maybe there is a reason, or future us will find a reason. So an assert would force them to think about that. Is that what you mean?


    instagibbs commented at 9:10 pm on January 12, 2018:

    Not unsafe, but defeats the privacy logic that this enables, I think.

    Maybe I’m making too much hay of this.


    TheBlueMatt commented at 6:25 pm on January 14, 2018:
    Really not sure what the point is here - we definitely should allowe sending to >v0 witness versions, its not our job to make sure someone giving us an address gives us something that is currently safe (ie has been forked to have some meaning), and we shouldn’t need an update just to send to a newly-defined segwit version.

    instagibbs commented at 2:31 pm on January 15, 2018:
    @TheBlueMatt Why are we having the wallet make v0 change outputs if the receiving address is v1+? Makes no sense to me.

    gmaxwell commented at 4:50 am on January 16, 2018:

    @instagibbs since we don’t support v1 there is no ‘private’ option possible for us, so given that privacy is impossible, we should at least choose the most efficient!

    The only reason we initially defaulted to do anything but produce native change is because under the assumption that most payments would be non-native that would be pretty bad for privacy. But that privacy argument only holds when the decision results in us matching.

    Perhaps in the future if we support both v0 and v1 and have no particular reason to choose between them (e.g. they’re both about equally efficient, and our wallet’s policy doesn’t demand one vs the other), we would just match whatever we’re paying… but we can’t do that now.


    promag commented at 3:05 pm on January 20, 2018:
    This can be avoided/skipped if there is a custom address in coin control. Move it below to where any_destination_native_segwit is needed.
  40. in src/wallet/wallet.cpp:2751 in 6349a30053 outdated
    2746@@ -2739,8 +2747,16 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
    2747                     return false;
    2748                 }
    2749 
    2750-                LearnRelatedScripts(vchPubKey, g_change_type);
    2751-                scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, g_change_type));
    2752+                // If -changetype is not specified and g_address_type is not legacy,
    2753+                // and any destination is (P2SH-)P2WPKH, use P2WPKH for the change 
    


    instagibbs commented at 4:11 pm on January 12, 2018:
    and any destination is (P2SH-)P2WPKH, I don’t think this is correct? should just be P2WPKH
  41. Sjors force-pushed on Jan 12, 2018
  42. sneurlax commented at 4:19 pm on January 12, 2018: none

    If this is merged, I think it ought to be paired with #12146. It’s a good, off-by-default option to have available for strange edge cases that want to run the latest full node software but have what would otherwise be an infuriating need to remain compatible with legacy systems.

    Just my 2 bits.

  43. Sjors commented at 4:23 pm on January 12, 2018: member
    This should be ready for review, pending Travis’ blessing. @sneurlax there’s no need for that. Those who would use #12146 would also launch with -addresstype=legacy, which prevents the behavior this PR adds.
  44. sneurlax commented at 4:34 pm on January 12, 2018: none

    I must still misunderstand their relation, then. Sorry for piping up.

    Does -changetype=bech32 also prevent the behavior this PR adds?

  45. in test/functional/address_types.py:278 in 6349a30053 outdated
    241+        self.test_change_output_type(2, [to_address_bech32_1], 'bech32')
    242+        self.test_change_output_type(2, [to_address_p2sh], 'bech32')
    243+
    244+        self.log.debug("Nodes with addresstype=bech32 always use a P2WPKH change output:")
    245+        self.test_change_output_type(3, [to_address_bech32_1], 'bech32')
    246+        self.test_change_output_type(3, [to_address_p2sh], 'bech32')
    


    instagibbs commented at 4:38 pm on January 12, 2018:
    I don’t see why this is the case. No payment here is native segwit?

    Sjors commented at 6:53 pm on January 12, 2018:

    addresstype implicitly sets changetype:

    g_change_type = ParseOutputType(gArgs.GetArg("-changetype", ""), g_address_type)

    If the user overrides changetype it will use that, so I could change the word “always” in the comment to reflect that better.


    instagibbs commented at 7:24 pm on January 12, 2018:
    riiiiiiiiiiight. Could you just write that? I actually forgot this and I’ve reviewed all the PRs so far :(

    instagibbs commented at 7:30 pm on January 12, 2018:
    add at end of above comment “unless changetype is set otherwise”

    Sjors commented at 8:48 pm on January 12, 2018:
    Done

    sipa commented at 9:21 pm on January 12, 2018:
    I think we can discuss what to do with v1 witnesses when the time comes.
  46. instagibbs commented at 4:43 pm on January 12, 2018: member
    concept ACK
  47. Sjors commented at 4:59 pm on January 12, 2018: member
    @sneurlax yes. If you set -changetype it will honor that. Only if you don’t set -changetype and you don’t set -addresstype=legacy it will use the following rule: use a native segwit change output* if any destination is a native segwit address.
  48. Sjors force-pushed on Jan 12, 2018
  49. in src/wallet/wallet.cpp:2670 in de8ea9c68a outdated
    2661@@ -2661,6 +2662,13 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
    2662 
    2663         if (recipient.fSubtractFeeFromAmount)
    2664             nSubtractFeeFromAmount++;
    2665+
    2666+        // Check if any destination contains a witness program:
    2667+        int witnessversion = 0;
    2668+        std::vector<unsigned char> witnessprogram;
    2669+        if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
    2670+          any_destination_native_segwit = true;
    


    TheBlueMatt commented at 6:18 pm on January 14, 2018:
    short indent here.

    Sjors commented at 2:24 pm on January 15, 2018:
    Fixed
  50. TheBlueMatt commented at 6:25 pm on January 14, 2018: member
    utACK de8ea9c68a69aa2c61abbe26d62e47c3b321fe78, though didn’t review the test changes (edit: looks like the tests are failing, serves me right for not checking why travis failed first).
  51. Sjors force-pushed on Jan 15, 2018
  52. Sjors commented at 2:26 pm on January 15, 2018: member
    @TheBlueMatt test failure seems indeterministic. Afaik I only added a comment since the last time Travis was happy. We’ll see what happens with this indentation fix.
  53. Sjors commented at 3:06 pm on January 15, 2018: member
    net.py fails on Travis, but passes locally. I’ll rebase because there are some new test related fixes on master.
  54. Sjors force-pushed on Jan 15, 2018
  55. Sjors force-pushed on Jan 15, 2018
  56. Sjors commented at 4:21 pm on January 15, 2018: member
    Rebase made Travis happy.
  57. in src/wallet/wallet.cpp:2754 in 2324577b76 outdated
    2751-                scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, g_change_type));
    2752+                // If -changetype is not specified and g_address_type is not legacy,
    2753+                // and any destination is (P2SH-)P2WPKH, use P2WPKH for the change
    2754+                // output.
    2755+                OutputType change_type = g_change_type;
    2756+                if (!gArgs.IsArgSet("-changetype") && g_address_type != OUTPUT_TYPE_LEGACY && any_destination_native_segwit) {
    


    laanwj commented at 4:49 pm on January 15, 2018:

    Suggestion: test g_address_type against OUTPUT_TYPE_DEFAULT, instead of touching gArgs here.

    If that is not possible, please move the gArgs.IsArgSet("-changetype") to wallet/init.cpp and set a global boolean g_changetype_is_default or such - don’t spread the handling for one option over two separate places.


    Sjors commented at 11:03 am on January 16, 2018:
    OUTPUT_TYPE_DEFAULT is used for the help message and is defined as OUTPUT_TYPE_P2SH_SEGWIT. I’ll try (something along the lines of) @luke-jr’s 5efd54745.
  58. Sjors force-pushed on Jan 16, 2018
  59. Sjors commented at 12:00 pm on January 16, 2018: member
    g_change_type is no longer based on -addresstype. Functions that use g_change_type need to handle OUTPUT_TYPE_NONE, which I’ve done everywhere with: g_change_type ? g_change_type : g_address_type.
  60. sipa commented at 2:47 pm on January 16, 2018: member

    utACK 327634f97e5921815c30e50d076cde0f6ff51aaf

    I think the code can be made a bit cleaner by introducing an explicit P2SH_SEGWIT_BUT_NATIVE_WHEN_NATIVE_DESTINATION or so type in OutputType, but let’s do that later.

  61. in src/wallet/wallet.cpp:2751 in 327634f97e outdated
    2746@@ -2739,8 +2747,16 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
    2747                     return false;
    2748                 }
    2749 
    2750-                LearnRelatedScripts(vchPubKey, g_change_type);
    2751-                scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, g_change_type));
    2752+                // If -changetype is not specified and g_address_type is not legacy,
    2753+                // and any destination is (P2SH-)P2WPKH, use P2WPKH for the change
    


    instagibbs commented at 6:27 pm on January 16, 2018:

    shouldn’t this read:

    // and any destination is P2WPKH, use P2WPKH for the change


    Sjors commented at 7:42 pm on January 16, 2018:
    Actually I meant P2WPKH or P2WSH. Fixed.
  62. instagibbs approved
  63. instagibbs commented at 6:32 pm on January 16, 2018: member
    utACK
  64. Sjors force-pushed on Jan 16, 2018
  65. Sjors force-pushed on Jan 16, 2018
  66. Sjors renamed this:
    [wallet] use P2WPKH change output if any destination is P2WPKH or P2SH-P2WSH
    [wallet] use P2WPKH change output if any destination is P2WPKH or P2WSH
    on Jan 16, 2018
  67. in src/qt/paymentserver.cpp:646 in d2c2be7200 outdated
    642@@ -643,8 +643,9 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, const SendCoinsRecipient& r
    643         // use for change. Despite an actual payment and not change, this is a close match:
    644         // it's the output type we use subject to privacy issues, but not restricted by what
    645         // other software supports.
    646-        wallet->LearnRelatedScripts(newKey, g_change_type);
    647-        CTxDestination dest = GetDestinationForKey(newKey, g_change_type);
    648+        OutputType change_type = g_change_type ? g_change_type : g_address_type;
    


    ajtowns commented at 11:51 am on January 17, 2018:
    Shouldn’t this compare explicitly? ie change_type = g_change_type != OUTPUT_TYPE_NONE ? g_change_type : g_address_type

    Sjors commented at 12:43 pm on January 17, 2018:
    I wanted to keep it short for readability and leverage that OUTPUT_TYPE_NONE is 0. I can change that if you think it’s unsafe.

    MarcoFalke commented at 2:08 pm on January 17, 2018:
    I think it should be changed (in the future). The whole enum should be a c++11 enum class to be type safe. Though, that will touch all the code, so maybe too late for 0.16…

    promag commented at 2:38 pm on January 20, 2018:

    Agree with @ajtowns. Nit, const OutputType ..... Suggestion:

    0const OutputType change_type = g_change_type == OUTPUT_TYPE_NONE ? g_address_type : g_change_type;
    
  68. in test/functional/address_types.py:157 in d2c2be7200 outdated
    125+        # Make sure the destinations are included, and remove it:
    126+        for destination in destinations:
    127+            output_addresses.pop(output_addresses.index(destination))
    128+
    129+        assert_equal(len(output_addresses), 1)
    130+
    


    MarcoFalke commented at 2:22 pm on January 17, 2018:
    style-nit: heh, you are excessively using new lines in python code

    Sjors commented at 5:01 pm on January 17, 2018:
    Removed a few blank lines…
  69. in test/functional/address_types.py:135 in d2c2be7200 outdated
    130+
    131+        change_address = output_addresses[0]
    132+
    133+        self.log.debug("Check if change address " +  change_address + " is " + expected_type)
    134+
    135+        self.test_address(node_sender, change_address, False, expected_type)
    


    MarcoFalke commented at 2:23 pm on January 17, 2018:
    style-nit: Should probably use named args, as it is not clear what “False” refers to.

    Sjors commented at 5:01 pm on January 17, 2018:
    Fixed
  70. MarcoFalke approved
  71. MarcoFalke commented at 2:27 pm on January 17, 2018: member
    utACK d2c2be7200307d1ed9b6291745b34d5db2c645a4 (didn’t look too closely at the cpp code, but the tests make sense)
  72. Sjors force-pushed on Jan 17, 2018
  73. in src/wallet/init.cpp:185 in b9f211c6d3 outdated
    181@@ -182,9 +182,11 @@ bool WalletParameterInteraction()
    182         return InitError(strprintf(_("Unknown address type '%s'"), gArgs.GetArg("-addresstype", "")));
    183     }
    184 
    185-    g_change_type = ParseOutputType(gArgs.GetArg("-changetype", ""), g_address_type);
    


    ryanofsky commented at 8:37 pm on January 17, 2018:

    Suggest writing this as:

    0g_change_type = ParseOutputType(gArgs.GetArg("-changetype", ""), OUTPUT_TYPE_NONE);
    1if (g_change_type == OUTPUT_TYPE_NONE && gArgs.IsArgSet("-changetype")) {
    2      return InitError(strprintf(_("Unknown change type '%s'"), gArgs.GetArg("-changetype", "")));
    3}
    

    Reasons:

    • More consistent with g_address_type code above
    • Smaller diff, less code, less nesting
    • Doesn’t leave g_change_type set to indeterminate value if !IsArgSet.

    You may additionally want to replace gArgs.IsArgSet("-changetype") with !gArgs.GetArg("-changetype", "").empty(), so passing the empty string is the same as not passing a value, and will not trigger an error. This would be more consistent with addresstype parsing above, and might be practically useful, e.g. if somebody has an initial value set in a config file but wants to reset it on the command line.


    Sjors commented at 8:45 am on January 18, 2018:
    Being able to override config file seems quite useful, I’ll try your suggestion.

    Sjors commented at 12:33 pm on January 18, 2018:
    @ryanofsky setting changetype=... in the config file makes gArgs.IsArgSet("-changetype" evaluate to true. The config file value is ignored if a parameter overrides it. I still like your shorter notation, so using that now.

    ryanofsky commented at 7:18 pm on January 19, 2018:

    #12119 (review)

    @ryanofsky setting changetype=… in the config file makes gArgs.IsArgSet("-changetype" evaluate to true

    Yes, that’s the reason I also suggested replacing gArgs.IsArgSet("-changetype") with !gArgs.GetArg("-changetype", "").empty()


    Sjors commented at 3:33 pm on January 22, 2018:
    Oh wait, now I get it. So if the config file says changetype=bech32 the user could unset it with -changetype=. Fixed.
  74. in src/wallet/rpcwallet.cpp:261 in b9f211c6d3 outdated
    255@@ -256,9 +256,9 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
    256         pwallet->TopUpKeyPool();
    257     }
    258 
    259-    OutputType output_type = g_change_type;
    260+    OutputType output_type = g_change_type ? g_change_type : g_address_type;
    261     if (!request.params[0].isNull()) {
    262-        output_type = ParseOutputType(request.params[0].get_str(), g_change_type);
    263+        output_type = ParseOutputType(request.params[0].get_str(), OUTPUT_TYPE_NONE);
    


    ryanofsky commented at 8:49 pm on January 17, 2018:
    This is introducing an inconsistency between getnewaddress and getrawchangeaddress. Former will continue treating empty string like null, and latter will now treat the empty string as an error. Would suggest either passing output_type here to continue treating empty string like null both places, or passing OUTPUT_TYPE_NONE in getnewaddress to now raise an error both places.

    Sjors commented at 12:33 pm on January 18, 2018:
    There was a bug there: OutputType output_type = g_change_type ? g_change_type : g_address_type above is overridden in this line. I added some tests for getrawchangeaddress.
  75. in test/functional/address_types.py:121 in b9f211c6d3 outdated
    116+
    117+        # Make sure the transaction has change:
    118+        assert_equal(len(tx["vout"]), len(destinations) + 1)
    119+
    120+        # Make sure the destinations are included, and remove them:
    121+        output_addresses = list(map(lambda vout: vout['scriptPubKey']['addresses'][0], tx["vout"]))
    


    ryanofsky commented at 8:52 pm on January 17, 2018:

    May be easier to read as

    0output_addresses = [vout['scriptPubKey']['addresses'][0] for vout in tx["vout"]]
    

    Sjors commented at 12:33 pm on January 18, 2018:
    It is, done.
  76. in test/functional/address_types.py:123 in b9f211c6d3 outdated
    118+        assert_equal(len(tx["vout"]), len(destinations) + 1)
    119+
    120+        # Make sure the destinations are included, and remove them:
    121+        output_addresses = list(map(lambda vout: vout['scriptPubKey']['addresses'][0], tx["vout"]))
    122+        for destination in destinations:
    123+            output_addresses.pop(output_addresses.index(destination))
    


    ryanofsky commented at 8:52 pm on January 17, 2018:

    Could simplify to output_addresses.remove(destination). Another alternative could be

    0change_addresses = [d for d in output_addresses if d not in destinations]
    

    Sjors commented at 12:33 pm on January 18, 2018:

    That’s what happens if you write javascript for a few years… :-)

    Fixed

  77. in src/wallet/wallet.cpp:2756 in b9f211c6d3 outdated
    2753+                // and any destination is P2WPKH or P2WSH, use P2WPKH for the change
    2754+                // output.
    2755+                OutputType change_type = g_change_type ? g_change_type : g_address_type;
    2756+                if (g_change_type == OUTPUT_TYPE_NONE && g_address_type != OUTPUT_TYPE_LEGACY && any_destination_native_segwit) {
    2757+                    change_type = OUTPUT_TYPE_BECH32;
    2758+                }
    


    ryanofsky commented at 8:59 pm on January 17, 2018:

    Just a suggestion, but perhaps this would be simpler as

    0OutputType change_type =
    1    g_change_type != OUTPUT_TYPE_NONE ? g_change_type :
    2    g_address_type != OUTPUT_TYPE_LEGACY && any_destination_native_segwit ? OUTPUT_TYPE_BECH32 :
    3    g_address_type;
    

    Sjors commented at 12:33 pm on January 18, 2018:
    Nice! Done.
  78. ryanofsky commented at 9:21 pm on January 17, 2018: member
    utACK b9f211c6d3a96fa003732a0a21d7d04ecf80501c. Left some minor suggestions for readability / consistency, which you can feel free to ignore.
  79. Sjors commented at 8:48 am on January 18, 2018: member
    Interesting how Github put @ryanofsky’s review above @MarcoFalke’s even though it’s more recent.
  80. Sjors force-pushed on Jan 18, 2018
  81. Sjors force-pushed on Jan 18, 2018
  82. promag commented at 10:37 pm on January 18, 2018: member

    I think the code can be made a bit cleaner by introducing an explicit P2SH_SEGWIT_BUT_NATIVE_WHEN_NATIVE_DESTINATION or so type in OutputType, but let’s do that later. @sipa but -addresstype could have that value?

  83. ryanofsky commented at 7:47 pm on January 19, 2018: member

    ~utACK 0ee841a21aaea880e3af1e93b53d373295d1f829~ (EDIT: see #12119 (comment)). Changes since last review were various suggested changes and new tests. Thanks for the new tests!

    @sipa but -addresstype could have that value?

    Presumeably enum type could be added and used as a default value without having a corresponding string that would be parsed by -addresstype. Anyway, this does seem like a good idea.

  84. ryanofsky commented at 9:56 pm on January 19, 2018: member

    I just realized this PR doesn’t currently update the -changetype help text, so I withdraw my ACK until that is done. (The current text says default is same as -addresstype, which is no longer accurate.)

    Conditional utACK 0ee841a21aaea880e3af1e93b53d373295d1f829 if help is updated.

  85. Sjors commented at 1:36 pm on January 20, 2018: member

    current text says default is same as -addresstype, which is no longer accurate

    Maybe I can change it to: “Default is same as -addresstype, except when sending to a native segwit address and -addresstype is p2sh-segwit”?

  86. in src/wallet/wallet.cpp:2772 in 0ee841a21a outdated
    2755+                OutputType change_type =
    2756+                    g_change_type != OUTPUT_TYPE_NONE ? g_change_type :
    2757+                    g_address_type != OUTPUT_TYPE_LEGACY && any_destination_native_segwit ? OUTPUT_TYPE_BECH32 :
    2758+                    g_address_type;
    2759+
    2760+                LearnRelatedScripts(vchPubKey, change_type);
    


    promag commented at 3:18 pm on January 20, 2018:
    I know this was here before but shouldn’t this be called by CReserveKey only if the key isn’t returned to the pool?
  87. in src/wallet/wallet.cpp:2753 in 0ee841a21a outdated
    2746@@ -2739,8 +2747,16 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
    2747                     return false;
    2748                 }
    2749 
    2750-                LearnRelatedScripts(vchPubKey, g_change_type);
    2751-                scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, g_change_type));
    2752+                // If -changetype is not specified and g_address_type is not legacy,
    2753+                // and any destination is P2WPKH or P2WSH, use P2WPKH for the change
    2754+                // output.
    2755+                OutputType change_type =
    


    promag commented at 3:23 pm on January 20, 2018:

    IMO should avoid long expressions. Suggestion:

    0OutputType change_type = g_change_type;
    1if (change_type == OUTPUT_TYPE_NONE) {
    2    if (any_destination_native_segwit && g_address_type != OUTPUT_TYPE_LEGAC) {
    3        change_type = OUTPUT_TYPE_BECH32;
    4    } else {
    5        change_type = g_address_type;
    6    }
    7}
    

    Also, CreateTransaction is already a rather long function, could you factor out this to a new function? Then add tests for that in src/wallet/test/wallet_tests.cpp.


    Sjors commented at 3:33 pm on January 22, 2018:

    Switched to your version.

    I extracted the change type stuff to a new function. I made a note to add tests later.

  88. promag commented at 3:27 pm on January 20, 2018: member
    Concept ACK.
  89. Sjors force-pushed on Jan 22, 2018
  90. Sjors commented at 3:33 pm on January 22, 2018: member

    Updated help text: @promag added the const you suggested, as well as explicit == OUTPUT_TYPE_NONE in both places (@ajtowns).

    This can be avoided/skipped if there is a custom address in coin control. Move it below to where any_destination_native_segwit is needed.

    Done (which involved another loop over vecSend, but I assume that’s trivially fast).

  91. in src/wallet/wallet.cpp:2668 in 629fa755b8 outdated
    2654+        // Check if any destination contains a witness program:
    2655+        int witnessversion = 0;
    2656+        std::vector<unsigned char> witnessprogram;
    2657+        if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
    2658+            any_destination_native_segwit = true;
    2659+        }
    


    promag commented at 9:44 pm on January 22, 2018:

    Performance improvement:

    0    any_destination_native_segwit = true;
    1    break;
    

    Sjors commented at 2:59 pm on January 23, 2018:
    Done
  92. in src/wallet/wallet.cpp:2647 in 629fa755b8 outdated
    2643@@ -2644,6 +2644,31 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
    2644     return true;
    2645 }
    2646 
    2647+OutputType CWallet::TransactionChangeType(const std::vector<CRecipient>& vecSend)
    


    promag commented at 9:50 pm on January 22, 2018:

    Suggestions, for now, keep static and not a CWallet member:

    0static OutputType GetChangeTypeForRecipients(const std::vector<CRecipient>& recipients)
    1{
    

    And below:

    0const OutputType change_type = GetChangeTypeForRecipients(vecSend);
    

    don’t mind the function name.


    Sjors commented at 2:44 pm on January 23, 2018:
    What’s the advantage of making it static? Especially if the longer term aim is to specify launch options for individual wallets (and presumably replace references to g_change_type with a CWallet member).

    promag commented at 3:09 pm on January 23, 2018:
    I just think it’s too soon to expose this in the CWallet. Feel free to leave as it is.

    jnewbery commented at 3:40 pm on January 23, 2018:

    This is perhaps just personal taste, but I prefer early function return if we already know what change address we’ll use without having to look at the outputs. That also means we can remove the local variables any_destination_native_segwit and change_type. It’d look something like:

     0    // If -changetype is specified, always use that change type.
     1    if (g_change_type != OUTPUT_TYPE_NONE) {
     2        return g_change_type;
     3    }
     4
     5    // if g_address_type is legacy, use legacy address as change (even
     6    // if some of the outputs are P2WPKH or P2WSH).
     7    if (g_address_type == OUTPUT_TYPE_LEGACY) {
     8        return OUTPUT_TYPE_LEGACY;
     9    }
    10
    11    // if any destination is P2WPKH or P2WSH, use P2WPKH for the change
    12    // output.
    13    for (const auto& recipient : vecSend) {
    14        // Check if any destination contains a witness program:
    15        int witnessversion = 0;
    16        std::vector<unsigned char> witnessprogram;
    17        if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
    18            return OUTPUT_TYPE_BECH32;
    19        }
    20    }
    21
    22    // else use g_address_type for change
    23    return g_address_type;
    

    Sjors commented at 4:49 pm on January 23, 2018:
    I find your version more readable as well, so switched to that.

    Sjors commented at 7:08 pm on January 23, 2018:
    cc @jnewbery given your last comment. I’ll leave it as is, but useful to know what the future direction shouldl be.
  93. Sjors force-pushed on Jan 23, 2018
  94. promag commented at 3:33 pm on January 23, 2018: member

    utACK dc2812f.

    The only thing that bothers me is the fact that g_address_type = OUTPUT_TYPE_NONE is an error and now g_change_type = OUTPUT_TYPE_NONE is not.

  95. in test/functional/address_types.py:14 in 629fa755b8 outdated
     9@@ -10,7 +10,7 @@
    10     - node2 uses p2sh/segwit addresses and bech32 addresses for change
    11     - node3 uses bech32 addresses
    12 
    13-node4 exists to generate new blocks.
    14+node5 exists to generate new blocks.
    


    jnewbery commented at 3:40 pm on January 23, 2018:
    please update the lines above to say there are 5 nodes under test, and describe what node4 is for.

    Sjors commented at 4:49 pm on January 23, 2018:
    Fixed.
  96. in test/functional/address_types.py:136 in 629fa755b8 outdated
    131+
    132+        self.log.debug("Check if change address " +  change_addresses[0] + " is " + expected_type)
    133+        self.test_address(node_sender, change_addresses[0], multisig=False, typ=expected_type)
    134+
    135     def run_test(self):
    136         # Mine 101 blocks on node4 to bring nodes out of IBD and make sure that
    


    jnewbery commented at 3:44 pm on January 23, 2018:
    update comment: on node5

    Sjors commented at 4:50 pm on January 23, 2018:
    Fixed
  97. in test/functional/address_types.py:213 in 629fa755b8 outdated
    210@@ -183,7 +211,7 @@ def run_test(self):
    211                 assert_equal(unconf_balances[to_node], to_send * 10 * (2 + n))
    212 
    213             # node4 collects fee and block subsidy to keep accounting simple
    


    jnewbery commented at 3:46 pm on January 23, 2018:
    update comment

    Sjors commented at 4:50 pm on January 23, 2018:
    Fixed
  98. in test/functional/address_types.py:120 in 629fa755b8 outdated
    115@@ -104,10 +116,26 @@ def test_address(self, node, address, multisig, typ):
    116             # Unknown type
    117             assert(False)
    118 
    119+    def test_change_output_type(self, node_sender, destinations, expected_type):
    120+        txid = self.nodes[node_sender].sendmany("",  dict.fromkeys(destinations, 0.001))
    


    jnewbery commented at 3:56 pm on January 23, 2018:
    unnecessary double space

    jnewbery commented at 4:09 pm on January 23, 2018:
    suggest you make these named arguments so it’s clear what the "" is for.

    Sjors commented at 4:49 pm on January 23, 2018:
    Fixed

    Sjors commented at 4:49 pm on January 23, 2018:
    Done
  99. in test/functional/address_types.py:132 in 629fa755b8 outdated
    127+        # Make sure the destinations are included, and remove them:
    128+        output_addresses = [vout['scriptPubKey']['addresses'][0] for vout in tx["vout"]]
    129+        change_addresses = [d for d in output_addresses if d not in destinations]
    130+        assert_equal(len(change_addresses), 1)
    131+
    132+        self.log.debug("Check if change address " +  change_addresses[0] + " is " + expected_type)
    


    jnewbery commented at 3:56 pm on January 23, 2018:
    unnecessary double space

    Sjors commented at 4:49 pm on January 23, 2018:
    Fixed
  100. in test/functional/address_types.py:51 in 629fa755b8 outdated
    28+this also verifies that spending coins sent to all these address types works.
    29+
    30+Test that node1 uses a bech32 addresses for change if any destination address is
    31+bech32. Test that node0 always uses a legacy change address. Test that node4 uses
    32+p2sh/segwit output for change.
    33+"""
    


    jnewbery commented at 3:57 pm on January 23, 2018:

    I think the whole docstring could be updated to reflect better what the test is actually doing:

     0"""Test that the wallet can send and receive using all combinations of address types.
     1
     2There are 5 nodes-under-test:
     3    - node0 uses legacy addresses
     4    - node1 uses p2sh/segwit addresses
     5    - node2 uses p2sh/segwit addresses and bech32 addresses for change
     6    - node3 uses bech32 addresses
     7    - node4 uses p2sh/segwit addresses for change
     8
     9node5 exists to generate new blocks.
    10
    11## Multisig address test
    12
    13Test that adding a multisig address with:
    14    - an uncompressed pubkey always gives a legacy address
    15    - only compressed pubkeys gives the an `-addresstype` address
    16
    17## Sending to address types test
    18
    19A series of tests, iterating over node0-node4. In each iteration of the test, one node sends:
    20    - 10/101th of its balance to itself (using getrawchangeaddress for single key addresses)
    21    - 20/101th to the next node
    22    - 30/101th to the node after that
    23    - 40/101th to the remaining node
    24    - 1/101th remains as fee+change
    25
    26Iterate over each node for single key addresses, and then over each node for
    27multisig addresses.
    28
    29Repeat test, but with explicit address_type parameters passed to getnewaddress
    30and getrawchangeaddress:
    31    - node0 and node3 send to p2sh.
    32    - node1 sends to bech32.
    33    - node2 sends to legacy.
    34
    35As every node sends coins after receiving, this also
    36verifies that spending coins sent to all these address types works.
    37
    38## Change type test
    39
    40Test that the nodes generate the correct change address type:
    41    - node0 always uses a legacy change address.
    42    - node1 uses a bech32 addresses for change if any destination address is bech32.
    43    - node4 always uses p2sh/segwit output for change.
    44"""
    

    Sjors commented at 4:49 pm on January 23, 2018:
    That’s much clearer, thanks. I also ordered the change address tests by node id added node 2 and 3 to the description.
  101. in test/functional/address_types.py:237 in 629fa755b8 outdated
    232+        self.nodes[5].sendtoaddress(self.nodes[4].getnewaddress(), Decimal("1"))
    233+        self.nodes[5].generate(1)
    234+        sync_blocks(self.nodes)
    235+        assert_equal(self.nodes[4].getbalance(), 1)
    236+
    237+        self.log.debug("Nodes with change_type=bech32 always use a P2WPKH change output:")
    


    jnewbery commented at 4:09 pm on January 23, 2018:
    Suggest you make all of these logs INFO level

    Sjors commented at 4:50 pm on January 23, 2018:
    Done
  102. in test/functional/address_types.py:50 in 629fa755b8 outdated
    49 class AddressTypeTest(BitcoinTestFramework):
    50     def set_test_params(self):
    51-        self.num_nodes = 5
    52-        self.extra_args = [["-addresstype=legacy"], ["-addresstype=p2sh-segwit"], ["-addresstype=p2sh-segwit", "-changetype=bech32"], ["-addresstype=bech32"], []]
    53+        self.num_nodes = 6
    54+        self.extra_args = [["-addresstype=legacy"], ["-addresstype=p2sh-segwit"], ["-addresstype=p2sh-segwit", "-changetype=bech32"], ["-addresstype=bech32"], ["-changetype=p2sh-segwit"], []]
    


    jnewbery commented at 4:10 pm on January 23, 2018:

    Perhaps clearer as:

    0        self.extra_args = [["-addresstype=legacy"],                             # node0
    1                           ["-addresstype=p2sh-segwit"],                        # node1
    2                           ["-addresstype=p2sh-segwit", "-changetype=bech32"],  # node2
    3                           ["-addresstype=bech32"],                             # node3
    4                           ["-changetype=p2sh-segwit"],                         # node4
    5                           []]                                                  # node5
    

    Sjors commented at 4:49 pm on January 23, 2018:
    Fixed
  103. in test/functional/address_types.py:252 in 629fa755b8 outdated
    247+
    248+        self.log.debug('getrawchangeaddress fails with invalid changetype argument')
    249+        assert_raises_rpc_error(-5, "Unknown address type 'bech23'", self.nodes[3].getrawchangeaddress, 'bech23')
    250+
    251+        self.log.debug("Nodes with addresstype=p2sh-segwit only use a P2WPKH change output " +
    252+                       "if any destination address is bech32:")
    


    jnewbery commented at 4:14 pm on January 23, 2018:
    I think it’s fine to join these lines

    Sjors commented at 4:50 pm on January 23, 2018:
    Done
  104. jnewbery commented at 4:15 pm on January 23, 2018: member

    Tested ACK dc2812f3e40dbbc7533a2d2e15cf4dfb3ae079af.

    A bunch of style nits inline. Otherwise looks great.

  105. Sjors force-pushed on Jan 23, 2018
  106. [wallet] use P2WPKH change output if any destination is P2WPKH or P2WSH
    Only if -changetype is not set and -addresstype is not "legacy".
    596c44633f
  107. Sjors force-pushed on Jan 23, 2018
  108. in src/wallet/wallet.cpp:2647 in 596c44633f
    2643@@ -2644,6 +2644,34 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
    2644     return true;
    2645 }
    2646 
    2647+OutputType CWallet::TransactionChangeType(const std::vector<CRecipient>& vecSend)
    


    jnewbery commented at 6:31 pm on January 23, 2018:
    TransactionChangeType() doesn’t really need to be a class member function on CWallet, and could be a utility function in walletutil.cpp. In general, it’d be nice to start making the wallet class and wallet.cpp a bit smaller, but that’s probably a tidy-up for another day.

    promag commented at 7:14 pm on January 23, 2018:
    +1

    laanwj commented at 2:23 pm on January 24, 2018:
    Agree, this should be moved at some point.
  109. jnewbery commented at 6:31 pm on January 23, 2018: member
    Tested ACK 596c44633fd03e76cc12f2fd37452e223ba43115. One comment inline, but it’s an observation rather than a suggestion for change.
  110. MarcoFalke commented at 9:28 pm on January 23, 2018: member
    utACK 596c44633fd03e76cc12f2fd37452e223ba43115
  111. laanwj merged this on Jan 24, 2018
  112. laanwj closed this on Jan 24, 2018

  113. laanwj referenced this in commit 95941396ff on Jan 24, 2018
  114. in src/wallet/wallet.cpp:2650 in 596c44633f
    2643@@ -2644,6 +2644,34 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
    2644     return true;
    2645 }
    2646 
    2647+OutputType CWallet::TransactionChangeType(const std::vector<CRecipient>& vecSend)
    2648+{
    2649+    // If -changetype is specified, always use that change type.
    2650+    if (g_change_type != OUTPUT_TYPE_NONE) {
    


    ryanofsky commented at 3:34 pm on January 24, 2018:

    For the future, it might be a good idea add change_type and address_type fields to CoinControl class, and have this method accept a CoinControl argument, instead of relying on the globals. This would make it easier to ensure that when an RPC is being called that has address type options, that they will take precedence over the globals.

    Pieter’s suggestion of adding a separate P2SH_SEGWIT_BUT_NATIVE_WHEN_NATIVE_DESTINATION enum value instead of assigning multiple meanings to OUTPUT_TYPE_NONE is also a good idea #12119 (comment).

    Other cleanups are also possible. I think a nice refactoring PR that didn’t change behavior could be submitted that:

    • Changed OutputType from enum to enum class.
    • Got rid of DEFAULT enum value and just set P2SH_SEGWIT as default during initialization.
    • Got rid of NONE enum value, replacing it with PARSE_ERROR and P2SH_SEGWIT_BUT_NATIVE_WHEN_NATIVE_DESTINATION (or something shorter like MATCH_OTHER_OUTPUTS).

    promag commented at 3:39 pm on January 24, 2018:
    Nice wrap up @ryanofsky. In #12194 I’m adding change type to coin control, however I’m not passing coin control here.
  115. Sjors deleted the branch on Jan 24, 2018
  116. jonasschnelli referenced this in commit 7abb0f0929 on Jan 24, 2018
  117. ryanofsky referenced this in commit 3be62aeb1c on Mar 19, 2018
  118. ryanofsky referenced this in commit 305b6bc1d8 on Mar 20, 2018
  119. ryanofsky referenced this in commit 1e46d8ae89 on Apr 6, 2018
  120. laanwj referenced this in commit 979150bc23 on May 3, 2018
  121. Bushstar referenced this in commit 4cc9213d41 on May 4, 2018
  122. stamhe referenced this in commit cfcc3b28fc on Jun 27, 2018
  123. HashUnlimited referenced this in commit ed14ae75c7 on Sep 6, 2018
  124. lionello referenced this in commit 9fa89d1580 on Nov 7, 2018
  125. joemphilips referenced this in commit 8878260bd4 on Nov 9, 2018
  126. markblundeberg referenced this in commit ef4d376683 on Nov 19, 2019
  127. deadalnix referenced this in commit 1ff305f83c on May 19, 2020
  128. ftrader referenced this in commit 46d3158779 on Aug 17, 2020
  129. TheArbitrator referenced this in commit d0fefe7ec9 on Jun 21, 2021
  130. DrahtBot locked this on Aug 16, 2022

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-11-17 09:12 UTC

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