-
Notifications
You must be signed in to change notification settings - Fork 36.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add OutputType::BECH32M and related wallet support for fetching bech32m addresses #22154
Conversation
4549a29
to
3f68bb5
Compare
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
3f68bb5
to
73ddcb8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks close, only some style nits and open questions/suggestions.
@@ -254,6 +254,13 @@ class ScriptPubKeyMan | |||
boost::signals2::signal<void ()> NotifyCanGetAddressesChanged; | |||
}; | |||
|
|||
/** OutputTypes supported by the LegacyScriptPubKeyMan */ | |||
static const std::unordered_set<OutputType> LEGACY_OUTPUT_TYPES { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: could use constexpr
I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried that but it doesn't work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't constexpr
things that need allocation. You could use an array instead of an unordered_set, and use std::find
to look up (which will likely be faster too, due to the tiny size), but it really doesn't matter here.
02657d1
to
05ff76c
Compare
utACK 05ff76c |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -23,6 +23,11 @@ const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; | |||
|
|||
bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) | |||
{ | |||
if (LEGACY_OUTPUT_TYPES.count(type) == 0) { | |||
error = _("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types").translated; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a137346 : is this just belt and suspenders? When I do getnewaddress Test bech32m
on legacy wallet for this commit, it's caught earlier and returns No bech32m addresses available
. That's because CWallet::GetNewDestination
tries GetScriptPubKeyMan
first for the requested type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, just belt and suspenders.
src/wallet/wallet.cpp
Outdated
@@ -1906,7 +1906,12 @@ OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& chang | |||
// Check if any destination contains a witness program: | |||
int witnessversion = 0; | |||
std::vector<unsigned char> witnessprogram; | |||
if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { | |||
const bool is_wit = recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram); | |||
if (is_wit && witnessversion >= 1 && GetScriptPubKeyMan(OutputType::BECH32M, true)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an extra safety measure, maybe double-check that Taproot is active like in #22156
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is generic for future segwit versions, I'm not sure doing that makes sense. For taproot specifically, there won't be a bech32m ScriptPubKeyMan
until taproot activates anyways.
05ff76c
to
f70d55c
Compare
re-utACK f70d55c |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left two comments.
@@ -2115,7 +2115,7 @@ bool CWallet::GetNewDestination(const OutputType type, const std::string label, | |||
spk_man->TopUp(); | |||
result = spk_man->GetNewDestination(type, dest, error); | |||
} else { | |||
error = strprintf("Error: No %s addresses available.", FormatOutputType(type)); | |||
error = strprintf(_("Error: No %s addresses available."), FormatOutputType(type)).translated; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how difficult it would be to fix, but the general idea for translations is to pass up the bilingual string and let the caller pick the right version. Otherwise the RPC responses (and debug log) might be partially translated. Functional test might fail when run with the gui.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that will be a bit invasive since several of the functions involved use std::string error
rather than bilingual_str
. Perhaps for a followup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if you're going to translate anything, it's better to use bilingual_str
. I would prefer to do this right in the first go, instead of having another follow-up commit to prevent RPC messages from being translated.
Alternatively just leave this untranslated for now. This is marked for 22.0 and we've passed the translation string freeze.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the strings I added, I was just following the pattern already in use in these functions. For this change in particular, it was to make the style unified. Unless this is considered a blocker, I'm going to leave it as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok…
cae6672
to
f33361c
Compare
Concept ACK |
re-utACK f33361c |
re-ACK f33361c The only changes since my last review was a reorganization of the code changes in |
Code review ACK f33361c I think the potential RPC error message translation issue mentioned in #22154 (comment) needs to be addressed at some point, but also think it's not a blocker for 22.0. |
I get an error in wallet_taproot.py: Remaining jobs: [wallet_taproot.py]
1/1 - wallet_taproot.py failed, Duration: 1 s
stdout:
2021-06-23T01:47:26.095000Z TestFramework (INFO): Initializing test directory /tmp/test_runner_₿_🏃_20210623_134725/wallet_taproot_0
2021-06-23T01:47:26.359000Z TestFramework (INFO): Creating wallets...
2021-06-23T01:47:26.605000Z TestFramework (INFO): Mining blocks...
2021-06-23T01:47:26.694000Z TestFramework (INFO): Testing tr(XPRV) address derivation
2021-06-23T01:47:26.745000Z TestFramework (INFO): Testing tr(XPRV) through sendtoaddress
2021-06-23T01:47:26.770000Z TestFramework (ERROR): JSONRPC error
Traceback (most recent call last):
File "/home/meshcollider/bitcoin/test/functional/test_framework/test_framework.py", line 128, in main
self.run_test()
File "/home/meshcollider/bitcoin/test/functional/wallet_taproot.py", line 347, in run_test
1
File "/home/meshcollider/bitcoin/test/functional/wallet_taproot.py", line 314, in do_test
self.do_test_sendtoaddress(comment, pattern, privmap, treefn, keys[0:nkeys], keys[nkeys:2*nkeys])
File "/home/meshcollider/bitcoin/test/functional/wallet_taproot.py", line 262, in do_test_sendtoaddress
addr_g = self.rpc_online.getnewaddress(address_type='bech32')
File "/home/meshcollider/bitcoin/test/functional/test_framework/coverage.py", line 47, in __call__
return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
File "/home/meshcollider/bitcoin/test/functional/test_framework/authproxy.py", line 146, in __call__
raise JSONRPCException(response['error'], status)
test_framework.authproxy.JSONRPCException: Error: No bech32 addresses available. (-12)
2021-06-23T01:47:26.821000Z TestFramework (INFO): Stopping nodes
2021-06-23T01:47:27.028000Z TestFramework (WARNING): Not cleaning up dir /tmp/test_runner_₿_🏃_20210623_134725/wallet_taproot_0
2021-06-23T01:47:27.028000Z TestFramework (ERROR): Test failed. Test logging available at /tmp/test_runner_₿_🏃_20210623_134725/wallet_taproot_0/test_framework.log |
Make sure that LegacyScriptPubKeyMan can only be used for legacy, p2sh-segwit, and bech32 address types.
Bech32m addresses need their own OutputType We are not ready to create DescriptorScriptPubKeyMans which produce bech32m addresses. So don't allow generating them.
If a transaction as a segwit output, use a bech32m change address if they are available. If not, fallback to bech32. If bech32 change addresses are unavailable, fallback to the default address type.
f33361c
to
fbf142e
Compare
The tr() descriptor, WitnessV1Taproot CTxDestination, and WitnessUnknown CTxDestination are OutputType::BECH32M so they should report as such.
We don't want the legacy wallet to ever have bech32m addresses so don't allow importing them. This includes addmultisigaddress as that is a legacy wallet only RPC Additionally, bech32m multisigs are not available yet, so disallow them in createmultisig.
Adds an error output parameter to all GetReservedDestination functions so that callers can get the actual reason that a change address could not be fetched. This more closely matches GetNewDestination. This allows for more granular error messages, such as one that indicates that bech32m addresses cannot be generated yet.
Silent merge conflict, rebased and fixed. |
fbf142e
to
754f134
Compare
re-utACK 754f134: only change is switching to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 754f134
When should bech32m
be added to the -addresstype
and -changetype
wallet config option helps?
@@ -640,20 +640,6 @@ class DescriptorImpl : public Descriptor | |||
std::optional<OutputType> GetOutputType() const override { return std::nullopt; } | |||
}; | |||
|
|||
static std::optional<OutputType> OutputTypeFromDestination(const CTxDestination& dest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit 87a0e7a, src/script/descriptor.cpp should include outputtype.h
where OutputTypeFromDestination()
is moved.
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -5,6 +5,7 @@
#include <script/descriptor.h>
#include <key_io.h>
+#include <outputtype.h>
#include <pubkey.h>
#include <script/script.h>
#include <script/standard.h>
@@ -373,5 +373,15 @@ def run_test(self): | |||
self.test_address(4, self.nodes[4].getrawchangeaddress(), multisig=False, typ='p2sh-segwit') | |||
self.test_address(4, self.nodes[4].getrawchangeaddress('bech32'), multisig=False, typ='bech32') | |||
|
|||
if self.options.descriptors: | |||
self.log.info("Descriptor wallets do not have bech32m addresses by default yet") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/addreses/addresses/ should be fixed one commit earlier in 87a0e7a
When we make bech32m descriptors by default. |
re-ACK 754f134 Confirmed only changes were rebasing and fixing |
I rebased my followup in #22260 (which probably works best if you don't set |
Currently bech32m addresses are classfied as bech32. Because bech32m is incompatible with bech32, we need to define a new
OutputType
for it so that it can be handled correctly. This PR addsOutputType::BECH32M
, updates all of the relevantOutputType
classifications, and handle requests for bech32m addresses. There is now abech32m
address type string that can be used.tr()
descriptors now report their output type asOutputType::BECH32M
.WtinessV1Taproot
andWitnessUnknown
are also classified asOutputType::BECH32M
.importaddress
andimportmulti
), will not be created when getting all destinations for a pubkey, and will not be added withaddmultisigaddress
. Additional protections have been added toLegacyScriptPubKeyMan
to disallow attempting to retrieve bech32m addresses.createmultisig
will also disallow the bech32m address type.DescriptorScriptPubKeyMan
cannot and will not create atr()
descriptor. Protections have been added to make sure this cannot occur.bech32m
when there is a segwit v1+ output script and the wallet has a bech32mScriptPubKeyMan
, falling back to bech32 if one is not available.