diff --git a/bitcoin/script/miniscript.cpp b/bitcoin/script/miniscript.cpp index 2e3feff..958fa45 100644 --- a/bitcoin/script/miniscript.cpp +++ b/bitcoin/script/miniscript.cpp @@ -351,16 +351,15 @@ InputStack operator|(InputStack a, InputStack b) { } } -bool DecomposeScript(const CScript& script, std::vector>>& out) +std::optional>>> DecomposeScript(const CScript& script) { - out.clear(); + std::vector>> out; CScript::const_iterator it = script.begin(), itend = script.end(); while (it != itend) { std::vector push_data; opcodetype opcode; if (!script.GetOp(it, opcode, push_data)) { - out.clear(); - return false; + return {}; } else if (opcode >= OP_1 && opcode <= OP_16) { // Deal with OP_n (GetOp does not turn them into pushes). push_data.assign(1, CScript::DecodeOP_N(opcode)); @@ -377,30 +376,28 @@ bool DecomposeScript(const CScript& script, std::vector()); opcode = OP_VERIFY; } else if (IsPushdataOp(opcode)) { - if (!CheckMinimalPush(push_data, opcode)) return false; + if (!CheckMinimalPush(push_data, opcode)) return {}; } else if (it != itend && (opcode == OP_CHECKSIG || opcode == OP_CHECKMULTISIG || opcode == OP_EQUAL) && (*it == OP_VERIFY)) { // Rule out non minimal VERIFY sequences - return false; + return {}; } out.emplace_back(opcode, std::move(push_data)); } std::reverse(out.begin(), out.end()); - return true; + return out; } -bool ParseScriptNumber(const std::pair>& in, int64_t& k) { +std::optional ParseScriptNumber(const std::pair>& in) { if (in.first == OP_0) { - k = 0; - return true; + return 0; } if (!in.second.empty()) { - if (IsPushdataOp(in.first) && !CheckMinimalPush(in.second, in.first)) return false; + if (IsPushdataOp(in.first) && !CheckMinimalPush(in.second, in.first)) return {}; try { - k = CScriptNum(in.second, true).GetInt64(); - return true; + return CScriptNum(in.second, true).GetInt64(); } catch(const scriptnum_error&) {} } - return false; + return {}; } int FindNextChar(Span sp, const char m) diff --git a/bitcoin/script/miniscript.h b/bitcoin/script/miniscript.h index 2a49c82..c252805 100644 --- a/bitcoin/script/miniscript.h +++ b/bitcoin/script/miniscript.h @@ -588,7 +588,7 @@ struct Node { } template - bool ToString(const CTx& ctx, std::string& ret) const { + std::optional ToString(const CTx& ctx) const { // To construct the std::string representation for a Miniscript object, we use // the TreeEvalMaybe algorithm. The State is a boolean: whether the parent node is a // wrapper. If so, non-wrapper expressions must be prefixed with a ":". @@ -612,15 +612,15 @@ struct Node { case Fragment::WRAP_C: if (node.subs[0]->fragment == Fragment::PK_K) { // pk(K) is syntactic sugar for c:pk_k(K) - std::string key_str; - if (!ctx.ToString(node.subs[0]->keys[0], key_str)) return {}; - return std::move(ret) + "pk(" + std::move(key_str) + ")"; + auto key_str = ctx.ToString(node.subs[0]->keys[0]); + if (!key_str) return {}; + return std::move(ret) + "pk(" + std::move(*key_str) + ")"; } if (node.subs[0]->fragment == Fragment::PK_H) { // pkh(K) is syntactic sugar for c:pk_h(K) - std::string key_str; - if (!ctx.ToString(node.subs[0]->keys[0], key_str)) return {}; - return std::move(ret) + "pkh(" + std::move(key_str) + ")"; + auto key_str = ctx.ToString(node.subs[0]->keys[0]); + if (!key_str) return {}; + return std::move(ret) + "pkh(" + std::move(*key_str) + ")"; } return "c" + std::move(subs[0]); case Fragment::WRAP_D: return "d" + std::move(subs[0]); @@ -639,14 +639,14 @@ struct Node { } switch (node.fragment) { case Fragment::PK_K: { - std::string key_str; - if (!ctx.ToString(node.keys[0], key_str)) return {}; - return std::move(ret) + "pk_k(" + std::move(key_str) + ")"; + auto key_str = ctx.ToString(node.keys[0]); + if (!key_str) return {}; + return std::move(ret) + "pk_k(" + std::move(*key_str) + ")"; } case Fragment::PK_H: { - std::string key_str; - if (!ctx.ToString(node.keys[0], key_str)) return {}; - return std::move(ret) + "pk_h(" + std::move(key_str) + ")"; + auto key_str = ctx.ToString(node.keys[0]); + if (!key_str) return {}; + return std::move(ret) + "pk_h(" + std::move(*key_str) + ")"; } case Fragment::AFTER: return std::move(ret) + "after(" + ::ToString(node.k) + ")"; case Fragment::OLDER: return std::move(ret) + "older(" + ::ToString(node.k) + ")"; @@ -669,9 +669,9 @@ struct Node { case Fragment::MULTI: { 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 {}; - str += "," + std::move(key_str); + auto key_str = ctx.ToString(key); + if (!key_str) return {}; + str += "," + std::move(*key_str); } return std::move(str) + ")"; } @@ -687,9 +687,7 @@ struct Node { return ""; // Should never be reached. }; - auto res = TreeEvalMaybe(false, downfn, upfn); - if (res.has_value()) ret = std::move(*res); - return res.has_value(); + return TreeEvalMaybe(false, downfn, upfn); } internal::Ops CalcOps() const { @@ -1181,11 +1179,11 @@ int FindNextChar(Span in, const char m); template std::optional> ParseKeyEnd(Span in, const Ctx& ctx) { - Key key; int key_size = FindNextChar(in, ')'); if (key_size < 1) return {}; - if (!ctx.FromString(in.begin(), in.begin() + key_size, key)) return {}; - return {{std::move(key), key_size}}; + auto key = ctx.FromString(in.begin(), in.begin() + key_size); + if (!key) return {}; + return {{std::move(*key), key_size}}; } /** Parse a hex string ending at the end of the fragment's text representation. */ @@ -1356,12 +1354,12 @@ inline NodeRef Parse(Span in, const Ctx& ctx) // Get keys std::vector keys; while (next_comma != -1) { - Key key; next_comma = FindNextChar(in, ','); int key_length = (next_comma == -1) ? FindNextChar(in, ')') : next_comma; if (key_length < 1) return {}; - if (!ctx.FromString(in.begin(), in.begin() + key_length, key)) return {}; - keys.push_back(std::move(key)); + auto key = ctx.FromString(in.begin(), in.begin() + key_length); + if (!key) return {}; + keys.push_back(std::move(*key)); in = in.subspan(key_length + 1); } if (keys.size() < 1 || keys.size() > 20) return {}; @@ -1532,10 +1530,10 @@ inline NodeRef Parse(Span in, const Ctx& ctx) * and OP_EQUALVERIFY are decomposed into OP_CHECKSIG, OP_CHECKMULTISIG, OP_EQUAL * respectively, plus OP_VERIFY. */ -bool DecomposeScript(const CScript& script, std::vector>>& out); +std::optional>>> DecomposeScript(const CScript& script); /** Determine whether the passed pair (created by DecomposeScript) is pushing a number. */ -bool ParseScriptNumber(const std::pair>& in, int64_t& k); +std::optional ParseScriptNumber(const std::pair>& in); enum class DecodeContext { /** A single expression of type B, K, or V. Specifically, this can't be an @@ -1642,34 +1640,38 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) } // Public keys if (in[0].second.size() == 33) { - Key key; - if (!ctx.FromPKBytes(in[0].second.begin(), in[0].second.end(), key)) return {}; + auto key = ctx.FromPKBytes(in[0].second.begin(), in[0].second.end()); + if (!key) return {}; ++in; - constructed.push_back(MakeNodeRef(Fragment::PK_K, Vector(std::move(key)))); + constructed.push_back(MakeNodeRef(Fragment::PK_K, Vector(std::move(*key)))); break; } if (last - in >= 5 && in[0].first == OP_VERIFY && in[1].first == OP_EQUAL && in[3].first == OP_HASH160 && in[4].first == OP_DUP && in[2].second.size() == 20) { - Key key; - if (!ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end(), key)) return {}; + auto key = ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end()); + if (!key) return {}; in += 5; - constructed.push_back(MakeNodeRef(Fragment::PK_H, Vector(std::move(key)))); + constructed.push_back(MakeNodeRef(Fragment::PK_H, Vector(std::move(*key)))); break; } // Time locks - if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && ParseScriptNumber(in[1], k)) { + if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY) { + auto num = ParseScriptNumber(in[1]); in += 2; - if (k < 1 || k > 0x7FFFFFFFL) return {}; - constructed.push_back(MakeNodeRef(Fragment::OLDER, k)); + if (!num || *num < 1 || *num > 0x7FFFFFFFL) return {}; + constructed.push_back(MakeNodeRef(Fragment::OLDER, *num)); break; } - if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && ParseScriptNumber(in[1], k)) { + if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY) { + auto num = ParseScriptNumber(in[1]); in += 2; - if (k < 1 || k > 0x7FFFFFFFL) return {}; - constructed.push_back(MakeNodeRef(Fragment::AFTER, k)); + if (!num || num < 1 || num > 0x7FFFFFFFL) return {}; + constructed.push_back(MakeNodeRef(Fragment::AFTER, *num)); break; } // Hashes - if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && ParseScriptNumber(in[5], k) && k == 32 && in[6].first == OP_SIZE) { + if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && in[6].first == OP_SIZE) { + auto num = ParseScriptNumber(in[5]); + if (!num || num != 32) return {}; if (in[2].first == OP_SHA256 && in[1].second.size() == 32) { constructed.push_back(MakeNodeRef(Fragment::SHA256, in[1].second)); in += 7; @@ -1691,20 +1693,22 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) // Multi if (last - in >= 3 && in[0].first == OP_CHECKMULTISIG) { std::vector keys; - if (!ParseScriptNumber(in[1], n)) return {}; - if (last - in < 3 + n) return {}; - if (n < 1 || n > 20) return {}; - for (int i = 0; i < n; ++i) { - Key key; + auto n = ParseScriptNumber(in[1]); + if (!n) return {}; + if (last - in < 3 + *n) return {}; + if (*n < 1 || *n > 20) return {}; + for (int i = 0; i < *n; ++i) { if (in[2 + i].second.size() != 33) return {}; - if (!ctx.FromPKBytes(in[2 + i].second.begin(), in[2 + i].second.end(), key)) return {}; - keys.push_back(std::move(key)); + auto key = ctx.FromPKBytes(in[2 + i].second.begin(), in[2 + i].second.end()); + if (!key) return {}; + keys.push_back(std::move(*key)); } - if (!ParseScriptNumber(in[2 + n], k)) return {}; - if (k < 1 || k > n) return {}; - in += 3 + n; + auto k = ParseScriptNumber(in[2 + *n]); + if (!k) return {}; + if (*k < 1 || *k > *n) return {}; + in += 3 + *n; std::reverse(keys.begin(), keys.end()); - constructed.push_back(MakeNodeRef(Fragment::MULTI, std::move(keys), k)); + constructed.push_back(MakeNodeRef(Fragment::MULTI, std::move(keys), *k)); break; } /** In the following wrappers, we only need to push SINGLE_BKV_EXPR rather @@ -1732,10 +1736,11 @@ inline NodeRef DecodeScript(I& in, I last, const Ctx& ctx) break; } // Thresh - if (last - in >= 3 && in[0].first == OP_EQUAL && ParseScriptNumber(in[1], k)) { - if (k < 1) return {}; + if (last - in >= 3 && in[0].first == OP_EQUAL) { + auto k = ParseScriptNumber(in[1]); + if (!k || *k < 1) return {}; in += 2; - to_parse.emplace_back(DecodeContext::THRESH_W, 0, k); + to_parse.emplace_back(DecodeContext::THRESH_W, 0, *k); break; } // OP_ENDIF can be WRAP_J, WRAP_D, ANDOR, OR_C, OR_D, or OR_I @@ -1970,12 +1975,12 @@ inline NodeRef FromString(const std::string& str, const Ctx& template inline NodeRef FromScript(const CScript& script, const Ctx& ctx) { using namespace internal; - std::vector>> decomposed; - if (!DecomposeScript(script, decomposed)) return {}; - auto it = decomposed.begin(); - auto ret = DecodeScript(it, decomposed.end(), ctx); + auto decomposed = DecomposeScript(script); + if (!decomposed) return {}; + auto it = decomposed->begin(); + auto ret = DecodeScript(it, decomposed->end(), ctx); if (!ret) return {}; - if (it != decomposed.end()) return {}; + if (it != decomposed->end()) return {}; return ret; } diff --git a/bitcoin/test/fuzz/miniscript.cpp b/bitcoin/test/fuzz/miniscript.cpp index c776eb2..1231cbe 100644 --- a/bitcoin/test/fuzz/miniscript.cpp +++ b/bitcoin/test/fuzz/miniscript.cpp @@ -76,13 +76,12 @@ struct TestData { struct ParserContext { typedef CPubKey Key; - bool ToString(const Key& key, std::string& ret) const + std::optional ToString(const Key& key) const { auto it = TEST_DATA.dummy_key_idx_map.find(key); - if (it == TEST_DATA.dummy_key_idx_map.end()) return false; + if (it == TEST_DATA.dummy_key_idx_map.end()) return {}; uint8_t idx = it->second; - ret = HexStr(Span{&idx, 1}); - return true; + return HexStr(Span{&idx, 1}); } const std::vector ToPKBytes(const Key& key) const @@ -97,29 +96,29 @@ struct ParserContext { } template - bool FromString(I first, I last, Key& key) const { - if (last - first != 2) return false; + std::optional FromString(I first, I last) const { + if (last - first != 2) return {}; auto idx = ParseHex(std::string(first, last)); - if (idx.size() != 1) return false; - key = TEST_DATA.dummy_keys[idx[0]]; - return true; + if (idx.size() != 1) return {}; + return TEST_DATA.dummy_keys[idx[0]]; } template - bool FromPKBytes(I first, I last, CPubKey& key) const { + std::optional FromPKBytes(I first, I last) const { + CPubKey key; key.Set(first, last); - return key.IsValid(); + if (!key.IsValid()) return {}; + return key; } template - bool FromPKHBytes(I first, I last, CPubKey& key) const { + std::optional FromPKHBytes(I first, I last) const { assert(last - first == 20); CKeyID keyid; std::copy(first, last, keyid.begin()); const auto it = TEST_DATA.dummy_keys_map.find(keyid); - if (it == TEST_DATA.dummy_keys_map.end()) return false; - key = it->second; - return true; + if (it == TEST_DATA.dummy_keys_map.end()) return {}; + return it->second; } } PARSER_CTX; @@ -144,19 +143,21 @@ struct ScriptParserContext { } template - bool FromPKBytes(I first, I last, Key& key) const + std::optional FromPKBytes(I first, I last) const { + Key key; key.data.assign(first, last); key.is_hash = false; - return true; + return key; } template - bool FromPKHBytes(I first, I last, Key& key) const + std::optional FromPKHBytes(I first, I last) const { + Key key; key.data.assign(first, last); key.is_hash = true; - return true; + return key; } } SCRIPT_PARSER_CONTEXT; diff --git a/bitcoin/test/miniscript_tests.cpp b/bitcoin/test/miniscript_tests.cpp index bf76702..0718ef2 100644 --- a/bitcoin/test/miniscript_tests.cpp +++ b/bitcoin/test/miniscript_tests.cpp @@ -126,27 +126,33 @@ struct KeyConverter { //! Parse a public key from a range of hex characters. template - bool FromString(I first, I last, CPubKey& key) const { + std::optional FromString(I first, I last) const + { auto bytes = ParseHex(std::string(first, last)); + CPubKey key; key.Set(bytes.begin(), bytes.end()); - return key.IsValid(); + if (!key.IsValid()) return {}; + return key; } template - bool FromPKBytes(I first, I last, CPubKey& key) const { + std::optional bool FromPKBytes(I first, I last) const + { + CPubKey key; key.Set(first, last); - return key.IsValid(); + if (!key.IsValid()) return {}; + return key; } template - bool FromPKHBytes(I first, I last, CPubKey& key) const { + std::optional FromPKHBytes(I first, I last) const + { assert(last - first == 20); CKeyID keyid; std::copy(first, last, keyid.begin()); auto it = g_testdata->pkmap.find(keyid); assert(it != g_testdata->pkmap.end()); - key = it->second; - return true; + return it->second; } }; diff --git a/compiler.cpp b/compiler.cpp index 3ced936..d0b6de3 100644 --- a/compiler.cpp +++ b/compiler.cpp @@ -55,15 +55,16 @@ struct Policy { Policy& operator=(Policy&& x) = default; Policy(Policy&& x) = default; - explicit Policy(Type nt) : node_type(nt) {} - explicit Policy(Type nt, uint32_t kv) : node_type(nt), k(kv) {} - explicit Policy(Type nt, std::vector&& dat) : node_type(nt), data(std::move(dat)) {} - explicit Policy(Type nt, std::vector&& dat, uint32_t kv) : node_type(nt), data(std::move(dat)), k(kv) {} - explicit Policy(Type nt, std::vector&& subs) : node_type(nt), sub(std::move(subs)) {} - explicit Policy(Type nt, std::vector&& key) : node_type(nt), keys(std::move(key)) {} - explicit Policy(Type nt, std::vector&& subs, std::vector&& probs) : node_type(nt), sub(std::move(subs)), prob(std::move(probs)) {} - explicit Policy(Type nt, std::vector&& subs, uint32_t kv) : node_type(nt), sub(std::move(subs)), k(kv) {} - explicit Policy(Type nt, std::vector&& key, uint32_t kv) : node_type(nt), keys(std::move(key)), k(kv) {} + Policy() {} + Policy(Type nt) : node_type(nt) {} + Policy(Type nt, uint32_t kv) : node_type(nt), k(kv) {} + Policy(Type nt, std::vector&& dat) : node_type(nt), data(std::move(dat)) {} + Policy(Type nt, std::vector&& dat, uint32_t kv) : node_type(nt), data(std::move(dat)), k(kv) {} + Policy(Type nt, std::vector&& subs) : node_type(nt), sub(std::move(subs)) {} + Policy(Type nt, std::vector&& key) : node_type(nt), keys(std::move(key)) {} + Policy(Type nt, std::vector&& subs, std::vector&& probs) : node_type(nt), sub(std::move(subs)), prob(std::move(probs)) {} + Policy(Type nt, std::vector&& subs, uint32_t kv) : node_type(nt), sub(std::move(subs)), k(kv) {} + Policy(Type nt, std::vector&& key, uint32_t kv) : node_type(nt), keys(std::move(key)), k(kv) {} bool operator()() const { return node_type != Type::NONE; } }; @@ -96,87 +97,85 @@ Policy Parse(Span& in) { using namespace spanparsing; auto expr = Expr(in); if (Func("pk", expr)) { - CompilerContext::Key key; - if (COMPILER_CTX.FromString(expr.begin(), expr.end(), key)) { - return Policy(Policy::Type::PK_K, Vector(std::move(key))); - } - return Policy(Policy::Type::NONE); + auto key = COMPILER_CTX.FromString(expr.begin(), expr.end()); + if (key) return {Policy::Type::PK_K, Vector(std::move(*key))}; + return {}; } else if (Func("after", expr)) { uint64_t num; if (!ParseUInt64(std::string(expr.begin(), expr.end()), &num)) { - return Policy(Policy::Type::NONE); + return Policy::Type::NONE; } if (num >= 1 && num < 0x80000000UL) { - return Policy(Policy::Type::AFTER, num); + return {Policy::Type::AFTER, uint32_t(num)}; } - return Policy(Policy::Type::NONE); + return {}; } else if (Func("older", expr)) { uint64_t num; if (!ParseUInt64(std::string(expr.begin(), expr.end()), &num)) { - return Policy(Policy::Type::NONE); + return Policy::Type::NONE; } if (num >= 1 && num < 0x80000000UL) { - return Policy(Policy::Type::OLDER, num); + return {Policy::Type::OLDER, uint32_t(num)}; } - return Policy(Policy::Type::NONE); + return {}; } else if (Func("sha256", expr)) { auto hash = Hash(expr, 32); - if (hash.size()) return Policy(Policy::Type::SHA256, std::move(hash)); - return Policy(Policy::Type::NONE); + if (hash.size()) return {Policy::Type::SHA256, std::move(hash)}; + return {}; } else if (Func("ripemd160", expr)) { auto hash = Hash(expr, 20); - if (hash.size()) return Policy(Policy::Type::RIPEMD160, std::move(hash)); - return Policy(Policy::Type::NONE); + if (hash.size()) return {Policy::Type::RIPEMD160, std::move(hash)}; + return {}; } else if (Func("hash256", expr)) { auto hash = Hash(expr, 32); - if (hash.size()) return Policy(Policy::Type::HASH256, std::move(hash)); - return Policy(Policy::Type::NONE); + if (hash.size()) return {Policy::Type::HASH256, std::move(hash)}; + return {}; } else if (Func("hash160", expr)) { auto hash = Hash(expr, 20); - if (hash.size()) return Policy(Policy::Type::HASH160, std::move(hash)); - return Policy(Policy::Type::NONE); + if (hash.size()) return {Policy::Type::HASH160, std::move(hash)}; + return {}; } else if (Func("or", expr)) { std::vector sub; std::vector prob; uint32_t p; sub.emplace_back(ParseProb(expr, p)); - if (!sub.back()()) return Policy(Policy::Type::NONE); + if (!sub.back()()) return {}; prob.push_back(p); while (expr.size()) { - if (!Const(",", expr)) return Policy(Policy::Type::NONE); + if (!Const(",", expr)) return {}; sub.emplace_back(ParseProb(expr, p)); - if (!sub.back()()) return Policy(Policy::Type::NONE); + if (!sub.back()()) return {}; prob.push_back(p); } - return Policy(Policy::Type::OR, std::move(sub), std::move(prob)); + return {Policy::Type::OR, std::move(sub), std::move(prob)}; } else if (Func("and", expr)) { std::vector sub; sub.emplace_back(Parse(expr)); - if (!sub.back()()) return Policy(Policy::Type::NONE); + if (!sub.back()()) return {}; while (expr.size()) { - if (!Const(",", expr)) return Policy(Policy::Type::NONE); + if (!Const(",", expr)) return {}; sub.emplace_back(Parse(expr)); - if (!sub.back()()) return Policy(Policy::Type::NONE); + if (!sub.back()()) return {}; } - return Policy(Policy::Type::AND, std::move(sub)); + return {Policy::Type::AND, std::move(sub)}; } else if (Func("thresh", expr)) { auto arg = Expr(expr); uint32_t count; if (!ParseUInt32(std::string(arg.begin(), arg.end()), &count)) { - return Policy(Policy::Type::NONE); + return {}; } - if (count < 1) return Policy(Policy::Type::NONE); + if (count < 1) return {}; std::vector sub; while (expr.size()) { - if (!Const(",", expr)) return Policy(Policy::Type::NONE); + if (!Const(",", expr)) return {}; sub.emplace_back(Parse(expr)); - if (!sub.back()()) return Policy(Policy::Type::NONE); + if (!sub.back()()) return {}; } - if (sub.size() > 100 || count > sub.size()) return Policy(Policy::Type::NONE); - return Policy(Policy::Type::THRESH, std::move(sub), count); + if (sub.size() > 100 || count > sub.size()) return {}; + return {Policy::Type::THRESH, std::move(sub), count}; } - return Policy(Policy::Type::NONE); + return {}; } Policy Parse(const std::string& in) { diff --git a/compiler.h b/compiler.h index 04c3df6..6056253 100644 --- a/compiler.h +++ b/compiler.h @@ -12,13 +12,12 @@ struct CompilerContext { typedef std::string Key; - bool ToString(const Key& key, std::string& str) const { str = key; return true; } + std::optional ToString(const Key& key) const { return key; } template - bool FromString(I first, I last, Key& key) const { - if (std::distance(first, last) == 0 || std::distance(first, last) > 17) return false; - key = std::string(first, last); - return true; + std::optional FromString(I first, I last) const { + if (std::distance(first, last) == 0 || std::distance(first, last) > 17) return {}; + return std::string(first, last); } std::vector ToPKBytes(const Key& key) const { diff --git a/main.cpp b/main.cpp index 6421dd0..32dfc67 100644 --- a/main.cpp +++ b/main.cpp @@ -17,13 +17,13 @@ static bool run(std::string&& line, int64_t count) { miniscript::NodeRef ret; double avgcost = 0; if (Compile(Expand(line), ret, avgcost)) { - std::string str; - ret->ToString(COMPILER_CTX, str); - printf("X %17.10f %5i %s %s\n", ret->ScriptSize() + avgcost, (int)ret->ScriptSize(), Abbreviate(std::move(str)).c_str(), line.c_str()); + auto str = ret->ToString(COMPILER_CTX); + assert(str); + printf("X %17.10f %5i %s %s\n", ret->ScriptSize() + avgcost, (int)ret->ScriptSize(), Abbreviate(std::move(*str)).c_str(), line.c_str()); } else if ((ret = miniscript::FromString(Expand(line), COMPILER_CTX))) { - std::string ms; - ret->ToString(COMPILER_CTX, ms); - printf("%7li scriptlen=%i maxops=%i type=%s safe=%s nonmal=%s dissat=%s input=%s output=%s timelock_mix=%s miniscript=%s\n", (long)count, (int)ret->ScriptSize(), (int)ret->GetOps(), ret->GetType() << "B"_mst ? "B" : ret->GetType() << "V"_mst ? "V" : ret->GetType() << "W"_mst ? "W" : ret->GetType() << "K"_mst ? "K" : "(invalid)", ret->GetType() << "s"_mst ? "yes" : "no", ret->GetType() << "m"_mst ? "yes" : "no", ret->GetType() << "f"_mst ? "no" : ret->GetType() << "e"_mst ? "unique" : ret->GetType() << "d"_mst ? "yes" : "unknown", ret->GetType() << "z"_mst ? "0" : ret->GetType() << "o"_mst ? (ret->GetType() << "n"_mst ? "1n" : "1") : ret->GetType() << "n"_mst ? "n" : "-", ret->GetType() << "u"_mst ? "1" : "nonzero", ret->GetType() << "k"_mst ? "no": "yes", Abbreviate(ms).c_str()); + auto ms = ret->ToString(COMPILER_CTX); + assert(ms); + printf("%7li scriptlen=%i maxops=%i type=%s safe=%s nonmal=%s dissat=%s input=%s output=%s timelock_mix=%s miniscript=%s\n", (long)count, (int)ret->ScriptSize(), (int)ret->GetOps(), ret->GetType() << "B"_mst ? "B" : ret->GetType() << "V"_mst ? "V" : ret->GetType() << "W"_mst ? "W" : ret->GetType() << "K"_mst ? "K" : "(invalid)", ret->GetType() << "s"_mst ? "yes" : "no", ret->GetType() << "m"_mst ? "yes" : "no", ret->GetType() << "f"_mst ? "no" : ret->GetType() << "e"_mst ? "unique" : ret->GetType() << "d"_mst ? "yes" : "unknown", ret->GetType() << "z"_mst ? "0" : ret->GetType() << "o"_mst ? (ret->GetType() << "n"_mst ? "1n" : "1") : ret->GetType() << "n"_mst ? "n" : "-", ret->GetType() << "u"_mst ? "1" : "nonzero", ret->GetType() << "k"_mst ? "no": "yes", Abbreviate(*ms).c_str()); } else { printf("Failed to parse as policy or miniscript '%s'\n", line.c_str()); }