diff options
Diffstat (limited to 'src/script')
-rw-r--r-- | src/script/descriptor.cpp | 581 | ||||
-rw-r--r-- | src/script/descriptor.h | 4 | ||||
-rw-r--r-- | src/script/interpreter.cpp | 6 | ||||
-rw-r--r-- | src/script/interpreter.h | 2 | ||||
-rw-r--r-- | src/script/miniscript.h | 33 | ||||
-rw-r--r-- | src/script/script.cpp | 17 | ||||
-rw-r--r-- | src/script/script.h | 79 | ||||
-rw-r--r-- | src/script/sigcache.cpp | 131 | ||||
-rw-r--r-- | src/script/sigcache.h | 51 | ||||
-rw-r--r-- | src/script/sign.cpp | 26 | ||||
-rw-r--r-- | src/script/sign.h | 23 | ||||
-rw-r--r-- | src/script/signingprovider.h | 2 | ||||
-rw-r--r-- | src/script/solver.cpp | 4 | ||||
-rw-r--r-- | src/script/solver.h | 1 |
14 files changed, 632 insertions, 328 deletions
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 0987db194c..5026470edc 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -21,6 +21,7 @@ #include <util/strencodings.h> #include <util/vector.h> +#include <algorithm> #include <memory> #include <numeric> #include <optional> @@ -116,13 +117,13 @@ std::string DescriptorChecksum(const Span<const char>& span) * As a result, within-group-of-32 errors count as 1 symbol, as do cross-group errors that don't affect * the position within the groups. */ - static std::string INPUT_CHARSET = + static const std::string INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}" "IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~" "ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "; /** The character set for the checksum itself (same as bech32). */ - static std::string CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + static const std::string CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; uint64_t c = 1; int cls = 0; @@ -219,6 +220,9 @@ public: virtual std::optional<CPubKey> GetRootPubKey() const = 0; /** Return the extended public key for this PubkeyProvider, if it has one. */ virtual std::optional<CExtPubKey> GetRootExtPubKey() const = 0; + + /** Make a deep copy of this PubkeyProvider */ + virtual std::unique_ptr<PubkeyProvider> Clone() const = 0; }; class OriginPubkeyProvider final : public PubkeyProvider @@ -280,6 +284,10 @@ public: { return m_provider->GetRootExtPubKey(); } + std::unique_ptr<PubkeyProvider> Clone() const override + { + return std::make_unique<OriginPubkeyProvider>(m_expr_index, m_origin, m_provider->Clone(), m_apostrophe); + } }; /** An object representing a parsed constant public key in a descriptor. */ @@ -333,6 +341,10 @@ public: { return std::nullopt; } + std::unique_ptr<PubkeyProvider> Clone() const override + { + return std::make_unique<ConstPubkeyProvider>(m_expr_index, m_pubkey, m_xonly); + } }; enum class DeriveType { @@ -556,6 +568,10 @@ public: { return m_root_extkey; } + std::unique_ptr<PubkeyProvider> Clone() const override + { + return std::make_unique<BIP32PubkeyProvider>(m_expr_index, m_root_extkey, m_path, m_derive, m_apostrophe); + } }; /** Base class for all Descriptor implementations. */ @@ -771,6 +787,8 @@ public: arg->GetPubKeys(pubkeys, ext_pubs); } } + + virtual std::unique_ptr<DescriptorImpl> Clone() const = 0; }; /** A parsed addr(A) descriptor. */ @@ -792,6 +810,10 @@ public: bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; } std::optional<int64_t> ScriptSize() const override { return GetScriptForDestination(m_destination).size(); } + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<AddressDescriptor>(m_destination); + } }; /** A parsed raw(H) descriptor. */ @@ -815,6 +837,11 @@ public: bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; } std::optional<int64_t> ScriptSize() const override { return m_script.size(); } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<RawDescriptor>(m_script); + } }; /** A parsed pk(P) descriptor. */ @@ -850,6 +877,11 @@ public: } std::optional<int64_t> MaxSatisfactionElems() const override { return 1; } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<PKDescriptor>(m_pubkey_args.at(0)->Clone(), m_xonly); + } }; /** A parsed pkh(P) descriptor. */ @@ -879,6 +911,11 @@ public: } std::optional<int64_t> MaxSatisfactionElems() const override { return 2; } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<PKHDescriptor>(m_pubkey_args.at(0)->Clone()); + } }; /** A parsed wpkh(P) descriptor. */ @@ -908,6 +945,11 @@ public: } std::optional<int64_t> MaxSatisfactionElems() const override { return 2; } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<WPKHDescriptor>(m_pubkey_args.at(0)->Clone()); + } }; /** A parsed combo(P) descriptor. */ @@ -932,6 +974,10 @@ protected: public: ComboDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "combo") {} bool IsSingleType() const final { return false; } + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<ComboDescriptor>(m_pubkey_args.at(0)->Clone()); + } }; /** A parsed multi(...) or sortedmulti(...) descriptor */ @@ -970,6 +1016,14 @@ public: } std::optional<int64_t> MaxSatisfactionElems() const override { return 1 + m_threshold; } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + std::vector<std::unique_ptr<PubkeyProvider>> providers; + providers.reserve(m_pubkey_args.size()); + std::transform(m_pubkey_args.begin(), m_pubkey_args.end(), providers.begin(), [](const std::unique_ptr<PubkeyProvider>& p) { return p->Clone(); }); + return std::make_unique<MultisigDescriptor>(m_threshold, std::move(providers), m_sorted); + } }; /** A parsed (sorted)multi_a(...) descriptor. Always uses x-only pubkeys. */ @@ -1006,6 +1060,16 @@ public: } std::optional<int64_t> MaxSatisfactionElems() const override { return m_pubkey_args.size(); } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + std::vector<std::unique_ptr<PubkeyProvider>> providers; + providers.reserve(m_pubkey_args.size()); + for (const auto& arg : m_pubkey_args) { + providers.push_back(arg->Clone()); + } + return std::make_unique<MultiADescriptor>(m_threshold, std::move(providers), m_sorted); + } }; /** A parsed sh(...) descriptor. */ @@ -1051,6 +1115,11 @@ public: if (const auto sub_elems = m_subdescriptor_args[0]->MaxSatisfactionElems()) return 1 + *sub_elems; return {}; } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<SHDescriptor>(m_subdescriptor_args.at(0)->Clone()); + } }; /** A parsed wsh(...) descriptor. */ @@ -1087,6 +1156,11 @@ public: if (const auto sub_elems = m_subdescriptor_args[0]->MaxSatisfactionElems()) return 1 + *sub_elems; return {}; } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<WSHDescriptor>(m_subdescriptor_args.at(0)->Clone()); + } }; /** A parsed tr(...) descriptor. */ @@ -1152,6 +1226,14 @@ public: // FIXME: See above, we assume keypath spend. return 1; } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + std::vector<std::unique_ptr<DescriptorImpl>> subdescs; + subdescs.reserve(m_subdescriptor_args.size()); + std::transform(m_subdescriptor_args.begin(), m_subdescriptor_args.end(), subdescs.begin(), [](const std::unique_ptr<DescriptorImpl>& d) { return d->Clone(); }); + return std::make_unique<TRDescriptor>(m_pubkey_args.at(0)->Clone(), std::move(subdescs), m_depths); + } }; /* We instantiate Miniscript here with a simple integer as key type. @@ -1270,6 +1352,16 @@ public: std::optional<int64_t> MaxSatisfactionElems() const override { return m_node->GetStackSize(); } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + std::vector<std::unique_ptr<PubkeyProvider>> providers; + providers.reserve(m_pubkey_args.size()); + for (const auto& arg : m_pubkey_args) { + providers.push_back(arg->Clone()); + } + return std::make_unique<MiniscriptDescriptor>(std::move(providers), miniscript::MakeNodeRef<uint32_t>(*m_node)); + } }; /** A parsed rawtr(...) descriptor. */ @@ -1300,6 +1392,11 @@ public: // See above, we assume keypath spend. return 1; } + + std::unique_ptr<DescriptorImpl> Clone() const override + { + return std::make_unique<RawTRDescriptor>(m_pubkey_args.at(0)->Clone()); + } }; //////////////////////////////////////////////////////////////////////////// @@ -1314,50 +1411,110 @@ enum class ParseScriptContext { P2TR, //!< Inside tr() (either internal key, or BIP342 script leaf) }; +std::optional<uint32_t> ParseKeyPathNum(Span<const char> elem, bool& apostrophe, std::string& error) +{ + bool hardened = false; + if (elem.size() > 0) { + const char last = elem[elem.size() - 1]; + if (last == '\'' || last == 'h') { + elem = elem.first(elem.size() - 1); + hardened = true; + apostrophe = last == '\''; + } + } + uint32_t p; + if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p)) { + error = strprintf("Key path value '%s' is not a valid uint32", std::string(elem.begin(), elem.end())); + return std::nullopt; + } else if (p > 0x7FFFFFFFUL) { + error = strprintf("Key path value %u is out of range", p); + return std::nullopt; + } + + return std::make_optional<uint32_t>(p | (((uint32_t)hardened) << 31)); +} + /** - * Parse a key path, being passed a split list of elements (the first element is ignored). + * Parse a key path, being passed a split list of elements (the first element is ignored because it is always the key). * * @param[in] split BIP32 path string, using either ' or h for hardened derivation - * @param[out] out the key path + * @param[out] out Vector of parsed key paths * @param[out] apostrophe only updated if hardened derivation is found * @param[out] error parsing error message + * @param[in] allow_multipath Allows the parsed path to use the multipath specifier * @returns false if parsing failed **/ -[[nodiscard]] bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out, bool& apostrophe, std::string& error) +[[nodiscard]] bool ParseKeyPath(const std::vector<Span<const char>>& split, std::vector<KeyPath>& out, bool& apostrophe, std::string& error, bool allow_multipath) { + KeyPath path; + std::optional<size_t> multipath_segment_index; + std::vector<uint32_t> multipath_values; + std::unordered_set<uint32_t> seen_multipath; + for (size_t i = 1; i < split.size(); ++i) { - Span<const char> elem = split[i]; - bool hardened = false; - if (elem.size() > 0) { - const char last = elem[elem.size() - 1]; - if (last == '\'' || last == 'h') { - elem = elem.first(elem.size() - 1); - hardened = true; - apostrophe = last == '\''; + const Span<const char>& elem = split[i]; + + // Check if element contain multipath specifier + if (!elem.empty() && elem.front() == '<' && elem.back() == '>') { + if (!allow_multipath) { + error = strprintf("Key path value '%s' specifies multipath in a section where multipath is not allowed", std::string(elem.begin(), elem.end())); + return false; + } + if (multipath_segment_index) { + error = "Multiple multipath key path specifiers found"; + return false; + } + + // Parse each possible value + std::vector<Span<const char>> nums = Split(Span(elem.begin()+1, elem.end()-1), ";"); + if (nums.size() < 2) { + error = "Multipath key path specifiers must have at least two items"; + return false; } + + for (const auto& num : nums) { + const auto& op_num = ParseKeyPathNum(num, apostrophe, error); + if (!op_num) return false; + auto [_, inserted] = seen_multipath.insert(*op_num); + if (!inserted) { + error = strprintf("Duplicated key path value %u in multipath specifier", *op_num); + return false; + } + multipath_values.emplace_back(*op_num); + } + + path.emplace_back(); // Placeholder for multipath segment + multipath_segment_index = path.size()-1; + } else { + const auto& op_num = ParseKeyPathNum(elem, apostrophe, error); + if (!op_num) return false; + path.emplace_back(*op_num); } - uint32_t p; - if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p)) { - error = strprintf("Key path value '%s' is not a valid uint32", std::string(elem.begin(), elem.end())); - return false; - } else if (p > 0x7FFFFFFFUL) { - error = strprintf("Key path value %u is out of range", p); - return false; + } + + if (!multipath_segment_index) { + out.emplace_back(std::move(path)); + } else { + // Replace the multipath placeholder with each value while generating paths + for (size_t i = 0; i < multipath_values.size(); i++) { + KeyPath branch_path = path; + branch_path[*multipath_segment_index] = multipath_values[i]; + out.emplace_back(std::move(branch_path)); } - out.push_back(p | (((uint32_t)hardened) << 31)); } return true; } /** 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)) { @@ -1365,35 +1522,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 {}; } } } @@ -1401,33 +1561,37 @@ 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; + std::vector<KeyPath> paths; DeriveType type = DeriveType::NO; - if (split.back() == Span{"*"}.first(1)) { + if (std::ranges::equal(split.back(), Span{"*"}.first(1))) { split.pop_back(); type = DeriveType::UNHARDENED; - } else if (split.back() == Span{"*'"}.first(2) || split.back() == Span{"*h"}.first(2)) { - apostrophe = split.back() == Span{"*'"}.first(2); + } else if (std::ranges::equal(split.back(), Span{"*'"}.first(2)) || std::ranges::equal(split.back(), Span{"*h"}.first(2))) { + apostrophe = std::ranges::equal(split.back(), Span{"*'"}.first(2)); split.pop_back(); type = DeriveType::HARDENED; } - if (!ParseKeyPath(split, path, apostrophe, error)) return nullptr; + if (!ParseKeyPath(split, paths, apostrophe, error, /*allow_multipath=*/true)) 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); + for (auto& path : paths) { + 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; @@ -1437,27 +1601,33 @@ 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); + std::vector<KeyPath> path; + if (!ParseKeyPath(slash_split, path, apostrophe, error, /*allow_multipath=*/false)) return {}; + info.path = path.at(0); + 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) @@ -1499,8 +1669,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). @@ -1513,7 +1683,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 { @@ -1529,14 +1699,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 @@ -1547,13 +1717,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; } } @@ -1571,7 +1743,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; } } @@ -1585,44 +1758,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); @@ -1632,118 +1815,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. @@ -1757,19 +1979,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 '}' } @@ -1777,7 +2000,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. } @@ -1785,40 +2008,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. { @@ -1827,12 +2092,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. @@ -1857,24 +2122,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) @@ -2012,7 +2305,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)); } } @@ -2067,14 +2365,21 @@ bool CheckChecksum(Span<const char>& sp, bool require_checksum, std::string& err return true; } -std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum) +std::vector<std::unique_ptr<Descriptor>> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum) { Span<const char> sp{descriptor}; - if (!CheckChecksum(sp, require_checksum, error)) return nullptr; + if (!CheckChecksum(sp, require_checksum, error)) return {}; 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)); - return nullptr; + if (sp.size() == 0 && !ret.empty()) { + std::vector<std::unique_ptr<Descriptor>> descs; + descs.reserve(ret.size()); + for (auto& r : ret) { + descs.emplace_back(std::unique_ptr<Descriptor>(std::move(r))); + } + return descs; + } + return {}; } std::string GetDescriptorChecksum(const std::string& descriptor) diff --git a/src/script/descriptor.h b/src/script/descriptor.h index e78a775330..473649a314 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -173,9 +173,9 @@ struct Descriptor { * is set, the checksum is mandatory - otherwise it is optional. * * If a parse error occurs, or the checksum is missing/invalid, or anything - * else is wrong, `nullptr` is returned. + * else is wrong, an empty vector is returned. */ -std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum = false); +std::vector<std::unique_ptr<Descriptor>> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum = false); /** Get the checksum for a `descriptor`. * diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 20a9830d0e..dcdddb88e9 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1303,7 +1303,7 @@ public: // Serialize the nSequence if (nInput != nIn && (fHashSingle || fHashNone)) // let the others update at will - ::Serialize(s, int{0}); + ::Serialize(s, int32_t{0}); else ::Serialize(s, txTo.vin[nInput].nSequence); } @@ -1565,7 +1565,7 @@ bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, cons } template <class T> -uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) +uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int32_t nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { assert(nIn < txTo.vin.size()); @@ -1943,6 +1943,8 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, } return set_success(serror); } + } else if (!is_p2sh && CScript::IsPayToAnchor(witversion, program)) { + return true; } else { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 836c2e7982..8ba0018c23 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -265,7 +265,7 @@ public: return false; } - virtual ~BaseSignatureChecker() {} + virtual ~BaseSignatureChecker() = default; }; /** Enum to specify what *TransactionSignatureChecker's behavior should be diff --git a/src/script/miniscript.h b/src/script/miniscript.h index a269709e72..58f24434f0 100644 --- a/src/script/miniscript.h +++ b/src/script/miniscript.h @@ -305,7 +305,7 @@ struct InputStack { //! Data elements. std::vector<std::vector<unsigned char>> stack; //! Construct an empty stack (valid). - InputStack() {} + InputStack() = default; //! Construct a valid single-element stack (with an element up to 75 bytes). InputStack(std::vector<unsigned char> in) : size(in.size() + 1), stack(Vector(std::move(in))) {} //! Change availability @@ -1793,8 +1793,9 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) // Get threshold int next_comma = FindNextChar(in, ','); if (next_comma < 1) return false; - int64_t k; - if (!ParseInt64(std::string(in.begin(), in.begin() + next_comma), &k)) return false; + const auto k_to_integral{ToIntegral<int64_t>(std::string_view(in.begin(), next_comma))}; + if (!k_to_integral.has_value()) return false; + const int64_t k{k_to_integral.value()}; in = in.subspan(next_comma + 1); // Get keys. It is compatible for both compressed and x-only keys. std::vector<Key> keys; @@ -1948,21 +1949,19 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) } else if (Const("after(", in)) { int arg_size = FindNextChar(in, ')'); if (arg_size < 1) return {}; - int64_t num; - if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {}; - if (num < 1 || num >= 0x80000000L) return {}; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::AFTER, num)); + const auto num{ToIntegral<int64_t>(std::string_view(in.begin(), arg_size))}; + if (!num.has_value() || *num < 1 || *num >= 0x80000000L) return {}; + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::AFTER, *num)); in = in.subspan(arg_size + 1); - script_size += 1 + (num > 16) + (num > 0x7f) + (num > 0x7fff) + (num > 0x7fffff); + script_size += 1 + (*num > 16) + (*num > 0x7f) + (*num > 0x7fff) + (*num > 0x7fffff); } else if (Const("older(", in)) { int arg_size = FindNextChar(in, ')'); if (arg_size < 1) return {}; - int64_t num; - if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {}; - if (num < 1 || num >= 0x80000000L) return {}; - constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::OLDER, num)); + const auto num{ToIntegral<int64_t>(std::string_view(in.begin(), arg_size))}; + if (!num.has_value() || *num < 1 || *num >= 0x80000000L) return {}; + constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::OLDER, *num)); in = in.subspan(arg_size + 1); - script_size += 1 + (num > 16) + (num > 0x7f) + (num > 0x7fff) + (num > 0x7fffff); + script_size += 1 + (*num > 16) + (*num > 0x7f) + (*num > 0x7fff) + (*num > 0x7fffff); } else if (Const("multi(", in)) { if (!parse_multi_exp(in, /* is_multi_a = */false)) return {}; } else if (Const("multi_a(", in)) { @@ -1970,13 +1969,13 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) } else if (Const("thresh(", in)) { int next_comma = FindNextChar(in, ','); if (next_comma < 1) return {}; - if (!ParseInt64(std::string(in.begin(), in.begin() + next_comma), &k)) return {}; - if (k < 1) return {}; + const auto k{ToIntegral<int64_t>(std::string_view(in.begin(), next_comma))}; + if (!k.has_value() || *k < 1) return {}; in = in.subspan(next_comma + 1); // n = 1 here because we read the first WRAPPED_EXPR before reaching THRESH - to_parse.emplace_back(ParseContext::THRESH, 1, k); + to_parse.emplace_back(ParseContext::THRESH, 1, *k); to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1); - script_size += 2 + (k > 16) + (k > 0x7f) + (k > 0x7fff) + (k > 0x7fffff); + script_size += 2 + (*k > 16) + (*k > 0x7f) + (*k > 0x7fff) + (*k > 0x7fffff); } else if (Const("andor(", in)) { to_parse.emplace_back(ParseContext::ANDOR, -1, -1); to_parse.emplace_back(ParseContext::CLOSE_BRACKET, -1, -1); diff --git a/src/script/script.cpp b/src/script/script.cpp index 73ea336c4f..d650db9a0d 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -204,6 +204,23 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const return subscript.GetSigOpCount(true); } +bool CScript::IsPayToAnchor() const +{ + return (this->size() == 4 && + (*this)[0] == OP_1 && + (*this)[1] == 0x02 && + (*this)[2] == 0x4e && + (*this)[3] == 0x73); +} + +bool CScript::IsPayToAnchor(int version, const std::vector<unsigned char>& program) +{ + return version == 1 && + program.size() == 2 && + program[0] == 0x4e && + program[1] == 0x73; +} + bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: diff --git a/src/script/script.h b/src/script/script.h index 66d63fae89..f457984980 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -17,6 +17,7 @@ #include <cstdint> #include <cstring> #include <limits> +#include <span> #include <stdexcept> #include <string> #include <type_traits> @@ -412,6 +413,32 @@ bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator en /** Serialized script, used inside transaction inputs and outputs */ class CScript : public CScriptBase { +private: + inline void AppendDataSize(const uint32_t size) + { + if (size < OP_PUSHDATA1) { + insert(end(), static_cast<value_type>(size)); + } else if (size <= 0xff) { + insert(end(), OP_PUSHDATA1); + insert(end(), static_cast<value_type>(size)); + } else if (size <= 0xffff) { + insert(end(), OP_PUSHDATA2); + value_type data[2]; + WriteLE16(data, size); + insert(end(), std::cbegin(data), std::cend(data)); + } else { + insert(end(), OP_PUSHDATA4); + value_type data[4]; + WriteLE32(data, size); + insert(end(), std::cbegin(data), std::cend(data)); + } + } + + void AppendData(std::span<const value_type> data) + { + insert(end(), data.begin(), data.end()); + } + protected: CScript& push_int64(int64_t n) { @@ -429,11 +456,11 @@ protected: } return *this; } + public: - CScript() { } - CScript(const_iterator pbegin, const_iterator pend) : CScriptBase(pbegin, pend) { } - CScript(std::vector<unsigned char>::const_iterator pbegin, std::vector<unsigned char>::const_iterator pend) : CScriptBase(pbegin, pend) { } - CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { } + CScript() = default; + template <std::input_iterator InputIterator> + CScript(InputIterator first, InputIterator last) : CScriptBase{first, last} { } SERIALIZE_METHODS(CScript, obj) { READWRITE(AsBase<CScriptBase>(obj)); } @@ -463,35 +490,19 @@ public: return *this; } - CScript& operator<<(const std::vector<unsigned char>& b) LIFETIMEBOUND + CScript& operator<<(std::span<const std::byte> b) LIFETIMEBOUND { - if (b.size() < OP_PUSHDATA1) - { - insert(end(), (unsigned char)b.size()); - } - else if (b.size() <= 0xff) - { - insert(end(), OP_PUSHDATA1); - insert(end(), (unsigned char)b.size()); - } - else if (b.size() <= 0xffff) - { - insert(end(), OP_PUSHDATA2); - uint8_t _data[2]; - WriteLE16(_data, b.size()); - insert(end(), _data, _data + sizeof(_data)); - } - else - { - insert(end(), OP_PUSHDATA4); - uint8_t _data[4]; - WriteLE32(_data, b.size()); - insert(end(), _data, _data + sizeof(_data)); - } - insert(end(), b.begin(), b.end()); + AppendDataSize(b.size()); + AppendData({reinterpret_cast<const value_type*>(b.data()), b.size()}); return *this; } + // For compatibility reasons. In new code, prefer using std::byte instead of uint8_t. + CScript& operator<<(std::span<const value_type> b) LIFETIMEBOUND + { + return *this << std::as_bytes(b); + } + bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const { return GetScriptOp(pc, end(), opcodeRet, &vchRet); @@ -533,6 +544,14 @@ public: */ unsigned int GetSigOpCount(const CScript& scriptSig) const; + /* + * OP_1 <0x4e73> + */ + bool IsPayToAnchor() const; + /** Checks if output of IsWitnessProgram comes from a P2A output script + */ + static bool IsPayToAnchor(int version, const std::vector<unsigned char>& program); + bool IsPayToScriptHash() const; bool IsPayToWitnessScriptHash() const; bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const; @@ -569,7 +588,7 @@ struct CScriptWitness std::vector<std::vector<unsigned char> > stack; // Some compilers complain without a default constructor - CScriptWitness() { } + CScriptWitness() = default; bool IsNull() const { return stack.empty(); } diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 7c6c282cc4..33531e6bf5 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -5,125 +5,80 @@ #include <script/sigcache.h> -#include <common/system.h> +#include <crypto/sha256.h> #include <logging.h> #include <pubkey.h> #include <random.h> +#include <script/interpreter.h> +#include <span.h> #include <uint256.h> -#include <cuckoocache.h> - -#include <algorithm> #include <mutex> -#include <optional> #include <shared_mutex> #include <vector> -namespace { -/** - * Valid signature cache, to avoid doing expensive ECDSA signature checking - * twice for every transaction (once when accepted into memory pool, and - * again when accepted into the block chain) - */ -class CSignatureCache +SignatureCache::SignatureCache(const size_t max_size_bytes) { -private: - //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature): - CSHA256 m_salted_hasher_ecdsa; - CSHA256 m_salted_hasher_schnorr; - typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type; - map_type setValid; - std::shared_mutex cs_sigcache; - -public: - CSignatureCache() - { - uint256 nonce = GetRandHash(); - // We want the nonce to be 64 bytes long to force the hasher to process - // this chunk, which makes later hash computations more efficient. We - // just write our 32-byte entropy, and then pad with 'E' for ECDSA and - // 'S' for Schnorr (followed by 0 bytes). - static constexpr unsigned char PADDING_ECDSA[32] = {'E'}; - static constexpr unsigned char PADDING_SCHNORR[32] = {'S'}; - m_salted_hasher_ecdsa.Write(nonce.begin(), 32); - m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32); - m_salted_hasher_schnorr.Write(nonce.begin(), 32); - m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32); - } - - void - ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const - { - CSHA256 hasher = m_salted_hasher_ecdsa; - hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(vchSig.data(), vchSig.size()).Finalize(entry.begin()); - } - - void - ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const - { - CSHA256 hasher = m_salted_hasher_schnorr; - hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin()); - } + uint256 nonce = GetRandHash(); + // We want the nonce to be 64 bytes long to force the hasher to process + // this chunk, which makes later hash computations more efficient. We + // just write our 32-byte entropy, and then pad with 'E' for ECDSA and + // 'S' for Schnorr (followed by 0 bytes). + static constexpr unsigned char PADDING_ECDSA[32] = {'E'}; + static constexpr unsigned char PADDING_SCHNORR[32] = {'S'}; + m_salted_hasher_ecdsa.Write(nonce.begin(), 32); + m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32); + m_salted_hasher_schnorr.Write(nonce.begin(), 32); + m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32); - bool - Get(const uint256& entry, const bool erase) - { - std::shared_lock<std::shared_mutex> lock(cs_sigcache); - return setValid.contains(entry, erase); - } + const auto [num_elems, approx_size_bytes] = setValid.setup_bytes(max_size_bytes); + LogPrintf("Using %zu MiB out of %zu MiB requested for signature cache, able to store %zu elements\n", + approx_size_bytes >> 20, max_size_bytes >> 20, num_elems); +} - void Set(const uint256& entry) - { - std::unique_lock<std::shared_mutex> lock(cs_sigcache); - setValid.insert(entry); - } - std::optional<std::pair<uint32_t, size_t>> setup_bytes(size_t n) - { - return setValid.setup_bytes(n); - } -}; +void SignatureCache::ComputeEntryECDSA(uint256& entry, const uint256& hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const +{ + CSHA256 hasher = m_salted_hasher_ecdsa; + hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(vchSig.data(), vchSig.size()).Finalize(entry.begin()); +} -/* In previous versions of this code, signatureCache was a local static variable - * in CachingTransactionSignatureChecker::VerifySignature. We initialize - * signatureCache outside of VerifySignature to avoid the atomic operation per - * call overhead associated with local static variables even though - * signatureCache could be made local to VerifySignature. -*/ -static CSignatureCache signatureCache; -} // namespace +void SignatureCache::ComputeEntrySchnorr(uint256& entry, const uint256& hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const +{ + CSHA256 hasher = m_salted_hasher_schnorr; + hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin()); +} -// To be called once in AppInitMain/BasicTestingSetup to initialize the -// signatureCache. -bool InitSignatureCache(size_t max_size_bytes) +bool SignatureCache::Get(const uint256& entry, const bool erase) { - auto setup_results = signatureCache.setup_bytes(max_size_bytes); - if (!setup_results) return false; + std::shared_lock<std::shared_mutex> lock(cs_sigcache); + return setValid.contains(entry, erase); +} - const auto [num_elems, approx_size_bytes] = *setup_results; - LogPrintf("Using %zu MiB out of %zu MiB requested for signature cache, able to store %zu elements\n", - approx_size_bytes >> 20, max_size_bytes >> 20, num_elems); - return true; +void SignatureCache::Set(const uint256& entry) +{ + std::unique_lock<std::shared_mutex> lock(cs_sigcache); + setValid.insert(entry); } bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const { uint256 entry; - signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey); - if (signatureCache.Get(entry, !store)) + m_signature_cache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey); + if (m_signature_cache.Get(entry, !store)) return true; if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash)) return false; if (store) - signatureCache.Set(entry); + m_signature_cache.Set(entry); return true; } bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const { uint256 entry; - signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey); - if (signatureCache.Get(entry, !store)) return true; + m_signature_cache.ComputeEntrySchnorr(entry, sighash, sig, pubkey); + if (m_signature_cache.Get(entry, !store)) return true; if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false; - if (store) signatureCache.Set(entry); + if (store) m_signature_cache.Set(entry); return true; } diff --git a/src/script/sigcache.h b/src/script/sigcache.h index d33d60d5bc..76802e6a7c 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -6,32 +6,71 @@ #ifndef BITCOIN_SCRIPT_SIGCACHE_H #define BITCOIN_SCRIPT_SIGCACHE_H +#include <consensus/amount.h> +#include <crypto/sha256.h> +#include <cuckoocache.h> #include <script/interpreter.h> #include <span.h> +#include <uint256.h> #include <util/hasher.h> -#include <optional> +#include <cstddef> +#include <shared_mutex> #include <vector> +class CPubKey; +class CTransaction; +class XOnlyPubKey; + // DoS prevention: limit cache size to 32MiB (over 1000000 entries on 64-bit // systems). Due to how we count cache size, actual memory usage is slightly // more (~32.25 MiB) -static constexpr size_t DEFAULT_MAX_SIG_CACHE_BYTES{32 << 20}; +static constexpr size_t DEFAULT_VALIDATION_CACHE_BYTES{32 << 20}; +static constexpr size_t DEFAULT_SIGNATURE_CACHE_BYTES{DEFAULT_VALIDATION_CACHE_BYTES / 2}; +static constexpr size_t DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES{DEFAULT_VALIDATION_CACHE_BYTES / 2}; +static_assert(DEFAULT_VALIDATION_CACHE_BYTES == DEFAULT_SIGNATURE_CACHE_BYTES + DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES); -class CPubKey; +/** + * Valid signature cache, to avoid doing expensive ECDSA signature checking + * twice for every transaction (once when accepted into memory pool, and + * again when accepted into the block chain) + */ +class SignatureCache +{ +private: + //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature): + CSHA256 m_salted_hasher_ecdsa; + CSHA256 m_salted_hasher_schnorr; + typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type; + map_type setValid; + std::shared_mutex cs_sigcache; + +public: + SignatureCache(size_t max_size_bytes); + + SignatureCache(const SignatureCache&) = delete; + SignatureCache& operator=(const SignatureCache&) = delete; + + void ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const; + + void ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const; + + bool Get(const uint256& entry, const bool erase); + + void Set(const uint256& entry); +}; class CachingTransactionSignatureChecker : public TransactionSignatureChecker { private: bool store; + SignatureCache& m_signature_cache; public: - CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn) {} + CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, SignatureCache& signature_cache, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn), m_signature_cache(signature_cache) {} bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override; bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override; }; -[[nodiscard]] bool InitSignatureCache(size_t max_size_bytes); - #endif // BITCOIN_SCRIPT_SIGCACHE_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 22ac062a63..42db251359 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -475,6 +475,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TxoutType::WITNESS_V1_TAPROOT: return SignTaproot(provider, creator, WitnessV1Taproot(XOnlyPubKey{vSolutions[0]}), sigdata, ret); + + case TxoutType::ANCHOR: + return true; } // no default case, so the compiler can warn about missing cases assert(false); } @@ -691,27 +694,6 @@ void SignatureData::MergeSignatureData(SignatureData sigdata) signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end())); } -bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType, SignatureData& sig_data) -{ - assert(nIn < txTo.vin.size()); - - MutableTransactionSignatureCreator creator(txTo, nIn, amount, nHashType); - - bool ret = ProduceSignature(provider, creator, fromPubKey, sig_data); - UpdateInput(txTo.vin.at(nIn), sig_data); - return ret; -} - -bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType, SignatureData& sig_data) -{ - assert(nIn < txTo.vin.size()); - const CTxIn& txin = txTo.vin[nIn]; - assert(txin.prevout.n < txFrom.vout.size()); - const CTxOut& txout = txFrom.vout[txin.prevout.n]; - - return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType, sig_data); -} - namespace { /** Dummy signature checker which accepts all signatures. */ class DummySignatureChecker final : public BaseSignatureChecker @@ -831,7 +813,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, } ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, MissingDataBehavior::FAIL), &serror)) { + if (!sigdata.complete && !VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, MissingDataBehavior::FAIL), &serror)) { if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { // Unable to sign input and verification failed (possible attempt to partially sign). input_errors[i] = Untranslated("Unable to sign input, invalid stack size (possibly missing key)"); diff --git a/src/script/sign.h b/src/script/sign.h index ace2ba7856..fe2c470bc6 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -27,7 +27,7 @@ struct CMutableTransaction; /** Interface for signature creators. */ class BaseSignatureCreator { public: - virtual ~BaseSignatureCreator() {} + virtual ~BaseSignatureCreator() = default; virtual const BaseSignatureChecker& Checker() const =0; /** Create a singular (non-script) signature. */ @@ -89,7 +89,7 @@ struct SignatureData { std::map<std::vector<uint8_t>, std::vector<uint8_t>> ripemd160_preimages; ///< Mapping from a RIPEMD160 hash to its preimage provided to solve a Script std::map<std::vector<uint8_t>, std::vector<uint8_t>> hash160_preimages; ///< Mapping from a HASH160 hash to its preimage provided to solve a Script - SignatureData() {} + SignatureData() = default; explicit SignatureData(const CScript& script) : scriptSig(script) {} void MergeSignatureData(SignatureData sigdata); }; @@ -97,25 +97,6 @@ struct SignatureData { /** Produce a script signature using a generic signature creator. */ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); -/** - * Produce a satisfying script (scriptSig or witness). - * - * @param provider Utility containing the information necessary to solve a script. - * @param fromPubKey The script to produce a satisfaction for. - * @param txTo The spending transaction. - * @param nIn The index of the input in `txTo` referring the output being spent. - * @param amount The value of the output being spent. - * @param nHashType Signature hash type. - * @param sig_data Additional data provided to solve a script. Filled with the resulting satisfying - * script and whether the satisfaction is complete. - * - * @return True if the produced script is entirely satisfying `fromPubKey`. - **/ -bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, - unsigned int nIn, const CAmount& amount, int nHashType, SignatureData& sig_data); -bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, - unsigned int nIn, int nHashType, SignatureData& sig_data); - /** Extract signature data from a transaction input, and insert it. */ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); void UpdateInput(CTxIn& input, const SignatureData& data); diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h index 3298376389..efdfd9ee56 100644 --- a/src/script/signingprovider.h +++ b/src/script/signingprovider.h @@ -150,7 +150,7 @@ std::optional<std::vector<std::tuple<int, std::vector<unsigned char>, int>>> Inf class SigningProvider { public: - virtual ~SigningProvider() {} + virtual ~SigningProvider() = default; virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; } virtual bool HaveCScript(const CScriptID &scriptid) const { return false; } virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; } diff --git a/src/script/solver.cpp b/src/script/solver.cpp index 3dfa9cd6ba..bd3c5cdf72 100644 --- a/src/script/solver.cpp +++ b/src/script/solver.cpp @@ -24,6 +24,7 @@ std::string GetTxnOutputType(TxoutType t) case TxoutType::SCRIPTHASH: return "scripthash"; case TxoutType::MULTISIG: return "multisig"; case TxoutType::NULL_DATA: return "nulldata"; + case TxoutType::ANCHOR: return "anchor"; case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot"; @@ -165,6 +166,9 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c vSolutionsRet.push_back(std::move(witnessprogram)); return TxoutType::WITNESS_V1_TAPROOT; } + if (scriptPubKey.IsPayToAnchor()) { + return TxoutType::ANCHOR; + } if (witnessversion != 0) { vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); vSolutionsRet.push_back(std::move(witnessprogram)); diff --git a/src/script/solver.h b/src/script/solver.h index dc8f4c357d..5a7b685a6f 100644 --- a/src/script/solver.h +++ b/src/script/solver.h @@ -22,6 +22,7 @@ template <typename C> class Span; enum class TxoutType { NONSTANDARD, // 'standard' transaction types: + ANCHOR, //!< anyone can spend script PUBKEY, PUBKEYHASH, SCRIPTHASH, |