1
- // Copyright (c) 2021 The Bitcoin Core developers
1
+ // Copyright (c) 2021-2022 The Bitcoin Core developers
2
2
// Distributed under the MIT software license, see the accompanying
3
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
4
14
14
15
15
namespace {
16
16
17
- // ! Some pre-computed data to simulate challenges.
17
+ // ! Some pre-computed data for more efficient string roundtrips and to simulate challenges.
18
18
struct TestData {
19
19
typedef CPubKey Key;
20
20
@@ -72,10 +72,18 @@ struct TestData {
72
72
}
73
73
} TEST_DATA;
74
74
75
- // ! Context to parse a Miniscript node to and from Script or text representation.
75
+ /* *
76
+ * Context to parse a Miniscript node to and from Script or text representation.
77
+ * Uses an integer (an index in the dummy keys array from the test data) as keys in order
78
+ * to focus on fuzzing the Miniscript nodes' test representation, not the key representation.
79
+ */
76
80
struct ParserContext {
77
81
typedef CPubKey Key;
78
82
83
+ bool KeyCompare (const Key& a, const Key& b) const {
84
+ return a < b;
85
+ }
86
+
79
87
std::optional<std::string> ToString (const Key& key) const
80
88
{
81
89
auto it = TEST_DATA.dummy_key_idx_map .find (key);
@@ -84,12 +92,12 @@ struct ParserContext {
84
92
return HexStr (Span{&idx, 1 });
85
93
}
86
94
87
- const std::vector<unsigned char > ToPKBytes (const Key& key) const
95
+ std::vector<unsigned char > ToPKBytes (const Key& key) const
88
96
{
89
97
return {key.begin (), key.end ()};
90
98
}
91
99
92
- const std::vector<unsigned char > ToPKHBytes (const Key& key) const
100
+ std::vector<unsigned char > ToPKHBytes (const Key& key) const
93
101
{
94
102
const auto h = Hash160 (key);
95
103
return {h.begin (), h.end ()};
@@ -104,15 +112,15 @@ struct ParserContext {
104
112
}
105
113
106
114
template <typename I>
107
- std::optional<CPubKey > FromPKBytes (I first, I last) const {
115
+ std::optional<Key > FromPKBytes (I first, I last) const {
108
116
CPubKey key;
109
117
key.Set (first, last);
110
118
if (!key.IsValid ()) return {};
111
119
return key;
112
120
}
113
121
114
122
template <typename I>
115
- std::optional<CPubKey > FromPKHBytes (I first, I last) const {
123
+ std::optional<Key > FromPKHBytes (I first, I last) const {
116
124
assert (last - first == 20 );
117
125
CKeyID keyid;
118
126
std::copy (first, last, keyid.begin ());
@@ -124,20 +132,23 @@ struct ParserContext {
124
132
125
133
// ! Context that implements naive conversion from/to script only, for roundtrip testing.
126
134
struct ScriptParserContext {
135
+ // ! For Script roundtrip we never need the key from a key hash.
127
136
struct Key {
128
137
bool is_hash;
129
138
std::vector<unsigned char > data;
130
-
131
- bool operator <(Key k) const { return data < k.data ; }
132
139
};
133
140
141
+ bool KeyCompare (const Key& a, const Key& b) const {
142
+ return a.data < b.data ;
143
+ }
144
+
134
145
const std::vector<unsigned char >& ToPKBytes (const Key& key) const
135
146
{
136
147
assert (!key.is_hash );
137
148
return key.data ;
138
149
}
139
150
140
- const std::vector<unsigned char > ToPKHBytes (const Key& key) const
151
+ std::vector<unsigned char > ToPKHBytes (const Key& key) const
141
152
{
142
153
if (key.is_hash ) return key.data ;
143
154
const auto h = Hash160 (key.data );
@@ -223,17 +234,26 @@ struct CheckerContext: BaseSignatureChecker {
223
234
bool CheckSequence (const CScriptNum& nSequence) const override { return nSequence.GetInt64 () & 1 ; }
224
235
} CHECKER_CTX;
225
236
237
+ // ! Context to check for duplicates when instancing a Node.
238
+ struct KeyComparator {
239
+ bool KeyCompare (const CPubKey& a, const CPubKey& b) const {
240
+ return a < b;
241
+ }
242
+ } KEY_COMP;
243
+
226
244
// A dummy scriptsig to pass to VerifyScript (we always use Segwit v0).
227
245
const CScript DUMMY_SCRIPTSIG;
228
246
229
247
using Fragment = miniscript::Fragment;
230
248
using NodeRef = miniscript::NodeRef<CPubKey>;
231
249
using Node = miniscript::Node<CPubKey>;
232
250
using Type = miniscript::Type;
251
+ // https://github.com/llvm/llvm-project/issues/53444
252
+ // NOLINTNEXTLINE(misc-unused-using-decls)
233
253
using miniscript::operator " " _mst;
234
254
235
255
// ! Construct a miniscript node as a shared_ptr.
236
- template <typename ... Args> NodeRef MakeNodeRef (Args&&... args) { return miniscript::MakeNodeRef<CPubKey>(std::forward<Args>(args)...); }
256
+ template <typename ... Args> NodeRef MakeNodeRef (Args&&... args) { return miniscript::MakeNodeRef<CPubKey>(KEY_COMP, std::forward<Args>(args)...); }
237
257
238
258
/* * Information about a yet to be constructed Miniscript node. */
239
259
struct NodeInfo {
@@ -577,7 +597,7 @@ struct SmartInfo
577
597
// Remove all recipes which involve non-constructible types.
578
598
type_it->second .erase (std::remove_if (type_it->second .begin (), type_it->second .end (),
579
599
[&](const recipe& rec) {
580
- return !std::all_of (rec.second .begin (), rec.second .end (), known_constructible);
600
+ return !std::all_of (rec.second .begin (), rec.second .end (), known_constructible);
581
601
}), type_it->second .end ());
582
602
// Delete types entirely which have no recipes left.
583
603
if (type_it->second .empty ()) {
@@ -700,7 +720,7 @@ NodeRef GenNode(F ConsumeNode, Type root_type = ""_mst, bool strict_valid = fals
700
720
// Fragment/children have not been decided yet. Decide them.
701
721
auto node_info = ConsumeNode (type_needed);
702
722
if (!node_info) return {};
703
- auto subtypes = std::move ( node_info) ->subtypes ;
723
+ auto subtypes = node_info->subtypes ;
704
724
todo.back ().second = std::move (node_info);
705
725
todo.reserve (todo.size () + subtypes.size ());
706
726
// As elements on the todo stack are processed back to front, construct
@@ -713,7 +733,7 @@ NodeRef GenNode(F ConsumeNode, Type root_type = ""_mst, bool strict_valid = fals
713
733
// those children have been constructed at the back of stack. Pop
714
734
// that entry off todo, and use it to construct a new NodeRef on
715
735
// stack.
716
- const NodeInfo& info = *todo.back ().second ;
736
+ NodeInfo& info = *todo.back ().second ;
717
737
// Gather children from the back of stack.
718
738
std::vector<NodeRef> sub;
719
739
sub.reserve (info.n_subs );
@@ -751,9 +771,9 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider)
751
771
if (!node) return ;
752
772
753
773
// Check that it roundtrips to text representation
754
- std::string str;
755
- assert (node-> ToString (PARSER_CTX, str) );
756
- auto parsed = miniscript::FromString (str, PARSER_CTX);
774
+ std::optional<std:: string> str{node-> ToString (PARSER_CTX)} ;
775
+ assert (str);
776
+ auto parsed = miniscript::FromString (* str, PARSER_CTX);
757
777
assert (parsed);
758
778
assert (*parsed == *node);
759
779
@@ -780,7 +800,7 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider)
780
800
assert (decoded->ToScript (PARSER_CTX) == script);
781
801
assert (decoded->GetType () == node->GetType ());
782
802
783
- if (provider.ConsumeBool ()) {
803
+ if (provider.ConsumeBool () && node-> GetOps () < MAX_OPS_PER_SCRIPT && node-> ScriptSize () < MAX_STANDARD_P2WSH_SCRIPT_SIZE ) {
784
804
// Optionally pad the script with OP_NOPs to max op the ops limit of the constructed script.
785
805
// This makes the script obviously not actually miniscript-compatible anymore, but the
786
806
// signatures constructed in this test don't commit to the script anyway, so the same
@@ -835,13 +855,13 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider)
835
855
assert (res || serror == ScriptError::SCRIPT_ERR_OP_COUNT || serror == ScriptError::SCRIPT_ERR_STACK_SIZE);
836
856
}
837
857
838
- if (node->IsSaneTopLevel ()) {
858
+ if (node->IsSane ()) {
839
859
// For sane nodes, the two algorithms behave identically.
840
860
assert (mal_success == nonmal_success);
841
861
}
842
862
843
863
// Verify that if a node is policy-satisfiable, the malleable satisfaction
844
- // algorithm succeeds. Given that under IsSaneTopLevel () both satisfactions
864
+ // algorithm succeeds. Given that under IsSane () both satisfactions
845
865
// are identical, this implies that for such nodes, the non-malleable
846
866
// satisfaction will also match the expected policy.
847
867
bool satisfiable = node->IsSatisfiable ([](const Node& node) -> bool {
@@ -880,6 +900,8 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider)
880
900
assert (mal_success == satisfiable);
881
901
}
882
902
903
+ } // namespace
904
+
883
905
void FuzzInit ()
884
906
{
885
907
ECC_Start ();
@@ -892,8 +914,6 @@ void FuzzInitSmart()
892
914
SMARTINFO.Init ();
893
915
}
894
916
895
- } // namespace
896
-
897
917
/* * Fuzz target that runs TestNode on nodes generated using ConsumeNodeStable. */
898
918
FUZZ_TARGET_INIT (miniscript_stable, FuzzInit)
899
919
{
@@ -923,9 +943,9 @@ FUZZ_TARGET_INIT(miniscript_string, FuzzInit)
923
943
auto parsed = miniscript::FromString (str, PARSER_CTX);
924
944
if (!parsed) return ;
925
945
926
- std::string str2;
927
- assert (parsed-> ToString (PARSER_CTX, str2) );
928
- auto parsed2 = miniscript::FromString (str2, PARSER_CTX);
946
+ const auto str2 = parsed-> ToString (PARSER_CTX) ;
947
+ assert (str2);
948
+ auto parsed2 = miniscript::FromString (* str2, PARSER_CTX);
929
949
assert (parsed2);
930
950
assert (*parsed == *parsed2);
931
951
}
0 commit comments