Add support for inferring descriptors from scripts

This commit is contained in:
Pieter Wuille 2018-10-12 18:22:22 -07:00
parent f4e4ea1cee
commit 4d78bd93b5
2 changed files with 95 additions and 1 deletions

View file

@ -625,6 +625,80 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext
return nullptr; return nullptr;
} }
std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
{
std::unique_ptr<PubkeyProvider> key_provider = MakeUnique<ConstPubkeyProvider>(pubkey);
KeyOriginInfo info;
if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
return MakeUnique<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<unsigned char>> data;
txnouttype txntype = Solver(script, data);
if (txntype == TX_PUBKEY) {
CPubKey pubkey(data[0].begin(), data[0].end());
if (pubkey.IsValid()) {
return MakeUnique<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 MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKHGetScript, "pkh");
}
}
if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
if (provider.GetPubKey(keyid, pubkey)) {
return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2WPKHGetScript, "wpkh");
}
}
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 MakeUnique<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 MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2SH, "sh");
}
}
if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
CScriptID scriptid;
CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin());
CScript subscript;
if (provider.GetCScript(scriptid, subscript)) {
auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);
if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2WSH, "wsh");
}
}
CTxDestination dest;
if (ExtractDestination(script, dest)) {
if (GetScriptForDestination(dest) == script) {
return MakeUnique<AddressDescriptor>(std::move(dest));
}
}
return MakeUnique<RawDescriptor>(script);
}
} // namespace } // namespace
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out) std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out)
@ -634,3 +708,8 @@ std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProv
if (sp.size() == 0 && ret) return ret; if (sp.size() == 0 && ret) return ret;
return nullptr; return nullptr;
} }
std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
{
return InferScript(script, ParseScriptContext::TOP, provider);
}

View file

@ -51,5 +51,20 @@ struct Descriptor {
/** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */ /** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out); std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out);
#endif // BITCOIN_SCRIPT_DESCRIPTOR_H /** 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