Skip to content

Conversation

achow101
Copy link
Member

@achow101 achow101 commented Oct 5, 2023

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 a wpkh() with the uncompressed key. Additionally, the hybrid key check was only being done for pk() 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, then nullptr is returned and the inferring will fall through to the defaults of either raw() or addr().

This also resolves an issue with migrating legacy wallets that contain hybrid pubkeys as such watchonly scripts will become raw() or addr() 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.

@DrahtBot
Copy link
Contributor

DrahtBot commented Oct 5, 2023

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

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK furszy, Sjors
Stale ACK sipa

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #28610 (wallet: Migrate entire address book entries to watchonly and solvables too by achow101)
  • #28609 (wallet: Reload watchonly and solvables wallets after migration by achow101)
  • #22838 (descriptors: Be able to specify change and receiving in a single descriptor string by achow101)

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.

@achow101 achow101 added this to the 26.0 milestone Oct 5, 2023
@fanquake fanquake requested review from darosior and sipa October 5, 2023 18:04
for (const auto& [pubkey_hex, origin_str] : str_origins) {
CPubKey origin_pubkey{ParseHex(pubkey_hex)};

using namespace spanparsing;
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

@furszy furszy left a 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.

@sipa
Copy link
Member

sipa commented Oct 8, 2023

utACK a49081f

@DrahtBot DrahtBot requested a review from furszy October 8, 2023 18:34
Copy link
Member

@Sjors Sjors left a 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)) {
Copy link
Member

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?

Copy link
Member Author

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 DrahtBot requested a review from Sjors October 9, 2023 07:26
@Sjors
Copy link
Member

Sjors commented Oct 9, 2023

@DrahtBot you're drunk, I just reviewed it.

@maflcko maflcko removed the request for review from Sjors October 9, 2023 10:20
@maflcko
Copy link
Member

maflcko commented Oct 9, 2023

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.
@achow101 achow101 force-pushed the migrate-hybrid-keys branch from a49081f to 74c7782 Compare October 9, 2023 18:07
Copy link
Member

@furszy furszy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 74c7782

Comment on lines +1885 to +1896
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));
Copy link
Member

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()
Copy link
Member

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.

@DrahtBot DrahtBot requested review from sipa and Sjors October 9, 2023 22:33
Copy link
Member

@Sjors Sjors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 74c7782

@fanquake fanquake merged commit 744157e into bitcoin:master Oct 11, 2023
Frank-GER pushed a commit to syscoin/syscoin that referenced this pull request Oct 13, 2023
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request Oct 18, 2023
InferPubkey can return a nullptr, so check it's result before continuing
with creating the inferred descriptor.

Github-Pull: bitcoin#28602
Rebased-From: b7485f1
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request Oct 18, 2023
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request Oct 18, 2023
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
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request Oct 18, 2023
@bitcoin bitcoin locked and limited conversation to collaborators Oct 10, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants