-
Notifications
You must be signed in to change notification settings - Fork 37.7k
descriptors: Disallow hybrid and uncompressed keys when inferring #28602
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
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. 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. |
6c80e82
to
25a8a17
Compare
src/test/descriptor_tests.cpp
Outdated
for (const auto& [pubkey_hex, origin_str] : str_origins) { | ||
CPubKey origin_pubkey{ParseHex(pubkey_hex)}; | ||
|
||
using namespace spanparsing; |
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.
The body of this loop looks like it's duplicating code from existing key parsing code.
Would it be possible to e.g. construct a [origin]pubkey
string, pass that to ParsePubkey
, and use the resulting FlatSigningProvider& out
?
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 without a lot of refactoring that ends up exposing several descriptor parsing internals.
25a8a17
to
6f00af4
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.
Code review ACK 6f00af4, left few nits, nothing blocking.
2b95b11
to
9d639f0
Compare
9d639f0
to
a49081f
Compare
utACK a49081f |
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.
Concept ACK
Mostly happy with a49081f
@@ -1876,7 +1880,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo | |||
CKeyID keyid(hash); | |||
CPubKey pubkey; | |||
if (provider.GetPubKey(keyid, pubkey)) { | |||
if (auto pubkey_provider = InferPubkey(pubkey, ctx, provider)) { | |||
if (auto pubkey_provider = InferPubkey(pubkey, ParseScriptContext::P2WPKH, provider)) { |
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.
62ca75b: what's wrong with passing ctx
here?
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.
The context for the pubkey inferring needs to be P2WPKH
. ctx
won't ever be that, instead it will either be TOP
or P2SH
, both of which allow uncompressed pubkeys, but P2WPKH does not. So the context needs to be updated in order to enforce that.
@DrahtBot you're drunk, I just reviewed it. |
Thanks, fixed in maflcko/DrahtBot@f8d7c68 You'll be re-requested to review at a later point in time. |
InferPubkey can return a nullptr, so check it's result before continuing with creating the inferred descriptor.
Descriptors disallows hybrid pubkeys. Anything with hybrid pubkeys should becomes a raw() descriptor that shows up in the watchonly wallet.
a49081f
to
74c7782
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.
ACK 74c7782
bool ok = true; | ||
std::vector<std::unique_ptr<PubkeyProvider>> providers; | ||
for (size_t i = 1; i + 1 < data.size(); ++i) { | ||
CPubKey pubkey(data[i]); | ||
providers.push_back(InferPubkey(pubkey, ctx, provider)); | ||
if (auto pubkey_provider = InferPubkey(pubkey, ctx, provider)) { | ||
providers.push_back(std::move(pubkey_provider)); | ||
} else { | ||
ok = false; | ||
break; | ||
} | ||
} | ||
return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers)); | ||
if (ok) return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers)); |
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.
just a coding style nit. No need to implement it if you don't like it.
std::vector<std::unique_ptr<PubkeyProvider>> providers;
for (size_t i = 1; i + 1 < data.size(); ++i) {
CPubKey pubkey(data[i]);
auto pubkey_provider = InferPubkey(pubkey, ctx, provider);
if (!pubkey_provider) break;
providers.push_back(std::move(pubkey_provider));
}
size_t expected_num = data.size() - 2; // First position is the min number of signers, and the last one is the op_checkmultisig
if (providers.size() == expected_num) return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers));
continue | ||
assert False, "Hybrid pubkey watchonly wallet has more than just raw() and addr()" | ||
|
||
wallet.unloadwallet() |
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 f895f97:
would be good to also unload the watchonly_wallet
wallet.
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.
utACK 74c7782
…ys when inferring
InferPubkey can return a nullptr, so check it's result before continuing with creating the inferred descriptor. Github-Pull: bitcoin#28602 Rebased-From: b7485f1
Github-Pull: bitcoin#28602 Rebased-From: 37b9b73
Descriptors disallows hybrid pubkeys. Anything with hybrid pubkeys should becomes a raw() descriptor that shows up in the watchonly wallet. Github-Pull: bitcoin#28602 Rebased-From: f895f97
Github-Pull: bitcoin#28602 Rebased-From: 74c7782
InferDescriptor
was not always checking that the pubkey it was placing into the descriptor was an allowed pubkey. For example, given a P2WPKH script that uses an uncompressed pubkey, it would produce awpkh()
with the uncompressed key. Additionally, the hybrid key check was only being done forpk()
scripts, where it should've been done for all scripts.This PR moves the key checking into
InferPubkey
. If the key is not valid for the context, thennullptr
is returned and the inferring will fall through to the defaults of eitherraw()
oraddr()
.This also resolves an issue with migrating legacy wallets that contain hybrid pubkeys as such watchonly scripts will become
raw()
oraddr()
and go to the watchonly wallet. Note that a legacy wallet cannot sign for hybrid pubkeys. A test has been added for the migration case.Also added unit tests for
InferDescriptor
itself as the edge cases with that function are not covered by the descriptor roundtrip test.