aboutsummaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
Diffstat (limited to 'src/script')
-rw-r--r--src/script/descriptor.cpp581
-rw-r--r--src/script/descriptor.h4
-rw-r--r--src/script/interpreter.cpp6
-rw-r--r--src/script/interpreter.h2
-rw-r--r--src/script/miniscript.h33
-rw-r--r--src/script/script.cpp17
-rw-r--r--src/script/script.h79
-rw-r--r--src/script/sigcache.cpp131
-rw-r--r--src/script/sigcache.h51
-rw-r--r--src/script/sign.cpp26
-rw-r--r--src/script/sign.h23
-rw-r--r--src/script/signingprovider.h2
-rw-r--r--src/script/solver.cpp4
-rw-r--r--src/script/solver.h1
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,