Page MenuHomePhabricator

descriptor.cpp
No OneTemporary

descriptor.cpp

// Copyright (c) 2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <script/descriptor.h>
#include <chainparams.h> // For Params()
#include <config.h>
#include <key_io.h>
#include <pubkey.h>
#include <script/script.h>
#include <script/standard.h>
#include <span.h>
#include <util/bip32.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <memory>
#include <string>
#include <vector>
namespace {
////////////////////////////////////////////////////////////////////////////
// Internal representation //
////////////////////////////////////////////////////////////////////////////
typedef std::vector<uint32_t> KeyPath;
/** Interface for public key objects in descriptors. */
struct PubkeyProvider {
virtual ~PubkeyProvider() = default;
/** Derive a public key. If key==nullptr, only info is desired. */
virtual bool GetPubKey(int pos, const SigningProvider &arg, CPubKey *key,
KeyOriginInfo &info) const = 0;
/** Whether this represent multiple public keys at different positions. */
virtual bool IsRange() const = 0;
/** Get the size of the generated public key(s) in bytes (33 or 65). */
virtual size_t GetSize() const = 0;
/** Get the descriptor string form. */
virtual std::string ToString() const = 0;
/**
* Get the descriptor string form including private data (if available in
* arg).
*/
virtual bool ToPrivateString(const SigningProvider &arg,
std::string &out) const = 0;
};
class OriginPubkeyProvider final : public PubkeyProvider {
KeyOriginInfo m_origin;
std::unique_ptr<PubkeyProvider> m_provider;
std::string OriginString() const {
return HexStr(std::begin(m_origin.fingerprint),
std::end(m_origin.fingerprint)) +
FormatHDKeypath(m_origin.path);
}
public:
OriginPubkeyProvider(KeyOriginInfo info,
std::unique_ptr<PubkeyProvider> provider)
: m_origin(std::move(info)), m_provider(std::move(provider)) {}
bool GetPubKey(int pos, const SigningProvider &arg, CPubKey *key,
KeyOriginInfo &info) const override {
if (!m_provider->GetPubKey(pos, arg, key, info)) {
return false;
}
std::copy(std::begin(m_origin.fingerprint),
std::end(m_origin.fingerprint), info.fingerprint);
info.path.insert(info.path.begin(), m_origin.path.begin(),
m_origin.path.end());
return true;
}
bool IsRange() const override { return m_provider->IsRange(); }
size_t GetSize() const override { return m_provider->GetSize(); }
std::string ToString() const override {
return "[" + OriginString() + "]" + m_provider->ToString();
}
bool ToPrivateString(const SigningProvider &arg,
std::string &ret) const override {
std::string sub;
if (!m_provider->ToPrivateString(arg, sub)) {
return false;
}
ret = "[" + OriginString() + "]" + std::move(sub);
return true;
}
};
/** An object representing a parsed constant public key in a descriptor. */
class ConstPubkeyProvider final : public PubkeyProvider {
CPubKey m_pubkey;
public:
ConstPubkeyProvider(const CPubKey &pubkey) : m_pubkey(pubkey) {}
bool GetPubKey(int pos, const SigningProvider &arg, CPubKey *key,
KeyOriginInfo &info) const override {
if (key) {
*key = m_pubkey;
}
info.path.clear();
CKeyID keyid = m_pubkey.GetID();
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint),
info.fingerprint);
return true;
}
bool IsRange() const override { return false; }
size_t GetSize() const override { return m_pubkey.size(); }
std::string ToString() const override {
return HexStr(m_pubkey.begin(), m_pubkey.end());
}
bool ToPrivateString(const SigningProvider &arg,
std::string &ret) const override {
CKey key;
if (!arg.GetKey(m_pubkey.GetID(), key)) {
return false;
}
ret = EncodeSecret(key);
return true;
}
};
enum class DeriveType {
NO,
UNHARDENED,
HARDENED,
};
/** An object representing a parsed extended public key in a descriptor. */
class BIP32PubkeyProvider final : public PubkeyProvider {
CExtPubKey m_extkey;
KeyPath m_path;
DeriveType m_derive;
bool GetExtKey(const SigningProvider &arg, CExtKey &ret) const {
CKey key;
if (!arg.GetKey(m_extkey.pubkey.GetID(), key)) {
return false;
}
ret.nDepth = m_extkey.nDepth;
std::copy(m_extkey.vchFingerprint,
m_extkey.vchFingerprint + sizeof(ret.vchFingerprint),
ret.vchFingerprint);
ret.nChild = m_extkey.nChild;
ret.chaincode = m_extkey.chaincode;
ret.key = key;
return true;
}
bool IsHardened() const {
if (m_derive == DeriveType::HARDENED) {
return true;
}
for (auto entry : m_path) {
if (entry >> 31) {
return true;
}
}
return false;
}
public:
BIP32PubkeyProvider(const CExtPubKey &extkey, KeyPath path,
DeriveType derive)
: m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
bool IsRange() const override { return m_derive != DeriveType::NO; }
size_t GetSize() const override { return 33; }
bool GetPubKey(int pos, const SigningProvider &arg, CPubKey *key,
KeyOriginInfo &info) const override {
if (key) {
if (IsHardened()) {
CExtKey extkey;
if (!GetExtKey(arg, extkey)) {
return false;
}
for (auto entry : m_path) {
extkey.Derive(extkey, entry);
}
if (m_derive == DeriveType::UNHARDENED) {
extkey.Derive(extkey, pos);
}
if (m_derive == DeriveType::HARDENED) {
extkey.Derive(extkey, pos | 0x80000000UL);
}
*key = extkey.Neuter().pubkey;
} else {
// TODO: optimize by caching
CExtPubKey extkey = m_extkey;
for (auto entry : m_path) {
extkey.Derive(extkey, entry);
}
if (m_derive == DeriveType::UNHARDENED) {
extkey.Derive(extkey, pos);
}
assert(m_derive != DeriveType::HARDENED);
*key = extkey.pubkey;
}
}
CKeyID keyid = m_extkey.pubkey.GetID();
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint),
info.fingerprint);
info.path = m_path;
if (m_derive == DeriveType::UNHARDENED) {
info.path.push_back(uint32_t(pos));
}
if (m_derive == DeriveType::HARDENED) {
info.path.push_back(uint32_t(pos) | 0x80000000L);
}
return true;
}
std::string ToString() const override {
std::string ret = EncodeExtPubKey(m_extkey) + FormatHDKeypath(m_path);
if (IsRange()) {
ret += "/*";
if (m_derive == DeriveType::HARDENED) {
ret += '\'';
}
}
return ret;
}
bool ToPrivateString(const SigningProvider &arg,
std::string &out) const override {
CExtKey key;
if (!GetExtKey(arg, key)) {
return false;
}
out = EncodeExtKey(key) + FormatHDKeypath(m_path);
if (IsRange()) {
out += "/*";
if (m_derive == DeriveType::HARDENED) {
out += '\'';
}
}
return true;
}
};
/** Base class for all Descriptor implementations. */
class DescriptorImpl : public Descriptor {
//! Public key arguments for this descriptor (size 1 for PK, PKH; any size
//! of Multisig).
const std::vector<std::unique_ptr<PubkeyProvider>> m_pubkey_args;
//! The sub-descriptor argument (nullptr for everything but SH).
const std::unique_ptr<DescriptorImpl> m_script_arg;
//! The string name of the descriptor function.
const std::string m_name;
protected:
//! Return a serialization of anything except pubkey and script arguments,
//! to be prepended to those.
virtual std::string ToStringExtra() const { return ""; }
/**
* A helper function to construct the scripts for this descriptor.
*
* This function is invoked once for every CScript produced by evaluating
* m_script_arg, or just once in case m_script_arg is nullptr.
* @param pubkeys The evaluations of the m_pubkey_args field.
* @param script The evaluation of m_script_arg (or nullptr when
m_script_arg is nullptr).
* @param out A FlatSigningProvider to put scripts or public keys in that
are necessary to the solver.
* The script arguments to this function are automatically
added, as is the origin info of the provided pubkeys.
* @return A vector with scriptPubKeys for this descriptor.
*/
virtual std::vector<CScript>
MakeScripts(const std::vector<CPubKey> &pubkeys, const CScript *script,
FlatSigningProvider &out) const = 0;
public:
DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys,
std::unique_ptr<DescriptorImpl> script,
const std::string &name)
: m_pubkey_args(std::move(pubkeys)), m_script_arg(std::move(script)),
m_name(name) {}
bool IsSolvable() const override {
if (m_script_arg) {
if (!m_script_arg->IsSolvable()) {
return false;
}
}
return true;
}
bool IsRange() const final {
for (const auto &pubkey : m_pubkey_args) {
if (pubkey->IsRange()) {
return true;
}
}
if (m_script_arg) {
if (m_script_arg->IsRange()) {
return true;
}
}
return false;
}
bool ToStringHelper(const SigningProvider *arg, std::string &out,
bool priv) const {
std::string extra = ToStringExtra();
size_t pos = extra.size() > 0 ? 1 : 0;
std::string ret = m_name + "(" + extra;
for (const auto &pubkey : m_pubkey_args) {
if (pos++) {
ret += ",";
}
std::string tmp;
if (priv) {
if (!pubkey->ToPrivateString(*arg, tmp)) {
return false;
}
} else {
tmp = pubkey->ToString();
}
ret += std::move(tmp);
}
if (m_script_arg) {
if (pos++) {
ret += ",";
}
std::string tmp;
if (!m_script_arg->ToStringHelper(arg, tmp, priv)) {
return false;
}
ret += std::move(tmp);
}
out = std::move(ret) + ")";
return true;
}
std::string ToString() const final {
std::string ret;
ToStringHelper(nullptr, ret, false);
return ret;
}
bool ToPrivateString(const SigningProvider &arg,
std::string &out) const override final {
return ToStringHelper(&arg, out, true);
}
bool ExpandHelper(int pos, const SigningProvider &arg,
Span<const uint8_t> *cache_read,
std::vector<CScript> &output_scripts,
FlatSigningProvider &out,
std::vector<uint8_t> *cache_write) const {
std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
entries.reserve(m_pubkey_args.size());
// Construct temporary data in `entries` and `subscripts`, to avoid
// producing output in case of failure.
for (const auto &p : m_pubkey_args) {
entries.emplace_back();
if (!p->GetPubKey(pos, arg,
cache_read ? nullptr : &entries.back().first,
entries.back().second)) {
return false;
}
if (cache_read) {
// Cached expanded public key exists, use it.
if (cache_read->size() == 0) {
return false;
}
bool compressed =
((*cache_read)[0] == 0x02 || (*cache_read)[0] == 0x03) &&
cache_read->size() >= 33;
bool uncompressed =
((*cache_read)[0] == 0x04) && cache_read->size() >= 65;
if (!(compressed || uncompressed)) {
return false;
}
CPubKey pubkey(cache_read->begin(),
cache_read->begin() + (compressed ? 33 : 65));
entries.back().first = pubkey;
*cache_read = cache_read->subspan(compressed ? 33 : 65);
}
if (cache_write) {
cache_write->insert(cache_write->end(),
entries.back().first.begin(),
entries.back().first.end());
}
}
std::vector<CScript> subscripts;
if (m_script_arg) {
FlatSigningProvider subprovider;
if (!m_script_arg->ExpandHelper(pos, arg, cache_read, subscripts,
subprovider, cache_write)) {
return false;
}
out = Merge(out, subprovider);
}
std::vector<CPubKey> pubkeys;
pubkeys.reserve(entries.size());
for (auto &entry : entries) {
pubkeys.push_back(entry.first);
out.origins.emplace(entry.first.GetID(), std::move(entry.second));
}
if (m_script_arg) {
for (const auto &subscript : subscripts) {
out.scripts.emplace(CScriptID(subscript), subscript);
std::vector<CScript> addscripts =
MakeScripts(pubkeys, &subscript, out);
for (auto &addscript : addscripts) {
output_scripts.push_back(std::move(addscript));
}
}
} else {
output_scripts = MakeScripts(pubkeys, nullptr, out);
}
return true;
}
bool Expand(int pos, const SigningProvider &provider,
std::vector<CScript> &output_scripts, FlatSigningProvider &out,
std::vector<uint8_t> *cache = nullptr) const final {
return ExpandHelper(pos, provider, nullptr, output_scripts, out, cache);
}
bool ExpandFromCache(int pos, const std::vector<uint8_t> &cache,
std::vector<CScript> &output_scripts,
FlatSigningProvider &out) const final {
Span<const uint8_t> span = MakeSpan(cache);
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts,
out, nullptr) &&
span.size() == 0;
}
};
/** Construct a vector with one element, which is moved into it. */
template <typename T> std::vector<T> Singleton(T elem) {
std::vector<T> ret;
ret.emplace_back(std::move(elem));
return ret;
}
/** A parsed addr(A) descriptor. */
class AddressDescriptor final : public DescriptorImpl {
const CTxDestination m_destination;
protected:
std::string ToStringExtra() const override {
return EncodeDestination(m_destination, GetConfig());
}
std::vector<CScript> MakeScripts(const std::vector<CPubKey> &,
const CScript *,
FlatSigningProvider &) const override {
return Singleton(GetScriptForDestination(m_destination));
}
public:
AddressDescriptor(CTxDestination destination)
: DescriptorImpl({}, {}, "addr"),
m_destination(std::move(destination)) {}
bool IsSolvable() const final { return false; }
};
/** A parsed raw(H) descriptor. */
class RawDescriptor final : public DescriptorImpl {
const CScript m_script;
protected:
std::string ToStringExtra() const override {
return HexStr(m_script.begin(), m_script.end());
}
std::vector<CScript> MakeScripts(const std::vector<CPubKey> &,
const CScript *,
FlatSigningProvider &) const override {
return Singleton(m_script);
}
public:
RawDescriptor(CScript script)
: DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
bool IsSolvable() const final { return false; }
};
/** A parsed pk(P) descriptor. */
class PKDescriptor final : public DescriptorImpl {
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey> &keys,
const CScript *,
FlatSigningProvider &) const override {
return Singleton(GetScriptForRawPubKey(keys[0]));
}
public:
PKDescriptor(std::unique_ptr<PubkeyProvider> prov)
: DescriptorImpl(Singleton(std::move(prov)), {}, "pk") {}
};
/** A parsed pkh(P) descriptor. */
class PKHDescriptor final : public DescriptorImpl {
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey> &keys,
const CScript *,
FlatSigningProvider &out) const override {
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
return Singleton(GetScriptForDestination(id));
}
public:
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov)
: DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {}
};
/** A parsed combo(P) descriptor. */
class ComboDescriptor final : public DescriptorImpl {
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey> &keys,
const CScript *,
FlatSigningProvider &out) const override {
std::vector<CScript> ret;
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
// P2PK
ret.emplace_back(GetScriptForRawPubKey(keys[0]));
// P2PKH
ret.emplace_back(GetScriptForDestination(id));
return ret;
}
public:
ComboDescriptor(std::unique_ptr<PubkeyProvider> prov)
: DescriptorImpl(Singleton(std::move(prov)), {}, "combo") {}
};
/** A parsed multi(...) descriptor. */
class MultisigDescriptor final : public DescriptorImpl {
const int m_threshold;
protected:
std::string ToStringExtra() const override {
return strprintf("%i", m_threshold);
}
std::vector<CScript> MakeScripts(const std::vector<CPubKey> &keys,
const CScript *,
FlatSigningProvider &) const override {
return Singleton(GetScriptForMultisig(m_threshold, keys));
}
public:
MultisigDescriptor(int threshold,
std::vector<std::unique_ptr<PubkeyProvider>> providers)
: DescriptorImpl(std::move(providers), {}, "multi"),
m_threshold(threshold) {}
};
/** A parsed sh(...) descriptor. */
class SHDescriptor final : public DescriptorImpl {
protected:
std::vector<CScript> MakeScripts(const std::vector<CPubKey> &,
const CScript *script,
FlatSigningProvider &) const override {
return Singleton(GetScriptForDestination(CScriptID(*script)));
}
public:
SHDescriptor(std::unique_ptr<DescriptorImpl> desc)
: DescriptorImpl({}, std::move(desc), "sh") {}
};
////////////////////////////////////////////////////////////////////////////
// Parser //
////////////////////////////////////////////////////////////////////////////
enum class ParseScriptContext {
TOP,
P2SH,
};
/**
* Parse a constant. If successful, sp is updated to skip the constant and
* return true.
*/
bool Const(const std::string &str, Span<const char> &sp) {
if ((size_t)sp.size() >= str.size() &&
std::equal(str.begin(), str.end(), sp.begin())) {
sp = sp.subspan(str.size());
return true;
}
return false;
}
/**
* Parse a function call. If successful, sp is updated to be the function's
* argument(s).
*/
bool Func(const std::string &str, Span<const char> &sp) {
if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' &&
sp[sp.size() - 1] == ')' &&
std::equal(str.begin(), str.end(), sp.begin())) {
sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2);
return true;
}
return false;
}
/** Return the expression that sp begins with, and update sp to skip it. */
Span<const char> Expr(Span<const char> &sp) {
int level = 0;
auto it = sp.begin();
while (it != sp.end()) {
if (*it == '(') {
++level;
} else if (level && *it == ')') {
--level;
} else if (level == 0 && (*it == ')' || *it == ',')) {
break;
}
++it;
}
Span<const char> ret = sp.first(it - sp.begin());
sp = sp.subspan(it - sp.begin());
return ret;
}
/** Split a string on every instance of sep, returning a vector. */
std::vector<Span<const char>> Split(const Span<const char> &sp, char sep) {
std::vector<Span<const char>> ret;
auto it = sp.begin();
auto start = it;
while (it != sp.end()) {
if (*it == sep) {
ret.emplace_back(start, it);
start = it + 1;
}
++it;
}
ret.emplace_back(start, it);
return ret;
}
/**
* Parse a key path, being passed a split list of elements (the first element is
* ignored).
*/
NODISCARD bool ParseKeyPath(const std::vector<Span<const char>> &split,
KeyPath &out) {
for (size_t i = 1; i < split.size(); ++i) {
Span<const char> elem = split[i];
bool hardened = false;
if (elem.size() > 0 &&
(elem[elem.size() - 1] == '\'' || elem[elem.size() - 1] == 'h')) {
elem = elem.first(elem.size() - 1);
hardened = true;
}
uint32_t p;
if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p) ||
p > 0x7FFFFFFFUL) {
return false;
}
out.push_back(p | (uint32_t(hardened) << 31));
}
return true;
}
/** Parse a public key that excludes origin information. */
std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char> &sp,
FlatSigningProvider &out) {
auto split = Split(sp, '/');
std::string str(split[0].begin(), split[0].end());
if (split.size() == 1) {
if (IsHex(str)) {
std::vector<uint8_t> data = ParseHex(str);
CPubKey pubkey(data);
if (pubkey.IsFullyValid()) {
return std::make_unique<ConstPubkeyProvider>(pubkey);
}
}
CKey key = DecodeSecret(str);
if (key.IsValid()) {
CPubKey pubkey = key.GetPubKey();
out.keys.emplace(pubkey.GetID(), key);
return std::make_unique<ConstPubkeyProvider>(pubkey);
}
}
CExtKey extkey = DecodeExtKey(str);
CExtPubKey extpubkey = DecodeExtPubKey(str);
if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) {
return nullptr;
}
KeyPath path;
DeriveType type = DeriveType::NO;
if (split.back() == MakeSpan("*").first(1)) {
split.pop_back();
type = DeriveType::UNHARDENED;
} else if (split.back() == MakeSpan("*'").first(2) ||
split.back() == MakeSpan("*h").first(2)) {
split.pop_back();
type = DeriveType::HARDENED;
}
if (!ParseKeyPath(split, path)) {
return nullptr;
}
if (extkey.key.IsValid()) {
extpubkey = extkey.Neuter();
out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
}
return std::make_unique<BIP32PubkeyProvider>(extpubkey, std::move(path),
type);
}
/** Parse a public key including origin information (if enabled). */
std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char> &sp,
FlatSigningProvider &out) {
auto origin_split = Split(sp, ']');
if (origin_split.size() > 2) {
return nullptr;
}
if (origin_split.size() == 1) {
return ParsePubkeyInner(origin_split[0], out);
}
if (origin_split[0].size() < 1 || origin_split[0][0] != '[') {
return nullptr;
}
auto slash_split = Split(origin_split[0].subspan(1), '/');
if (slash_split[0].size() != 8) {
return nullptr;
}
std::string fpr_hex =
std::string(slash_split[0].begin(), slash_split[0].end());
if (!IsHex(fpr_hex)) {
return nullptr;
}
auto fpr_bytes = ParseHex(fpr_hex);
KeyOriginInfo info;
static_assert(sizeof(info.fingerprint) == 4, "Fingerprint must be 4 bytes");
assert(fpr_bytes.size() == 4);
std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint);
if (!ParseKeyPath(slash_split, info.path)) {
return nullptr;
}
auto provider = ParsePubkeyInner(origin_split[1], out);
if (!provider) {
return nullptr;
}
return std::make_unique<OriginPubkeyProvider>(std::move(info),
std::move(provider));
}
/** Parse a script in a particular context. */
std::unique_ptr<DescriptorImpl> ParseScript(Span<const char> &sp,
ParseScriptContext ctx,
FlatSigningProvider &out) {
auto expr = Expr(sp);
if (Func("pk", expr)) {
auto pubkey = ParsePubkey(expr, out);
if (!pubkey) {
return nullptr;
}
return std::make_unique<PKDescriptor>(std::move(pubkey));
}
if (Func("pkh", expr)) {
auto pubkey = ParsePubkey(expr, out);
if (!pubkey) {
return nullptr;
}
return std::make_unique<PKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
auto pubkey = ParsePubkey(expr, out);
if (!pubkey) {
return nullptr;
}
return std::make_unique<ComboDescriptor>(std::move(pubkey));
}
if (Func("multi", expr)) {
auto threshold = Expr(expr);
uint32_t thres;
std::vector<std::unique_ptr<PubkeyProvider>> providers;
if (!ParseUInt32(std::string(threshold.begin(), threshold.end()),
&thres)) {
return nullptr;
}
size_t script_size = 0;
while (expr.size()) {
if (!Const(",", expr)) {
return nullptr;
}
auto arg = Expr(expr);
auto pk = ParsePubkey(arg, out);
if (!pk) {
return nullptr;
}
script_size += pk->GetSize() + 1;
providers.emplace_back(std::move(pk));
}
if (providers.size() < 1 || providers.size() > 16 || thres < 1 ||
thres > providers.size()) {
return nullptr;
}
if (ctx == ParseScriptContext::TOP) {
if (providers.size() > 3) {
// Not more than 3 pubkeys for raw multisig
return nullptr;
}
}
if (ctx == ParseScriptContext::P2SH) {
if (script_size + 3 > 520) {
// Enforce P2SH script size limit
return nullptr;
}
}
return std::make_unique<MultisigDescriptor>(thres,
std::move(providers));
}
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
auto desc = ParseScript(expr, ParseScriptContext::P2SH, out);
if (!desc || expr.size()) {
return nullptr;
}
return std::make_unique<SHDescriptor>(std::move(desc));
}
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
CTxDestination dest =
DecodeDestination(std::string(expr.begin(), expr.end()), Params());
if (!IsValidDestination(dest)) {
return nullptr;
}
return std::make_unique<AddressDescriptor>(std::move(dest));
}
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
std::string str(expr.begin(), expr.end());
if (!IsHex(str)) {
return nullptr;
}
auto bytes = ParseHex(str);
return std::make_unique<RawDescriptor>(
CScript(bytes.begin(), bytes.end()));
}
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<DescriptorImpl> 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<PKDescriptor>(
InferPubkey(pubkey, ctx, provider));
}
}
if (txntype == TX_PUBKEYHASH) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
if (provider.GetPubKey(keyid, pubkey)) {
return std::make_unique<PKHDescriptor>(
InferPubkey(pubkey, ctx, provider));
}
}
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<SHDescriptor>(std::move(sub));
}
}
}
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,
FlatSigningProvider &out) {
Span<const char> sp(descriptor.data(), descriptor.size());
auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
if (sp.size() == 0 && ret) {
return std::unique_ptr<Descriptor>(std::move(ret));
}
return nullptr;
}
std::unique_ptr<Descriptor> InferDescriptor(const CScript &script,
const SigningProvider &provider) {
return InferScript(script, ParseScriptContext::TOP, provider);
}

File Metadata

Mime Type
text/x-c++
Expires
Mon, Nov 25, 07:59 (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4584210
Default Alt Text
descriptor.cpp (31 KB)

Event Timeline