aboutsummaryrefslogtreecommitdiff
path: root/src/script/descriptor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/descriptor.cpp')
-rw-r--r--src/script/descriptor.cpp418
1 files changed, 372 insertions, 46 deletions
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 798c4b3ea0..864eb8864f 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -6,6 +6,7 @@
#include <key_io.h>
#include <pubkey.h>
+#include <script/miniscript.h>
#include <script/script.h>
#include <script/standard.h>
@@ -161,6 +162,20 @@ public:
virtual ~PubkeyProvider() = default;
+ /** Compare two public keys represented by this provider.
+ * Used by the Miniscript descriptors to check for duplicate keys in the script.
+ */
+ bool operator<(PubkeyProvider& other) const {
+ CPubKey a, b;
+ SigningProvider dummy;
+ KeyOriginInfo dummy_info;
+
+ GetPubKey(0, dummy, a, dummy_info);
+ other.GetPubKey(0, dummy, b, dummy_info);
+
+ return a < b;
+ }
+
/** Derive a public key.
* read_cache is the cache to read keys from (if not nullptr)
* write_cache is the cache to write keys to (if not nullptr)
@@ -313,7 +328,7 @@ class BIP32PubkeyProvider final : public PubkeyProvider
{
if (!GetExtKey(arg, xprv)) return false;
for (auto entry : m_path) {
- xprv.Derive(xprv, entry);
+ if (!xprv.Derive(xprv, entry)) return false;
if (entry >> 31) {
last_hardened = xprv;
}
@@ -373,14 +388,13 @@ public:
}
} else {
for (auto entry : m_path) {
- der = parent_extkey.Derive(parent_extkey, entry);
- assert(der);
+ if (!parent_extkey.Derive(parent_extkey, entry)) return false;
}
final_extkey = parent_extkey;
if (m_derive == DeriveType::UNHARDENED) der = parent_extkey.Derive(final_extkey, pos);
assert(m_derive != DeriveType::HARDENED);
}
- assert(der);
+ if (!der) return false;
final_info_out = final_info_out_tmp;
key_out = final_extkey.pubkey;
@@ -483,8 +497,8 @@ public:
CExtKey extkey;
CExtKey dummy;
if (!GetDerivedExtKey(arg, extkey, dummy)) return false;
- if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
- if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
+ if (m_derive == DeriveType::UNHARDENED && !extkey.Derive(extkey, pos)) return false;
+ if (m_derive == DeriveType::HARDENED && !extkey.Derive(extkey, pos | 0x80000000UL)) return false;
key = extkey.key;
return true;
}
@@ -493,12 +507,12 @@ public:
/** Base class for all Descriptor implementations. */
class DescriptorImpl : public Descriptor
{
- //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size for Multisig).
+protected:
+ //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size for WSH and Multisig).
const std::vector<std::unique_ptr<PubkeyProvider>> m_pubkey_args;
//! The string name of the descriptor function.
const std::string m_name;
-protected:
//! The sub-descriptor arguments (empty for everything but SH and WSH).
//! In doc/descriptors.m this is referred to as SCRIPT expressions sh(SCRIPT)
//! and wsh(SCRIPT), and distinct from KEY expressions and ADDR expressions.
@@ -558,12 +572,12 @@ public:
if (pos++) ret += ",";
std::string tmp;
if (!scriptarg->ToStringHelper(arg, tmp, type, cache)) return false;
- ret += std::move(tmp);
+ ret += tmp;
}
return true;
}
- bool ToStringHelper(const SigningProvider* arg, std::string& out, const StringType type, const DescriptorCache* cache = nullptr) const
+ virtual bool ToStringHelper(const SigningProvider* arg, std::string& out, const StringType type, const DescriptorCache* cache = nullptr) const
{
std::string extra = ToStringExtra();
size_t pos = extra.size() > 0 ? 1 : 0;
@@ -582,7 +596,7 @@ public:
tmp = pubkey->ToString();
break;
}
- ret += std::move(tmp);
+ ret += tmp;
}
std::string subscript;
if (!ToStringSubScriptHelper(arg, subscript, type, cache)) return false;
@@ -598,7 +612,7 @@ public:
return AddChecksum(ret);
}
- bool ToPrivateString(const SigningProvider& arg, std::string& out) const final
+ bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
{
bool ret = ToStringHelper(&arg, out, StringType::PRIVATE);
out = AddChecksum(out);
@@ -630,7 +644,7 @@ public:
assert(outscripts.size() == 1);
subscripts.emplace_back(std::move(outscripts[0]));
}
- out = Merge(std::move(out), std::move(subprovider));
+ out.Merge(std::move(subprovider));
std::vector<CPubKey> pubkeys;
pubkeys.reserve(entries.size());
@@ -684,6 +698,7 @@ public:
return OutputTypeFromDestination(m_destination);
}
bool IsSingleType() const final { return true; }
+ bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; }
};
/** A parsed raw(H) descriptor. */
@@ -704,6 +719,7 @@ public:
return OutputTypeFromDestination(dest);
}
bool IsSingleType() const final { return true; }
+ bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; }
};
/** A parsed pk(P) descriptor. */
@@ -802,6 +818,30 @@ public:
bool IsSingleType() const final { return true; }
};
+/** A parsed (sorted)multi_a(...) descriptor. Always uses x-only pubkeys. */
+class MultiADescriptor final : public DescriptorImpl
+{
+ const int m_threshold;
+ const bool m_sorted;
+protected:
+ std::string ToStringExtra() const override { return strprintf("%i", m_threshold); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override {
+ CScript ret;
+ std::vector<XOnlyPubKey> xkeys;
+ for (const auto& key : keys) xkeys.emplace_back(key);
+ if (m_sorted) std::sort(xkeys.begin(), xkeys.end());
+ ret << ToByteVector(xkeys[0]) << OP_CHECKSIG;
+ for (size_t i = 1; i < keys.size(); ++i) {
+ ret << ToByteVector(xkeys[i]) << OP_CHECKSIGADD;
+ }
+ ret << m_threshold << OP_NUMEQUAL;
+ return Vector(std::move(ret));
+ }
+public:
+ MultiADescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti_a" : "multi_a"), m_threshold(threshold), m_sorted(sorted) {}
+ bool IsSingleType() const final { return true; }
+};
+
/** A parsed sh(...) descriptor. */
class SHDescriptor final : public DescriptorImpl
{
@@ -858,7 +898,7 @@ protected:
if (!xpk.IsFullyValid()) return {};
builder.Finalize(xpk);
WitnessV1Taproot output = builder.GetOutput();
- out.tr_spenddata[output].Merge(builder.GetSpendData());
+ out.tr_trees[output] = builder;
out.pubkeys.emplace(keys[0].GetID(), keys[0]);
return Vector(GetScriptForDestination(output));
}
@@ -874,7 +914,7 @@ protected:
}
std::string tmp;
if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, type, cache)) return false;
- ret += std::move(tmp);
+ ret += tmp;
while (!path.empty() && path.back()) {
if (path.size() > 1) ret += '}';
path.pop_back();
@@ -893,6 +933,107 @@ public:
bool IsSingleType() const final { return true; }
};
+/* We instantiate Miniscript here with a simple integer as key type.
+ * The value of these key integers are an index in the
+ * DescriptorImpl::m_pubkey_args vector.
+ */
+
+/**
+ * The context for converting a Miniscript descriptor into a Script.
+ */
+class ScriptMaker {
+ //! Keys contained in the Miniscript (the evaluation of DescriptorImpl::m_pubkey_args).
+ const std::vector<CPubKey>& m_keys;
+
+public:
+ ScriptMaker(const std::vector<CPubKey>& keys LIFETIMEBOUND) : m_keys(keys) {}
+
+ std::vector<unsigned char> ToPKBytes(uint32_t key) const {
+ return {m_keys[key].begin(), m_keys[key].end()};
+ }
+
+ std::vector<unsigned char> ToPKHBytes(uint32_t key) const {
+ auto id = m_keys[key].GetID();
+ return {id.begin(), id.end()};
+ }
+};
+
+/**
+ * The context for converting a Miniscript descriptor to its textual form.
+ */
+class StringMaker {
+ //! To convert private keys for private descriptors.
+ const SigningProvider* m_arg;
+ //! Keys contained in the Miniscript (a reference to DescriptorImpl::m_pubkey_args).
+ const std::vector<std::unique_ptr<PubkeyProvider>>& m_pubkeys;
+ //! Whether to serialize keys as private or public.
+ bool m_private;
+
+public:
+ StringMaker(const SigningProvider* arg LIFETIMEBOUND, const std::vector<std::unique_ptr<PubkeyProvider>>& pubkeys LIFETIMEBOUND, bool priv)
+ : m_arg(arg), m_pubkeys(pubkeys), m_private(priv) {}
+
+ std::optional<std::string> ToString(uint32_t key) const
+ {
+ std::string ret;
+ if (m_private) {
+ if (!m_pubkeys[key]->ToPrivateString(*m_arg, ret)) return {};
+ } else {
+ ret = m_pubkeys[key]->ToString();
+ }
+ return ret;
+ }
+};
+
+class MiniscriptDescriptor final : public DescriptorImpl
+{
+private:
+ miniscript::NodeRef<uint32_t> m_node;
+
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts,
+ FlatSigningProvider& provider) const override
+ {
+ for (const auto& key : keys) provider.pubkeys.emplace(key.GetID(), key);
+ return Vector(m_node->ToScript(ScriptMaker(keys)));
+ }
+
+public:
+ MiniscriptDescriptor(std::vector<std::unique_ptr<PubkeyProvider>> providers, miniscript::NodeRef<uint32_t> node)
+ : DescriptorImpl(std::move(providers), "?"), m_node(std::move(node)) {}
+
+ bool ToStringHelper(const SigningProvider* arg, std::string& out, const StringType type,
+ const DescriptorCache* cache = nullptr) const override
+ {
+ if (const auto res = m_node->ToString(StringMaker(arg, m_pubkey_args, type == StringType::PRIVATE))) {
+ out = *res;
+ return true;
+ }
+ return false;
+ }
+
+ bool IsSolvable() const override { return false; } // For now, mark these descriptors as non-solvable (as we don't have signing logic for them).
+ bool IsSingleType() const final { return true; }
+};
+
+/** A parsed rawtr(...) descriptor. */
+class RawTRDescriptor final : public DescriptorImpl
+{
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& out) const override
+ {
+ assert(keys.size() == 1);
+ XOnlyPubKey xpk(keys[0]);
+ if (!xpk.IsFullyValid()) return {};
+ WitnessV1Taproot output{xpk};
+ return Vector(GetScriptForDestination(output));
+ }
+public:
+ RawTRDescriptor(std::unique_ptr<PubkeyProvider> output_key) : DescriptorImpl(Vector(std::move(output_key)), "rawtr") {}
+ std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32M; }
+ bool IsSingleType() const final { return true; }
+};
+
////////////////////////////////////////////////////////////////////////////
// Parser //
////////////////////////////////////////////////////////////////////////////
@@ -1034,22 +1175,115 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(uint32_t key_exp_index, const Span<c
return std::make_unique<OriginPubkeyProvider>(key_exp_index, std::move(info), std::move(provider));
}
+std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
+{
+ std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, false);
+ KeyOriginInfo info;
+ if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
+ return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
+ }
+ return key_provider;
+}
+
+std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseScriptContext ctx, const SigningProvider& provider)
+{
+ unsigned char full_key[CPubKey::COMPRESSED_SIZE] = {0x02};
+ std::copy(xkey.begin(), xkey.end(), full_key + 1);
+ CPubKey pubkey(full_key);
+ std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, true);
+ KeyOriginInfo info;
+ if (provider.GetKeyOriginByXOnly(xkey, info)) {
+ return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
+ }
+ return key_provider;
+}
+
+/**
+ * The context for parsing a Miniscript descriptor (either from Script or from its textual representation).
+ */
+struct KeyParser {
+ //! The Key type is an index in DescriptorImpl::m_pubkey_args
+ using Key = uint32_t;
+ //! Must not be nullptr if parsing from string.
+ 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;
+ //! Used to detect key parsing errors within a Miniscript.
+ mutable std::string m_key_parsing_error;
+
+ KeyParser(FlatSigningProvider* out LIFETIMEBOUND, const SigningProvider* in LIFETIMEBOUND) : m_out(out), m_in(in) {}
+
+ bool KeyCompare(const Key& a, const Key& b) const {
+ return *m_keys.at(a) < *m_keys.at(b);
+ }
+
+ template<typename I> std::optional<Key> FromString(I begin, I end) const
+ {
+ assert(m_out);
+ Key key = m_keys.size();
+ auto pk = ParsePubkey(key, {&*begin, &*end}, ParseScriptContext::P2WSH, *m_out, m_key_parsing_error);
+ if (!pk) return {};
+ m_keys.push_back(std::move(pk));
+ return key;
+ }
+
+ std::optional<std::string> ToString(const Key& key) const
+ {
+ return m_keys.at(key)->ToString();
+ }
+
+ template<typename I> std::optional<Key> FromPKBytes(I begin, I end) const
+ {
+ assert(m_in);
+ CPubKey pubkey(begin, end);
+ if (pubkey.IsValid()) {
+ Key key = m_keys.size();
+ m_keys.push_back(InferPubkey(pubkey, ParseScriptContext::P2WSH, *m_in));
+ return key;
+ }
+ return {};
+ }
+
+ template<typename I> std::optional<Key> FromPKHBytes(I begin, I end) const
+ {
+ assert(end - begin == 20);
+ assert(m_in);
+ uint160 hash;
+ std::copy(begin, end, hash.begin());
+ CKeyID keyid(hash);
+ CPubKey pubkey;
+ if (m_in->GetPubKey(keyid, pubkey)) {
+ Key key = m_keys.size();
+ m_keys.push_back(InferPubkey(pubkey, ParseScriptContext::P2WSH, *m_in));
+ return key;
+ }
+ return {};
+ }
+};
+
/** Parse a script in a particular context. */
std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
{
using namespace spanparsing;
auto expr = Expr(sp);
- bool sorted_multi = false;
if (Func("pk", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
- if (!pubkey) return nullptr;
+ if (!pubkey) {
+ error = strprintf("pk(): %s", error);
+ return nullptr;
+ }
++key_exp_index;
return std::make_unique<PKDescriptor>(std::move(pubkey), ctx == ParseScriptContext::P2TR);
}
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) return nullptr;
+ if (!pubkey) {
+ error = strprintf("pkh(): %s", error);
+ return nullptr;
+ }
++key_exp_index;
return std::make_unique<PKHDescriptor>(std::move(pubkey));
} else if (Func("pkh", expr)) {
@@ -1058,14 +1292,22 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
- if (!pubkey) return nullptr;
+ if (!pubkey) {
+ error = strprintf("combo(): %s", error);
+ return nullptr;
+ }
++key_exp_index;
return std::make_unique<ComboDescriptor>(std::move(pubkey));
} else if (Func("combo", expr)) {
error = "Can only have combo() at top level";
return nullptr;
}
- if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr))) {
+ const bool multi = Func("multi", expr);
+ const bool sortedmulti = !multi && Func("sortedmulti", expr);
+ const bool multi_a = !(multi || sortedmulti) && Func("multi_a", expr);
+ const bool sortedmulti_a = !(multi || sortedmulti || multi_a) && Func("sortedmulti_a", expr);
+ if (((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && (multi || sortedmulti)) ||
+ (ctx == ParseScriptContext::P2TR && (multi_a || sortedmulti_a))) {
auto threshold = Expr(expr);
uint32_t thres;
std::vector<std::unique_ptr<PubkeyProvider>> providers;
@@ -1081,14 +1323,20 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
}
auto arg = Expr(expr);
auto pk = ParsePubkey(key_exp_index, arg, ctx, out, error);
- if (!pk) return nullptr;
+ if (!pk) {
+ error = strprintf("Multi: %s", error);
+ return nullptr;
+ }
script_size += pk->GetSize() + 1;
providers.emplace_back(std::move(pk));
key_exp_index++;
}
- if (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTISIG) {
+ 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;
+ } 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;
} else if (thres < 1) {
error = strprintf("Multisig threshold cannot be %d, must be at least 1", thres);
return nullptr;
@@ -1109,14 +1357,24 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
return nullptr;
}
}
- return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi);
- } else if (Func("sortedmulti", expr) || Func("multi", expr)) {
+ 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);
+ }
+ } else if (multi || sortedmulti) {
error = "Can only have multi/sortedmulti at top level, in sh(), or in wsh()";
return nullptr;
+ } else if (multi_a || sortedmulti_a) {
+ error = "Can only have multi_a/sortedmulti_a inside tr()";
+ return nullptr;
}
if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wpkh", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ParseScriptContext::P2WPKH, out, error);
- if (!pubkey) return nullptr;
+ if (!pubkey) {
+ error = strprintf("wpkh(): %s", error);
+ return nullptr;
+ }
key_exp_index++;
return std::make_unique<WPKHDescriptor>(std::move(pubkey));
} else if (Func("wpkh", expr)) {
@@ -1153,7 +1411,10 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
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) return nullptr;
+ if (!internal_key) {
+ error = strprintf("tr(): %s", error);
+ return nullptr;
+ }
++key_exp_index;
std::vector<std::unique_ptr<DescriptorImpl>> subscripts; //!< list of script subexpressions
std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts)
@@ -1211,6 +1472,20 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
error = "Can only have tr at top level";
return nullptr;
}
+ if (ctx == ParseScriptContext::TOP && Func("rawtr", expr)) {
+ auto arg = Expr(expr);
+ if (expr.size()) {
+ error = strprintf("rawtr(): only one key expected.");
+ return nullptr;
+ }
+ 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));
+ } else if (Func("rawtr", expr)) {
+ error = "Can only have rawtr at top level";
+ return nullptr;
+ }
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
std::string str(expr.begin(), expr.end());
if (!IsHex(str)) {
@@ -1223,6 +1498,45 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
error = "Can only have raw() at top level";
return nullptr;
}
+ // Process miniscript expressions.
+ {
+ KeyParser parser(&out, nullptr);
+ auto node = miniscript::FromString(std::string(expr.begin(), expr.end()), parser);
+ if (node) {
+ if (ctx != ParseScriptContext::P2WSH) {
+ error = "Miniscript expressions can only be used in wsh";
+ return nullptr;
+ }
+ if (parser.m_key_parsing_error != "") {
+ error = std::move(parser.m_key_parsing_error);
+ return nullptr;
+ }
+ if (!node->IsSane()) {
+ // Try to find the first insane sub for better error reporting.
+ auto insane_node = node.get();
+ if (const auto sub = node->FindInsaneSub()) insane_node = sub;
+ if (const auto str = insane_node->ToString(parser)) error = *str;
+ if (!insane_node->IsValid()) {
+ error += " is invalid";
+ } else {
+ error += " is not sane";
+ if (!insane_node->IsNonMalleable()) {
+ error += ": malleable witnesses exist";
+ } else if (insane_node == node.get() && !insane_node->NeedsSignature()) {
+ error += ": witnesses without signature exist";
+ } else if (!insane_node->CheckTimeLocksMix()) {
+ error += ": contains mixes of timelocks expressed in blocks and seconds";
+ } else if (!insane_node->CheckDuplicateKey()) {
+ error += ": contains duplicate public keys";
+ } else if (!insane_node->ValidSatisfactions()) {
+ error += ": needs witnesses that may exceed resource limits";
+ }
+ }
+ return nullptr;
+ }
+ return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node));
+ }
+ }
if (ctx == ParseScriptContext::P2SH) {
error = "A function is needed within P2SH";
return nullptr;
@@ -1234,27 +1548,19 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
return nullptr;
}
-std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
+std::unique_ptr<DescriptorImpl> InferMultiA(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
{
- std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, false);
- KeyOriginInfo info;
- if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
- return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
- }
- return key_provider;
-}
-
-std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseScriptContext ctx, const SigningProvider& provider)
-{
- unsigned char full_key[CPubKey::COMPRESSED_SIZE] = {0x02};
- std::copy(xkey.begin(), xkey.end(), full_key + 1);
- CPubKey pubkey(full_key);
- std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, true);
- KeyOriginInfo info;
- if (provider.GetKeyOriginByXOnly(xkey, info)) {
- return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider));
- }
- return key_provider;
+ auto match = MatchMultiA(script);
+ if (!match) return {};
+ std::vector<std::unique_ptr<PubkeyProvider>> keys;
+ keys.reserve(match->second.size());
+ for (const auto keyspan : match->second) {
+ if (keyspan.size() != 32) return {};
+ auto key = InferXOnlyPubkey(XOnlyPubKey{keyspan}, ctx, provider);
+ if (!key) return {};
+ keys.push_back(std::move(key));
+ }
+ return std::make_unique<MultiADescriptor>(match->first, std::move(keys));
}
std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
@@ -1264,6 +1570,11 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return std::make_unique<PKDescriptor>(InferXOnlyPubkey(key, ctx, provider), true);
}
+ if (ctx == ParseScriptContext::P2TR) {
+ auto ret = InferMultiA(script, ctx, provider);
+ if (ret) return ret;
+ }
+
std::vector<std::vector<unsigned char>> data;
TxoutType txntype = Solver(script, data);
@@ -1348,6 +1659,21 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
}
}
}
+ // If the above doesn't work, construct a rawtr() descriptor with just the encoded x-only pubkey.
+ if (pubkey.IsFullyValid()) {
+ auto key = InferXOnlyPubkey(pubkey, ParseScriptContext::P2TR, provider);
+ if (key) {
+ return std::make_unique<RawTRDescriptor>(std::move(key));
+ }
+ }
+ }
+
+ if (ctx == ParseScriptContext::P2WSH) {
+ KeyParser parser(nullptr, &provider);
+ auto node = miniscript::FromScript(script, parser);
+ if (node && node->IsSane()) {
+ return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node));
+ }
}
CTxDestination dest;