mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-18 21:35:13 +01:00
descriptors: Change ParseScript to return vector of descriptors
To prepare for returning multipath descriptors which will be a shorthand for specifying multiple descriptors, change ParseScript's signature to return a vector.
This commit is contained in:
parent
0d55deae15
commit
a5f39b1034
@ -1446,14 +1446,15 @@ enum class ParseScriptContext {
|
||||
}
|
||||
|
||||
/** Parse a public key that excludes origin information. */
|
||||
std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, bool& apostrophe, std::string& error)
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, bool& apostrophe, std::string& error)
|
||||
{
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> ret;
|
||||
bool permit_uncompressed = ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH;
|
||||
auto split = Split(sp, '/');
|
||||
std::string str(split[0].begin(), split[0].end());
|
||||
if (str.size() == 0) {
|
||||
error = "No key provided";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (split.size() == 1) {
|
||||
if (IsHex(str)) {
|
||||
@ -1461,35 +1462,38 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
|
||||
CPubKey pubkey(data);
|
||||
if (pubkey.IsValid() && !pubkey.IsValidNonHybrid()) {
|
||||
error = "Hybrid public keys are not allowed";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (pubkey.IsFullyValid()) {
|
||||
if (permit_uncompressed || pubkey.IsCompressed()) {
|
||||
return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, false);
|
||||
ret.emplace_back(std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, false));
|
||||
return ret;
|
||||
} else {
|
||||
error = "Uncompressed keys are not allowed";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
} else if (data.size() == 32 && ctx == ParseScriptContext::P2TR) {
|
||||
unsigned char fullkey[33] = {0x02};
|
||||
std::copy(data.begin(), data.end(), fullkey + 1);
|
||||
pubkey.Set(std::begin(fullkey), std::end(fullkey));
|
||||
if (pubkey.IsFullyValid()) {
|
||||
return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, true);
|
||||
ret.emplace_back(std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, true));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
error = strprintf("Pubkey '%s' is invalid", str);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
CKey key = DecodeSecret(str);
|
||||
if (key.IsValid()) {
|
||||
if (permit_uncompressed || key.IsCompressed()) {
|
||||
CPubKey pubkey = key.GetPubKey();
|
||||
out.keys.emplace(pubkey.GetID(), key);
|
||||
return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, ctx == ParseScriptContext::P2TR);
|
||||
ret.emplace_back(std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, ctx == ParseScriptContext::P2TR));
|
||||
return ret;
|
||||
} else {
|
||||
error = "Uncompressed keys are not allowed";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1497,7 +1501,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
|
||||
CExtPubKey extpubkey = DecodeExtPubKey(str);
|
||||
if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) {
|
||||
error = strprintf("key '%s' is not valid", str);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
KeyPath path;
|
||||
DeriveType type = DeriveType::NO;
|
||||
@ -1509,21 +1513,23 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
|
||||
split.pop_back();
|
||||
type = DeriveType::HARDENED;
|
||||
}
|
||||
if (!ParseKeyPath(split, path, apostrophe, error)) return nullptr;
|
||||
if (!ParseKeyPath(split, path, apostrophe, error)) return {};
|
||||
if (extkey.key.IsValid()) {
|
||||
extpubkey = extkey.Neuter();
|
||||
out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
|
||||
}
|
||||
return std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type, apostrophe);
|
||||
ret.emplace_back(std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type, apostrophe));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Parse a public key including origin information (if enabled). */
|
||||
std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey(uint32_t key_exp_index, const Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
|
||||
{
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> ret;
|
||||
auto origin_split = Split(sp, ']');
|
||||
if (origin_split.size() > 2) {
|
||||
error = "Multiple ']' characters found for a single pubkey";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
// This is set if either the origin or path suffix contains a hardened derivation.
|
||||
bool apostrophe = false;
|
||||
@ -1533,27 +1539,31 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c
|
||||
if (origin_split[0].empty() || origin_split[0][0] != '[') {
|
||||
error = strprintf("Key origin start '[ character expected but not found, got '%c' instead",
|
||||
origin_split[0].empty() ? /** empty, implies split char */ ']' : origin_split[0][0]);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
auto slash_split = Split(origin_split[0].subspan(1), '/');
|
||||
if (slash_split[0].size() != 8) {
|
||||
error = strprintf("Fingerprint is not 4 bytes (%u characters instead of 8 characters)", slash_split[0].size());
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
std::string fpr_hex = std::string(slash_split[0].begin(), slash_split[0].end());
|
||||
if (!IsHex(fpr_hex)) {
|
||||
error = strprintf("Fingerprint '%s' is not hex", fpr_hex);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
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, apostrophe, error)) return nullptr;
|
||||
auto provider = ParsePubkeyInner(key_exp_index, origin_split[1], ctx, out, apostrophe, error);
|
||||
if (!provider) return nullptr;
|
||||
return std::make_unique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider), apostrophe);
|
||||
if (!ParseKeyPath(slash_split, info.path, apostrophe, error)) return {};
|
||||
auto providers = ParsePubkeyInner(key_exp_index, origin_split[1], ctx, out, apostrophe, error);
|
||||
if (providers.empty()) return {};
|
||||
ret.reserve(providers.size());
|
||||
for (auto& prov : providers) {
|
||||
ret.emplace_back(std::make_unique<OriginPubkeyProvider>(key_exp_index, info, std::move(prov), apostrophe));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext ctx, const SigningProvider& provider)
|
||||
@ -1595,8 +1605,8 @@ struct KeyParser {
|
||||
FlatSigningProvider* m_out;
|
||||
//! Must not be nullptr if parsing from Script.
|
||||
const SigningProvider* m_in;
|
||||
//! List of keys contained in the Miniscript.
|
||||
mutable std::vector<std::unique_ptr<PubkeyProvider>> m_keys;
|
||||
//! List of multipath expanded keys contained in the Miniscript.
|
||||
mutable std::vector<std::vector<std::unique_ptr<PubkeyProvider>>> m_keys;
|
||||
//! Used to detect key parsing errors within a Miniscript.
|
||||
mutable std::string m_key_parsing_error;
|
||||
//! The script context we're operating within (Tapscript or P2WSH).
|
||||
@ -1609,7 +1619,7 @@ struct KeyParser {
|
||||
: m_out(out), m_in(in), m_script_ctx(ctx), m_offset(offset) {}
|
||||
|
||||
bool KeyCompare(const Key& a, const Key& b) const {
|
||||
return *m_keys.at(a) < *m_keys.at(b);
|
||||
return *m_keys.at(a).at(0) < *m_keys.at(b).at(0);
|
||||
}
|
||||
|
||||
ParseScriptContext ParseContext() const {
|
||||
@ -1625,14 +1635,14 @@ struct KeyParser {
|
||||
assert(m_out);
|
||||
Key key = m_keys.size();
|
||||
auto pk = ParsePubkey(m_offset + key, {&*begin, &*end}, ParseContext(), *m_out, m_key_parsing_error);
|
||||
if (!pk) return {};
|
||||
m_keys.push_back(std::move(pk));
|
||||
if (pk.empty()) return {};
|
||||
m_keys.emplace_back(std::move(pk));
|
||||
return key;
|
||||
}
|
||||
|
||||
std::optional<std::string> ToString(const Key& key) const
|
||||
{
|
||||
return m_keys.at(key)->ToString();
|
||||
return m_keys.at(key).at(0)->ToString();
|
||||
}
|
||||
|
||||
template<typename I> std::optional<Key> FromPKBytes(I begin, I end) const
|
||||
@ -1643,13 +1653,15 @@ struct KeyParser {
|
||||
XOnlyPubKey pubkey;
|
||||
std::copy(begin, end, pubkey.begin());
|
||||
if (auto pubkey_provider = InferPubkey(pubkey.GetEvenCorrespondingCPubKey(), ParseContext(), *m_in)) {
|
||||
m_keys.push_back(std::move(pubkey_provider));
|
||||
m_keys.emplace_back();
|
||||
m_keys.back().push_back(std::move(pubkey_provider));
|
||||
return key;
|
||||
}
|
||||
} else if (!miniscript::IsTapscript(m_script_ctx)) {
|
||||
CPubKey pubkey(begin, end);
|
||||
if (auto pubkey_provider = InferPubkey(pubkey, ParseContext(), *m_in)) {
|
||||
m_keys.push_back(std::move(pubkey_provider));
|
||||
m_keys.emplace_back();
|
||||
m_keys.back().push_back(std::move(pubkey_provider));
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@ -1667,7 +1679,8 @@ struct KeyParser {
|
||||
if (m_in->GetPubKey(keyid, pubkey)) {
|
||||
if (auto pubkey_provider = InferPubkey(pubkey, ParseContext(), *m_in)) {
|
||||
Key key = m_keys.size();
|
||||
m_keys.push_back(std::move(pubkey_provider));
|
||||
m_keys.emplace_back();
|
||||
m_keys.back().push_back(std::move(pubkey_provider));
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@ -1681,44 +1694,54 @@ struct KeyParser {
|
||||
|
||||
/** Parse a script in a particular context. */
|
||||
// NOLINTNEXTLINE(misc-no-recursion)
|
||||
std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
|
||||
std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index, Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
|
||||
{
|
||||
using namespace script;
|
||||
|
||||
std::vector<std::unique_ptr<DescriptorImpl>> ret;
|
||||
auto expr = Expr(sp);
|
||||
if (Func("pk", expr)) {
|
||||
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
|
||||
if (!pubkey) {
|
||||
auto pubkeys = ParsePubkey(key_exp_index, expr, ctx, out, error);
|
||||
if (pubkeys.empty()) {
|
||||
error = strprintf("pk(): %s", error);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
++key_exp_index;
|
||||
return std::make_unique<PKDescriptor>(std::move(pubkey), ctx == ParseScriptContext::P2TR);
|
||||
for (auto& pubkey : pubkeys) {
|
||||
ret.emplace_back(std::make_unique<PKDescriptor>(std::move(pubkey), ctx == ParseScriptContext::P2TR));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && Func("pkh", expr)) {
|
||||
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
|
||||
if (!pubkey) {
|
||||
auto pubkeys = ParsePubkey(key_exp_index, expr, ctx, out, error);
|
||||
if (pubkeys.empty()) {
|
||||
error = strprintf("pkh(): %s", error);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
++key_exp_index;
|
||||
return std::make_unique<PKHDescriptor>(std::move(pubkey));
|
||||
for (auto& pubkey : pubkeys) {
|
||||
ret.emplace_back(std::make_unique<PKHDescriptor>(std::move(pubkey)));
|
||||
}
|
||||
return ret;
|
||||
} else if (ctx != ParseScriptContext::P2TR && Func("pkh", expr)) {
|
||||
// Under Taproot, always the Miniscript parser deal with it.
|
||||
error = "Can only have pkh at top level, in sh(), wsh(), or in tr()";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
|
||||
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
|
||||
if (!pubkey) {
|
||||
auto pubkeys = ParsePubkey(key_exp_index, expr, ctx, out, error);
|
||||
if (pubkeys.empty()) {
|
||||
error = strprintf("combo(): %s", error);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
++key_exp_index;
|
||||
return std::make_unique<ComboDescriptor>(std::move(pubkey));
|
||||
for (auto& pubkey : pubkeys) {
|
||||
ret.emplace_back(std::make_unique<ComboDescriptor>(std::move(pubkey)));
|
||||
}
|
||||
return ret;
|
||||
} else if (Func("combo", expr)) {
|
||||
error = "Can only have combo() at top level";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
const bool multi = Func("multi", expr);
|
||||
const bool sortedmulti = !multi && Func("sortedmulti", expr);
|
||||
@ -1728,118 +1751,157 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
||||
(ctx == ParseScriptContext::P2TR && (multi_a || sortedmulti_a))) {
|
||||
auto threshold = Expr(expr);
|
||||
uint32_t thres;
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> providers;
|
||||
std::vector<std::vector<std::unique_ptr<PubkeyProvider>>> providers; // List of multipath expanded pubkeys
|
||||
if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) {
|
||||
error = strprintf("Multi threshold '%s' is not valid", std::string(threshold.begin(), threshold.end()));
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
size_t script_size = 0;
|
||||
size_t max_providers_len = 0;
|
||||
while (expr.size()) {
|
||||
if (!Const(",", expr)) {
|
||||
error = strprintf("Multi: expected ',', got '%c'", expr[0]);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
auto arg = Expr(expr);
|
||||
auto pk = ParsePubkey(key_exp_index, arg, ctx, out, error);
|
||||
if (!pk) {
|
||||
auto pks = ParsePubkey(key_exp_index, arg, ctx, out, error);
|
||||
if (pks.empty()) {
|
||||
error = strprintf("Multi: %s", error);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
script_size += pk->GetSize() + 1;
|
||||
providers.emplace_back(std::move(pk));
|
||||
script_size += pks.at(0)->GetSize() + 1;
|
||||
max_providers_len = std::max(max_providers_len, pks.size());
|
||||
providers.emplace_back(std::move(pks));
|
||||
key_exp_index++;
|
||||
}
|
||||
if ((multi || sortedmulti) && (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTISIG)) {
|
||||
error = strprintf("Cannot have %u keys in multisig; must have between 1 and %d keys, inclusive", providers.size(), MAX_PUBKEYS_PER_MULTISIG);
|
||||
return nullptr;
|
||||
return {};
|
||||
} else if ((multi_a || sortedmulti_a) && (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTI_A)) {
|
||||
error = strprintf("Cannot have %u keys in multi_a; must have between 1 and %d keys, inclusive", providers.size(), MAX_PUBKEYS_PER_MULTI_A);
|
||||
return nullptr;
|
||||
return {};
|
||||
} else if (thres < 1) {
|
||||
error = strprintf("Multisig threshold cannot be %d, must be at least 1", thres);
|
||||
return nullptr;
|
||||
return {};
|
||||
} else if (thres > providers.size()) {
|
||||
error = strprintf("Multisig threshold cannot be larger than the number of keys; threshold is %d but only %u keys specified", thres, providers.size());
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP) {
|
||||
if (providers.size() > 3) {
|
||||
error = strprintf("Cannot have %u pubkeys in bare multisig; only at most 3 pubkeys", providers.size());
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (ctx == ParseScriptContext::P2SH) {
|
||||
// This limits the maximum number of compressed pubkeys to 15.
|
||||
if (script_size + 3 > MAX_SCRIPT_ELEMENT_SIZE) {
|
||||
error = strprintf("P2SH script is too large, %d bytes is larger than %d bytes", script_size + 3, MAX_SCRIPT_ELEMENT_SIZE);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (multi || sortedmulti) {
|
||||
return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sortedmulti);
|
||||
} else {
|
||||
return std::make_unique<MultiADescriptor>(thres, std::move(providers), sortedmulti_a);
|
||||
|
||||
// Make sure all vecs are of the same length, or exactly length 1
|
||||
// For length 1 vectors, clone key providers until vector is the same length
|
||||
for (auto& vec : providers) {
|
||||
if (vec.size() == 1) {
|
||||
for (size_t i = 1; i < max_providers_len; ++i) {
|
||||
vec.emplace_back(vec.at(0)->Clone());
|
||||
}
|
||||
} else if (vec.size() != max_providers_len) {
|
||||
error = strprintf("multi(): Multipath derivation paths have mismatched lengths");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Build the final descriptors vector
|
||||
for (size_t i = 0; i < max_providers_len; ++i) {
|
||||
// Build final pubkeys vectors by retrieving the i'th subscript for each vector in subscripts
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> pubs;
|
||||
pubs.reserve(providers.size());
|
||||
for (auto& pub : providers) {
|
||||
pubs.emplace_back(std::move(pub.at(i)));
|
||||
}
|
||||
if (multi || sortedmulti) {
|
||||
ret.emplace_back(std::make_unique<MultisigDescriptor>(thres, std::move(pubs), sortedmulti));
|
||||
} else {
|
||||
ret.emplace_back(std::make_unique<MultiADescriptor>(thres, std::move(pubs), sortedmulti_a));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} else if (multi || sortedmulti) {
|
||||
error = "Can only have multi/sortedmulti at top level, in sh(), or in wsh()";
|
||||
return nullptr;
|
||||
return {};
|
||||
} else if (multi_a || sortedmulti_a) {
|
||||
error = "Can only have multi_a/sortedmulti_a inside tr()";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wpkh", expr)) {
|
||||
auto pubkey = ParsePubkey(key_exp_index, expr, ParseScriptContext::P2WPKH, out, error);
|
||||
if (!pubkey) {
|
||||
auto pubkeys = ParsePubkey(key_exp_index, expr, ParseScriptContext::P2WPKH, out, error);
|
||||
if (pubkeys.empty()) {
|
||||
error = strprintf("wpkh(): %s", error);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
key_exp_index++;
|
||||
return std::make_unique<WPKHDescriptor>(std::move(pubkey));
|
||||
for (auto& pubkey : pubkeys) {
|
||||
ret.emplace_back(std::make_unique<WPKHDescriptor>(std::move(pubkey)));
|
||||
}
|
||||
return ret;
|
||||
} else if (Func("wpkh", expr)) {
|
||||
error = "Can only have wpkh() at top level or inside sh()";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
|
||||
auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2SH, out, error);
|
||||
if (!desc || expr.size()) return nullptr;
|
||||
return std::make_unique<SHDescriptor>(std::move(desc));
|
||||
auto descs = ParseScript(key_exp_index, expr, ParseScriptContext::P2SH, out, error);
|
||||
if (descs.empty() || expr.size()) return {};
|
||||
std::vector<std::unique_ptr<DescriptorImpl>> ret;
|
||||
ret.reserve(descs.size());
|
||||
for (auto& desc : descs) {
|
||||
ret.push_back(std::make_unique<SHDescriptor>(std::move(desc)));
|
||||
}
|
||||
return ret;
|
||||
} else if (Func("sh", expr)) {
|
||||
error = "Can only have sh() at top level";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wsh", expr)) {
|
||||
auto desc = ParseScript(key_exp_index, expr, ParseScriptContext::P2WSH, out, error);
|
||||
if (!desc || expr.size()) return nullptr;
|
||||
return std::make_unique<WSHDescriptor>(std::move(desc));
|
||||
auto descs = ParseScript(key_exp_index, expr, ParseScriptContext::P2WSH, out, error);
|
||||
if (descs.empty() || expr.size()) return {};
|
||||
for (auto& desc : descs) {
|
||||
ret.emplace_back(std::make_unique<WSHDescriptor>(std::move(desc)));
|
||||
}
|
||||
return ret;
|
||||
} else if (Func("wsh", expr)) {
|
||||
error = "Can only have wsh() at top level or inside sh()";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
|
||||
CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end()));
|
||||
if (!IsValidDestination(dest)) {
|
||||
error = "Address is not valid";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
return std::make_unique<AddressDescriptor>(std::move(dest));
|
||||
ret.emplace_back(std::make_unique<AddressDescriptor>(std::move(dest)));
|
||||
return ret;
|
||||
} else if (Func("addr", expr)) {
|
||||
error = "Can only have addr() at top level";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP && Func("tr", expr)) {
|
||||
auto arg = Expr(expr);
|
||||
auto internal_key = ParsePubkey(key_exp_index, arg, ParseScriptContext::P2TR, out, error);
|
||||
if (!internal_key) {
|
||||
auto internal_keys = ParsePubkey(key_exp_index, arg, ParseScriptContext::P2TR, out, error);
|
||||
if (internal_keys.empty()) {
|
||||
error = strprintf("tr(): %s", error);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
size_t max_providers_len = internal_keys.size();
|
||||
++key_exp_index;
|
||||
std::vector<std::unique_ptr<DescriptorImpl>> subscripts; //!< list of script subexpressions
|
||||
std::vector<std::vector<std::unique_ptr<DescriptorImpl>>> subscripts; //!< list of multipath expanded script subexpressions
|
||||
std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts)
|
||||
if (expr.size()) {
|
||||
if (!Const(",", expr)) {
|
||||
error = strprintf("tr: expected ',', got '%c'", expr[0]);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
/** The path from the top of the tree to what we're currently processing.
|
||||
* branches[i] == false: left branch in the i'th step from the top; true: right branch.
|
||||
@ -1853,19 +1915,20 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
||||
branches.push_back(false); // new left branch
|
||||
if (branches.size() > TAPROOT_CONTROL_MAX_NODE_COUNT) {
|
||||
error = strprintf("tr() supports at most %i nesting levels", TAPROOT_CONTROL_MAX_NODE_COUNT);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
// Process the actual script expression.
|
||||
auto sarg = Expr(expr);
|
||||
subscripts.emplace_back(ParseScript(key_exp_index, sarg, ParseScriptContext::P2TR, out, error));
|
||||
if (!subscripts.back()) return nullptr;
|
||||
if (subscripts.back().empty()) return {};
|
||||
max_providers_len = std::max(max_providers_len, subscripts.back().size());
|
||||
depths.push_back(branches.size());
|
||||
// Process closing braces; one is expected for every right branch we were in.
|
||||
while (branches.size() && branches.back()) {
|
||||
if (!Const("}", expr)) {
|
||||
error = strprintf("tr(): expected '}' after script expression");
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
branches.pop_back(); // move up one level after encountering '}'
|
||||
}
|
||||
@ -1873,7 +1936,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
||||
if (branches.size() && !branches.back()) {
|
||||
if (!Const(",", expr)) {
|
||||
error = strprintf("tr(): expected ',' after script expression");
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
branches.back() = true; // And now we're in a right branch.
|
||||
}
|
||||
@ -1881,40 +1944,82 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
||||
// After we've explored a whole tree, we must be at the end of the expression.
|
||||
if (expr.size()) {
|
||||
error = strprintf("tr(): expected ')' after script expression");
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
assert(TaprootBuilder::ValidDepths(depths));
|
||||
return std::make_unique<TRDescriptor>(std::move(internal_key), std::move(subscripts), std::move(depths));
|
||||
|
||||
// Make sure all vecs are of the same length, or exactly length 1
|
||||
// For length 1 vectors, clone subdescs until vector is the same length
|
||||
for (auto& vec : subscripts) {
|
||||
if (vec.size() == 1) {
|
||||
for (size_t i = 1; i < max_providers_len; ++i) {
|
||||
vec.emplace_back(vec.at(0)->Clone());
|
||||
}
|
||||
} else if (vec.size() != max_providers_len) {
|
||||
error = strprintf("tr(): Multipath subscripts have mismatched lengths");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (internal_keys.size() > 1 && internal_keys.size() != max_providers_len) {
|
||||
error = strprintf("tr(): Multipath internal key mismatches multipath subscripts lengths");
|
||||
return {};
|
||||
}
|
||||
|
||||
while (internal_keys.size() < max_providers_len) {
|
||||
internal_keys.emplace_back(internal_keys.at(0)->Clone());
|
||||
}
|
||||
|
||||
// Build the final descriptors vector
|
||||
for (size_t i = 0; i < max_providers_len; ++i) {
|
||||
// Build final subscripts vectors by retrieving the i'th subscript for each vector in subscripts
|
||||
std::vector<std::unique_ptr<DescriptorImpl>> this_subs;
|
||||
this_subs.reserve(subscripts.size());
|
||||
for (auto& subs : subscripts) {
|
||||
this_subs.emplace_back(std::move(subs.at(i)));
|
||||
}
|
||||
ret.emplace_back(std::make_unique<TRDescriptor>(std::move(internal_keys.at(i)), std::move(this_subs), depths));
|
||||
}
|
||||
return ret;
|
||||
|
||||
|
||||
} else if (Func("tr", expr)) {
|
||||
error = "Can only have tr at top level";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP && Func("rawtr", expr)) {
|
||||
auto arg = Expr(expr);
|
||||
if (expr.size()) {
|
||||
error = strprintf("rawtr(): only one key expected.");
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
auto output_keys = ParsePubkey(key_exp_index, arg, ParseScriptContext::P2TR, out, error);
|
||||
if (output_keys.empty()) {
|
||||
error = strprintf("rawtr(): %s", error);
|
||||
return {};
|
||||
}
|
||||
auto output_key = ParsePubkey(key_exp_index, arg, ParseScriptContext::P2TR, out, error);
|
||||
if (!output_key) return nullptr;
|
||||
++key_exp_index;
|
||||
return std::make_unique<RawTRDescriptor>(std::move(output_key));
|
||||
for (auto& pubkey : output_keys) {
|
||||
ret.emplace_back(std::make_unique<RawTRDescriptor>(std::move(pubkey)));
|
||||
}
|
||||
return ret;
|
||||
} else if (Func("rawtr", expr)) {
|
||||
error = "Can only have rawtr at top level";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
|
||||
std::string str(expr.begin(), expr.end());
|
||||
if (!IsHex(str)) {
|
||||
error = "Raw script is not hex";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
auto bytes = ParseHex(str);
|
||||
return std::make_unique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
|
||||
ret.emplace_back(std::make_unique<RawDescriptor>(CScript(bytes.begin(), bytes.end())));
|
||||
return ret;
|
||||
} else if (Func("raw", expr)) {
|
||||
error = "Can only have raw() at top level";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
// Process miniscript expressions.
|
||||
{
|
||||
@ -1923,12 +2028,12 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
||||
auto node = miniscript::FromString(std::string(expr.begin(), expr.end()), parser);
|
||||
if (parser.m_key_parsing_error != "") {
|
||||
error = std::move(parser.m_key_parsing_error);
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (node) {
|
||||
if (ctx != ParseScriptContext::P2WSH && ctx != ParseScriptContext::P2TR) {
|
||||
error = "Miniscript expressions can only be used in wsh or tr.";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (!node->IsSane() || node->IsNotSatisfiable()) {
|
||||
// Try to find the first insane sub for better error reporting.
|
||||
@ -1953,24 +2058,52 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
|
||||
} else {
|
||||
error += " is not satisfiable";
|
||||
}
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
// A signature check is required for a miniscript to be sane. Therefore no sane miniscript
|
||||
// may have an empty list of public keys.
|
||||
CHECK_NONFATAL(!parser.m_keys.empty());
|
||||
key_exp_index += parser.m_keys.size();
|
||||
return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node));
|
||||
// Make sure all vecs are of the same length, or exactly length 1
|
||||
// For length 1 vectors, clone subdescs until vector is the same length
|
||||
size_t num_multipath = std::max_element(parser.m_keys.begin(), parser.m_keys.end(),
|
||||
[](const std::vector<std::unique_ptr<PubkeyProvider>>& a, const std::vector<std::unique_ptr<PubkeyProvider>>& b) {
|
||||
return a.size() < b.size();
|
||||
})->size();
|
||||
|
||||
for (auto& vec : parser.m_keys) {
|
||||
if (vec.size() == 1) {
|
||||
for (size_t i = 1; i < num_multipath; ++i) {
|
||||
vec.emplace_back(vec.at(0)->Clone());
|
||||
}
|
||||
} else if (vec.size() != num_multipath) {
|
||||
error = strprintf("Miniscript: Multipath derivation paths have mismatched lengths");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Build the final descriptors vector
|
||||
for (size_t i = 0; i < num_multipath; ++i) {
|
||||
// Build final pubkeys vectors by retrieving the i'th subscript for each vector in subscripts
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> pubs;
|
||||
pubs.reserve(parser.m_keys.size());
|
||||
for (auto& pub : parser.m_keys) {
|
||||
pubs.emplace_back(std::move(pub.at(i)));
|
||||
}
|
||||
ret.emplace_back(std::make_unique<MiniscriptDescriptor>(std::move(pubs), node));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (ctx == ParseScriptContext::P2SH) {
|
||||
error = "A function is needed within P2SH";
|
||||
return nullptr;
|
||||
return {};
|
||||
} else if (ctx == ParseScriptContext::P2WSH) {
|
||||
error = "A function is needed within P2WSH";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
error = strprintf("'%s' is not a valid descriptor function", std::string(expr.begin(), expr.end()));
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<DescriptorImpl> InferMultiA(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
|
||||
@ -2108,7 +2241,12 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
|
||||
KeyParser parser(/* out = */nullptr, /* in = */&provider, /* ctx = */script_ctx);
|
||||
auto node = miniscript::FromScript(script, parser);
|
||||
if (node && node->IsSane()) {
|
||||
return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node));
|
||||
std::vector<std::unique_ptr<PubkeyProvider>> keys;
|
||||
keys.reserve(parser.m_keys.size());
|
||||
for (auto& key : parser.m_keys) {
|
||||
keys.emplace_back(std::move(key.at(0)));
|
||||
}
|
||||
return std::make_unique<MiniscriptDescriptor>(std::move(keys), std::move(node));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2169,7 +2307,7 @@ std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProv
|
||||
if (!CheckChecksum(sp, require_checksum, error)) return nullptr;
|
||||
uint32_t key_exp_index = 0;
|
||||
auto ret = ParseScript(key_exp_index, sp, ParseScriptContext::TOP, out, error);
|
||||
if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
|
||||
if (sp.size() == 0 && !ret.empty()) return std::unique_ptr<Descriptor>(std::move(ret.at(0)));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user