diff --git a/src/script/descriptor.h b/src/script/descriptor.h --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -72,4 +72,24 @@ std::unique_ptr 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 InferDescriptor(const CScript &script, + const SigningProvider &provider); + #endif // BITCOIN_SCRIPT_DESCRIPTOR_H diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -787,6 +787,73 @@ return nullptr; } +static std::unique_ptr +InferPubkey(const CPubKey &pubkey, ParseScriptContext, + const SigningProvider &provider) { + auto key_provider = std::make_unique(pubkey); + KeyOriginInfo info; + if (provider.GetKeyOrigin(pubkey.GetID(), info)) { + return std::make_unique(std::move(info), + std::move(key_provider)); + } + return key_provider; +} + +std::unique_ptr InferScript(const CScript &script, + ParseScriptContext ctx, + const SigningProvider &provider) { + std::vector> 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( + 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( + InferPubkey(pubkey, ctx, provider), P2PKHGetScript, "pkh"); + } + } + if (txntype == TX_MULTISIG) { + std::vector> 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((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(std::move(sub), + ConvertP2SH, "sh"); + } + } + } + + CTxDestination dest; + if (ExtractDestination(script, dest)) { + if (GetScriptForDestination(dest) == script) { + return std::make_unique(std::move(dest)); + } + } + + return std::make_unique(script); +} + } // namespace std::unique_ptr Parse(const std::string &descriptor, @@ -798,3 +865,8 @@ } return nullptr; } + +std::unique_ptr InferDescriptor(const CScript &script, + const SigningProvider &provider) { + return InferScript(script, ParseScriptContext::TOP, provider); +}