Add support for inferring descriptors from scripts
This commit is contained in:
parent
f4e4ea1cee
commit
4d78bd93b5
2 changed files with 95 additions and 1 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue