Skip to content

Commit ce1c881

Browse files
committed
Merge bitcoin/bitcoin#30577: miniscript: Use ToIntegral instead of ParseInt64
6714276 miniscript: Use `ToIntegral` instead of `ParseInt64` (brunoerg) Pull request description: Currently, miniscript code uses `ParseInt64` function for `after`, `older`, `multi` and `thresh` fragments. It means that a leading `+` or whitespace, among other things, are accepted into the fragments. However, these cases are not useful and cause Bitcoin Core to behave differently compared to other miniscript implementations (see bitcoinfuzz/bitcoinfuzz#34). This PR fixes it. ACKs for top commit: achow101: ACK 6714276 tdb3: cr ACK 6714276 danielabrozzoni: tACK 6714276 darosior: utACK 6714276 Tree-SHA512: d9eeb93f380f346d636513eeaf26865285e7b0907b8ed258fe1e02153a9eb69d484c82180eb1c78b0ed77ad5f0e5b244be6672c2f890b1d9fddc9e844bee6dde
2 parents bb25e06 + 6714276 commit ce1c881

File tree

2 files changed

+21
-16
lines changed

2 files changed

+21
-16
lines changed

src/script/miniscript.h

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,8 +1793,9 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
17931793
// Get threshold
17941794
int next_comma = FindNextChar(in, ',');
17951795
if (next_comma < 1) return false;
1796-
int64_t k;
1797-
if (!ParseInt64(std::string(in.begin(), in.begin() + next_comma), &k)) return false;
1796+
const auto k_to_integral{ToIntegral<int64_t>(std::string_view(in.begin(), next_comma))};
1797+
if (!k_to_integral.has_value()) return false;
1798+
const int64_t k{k_to_integral.value()};
17981799
in = in.subspan(next_comma + 1);
17991800
// Get keys. It is compatible for both compressed and x-only keys.
18001801
std::vector<Key> keys;
@@ -1948,35 +1949,33 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx)
19481949
} else if (Const("after(", in)) {
19491950
int arg_size = FindNextChar(in, ')');
19501951
if (arg_size < 1) return {};
1951-
int64_t num;
1952-
if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {};
1953-
if (num < 1 || num >= 0x80000000L) return {};
1954-
constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::AFTER, num));
1952+
const auto num{ToIntegral<int64_t>(std::string_view(in.begin(), arg_size))};
1953+
if (!num.has_value() || *num < 1 || *num >= 0x80000000L) return {};
1954+
constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::AFTER, *num));
19551955
in = in.subspan(arg_size + 1);
1956-
script_size += 1 + (num > 16) + (num > 0x7f) + (num > 0x7fff) + (num > 0x7fffff);
1956+
script_size += 1 + (*num > 16) + (*num > 0x7f) + (*num > 0x7fff) + (*num > 0x7fffff);
19571957
} else if (Const("older(", in)) {
19581958
int arg_size = FindNextChar(in, ')');
19591959
if (arg_size < 1) return {};
1960-
int64_t num;
1961-
if (!ParseInt64(std::string(in.begin(), in.begin() + arg_size), &num)) return {};
1962-
if (num < 1 || num >= 0x80000000L) return {};
1963-
constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::OLDER, num));
1960+
const auto num{ToIntegral<int64_t>(std::string_view(in.begin(), arg_size))};
1961+
if (!num.has_value() || *num < 1 || *num >= 0x80000000L) return {};
1962+
constructed.push_back(MakeNodeRef<Key>(internal::NoDupCheck{}, ctx.MsContext(), Fragment::OLDER, *num));
19641963
in = in.subspan(arg_size + 1);
1965-
script_size += 1 + (num > 16) + (num > 0x7f) + (num > 0x7fff) + (num > 0x7fffff);
1964+
script_size += 1 + (*num > 16) + (*num > 0x7f) + (*num > 0x7fff) + (*num > 0x7fffff);
19661965
} else if (Const("multi(", in)) {
19671966
if (!parse_multi_exp(in, /* is_multi_a = */false)) return {};
19681967
} else if (Const("multi_a(", in)) {
19691968
if (!parse_multi_exp(in, /* is_multi_a = */true)) return {};
19701969
} else if (Const("thresh(", in)) {
19711970
int next_comma = FindNextChar(in, ',');
19721971
if (next_comma < 1) return {};
1973-
if (!ParseInt64(std::string(in.begin(), in.begin() + next_comma), &k)) return {};
1974-
if (k < 1) return {};
1972+
const auto k{ToIntegral<int64_t>(std::string_view(in.begin(), next_comma))};
1973+
if (!k.has_value() || *k < 1) return {};
19751974
in = in.subspan(next_comma + 1);
19761975
// n = 1 here because we read the first WRAPPED_EXPR before reaching THRESH
1977-
to_parse.emplace_back(ParseContext::THRESH, 1, k);
1976+
to_parse.emplace_back(ParseContext::THRESH, 1, *k);
19781977
to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1);
1979-
script_size += 2 + (k > 16) + (k > 0x7f) + (k > 0x7fff) + (k > 0x7fffff);
1978+
script_size += 2 + (*k > 16) + (*k > 0x7f) + (*k > 0x7fff) + (*k > 0x7fffff);
19801979
} else if (Const("andor(", in)) {
19811980
to_parse.emplace_back(ParseContext::ANDOR, -1, -1);
19821981
to_parse.emplace_back(ParseContext::CLOSE_BRACKET, -1, -1);

src/test/miniscript_tests.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,12 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
699699
const auto insane_sub = ms_ins->FindInsaneSub();
700700
BOOST_CHECK(insane_sub && *insane_sub->ToString(wsh_converter) == "and_b(after(1),a:after(1000000000))");
701701

702+
// Numbers can't be prefixed by a sign.
703+
BOOST_CHECK(!miniscript::FromString("after(-1)", wsh_converter));
704+
BOOST_CHECK(!miniscript::FromString("after(+1)", wsh_converter));
705+
BOOST_CHECK(!miniscript::FromString("thresh(-1,pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", wsh_converter));
706+
BOOST_CHECK(!miniscript::FromString("multi(+1,03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)", wsh_converter));
707+
702708
// Timelock tests
703709
Test("after(100)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock
704710
Test("after(1000000000)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only timelock

0 commit comments

Comments
 (0)