Skip to content

Commit 4d78bd9

Browse files
committed
Add support for inferring descriptors from scripts
1 parent f4e4ea1 commit 4d78bd9

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

src/script/descriptor.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,80 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext
625625
return nullptr;
626626
}
627627

628+
std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
629+
{
630+
std::unique_ptr<PubkeyProvider> key_provider = MakeUnique<ConstPubkeyProvider>(pubkey);
631+
KeyOriginInfo info;
632+
if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
633+
return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(key_provider));
634+
}
635+
return key_provider;
636+
}
637+
638+
std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
639+
{
640+
std::vector<std::vector<unsigned char>> data;
641+
txnouttype txntype = Solver(script, data);
642+
643+
if (txntype == TX_PUBKEY) {
644+
CPubKey pubkey(data[0].begin(), data[0].end());
645+
if (pubkey.IsValid()) {
646+
return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKGetScript, "pk");
647+
}
648+
}
649+
if (txntype == TX_PUBKEYHASH) {
650+
uint160 hash(data[0]);
651+
CKeyID keyid(hash);
652+
CPubKey pubkey;
653+
if (provider.GetPubKey(keyid, pubkey)) {
654+
return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKHGetScript, "pkh");
655+
}
656+
}
657+
if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
658+
uint160 hash(data[0]);
659+
CKeyID keyid(hash);
660+
CPubKey pubkey;
661+
if (provider.GetPubKey(keyid, pubkey)) {
662+
return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2WPKHGetScript, "wpkh");
663+
}
664+
}
665+
if (txntype == TX_MULTISIG) {
666+
std::vector<std::unique_ptr<PubkeyProvider>> providers;
667+
for (size_t i = 1; i + 1 < data.size(); ++i) {
668+
CPubKey pubkey(data[i].begin(), data[i].end());
669+
providers.push_back(InferPubkey(pubkey, ctx, provider));
670+
}
671+
return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers));
672+
}
673+
if (txntype == TX_SCRIPTHASH && ctx == ParseScriptContext::TOP) {
674+
uint160 hash(data[0]);
675+
CScriptID scriptid(hash);
676+
CScript subscript;
677+
if (provider.GetCScript(scriptid, subscript)) {
678+
auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider);
679+
if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2SH, "sh");
680+
}
681+
}
682+
if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
683+
CScriptID scriptid;
684+
CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin());
685+
CScript subscript;
686+
if (provider.GetCScript(scriptid, subscript)) {
687+
auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);
688+
if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2WSH, "wsh");
689+
}
690+
}
691+
692+
CTxDestination dest;
693+
if (ExtractDestination(script, dest)) {
694+
if (GetScriptForDestination(dest) == script) {
695+
return MakeUnique<AddressDescriptor>(std::move(dest));
696+
}
697+
}
698+
699+
return MakeUnique<RawDescriptor>(script);
700+
}
701+
628702
} // namespace
629703

630704
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
634708
if (sp.size() == 0 && ret) return ret;
635709
return nullptr;
636710
}
711+
712+
std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
713+
{
714+
return InferScript(script, ParseScriptContext::TOP, provider);
715+
}

src/script/descriptor.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,20 @@ struct Descriptor {
5151
/** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */
5252
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out);
5353

54-
#endif // BITCOIN_SCRIPT_DESCRIPTOR_H
54+
/** Find a descriptor for the specified script, using information from provider where possible.
55+
*
56+
* A non-ranged descriptor which only generates the specified script will be returned in all
57+
* circumstances.
58+
*
59+
* For public keys with key origin information, this information will be preserved in the returned
60+
* descriptor.
61+
*
62+
* - If all information for solving `script` is present in `provider`, a descriptor will be returned
63+
* which is `IsSolvable()` and encapsulates said information.
64+
* - Failing that, if `script` corresponds to a known address type, an "addr()" descriptor will be
65+
* returned (which is not `IsSolvable()`).
66+
* - Failing that, a "raw()" descriptor is returned.
67+
*/
68+
std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider);
5569

70+
#endif // BITCOIN_SCRIPT_DESCRIPTOR_H

0 commit comments

Comments
 (0)