Skip to content

Commit

Permalink
Add support for inferring descriptors from scripts
Browse files Browse the repository at this point in the history
Summary:
This is a partial backport of Core [[bitcoin/bitcoin#14477 | PR14477]] : bitcoin/bitcoin@4d78bd9

I made InferPubkey static because it is only called from there.

Test Plan:
  ninja all check-all

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Subscribers: majcosta

Differential Revision: https://reviews.bitcoinabc.org/D6124
  • Loading branch information
sipa authored and deadalnix committed May 18, 2020
1 parent 7f84dcc commit f011dbc
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 0 deletions.
72 changes: 72 additions & 0 deletions src/script/descriptor.cpp
Expand Up @@ -787,6 +787,73 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char> &sp,
return nullptr;
}

static std::unique_ptr<PubkeyProvider>
InferPubkey(const CPubKey &pubkey, ParseScriptContext,
const SigningProvider &provider) {
auto key_provider = std::make_unique<ConstPubkeyProvider>(pubkey);
KeyOriginInfo info;
if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
return std::make_unique<OriginPubkeyProvider>(std::move(info),
std::move(key_provider));
}
return key_provider;
}

std::unique_ptr<Descriptor> InferScript(const CScript &script,
ParseScriptContext ctx,
const SigningProvider &provider) {
std::vector<std::vector<uint8_t>> data;
txnouttype txntype = Solver(script, data);

if (txntype == TX_PUBKEY) {
CPubKey pubkey(data[0].begin(), data[0].end());
if (pubkey.IsValid()) {
return std::make_unique<SingleKeyDescriptor>(
InferPubkey(pubkey, ctx, provider), P2PKGetScript, "pk");
}
}
if (txntype == TX_PUBKEYHASH) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
if (provider.GetPubKey(keyid, pubkey)) {
return std::make_unique<SingleKeyDescriptor>(
InferPubkey(pubkey, ctx, provider), P2PKHGetScript, "pkh");
}
}
if (txntype == TX_MULTISIG) {
std::vector<std::unique_ptr<PubkeyProvider>> providers;
for (size_t i = 1; i + 1 < data.size(); ++i) {
CPubKey pubkey(data[i].begin(), data[i].end());
providers.push_back(InferPubkey(pubkey, ctx, provider));
}
return std::make_unique<MultisigDescriptor>((int)data[0][0],
std::move(providers));
}
if (txntype == TX_SCRIPTHASH && ctx == ParseScriptContext::TOP) {
uint160 hash(data[0]);
CScriptID scriptid(hash);
CScript subscript;
if (provider.GetCScript(scriptid, subscript)) {
auto sub =
InferScript(subscript, ParseScriptContext::P2SH, provider);
if (sub) {
return std::make_unique<ConvertorDescriptor>(std::move(sub),
ConvertP2SH, "sh");
}
}
}

CTxDestination dest;
if (ExtractDestination(script, dest)) {
if (GetScriptForDestination(dest) == script) {
return std::make_unique<AddressDescriptor>(std::move(dest));
}
}

return std::make_unique<RawDescriptor>(script);
}

} // namespace

std::unique_ptr<Descriptor> Parse(const std::string &descriptor,
Expand All @@ -798,3 +865,8 @@ std::unique_ptr<Descriptor> Parse(const std::string &descriptor,
}
return nullptr;
}

std::unique_ptr<Descriptor> InferDescriptor(const CScript &script,
const SigningProvider &provider) {
return InferScript(script, ParseScriptContext::TOP, provider);
}
20 changes: 20 additions & 0 deletions src/script/descriptor.h
Expand Up @@ -72,4 +72,24 @@ struct Descriptor {
std::unique_ptr<Descriptor> Parse(const std::string &descriptor,
FlatSigningProvider &out);

/**
* Find a descriptor for the specified script, using information from provider
* where possible.
*
* A non-ranged descriptor which only generates the specified script will be
* returned in all circumstances.
*
* For public keys with key origin information, this information will be
* preserved in the returned descriptor.
*
* - If all information for solving `script` is present in `provider`, a
* descriptor will be returned which is `IsSolvable()` and encapsulates said
* information.
* - Failing that, if `script` corresponds to a known address type, an "addr()"
* descriptor will be returned (which is not `IsSolvable()`).
* - Failing that, a "raw()" descriptor is returned.
*/
std::unique_ptr<Descriptor> InferDescriptor(const CScript &script,
const SigningProvider &provider);

#endif // BITCOIN_SCRIPT_DESCRIPTOR_H

0 comments on commit f011dbc

Please sign in to comment.