From c0fda0b32899898363a2e73191845da1525e31dd Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Tue, 25 Jan 2022 12:27:07 +0100 Subject: [PATCH 1/3] Don't use the locale-dependant std::to_string() --- bitcoin/script/miniscript.h | 9 +-- bitcoin/test/miniscript_tests.cpp | 5 +- bitcoin/util/string.h | 106 ++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 bitcoin/util/string.h diff --git a/bitcoin/script/miniscript.h b/bitcoin/script/miniscript.h index 7d4aa00..ffef285 100644 --- a/bitcoin/script/miniscript.h +++ b/bitcoin/script/miniscript.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -624,8 +625,8 @@ struct Node { if (!ctx.ToString(node.keys[0], key_str)) return {}; return std::move(ret) + "pk_h(" + std::move(key_str) + ")"; } - case NodeType::AFTER: return std::move(ret) + "after(" + std::to_string(node.k) + ")"; - case NodeType::OLDER: return std::move(ret) + "older(" + std::to_string(node.k) + ")"; + case NodeType::AFTER: return std::move(ret) + "after(" + ::ToString(node.k) + ")"; + case NodeType::OLDER: return std::move(ret) + "older(" + ::ToString(node.k) + ")"; case NodeType::HASH256: return std::move(ret) + "hash256(" + HexStr(node.data) + ")"; case NodeType::HASH160: return std::move(ret) + "hash160(" + HexStr(node.data) + ")"; case NodeType::SHA256: return std::move(ret) + "sha256(" + HexStr(node.data) + ")"; @@ -643,7 +644,7 @@ struct Node { if (node.subs[2]->nodetype == NodeType::JUST_0) return std::move(ret) + "and_n(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")"; return std::move(ret) + "andor(" + std::move(subs[0]) + "," + std::move(subs[1]) + "," + std::move(subs[2]) + ")"; case NodeType::MULTI: { - auto str = std::move(ret) + "multi(" + std::to_string(node.k); + auto str = std::move(ret) + "multi(" + ::ToString(node.k); for (const auto& key : node.keys) { std::string key_str; if (!ctx.ToString(key, key_str)) return {}; @@ -652,7 +653,7 @@ struct Node { return std::move(str) + ")"; } case NodeType::THRESH: { - auto str = std::move(ret) + "thresh(" + std::to_string(node.k); + auto str = std::move(ret) + "thresh(" + ::ToString(node.k); for (auto& sub : subs) { str += "," + std::move(sub); } diff --git a/bitcoin/test/miniscript_tests.cpp b/bitcoin/test/miniscript_tests.cpp index e3bdc2c..aa25098 100644 --- a/bitcoin/test/miniscript_tests.cpp +++ b/bitcoin/test/miniscript_tests.cpp @@ -503,12 +503,11 @@ void Test(const std::string& ms, const std::string& hexscript, int mode, int ops auto inferred_miniscript = miniscript::FromScript(computed_script, CONVERTER); BOOST_CHECK_MESSAGE(inferred_miniscript, "Cannot infer miniscript from script: " + ms); BOOST_CHECK_MESSAGE(inferred_miniscript->ToScript(CONVERTER) == computed_script, "Roundtrip failure: miniscript->script != miniscript->script->miniscript->script: " + ms); - if (opslimit != -1) BOOST_CHECK_MESSAGE((int)node->GetOps() == opslimit, "Ops limit mismatch: " + ms + " (" + std::to_string(node->GetOps()) + " vs " + std::to_string(opslimit) + ")"); - if (stacklimit != -1) BOOST_CHECK_MESSAGE((int)node->GetStackSize() == stacklimit, "Stack limit mismatch: " + ms + " (" + std::to_string(node->GetStackSize()) + " vs " + std::to_string(stacklimit) + ")"); + if (opslimit != -1) BOOST_CHECK_MESSAGE((int)node->GetOps() == opslimit, "Ops limit mismatch: " << ms << " (" << node->GetOps() << " vs " << opslimit << ")"); + if (stacklimit != -1) BOOST_CHECK_MESSAGE((int)node->GetStackSize() == stacklimit, "Stack limit mismatch: " << ms << " (" << node->GetStackSize() << " vs " << stacklimit << ")"); TestSatisfy(ms, node); } } - } // namespace BOOST_FIXTURE_TEST_SUITE(miniscript_tests, BasicTestingSetup) diff --git a/bitcoin/util/string.h b/bitcoin/util/string.h new file mode 100644 index 0000000..a3b8df8 --- /dev/null +++ b/bitcoin/util/string.h @@ -0,0 +1,106 @@ +// Copyright (c) 2019-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_STRING_H +#define BITCOIN_UTIL_STRING_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +[[nodiscard]] inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v") +{ + std::string::size_type front = str.find_first_not_of(pattern); + if (front == std::string::npos) { + return std::string(); + } + std::string::size_type end = str.find_last_not_of(pattern); + return str.substr(front, end - front + 1); +} + +[[nodiscard]] inline std::string RemovePrefix(const std::string& str, const std::string& prefix) +{ + if (str.substr(0, prefix.size()) == prefix) { + return str.substr(prefix.size()); + } + return str; +} + +/** + * Join a list of items + * + * @param list The list to join + * @param separator The separator + * @param unary_op Apply this operator to each item in the list + */ +template +auto Join(const std::vector& list, const BaseType& separator, UnaryOp unary_op) + -> decltype(unary_op(list.at(0))) +{ + decltype(unary_op(list.at(0))) ret; + for (size_t i = 0; i < list.size(); ++i) { + if (i > 0) ret += separator; + ret += unary_op(list.at(i)); + } + return ret; +} + +template +T Join(const std::vector& list, const T& separator) +{ + return Join(list, separator, [](const T& i) { return i; }); +} + +// Explicit overload needed for c_str arguments, which would otherwise cause a substitution failure in the template above. +inline std::string Join(const std::vector& list, const std::string& separator) +{ + return Join(list, separator); +} + +/** + * Create an unordered multi-line list of items. + */ +inline std::string MakeUnorderedList(const std::vector& items) +{ + return Join(items, "\n", [](const std::string& item) { return "- " + item; }); +} + +/** + * Check if a string does not contain any embedded NUL (\0) characters + */ +[[nodiscard]] inline bool ValidAsCString(const std::string& str) noexcept +{ + return str.size() == strlen(str.c_str()); +} + +/** + * Locale-independent version of std::to_string + */ +template +std::string ToString(const T& t) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << t; + return oss.str(); +} + +/** + * Check whether a container begins with the given prefix. + */ +template +[[nodiscard]] inline bool HasPrefix(const T1& obj, + const std::array& prefix) +{ + return obj.size() >= PREFIX_LEN && + std::equal(std::begin(prefix), std::end(prefix), std::begin(obj)); +} + +#endif // BITCOIN_UTIL_STRING_H From 3cd71e9472f02b660b3eec670a848696402dda20 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Fri, 18 Feb 2022 14:46:25 +0100 Subject: [PATCH 2/3] qa: remove the random tests We are going to add a couple of fuzz target generating random Miniscript nodes instead. --- bitcoin/test/miniscript_tests.cpp | 165 ------------------------------ 1 file changed, 165 deletions(-) diff --git a/bitcoin/test/miniscript_tests.cpp b/bitcoin/test/miniscript_tests.cpp index aa25098..568be1f 100644 --- a/bitcoin/test/miniscript_tests.cpp +++ b/bitcoin/test/miniscript_tests.cpp @@ -113,9 +113,6 @@ typedef std::pair Challenge; struct KeyConverter { typedef CPubKey Key; - //! Public keys in text form are their usual hex notation (no xpubs, ...). - bool ToString(const CPubKey& key, std::string& ret) const { ret = HexStr(key); return true; } - //! Convert a public key to bytes. std::vector ToPKBytes(const CPubKey& key) const { return {key.begin(), key.end()}; } @@ -236,7 +233,6 @@ const KeyConverter CONVERTER{}; // Helper types and functions that use miniscript instantiated for CPubKey. using NodeType = miniscript::NodeType; using NodeRef = miniscript::NodeRef; -template NodeRef MakeNodeRef(Args&&... args) { return miniscript::MakeNodeRef(std::forward(args)...); } using miniscript::operator"" _mst; //! Determine whether a Miniscript node is satisfiable at all (and thus isn't equivalent to just "false"). @@ -266,135 +262,6 @@ bool Satisfiable(const NodeRef& ref) { return false; } -NodeRef GenNode(miniscript::Type typ, int complexity); - -//! Generate a random valid miniscript node of the given type and complexity. -NodeRef RandomNode(miniscript::Type typ, int complexity) { - assert(complexity > 0); - NodeRef ret; - do { - ret = GenNode(typ, complexity); - } while (!ret || !ret->IsValid() || !(ret->GetType() << typ)); - return ret; -} - -//! Generate a vector of valid miniscript nodes of the given types, and a specified complexity of their sum. -std::vector MultiNode(int complexity, const std::vector& types) -{ - int nodes = types.size(); - assert(complexity >= nodes); - std::vector subcomplex(nodes, 1); - if (nodes == 1) { - subcomplex[0] = complexity; - } else { - // This is a silly inefficient way to construct a multinomial distribution. - for (int i = 0; i < complexity - nodes; ++i) { - subcomplex[InsecureRandRange(nodes)]++; - } - } - std::vector subs; - for (int i = 0; i < nodes; ++i) { - subs.push_back(RandomNode(types[i], subcomplex[i])); - } - return subs; -} - -//! Generate a random (but occasionally invalid) miniscript node of the given type and complexity. -NodeRef GenNode(miniscript::Type typ, int complexity) { - if (typ << "B"_mst) { - // Generate a "B" node. - if (complexity == 1) { - switch (InsecureRandBits(2)) { - case 0: return MakeNodeRef(InsecureRandBool() ? NodeType::JUST_0 : NodeType::JUST_1); - case 1: return MakeNodeRef(InsecureRandBool() ? NodeType::OLDER : NodeType::AFTER, 1 + InsecureRandRange((1ULL << (1 + InsecureRandRange(31))) - 1)); - case 2: { - int hashtype = InsecureRandBits(2); - int index = InsecureRandRange(255); - switch (hashtype) { - case 0: return MakeNodeRef(NodeType::SHA256, g_testdata->sha256[index]); - case 1: return MakeNodeRef(NodeType::RIPEMD160, g_testdata->ripemd160[index]); - case 2: return MakeNodeRef(NodeType::HASH256, g_testdata->hash256[index]); - case 3: return MakeNodeRef(NodeType::HASH160, g_testdata->hash160[index]); - } - break; - } - case 3: return MakeNodeRef(NodeType::WRAP_C, MultiNode(complexity, Vector("K"_mst))); - } - assert(false); - } - switch (InsecureRandRange(7 + (complexity >= 3) * 7 + (complexity >= 4) * 2)) { - // Complexity >= 2 - case 0: return MakeNodeRef(NodeType::WRAP_C, MultiNode(complexity, Vector("K"_mst))); - case 1: return MakeNodeRef(NodeType::WRAP_D, MultiNode(complexity - 1, Vector("V"_mst))); - case 2: return MakeNodeRef(NodeType::WRAP_J, MultiNode(complexity - 1, Vector("B"_mst))); - case 3: return MakeNodeRef(NodeType::WRAP_N, MultiNode(complexity - 1, Vector("B"_mst))); - case 4: return MakeNodeRef(NodeType::OR_I, Cat(MultiNode(complexity - 1, Vector("B"_mst)), Vector(MakeNodeRef(NodeType::JUST_0)))); - case 5: return MakeNodeRef(NodeType::OR_I, Cat(Vector(MakeNodeRef(NodeType::JUST_0)), MultiNode(complexity - 1, Vector("B"_mst)))); - case 6: return MakeNodeRef(NodeType::AND_V, Cat(MultiNode(complexity - 1, Vector("V"_mst)), Vector(MakeNodeRef(NodeType::JUST_1)))); - // Complexity >= 3 - case 7: return MakeNodeRef(NodeType::AND_V, MultiNode(complexity - 1, Vector("V"_mst, "B"_mst))); - case 8: return MakeNodeRef(NodeType::ANDOR, Cat(MultiNode(complexity - 1, Vector("B"_mst, "B"_mst)), Vector(MakeNodeRef(NodeType::JUST_0)))); - case 9: return MakeNodeRef(NodeType::AND_B, MultiNode(complexity - 1, Vector("B"_mst, "W"_mst))); - case 10: return MakeNodeRef(NodeType::OR_B, MultiNode(complexity - 1, Vector("B"_mst, "W"_mst))); - case 11: return MakeNodeRef(NodeType::OR_D, MultiNode(complexity - 1, Vector("B"_mst, "B"_mst))); - case 12: return MakeNodeRef(NodeType::OR_I, MultiNode(complexity - 1, Vector("B"_mst, "B"_mst))); - case 13: { - if (complexity != 3) return {}; - int nkeys = 1 + (InsecureRandRange(15) * InsecureRandRange(25)) / 17; - int sigs = 1 + InsecureRandRange(nkeys); - std::vector keys; - for (int i = 0; i < nkeys; ++i) keys.push_back(g_testdata->pubkeys[InsecureRandRange(255)]); - return MakeNodeRef(NodeType::MULTI, std::move(keys), sigs); - } - // Complexity >= 4 - case 14: return MakeNodeRef(NodeType::ANDOR, MultiNode(complexity - 1, Vector("B"_mst, "B"_mst, "B"_mst))); - case 15: { - int args = 3 + InsecureRandRange(std::min(3, complexity - 3)); - int sats = 2 + InsecureRandRange(args - 2); - return MakeNodeRef(NodeType::THRESH, MultiNode(complexity - 1, Cat(Vector("B"_mst), std::vector(args - 1, "W"_mst))), sats); - } - } - } else if (typ << "V"_mst) { - // Generate a "V" node. - switch (InsecureRandRange(1 + (complexity >= 3) * 3 + (complexity >= 4))) { - // Complexity >= 1 - case 0: return MakeNodeRef(NodeType::WRAP_V, MultiNode(complexity, Vector("B"_mst))); - // Complexity >= 3 - case 1: return MakeNodeRef(NodeType::AND_V, MultiNode(complexity - 1, Vector("V"_mst, "V"_mst))); - case 2: return MakeNodeRef(NodeType::OR_C, MultiNode(complexity - 1, Vector("B"_mst, "V"_mst))); - case 3: return MakeNodeRef(NodeType::OR_I, MultiNode(complexity - 1, Vector("V"_mst, "V"_mst))); - // Complexity >= 4 - case 4: return MakeNodeRef(NodeType::ANDOR, MultiNode(complexity - 1, Vector("B"_mst, "V"_mst, "V"_mst))); - } - } else if (typ << "W"_mst) { - // Generate a "W" node by wrapping a "B" node. - auto sub = RandomNode("B"_mst, complexity); - if (sub->GetType() << "o"_mst) { - if (InsecureRandBool()) return MakeNodeRef(NodeType::WRAP_S, Vector(std::move(sub))); - } - return MakeNodeRef(NodeType::WRAP_A, Vector(std::move(sub))); - } else if (typ << "K"_mst) { - // Generate a "K" node. - if (complexity == 1 || complexity == 2) { - if (InsecureRandBool()) { - return MakeNodeRef(NodeType::PK_K, Vector(g_testdata->pubkeys[InsecureRandRange(255)])); - } else { - return MakeNodeRef(NodeType::PK_H, Vector(g_testdata->pubkeys[InsecureRandRange(255)])); - } - } - switch (InsecureRandRange(2 + (complexity >= 4))) { - // Complexity >= 3 - case 0: return MakeNodeRef(NodeType::AND_V, MultiNode(complexity - 1, Vector("V"_mst, "K"_mst))); - case 1: return MakeNodeRef(NodeType::OR_I, MultiNode(complexity - 1, Vector("K"_mst, "K"_mst))); - // Complexity >= 4 - case 2: return MakeNodeRef(NodeType::ANDOR, MultiNode(complexity - 1, Vector("B"_mst, "K"_mst, "K"_mst))); - } - } - assert(false); - return {}; -} - - /** Compute all challenges (pubkeys, hashes, timelocks) that occur in a given Miniscript. */ std::set FindChallenges(const NodeRef& ref) { std::set chal; @@ -639,36 +506,4 @@ BOOST_AUTO_TEST_CASE(fixed_tests) g_testdata.reset(); } -BOOST_AUTO_TEST_CASE(random_tests) -{ - // Initialize precomputed data. - g_testdata.reset(new TestData()); - - for (int i = 0; i < 1000; ++i) { - bool safe = InsecureRandRange(20) == 0; // In 5% of the cases, generate safe top-level expressions. - // Generate a random B (or Bms) node of variable complexity, which should be valid as a top-level expression. - auto node = RandomNode(safe ? "Bms"_mst : "B"_mst, 1 + InsecureRandRange(90)); - BOOST_CHECK(node && node->IsValid() && node->IsValidTopLevel()); - auto script = node->ToScript(CONVERTER); - BOOST_CHECK(node->ScriptSize() == script.size()); // Check consistency between script size estimation and real size - // Check consistency of "x" property with the script (relying on the fact that no top-level scripts end with a hash or key push, whose last byte could match these opcodes). - bool ends_in_verify = !(node->GetType() << "x"_mst); - BOOST_CHECK(ends_in_verify == (script.back() == OP_CHECKSIG || script.back() == OP_CHECKMULTISIG || script.back() == OP_EQUAL)); - std::string str; - BOOST_CHECK(node->ToString(CONVERTER, str)); // Check that we can convert to text - auto parsed = miniscript::FromString(str, CONVERTER); - BOOST_CHECK(parsed); // Check that we can convert back - BOOST_CHECK(*parsed == *node); // Check that it matches the original - auto decoded = miniscript::FromScript(script, CONVERTER); - BOOST_CHECK(decoded); // Check that we can decode the miniscript back from the script. - // Check that it matches the original (we can't use *decoded == *node because the miniscript representation may differ) - BOOST_CHECK(decoded->ToScript(CONVERTER) == script); // The script corresponding to that decoded form must match exactly. - BOOST_CHECK(decoded->GetType() == node->GetType()); // The type also has to match exactly. - // Random satisfaction tests. - TestSatisfy(str, node); - } - - g_testdata.reset(); -} - BOOST_AUTO_TEST_SUITE_END() From 55f2edecb57e6e166996ffcf1d480481bd1103ca Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Fri, 18 Feb 2022 14:51:00 +0100 Subject: [PATCH 3/3] Update with the latest version of the Bitcoin Core PR There are a few invasive changes that were made following review of the first Bitcoin Core PR: - Renaming NodeType to Fragment - Refactoring various switchs for better readability - Refactoring DecodeScript to use less long lines for parsing of hash fragments --- bitcoin/script/miniscript.cpp | 136 +++--- bitcoin/script/miniscript.h | 715 +++++++++++++++++------------- bitcoin/test/miniscript_tests.cpp | 42 +- 3 files changed, 497 insertions(+), 396 deletions(-) diff --git a/bitcoin/script/miniscript.cpp b/bitcoin/script/miniscript.cpp index de01f86..7b1815c 100644 --- a/bitcoin/script/miniscript.cpp +++ b/bitcoin/script/miniscript.cpp @@ -35,42 +35,42 @@ Type SanitizeType(Type e) { return e; } -Type ComputeType(NodeType nodetype, Type x, Type y, Type z, const std::vector& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys) { +Type ComputeType(Fragment nodetype, Type x, Type y, Type z, const std::vector& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys) { // Sanity check on data - if (nodetype == NodeType::SHA256 || nodetype == NodeType::HASH256) { + if (nodetype == Fragment::SHA256 || nodetype == Fragment::HASH256) { assert(data_size == 32); - } else if (nodetype == NodeType::RIPEMD160 || nodetype == NodeType::HASH160) { + } else if (nodetype == Fragment::RIPEMD160 || nodetype == Fragment::HASH160) { assert(data_size == 20); } else { assert(data_size == 0); } // Sanity check on k - if (nodetype == NodeType::OLDER || nodetype == NodeType::AFTER) { + if (nodetype == Fragment::OLDER || nodetype == Fragment::AFTER) { assert(k >= 1 && k < 0x80000000UL); - } else if (nodetype == NodeType::MULTI) { + } else if (nodetype == Fragment::MULTI) { assert(k >= 1 && k <= n_keys); - } else if (nodetype == NodeType::THRESH) { + } else if (nodetype == Fragment::THRESH) { assert(k >= 1 && k <= n_subs); } else { assert(k == 0); } // Sanity check on subs - if (nodetype == NodeType::AND_V || nodetype == NodeType::AND_B || nodetype == NodeType::OR_B || - nodetype == NodeType::OR_C || nodetype == NodeType::OR_I || nodetype == NodeType::OR_D) { + if (nodetype == Fragment::AND_V || nodetype == Fragment::AND_B || nodetype == Fragment::OR_B || + nodetype == Fragment::OR_C || nodetype == Fragment::OR_I || nodetype == Fragment::OR_D) { assert(n_subs == 2); - } else if (nodetype == NodeType::ANDOR) { + } else if (nodetype == Fragment::ANDOR) { assert(n_subs == 3); - } else if (nodetype == NodeType::WRAP_A || nodetype == NodeType::WRAP_S || nodetype == NodeType::WRAP_C || - nodetype == NodeType::WRAP_D || nodetype == NodeType::WRAP_V || nodetype == NodeType::WRAP_J || - nodetype == NodeType::WRAP_N) { + } else if (nodetype == Fragment::WRAP_A || nodetype == Fragment::WRAP_S || nodetype == Fragment::WRAP_C || + nodetype == Fragment::WRAP_D || nodetype == Fragment::WRAP_V || nodetype == Fragment::WRAP_J || + nodetype == Fragment::WRAP_N) { assert(n_subs == 1); - } else if (nodetype != NodeType::THRESH) { + } else if (nodetype != Fragment::THRESH) { assert(n_subs == 0); } // Sanity check on keys - if (nodetype == NodeType::PK_K || nodetype == NodeType::PK_H) { + if (nodetype == Fragment::PK_K || nodetype == Fragment::PK_H) { assert(n_keys == 1); - } else if (nodetype == NodeType::MULTI) { + } else if (nodetype == Fragment::MULTI) { assert(n_keys >= 1 && n_keys <= 20); } else { assert(n_keys == 0); @@ -80,59 +80,59 @@ Type ComputeType(NodeType nodetype, Type x, Type y, Type z, const std::vector= LOCKTIME_THRESHOLD) | "j"_mst.If(k < LOCKTIME_THRESHOLD) | "Bzfmxk"_mst; - case NodeType::SHA256: return "Bonudmk"_mst; - case NodeType::RIPEMD160: return "Bonudmk"_mst; - case NodeType::HASH256: return "Bonudmk"_mst; - case NodeType::HASH160: return "Bonudmk"_mst; - case NodeType::JUST_1: return "Bzufmxk"_mst; - case NodeType::JUST_0: return "Bzudemsxk"_mst; - case NodeType::WRAP_A: return + case Fragment::SHA256: return "Bonudmk"_mst; + case Fragment::RIPEMD160: return "Bonudmk"_mst; + case Fragment::HASH256: return "Bonudmk"_mst; + case Fragment::HASH160: return "Bonudmk"_mst; + case Fragment::JUST_1: return "Bzufmxk"_mst; + case Fragment::JUST_0: return "Bzudemsxk"_mst; + case Fragment::WRAP_A: return "W"_mst.If(x << "B"_mst) | // W=B_x (x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x (x & "udfems"_mst) | // u=u_x, d=d_x, f=f_x, e=e_x, m=m_x, s=s_x "x"_mst; // x - case NodeType::WRAP_S: return + case Fragment::WRAP_S: return "W"_mst.If(x << "Bo"_mst) | // W=B_x*o_x (x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x (x & "udfemsx"_mst); // u=u_x, d=d_x, f=f_x, e=e_x, m=m_x, s=s_x, x=x_x - case NodeType::WRAP_C: return + case Fragment::WRAP_C: return "B"_mst.If(x << "K"_mst) | // B=K_x (x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x (x & "ondfem"_mst) | // o=o_x, n=n_x, d=d_x, f=f_x, e=e_x, m=m_x "us"_mst; // u, s - case NodeType::WRAP_D: return + case Fragment::WRAP_D: return "B"_mst.If(x << "Vz"_mst) | // B=V_x*z_x "o"_mst.If(x << "z"_mst) | // o=z_x "e"_mst.If(x << "f"_mst) | // e=f_x (x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x (x & "ms"_mst) | // m=m_x, s=s_x "nudx"_mst; // n, u, d, x - case NodeType::WRAP_V: return + case Fragment::WRAP_V: return "V"_mst.If(x << "B"_mst) | // V=B_x (x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x (x & "zonms"_mst) | // z=z_x, o=o_x, n=n_x, m=m_x, s=s_x "fx"_mst; // f, x - case NodeType::WRAP_J: return + case Fragment::WRAP_J: return "B"_mst.If(x << "Bn"_mst) | // B=B_x*n_x "e"_mst.If(x << "f"_mst) | // e=f_x (x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x (x & "oums"_mst) | // o=o_x, u=u_x, m=m_x, s=s_x "ndx"_mst; // n, d, x - case NodeType::WRAP_N: return + case Fragment::WRAP_N: return (x & "ghijk"_mst) | // g=g_x, h=h_x, i=i_x, j=j_x, k=k_x (x & "Bzondfems"_mst) | // B=B_x, z=z_x, o=o_x, n=n_x, d=d_x, f=f_x, e=e_x, m=m_x, s=s_x "ux"_mst; // u, x - case NodeType::AND_V: return + case Fragment::AND_V: return (y & "KVB"_mst).If(x << "V"_mst) | // B=V_x*B_y, V=V_x*V_y, K=V_x*K_y (x & "n"_mst) | (y & "n"_mst).If(x << "z"_mst) | // n=n_x+z_x*n_y ((x | y) & "o"_mst).If((x | y) << "z"_mst) | // o=o_x*z_y+z_x*o_y @@ -146,7 +146,7 @@ Type ComputeType(NodeType nodetype, Type x, Type y, Type z, const std::vector 16) + (k > 16) + 34 * n_keys; + case Fragment::JUST_1: + case Fragment::JUST_0: return 1; + case Fragment::PK_K: return 34; + case Fragment::PK_H: return 3 + 21; + case Fragment::OLDER: return 1 + BuildScript(k).size(); + case Fragment::AFTER: return 1 + BuildScript(k).size(); + case Fragment::HASH256: + case Fragment::SHA256: return 4 + 2 + 33; + case Fragment::HASH160: + case Fragment::RIPEMD160: return 4 + 2 + 21; + case Fragment::MULTI: return 3 + (n_keys > 16) + (k > 16) + 34 * n_keys; + case Fragment::AND_V: return subsize; + case Fragment::WRAP_V: return subsize + (sub0typ << "x"_mst); + case Fragment::WRAP_S: + case Fragment::WRAP_C: + case Fragment::WRAP_N: + case Fragment::AND_B: + case Fragment::OR_B: return subsize + 1; + case Fragment::WRAP_A: + case Fragment::OR_C: return subsize + 2; + case Fragment::WRAP_D: + case Fragment::OR_D: + case Fragment::OR_I: + case Fragment::ANDOR: return subsize + 3; + case Fragment::WRAP_J: return subsize + 4; + case Fragment::THRESH: return subsize + n_subs + BuildScript(k).size(); } assert(false); return 0; diff --git a/bitcoin/script/miniscript.h b/bitcoin/script/miniscript.h index ffef285..b12f392 100644 --- a/bitcoin/script/miniscript.h +++ b/bitcoin/script/miniscript.h @@ -16,13 +16,13 @@ #include #include +#include #include