Skip to content

Commit ed45ee3

Browse files
darosiorsipa
andcommitted
miniscript: use optional instead of bool/outarg
Co-authored-by: Pieter Wuille <[email protected]>
1 parent 1ab8d89 commit ed45ee3

File tree

4 files changed

+92
-96
lines changed

4 files changed

+92
-96
lines changed

src/script/miniscript.cpp

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -281,16 +281,15 @@ size_t ComputeScriptLen(Fragment fragment, Type sub0typ, size_t subsize, uint32_
281281
return 0;
282282
}
283283

284-
bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, std::vector<unsigned char>>>& out)
284+
std::optional<std::vector<std::pair<opcodetype, std::vector<unsigned char>>>> DecomposeScript(const CScript& script)
285285
{
286-
out.clear();
286+
std::vector<std::pair<opcodetype, std::vector<unsigned char>>> out;
287287
CScript::const_iterator it = script.begin(), itend = script.end();
288288
while (it != itend) {
289289
std::vector<unsigned char> push_data;
290290
opcodetype opcode;
291291
if (!script.GetOp(it, opcode, push_data)) {
292-
out.clear();
293-
return false;
292+
return {};
294293
} else if (opcode >= OP_1 && opcode <= OP_16) {
295294
// Deal with OP_n (GetOp does not turn them into pushes).
296295
push_data.assign(1, CScript::DecodeOP_N(opcode));
@@ -307,30 +306,28 @@ bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, st
307306
out.emplace_back(OP_EQUAL, std::vector<unsigned char>());
308307
opcode = OP_VERIFY;
309308
} else if (IsPushdataOp(opcode)) {
310-
if (!CheckMinimalPush(push_data, opcode)) return false;
309+
if (!CheckMinimalPush(push_data, opcode)) return {};
311310
} else if (it != itend && (opcode == OP_CHECKSIG || opcode == OP_CHECKMULTISIG || opcode == OP_EQUAL) && (*it == OP_VERIFY)) {
312311
// Rule out non minimal VERIFY sequences
313-
return false;
312+
return {};
314313
}
315314
out.emplace_back(opcode, std::move(push_data));
316315
}
317316
std::reverse(out.begin(), out.end());
318-
return true;
317+
return out;
319318
}
320319

321-
bool ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in, int64_t& k) {
320+
std::optional<int64_t> ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in) {
322321
if (in.first == OP_0) {
323-
k = 0;
324-
return true;
322+
return 0;
325323
}
326324
if (!in.second.empty()) {
327-
if (IsPushdataOp(in.first) && !CheckMinimalPush(in.second, in.first)) return false;
325+
if (IsPushdataOp(in.first) && !CheckMinimalPush(in.second, in.first)) return {};
328326
try {
329-
k = CScriptNum(in.second, true).GetInt64();
330-
return true;
327+
return CScriptNum(in.second, true).GetInt64();
331328
} catch(const scriptnum_error&) {}
332329
}
333-
return false;
330+
return {};
334331
}
335332

336333
int FindNextChar(Span<const char> sp, const char m)

src/script/miniscript.h

Lines changed: 58 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ struct Node {
517517
}
518518

519519
template<typename CTx>
520-
bool ToString(const CTx& ctx, std::string& ret) const {
520+
std::optional<std::string> ToString(const CTx& ctx) const {
521521
// To construct the std::string representation for a Miniscript object, we use
522522
// the TreeEvalMaybe algorithm. The State is a boolean: whether the parent node is a
523523
// wrapper. If so, non-wrapper expressions must be prefixed with a ":".
@@ -541,15 +541,15 @@ struct Node {
541541
case Fragment::WRAP_C:
542542
if (node.subs[0]->fragment == Fragment::PK_K) {
543543
// pk(K) is syntactic sugar for c:pk_k(K)
544-
std::string key_str;
545-
if (!ctx.ToString(node.subs[0]->keys[0], key_str)) return {};
546-
return std::move(ret) + "pk(" + std::move(key_str) + ")";
544+
auto key_str = ctx.ToString(node.subs[0]->keys[0]);
545+
if (!key_str) return {};
546+
return std::move(ret) + "pk(" + std::move(*key_str) + ")";
547547
}
548548
if (node.subs[0]->fragment == Fragment::PK_H) {
549549
// pkh(K) is syntactic sugar for c:pk_h(K)
550-
std::string key_str;
551-
if (!ctx.ToString(node.subs[0]->keys[0], key_str)) return {};
552-
return std::move(ret) + "pkh(" + std::move(key_str) + ")";
550+
auto key_str = ctx.ToString(node.subs[0]->keys[0]);
551+
if (!key_str) return {};
552+
return std::move(ret) + "pkh(" + std::move(*key_str) + ")";
553553
}
554554
return "c" + std::move(subs[0]);
555555
case Fragment::WRAP_D: return "d" + std::move(subs[0]);
@@ -568,14 +568,14 @@ struct Node {
568568
}
569569
switch (node.fragment) {
570570
case Fragment::PK_K: {
571-
std::string key_str;
572-
if (!ctx.ToString(node.keys[0], key_str)) return {};
573-
return std::move(ret) + "pk_k(" + std::move(key_str) + ")";
571+
auto key_str = ctx.ToString(node.keys[0]);
572+
if (!key_str) return {};
573+
return std::move(ret) + "pk_k(" + std::move(*key_str) + ")";
574574
}
575575
case Fragment::PK_H: {
576-
std::string key_str;
577-
if (!ctx.ToString(node.keys[0], key_str)) return {};
578-
return std::move(ret) + "pk_h(" + std::move(key_str) + ")";
576+
auto key_str = ctx.ToString(node.keys[0]);
577+
if (!key_str) return {};
578+
return std::move(ret) + "pk_h(" + std::move(*key_str) + ")";
579579
}
580580
case Fragment::AFTER: return std::move(ret) + "after(" + ::ToString(node.k) + ")";
581581
case Fragment::OLDER: return std::move(ret) + "older(" + ::ToString(node.k) + ")";
@@ -598,9 +598,9 @@ struct Node {
598598
case Fragment::MULTI: {
599599
auto str = std::move(ret) + "multi(" + ::ToString(node.k);
600600
for (const auto& key : node.keys) {
601-
std::string key_str;
602-
if (!ctx.ToString(key, key_str)) return {};
603-
str += "," + std::move(key_str);
601+
auto key_str = ctx.ToString(key);
602+
if (!key_str) return {};
603+
str += "," + std::move(*key_str);
604604
}
605605
return std::move(str) + ")";
606606
}
@@ -616,9 +616,7 @@ struct Node {
616616
return ""; // Should never be reached.
617617
};
618618

619-
auto res = TreeEvalMaybe<std::string>(false, downfn, upfn);
620-
if (res.has_value()) ret = std::move(*res);
621-
return res.has_value();
619+
return TreeEvalMaybe<std::string>(false, downfn, upfn);
622620
}
623621

624622
internal::Ops CalcOps() const {
@@ -858,11 +856,11 @@ int FindNextChar(Span<const char> in, const char m);
858856
template<typename Key, typename Ctx>
859857
std::optional<std::pair<Key, int>> ParseKeyEnd(Span<const char> in, const Ctx& ctx)
860858
{
861-
Key key;
862859
int key_size = FindNextChar(in, ')');
863860
if (key_size < 1) return {};
864-
if (!ctx.FromString(in.begin(), in.begin() + key_size, key)) return {};
865-
return {{std::move(key), key_size}};
861+
auto key = ctx.FromString(in.begin(), in.begin() + key_size);
862+
if (!key) return {};
863+
return {{std::move(*key), key_size}};
866864
}
867865

868866
/** Parse a hex string ending at the end of the fragment's text representation. */
@@ -1029,12 +1027,12 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
10291027
// Get keys
10301028
std::vector<Key> keys;
10311029
while (next_comma != -1) {
1032-
Key key;
10331030
next_comma = FindNextChar(in, ',');
10341031
int key_length = (next_comma == -1) ? FindNextChar(in, ')') : next_comma;
10351032
if (key_length < 1) return {};
1036-
if (!ctx.FromString(in.begin(), in.begin() + key_length, key)) return {};
1037-
keys.push_back(std::move(key));
1033+
auto key = ctx.FromString(in.begin(), in.begin() + key_length);
1034+
if (!key) return {};
1035+
keys.push_back(std::move(*key));
10381036
in = in.subspan(key_length + 1);
10391037
}
10401038
if (keys.size() < 1 || keys.size() > 20) return {};
@@ -1207,10 +1205,10 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
12071205
* and OP_EQUALVERIFY are decomposed into OP_CHECKSIG, OP_CHECKMULTISIG, OP_EQUAL
12081206
* respectively, plus OP_VERIFY.
12091207
*/
1210-
bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, std::vector<unsigned char>>>& out);
1208+
std::optional<std::vector<std::pair<opcodetype, std::vector<unsigned char>>>> DecomposeScript(const CScript& script);
12111209

12121210
/** Determine whether the passed pair (created by DecomposeScript) is pushing a number. */
1213-
bool ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in, int64_t& k);
1211+
std::optional<int64_t> ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in);
12141212

12151213
enum class DecodeContext {
12161214
/** A single expression of type B, K, or V. Specifically, this can't be an
@@ -1317,34 +1315,35 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
13171315
}
13181316
// Public keys
13191317
if (in[0].second.size() == 33) {
1320-
Key key;
1321-
if (!ctx.FromPKBytes(in[0].second.begin(), in[0].second.end(), key)) return {};
1318+
auto key = ctx.FromPKBytes(in[0].second.begin(), in[0].second.end());
1319+
if (!key) return {};
13221320
++in;
1323-
constructed.push_back(MakeNodeRef<Key>(Fragment::PK_K, Vector(std::move(key))));
1321+
constructed.push_back(MakeNodeRef<Key>(Fragment::PK_K, Vector(std::move(*key))));
13241322
break;
13251323
}
13261324
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) {
1327-
Key key;
1328-
if (!ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end(), key)) return {};
1325+
auto key = ctx.FromPKHBytes(in[2].second.begin(), in[2].second.end());
1326+
if (!key) return {};
13291327
in += 5;
1330-
constructed.push_back(MakeNodeRef<Key>(Fragment::PK_H, Vector(std::move(key))));
1328+
constructed.push_back(MakeNodeRef<Key>(Fragment::PK_H, Vector(std::move(*key))));
13311329
break;
13321330
}
13331331
// Time locks
1334-
if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && ParseScriptNumber(in[1], k)) {
1332+
std::optional<int64_t> num;
1333+
if (last - in >= 2 && in[0].first == OP_CHECKSEQUENCEVERIFY && (num = ParseScriptNumber(in[1]))) {
13351334
in += 2;
1336-
if (k < 1 || k > 0x7FFFFFFFL) return {};
1337-
constructed.push_back(MakeNodeRef<Key>(Fragment::OLDER, k));
1335+
if (*num < 1 || *num > 0x7FFFFFFFL) return {};
1336+
constructed.push_back(MakeNodeRef<Key>(Fragment::OLDER, *num));
13381337
break;
13391338
}
1340-
if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && ParseScriptNumber(in[1], k)) {
1339+
if (last - in >= 2 && in[0].first == OP_CHECKLOCKTIMEVERIFY && (num = ParseScriptNumber(in[1]))) {
13411340
in += 2;
1342-
if (k < 1 || k > 0x7FFFFFFFL) return {};
1343-
constructed.push_back(MakeNodeRef<Key>(Fragment::AFTER, k));
1341+
if (num < 1 || num > 0x7FFFFFFFL) return {};
1342+
constructed.push_back(MakeNodeRef<Key>(Fragment::AFTER, *num));
13441343
break;
13451344
}
13461345
// Hashes
1347-
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) {
1346+
if (last - in >= 7 && in[0].first == OP_EQUAL && in[3].first == OP_VERIFY && in[4].first == OP_EQUAL && (num = ParseScriptNumber(in[5])) && num == 32 && in[6].first == OP_SIZE) {
13481347
if (in[2].first == OP_SHA256 && in[1].second.size() == 32) {
13491348
constructed.push_back(MakeNodeRef<Key>(Fragment::SHA256, in[1].second));
13501349
in += 7;
@@ -1366,20 +1365,20 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
13661365
// Multi
13671366
if (last - in >= 3 && in[0].first == OP_CHECKMULTISIG) {
13681367
std::vector<Key> keys;
1369-
if (!ParseScriptNumber(in[1], n)) return {};
1370-
if (last - in < 3 + n) return {};
1371-
if (n < 1 || n > 20) return {};
1372-
for (int i = 0; i < n; ++i) {
1373-
Key key;
1368+
const auto n = ParseScriptNumber(in[1]);
1369+
if (!n || last - in < 3 + *n) return {};
1370+
if (*n < 1 || *n > 20) return {};
1371+
for (int i = 0; i < *n; ++i) {
13741372
if (in[2 + i].second.size() != 33) return {};
1375-
if (!ctx.FromPKBytes(in[2 + i].second.begin(), in[2 + i].second.end(), key)) return {};
1376-
keys.push_back(std::move(key));
1373+
auto key = ctx.FromPKBytes(in[2 + i].second.begin(), in[2 + i].second.end());
1374+
if (!key) return {};
1375+
keys.push_back(std::move(*key));
13771376
}
1378-
if (!ParseScriptNumber(in[2 + n], k)) return {};
1379-
if (k < 1 || k > n) return {};
1380-
in += 3 + n;
1377+
const auto k = ParseScriptNumber(in[2 + *n]);
1378+
if (!k || *k < 1 || *k > *n) return {};
1379+
in += 3 + *n;
13811380
std::reverse(keys.begin(), keys.end());
1382-
constructed.push_back(MakeNodeRef<Key>(Fragment::MULTI, std::move(keys), k));
1381+
constructed.push_back(MakeNodeRef<Key>(Fragment::MULTI, std::move(keys), *k));
13831382
break;
13841383
}
13851384
/** In the following wrappers, we only need to push SINGLE_BKV_EXPR rather
@@ -1407,10 +1406,10 @@ inline NodeRef<Key> DecodeScript(I& in, I last, const Ctx& ctx)
14071406
break;
14081407
}
14091408
// Thresh
1410-
if (last - in >= 3 && in[0].first == OP_EQUAL && ParseScriptNumber(in[1], k)) {
1411-
if (k < 1) return {};
1409+
if (last - in >= 3 && in[0].first == OP_EQUAL && (num = ParseScriptNumber(in[1]))) {
1410+
if (*num < 1) return {};
14121411
in += 2;
1413-
to_parse.emplace_back(DecodeContext::THRESH_W, 0, k);
1412+
to_parse.emplace_back(DecodeContext::THRESH_W, 0, *num);
14141413
break;
14151414
}
14161415
// OP_ENDIF can be WRAP_J, WRAP_D, ANDOR, OR_C, OR_D, or OR_I
@@ -1645,12 +1644,12 @@ inline NodeRef<typename Ctx::Key> FromString(const std::string& str, const Ctx&
16451644
template<typename Ctx>
16461645
inline NodeRef<typename Ctx::Key> FromScript(const CScript& script, const Ctx& ctx) {
16471646
using namespace internal;
1648-
std::vector<std::pair<opcodetype, std::vector<unsigned char>>> decomposed;
1649-
if (!DecomposeScript(script, decomposed)) return {};
1650-
auto it = decomposed.begin();
1651-
auto ret = DecodeScript<typename Ctx::Key>(it, decomposed.end(), ctx);
1647+
auto decomposed = DecomposeScript(script);
1648+
if (!decomposed) return {};
1649+
auto it = decomposed->begin();
1650+
auto ret = DecodeScript<typename Ctx::Key>(it, decomposed->end(), ctx);
16521651
if (!ret) return {};
1653-
if (it != decomposed.end()) return {};
1652+
if (it != decomposed->end()) return {};
16541653
return ret;
16551654
}
16561655

src/test/fuzz/miniscript_decode.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ using miniscript::operator""_mst;
2121
struct Converter {
2222
typedef CPubKey Key;
2323

24-
bool ToString(const Key& key, std::string& ret) const {
25-
ret = HexStr(key);
26-
return true;
24+
std::optional<std::string> ToString(const Key& key) const {
25+
return HexStr(key);
2726
}
2827
const std::vector<unsigned char> ToPKBytes(const Key& key) const {
2928
return {key.begin(), key.end()};
@@ -34,20 +33,21 @@ struct Converter {
3433
}
3534

3635
template<typename I>
37-
bool FromString(I first, I last, Key& key) const {
36+
std::optional<Key> FromString(I first, I last) const {
3837
const auto bytes = ParseHex(std::string(first, last));
39-
key.Set(bytes.begin(), bytes.end());
40-
return key.IsValid();
38+
Key key{bytes.begin(), bytes.end()};
39+
if (key.IsValid()) return key;
40+
return {};
4141
}
4242
template<typename I>
43-
bool FromPKBytes(I first, I last, CPubKey& key) const {
44-
key.Set(first, last);
45-
return key.IsValid();
43+
std::optional<Key> FromPKBytes(I first, I last) const {
44+
Key key{first, last};
45+
if (key.IsValid()) return key;
46+
return {};
4647
}
4748
template<typename I>
48-
bool FromPKHBytes(I first, I last, CPubKey& key) const {
49-
assert(last - first == 20);
50-
return false;
49+
std::optional<Key> FromPKHBytes(I first, I last) const {
50+
return {};
5151
}
5252
};
5353

@@ -63,8 +63,7 @@ FUZZ_TARGET(miniscript_decode)
6363
if (!ms) return;
6464

6565
// We can roundtrip it to its string representation.
66-
std::string ms_str;
67-
assert(ms->ToString(CONVERTER, ms_str));
66+
std::string ms_str = *ms->ToString(CONVERTER);
6867
assert(*miniscript::FromString(ms_str, CONVERTER) == *ms);
6968
// The Script representation must roundtrip since we parsed it this way the first time.
7069
const CScript ms_script = ms->ToScript(CONVERTER);

src/test/miniscript_tests.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,27 +84,28 @@ struct KeyConverter {
8484

8585
//! Parse a public key from a range of hex characters.
8686
template<typename I>
87-
bool FromString(I first, I last, CPubKey& key) const {
87+
std::optional<Key> FromString(I first, I last) const {
8888
auto bytes = ParseHex(std::string(first, last));
89-
key.Set(bytes.begin(), bytes.end());
90-
return key.IsValid();
89+
Key key{bytes.begin(), bytes.end()};
90+
if (key.IsValid()) return key;
91+
return {};
9192
}
9293

9394
template<typename I>
94-
bool FromPKBytes(I first, I last, CPubKey& key) const {
95-
key.Set(first, last);
96-
return key.IsValid();
95+
std::optional<Key> FromPKBytes(I first, I last) const {
96+
Key key{first, last};
97+
if (key.IsValid()) return key;
98+
return {};
9799
}
98100

99101
template<typename I>
100-
bool FromPKHBytes(I first, I last, CPubKey& key) const {
102+
std::optional<Key> FromPKHBytes(I first, I last) const {
101103
assert(last - first == 20);
102104
CKeyID keyid;
103105
std::copy(first, last, keyid.begin());
104106
auto it = g_testdata->pkmap.find(keyid);
105107
assert(it != g_testdata->pkmap.end());
106-
key = it->second;
107-
return true;
108+
return it->second;
108109
}
109110
};
110111

0 commit comments

Comments
 (0)