1 // Copyright (c) 2020 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <test/fuzz/fuzz.h>
6 
7 #include <primitives/transaction.h>
8 #include <pubkey.h>
9 #include <script/interpreter.h>
10 #include <serialize.h>
11 #include <streams.h>
12 #include <univalue.h>
13 #include <util/strencodings.h>
14 
15 #include <boost/algorithm/string.hpp>
16 #include <cstdint>
17 #include <string>
18 #include <vector>
19 
20 // This fuzz "test" can be used to minimize test cases for script_assets_test in
21 // src/test/script_tests.cpp. While it written as a fuzz test, and can be used as such,
22 // fuzzing the inputs is unlikely to construct useful test cases.
23 //
24 // Instead, it is primarily intended to be run on a test set that was generated
25 // externally, for example using test/functional/feature_taproot.py's --dumptests mode.
26 // The minimized set can then be concatenated together, surrounded by '[' and ']',
27 // and used as the script_assets_test.json input to the script_assets_test unit test:
28 //
29 // (normal build)
30 // $ mkdir dump
31 // $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot --dumptests; done
32 // $ ...
33 //
34 // (fuzz test build)
35 // $ mkdir dump-min
36 // $ ./src/test/fuzz/script_assets_test_minimizer -merge=1 dump-min/ dump/
37 // $ (echo -en '[\n'; cat dump-min/* | head -c -2; echo -en '\n]') >script_assets_test.json
38 
39 namespace {
40 
CheckedParseHex(const std::string & str)41 std::vector<unsigned char> CheckedParseHex(const std::string& str)
42 {
43     if (str.size() && !IsHex(str)) throw std::runtime_error("Non-hex input '" + str + "'");
44     return ParseHex(str);
45 }
46 
ScriptFromHex(const std::string & str)47 CScript ScriptFromHex(const std::string& str)
48 {
49     std::vector<unsigned char> data = CheckedParseHex(str);
50     return CScript(data.begin(), data.end());
51 }
52 
TxFromHex(const std::string & str)53 CMutableTransaction TxFromHex(const std::string& str)
54 {
55     CMutableTransaction tx;
56     try {
57         VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, CheckedParseHex(str), 0) >> tx;
58     } catch (const std::ios_base::failure&) {
59         throw std::runtime_error("Tx deserialization failure");
60     }
61     return tx;
62 }
63 
TxOutsFromJSON(const UniValue & univalue)64 std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
65 {
66     if (!univalue.isArray()) throw std::runtime_error("Prevouts must be array");
67     std::vector<CTxOut> prevouts;
68     for (size_t i = 0; i < univalue.size(); ++i) {
69         CTxOut txout;
70         try {
71             VectorReader(SER_DISK, 0, CheckedParseHex(univalue[i].get_str()), 0) >> txout;
72         } catch (const std::ios_base::failure&) {
73             throw std::runtime_error("Prevout invalid format");
74         }
75         prevouts.push_back(std::move(txout));
76     }
77     return prevouts;
78 }
79 
ScriptWitnessFromJSON(const UniValue & univalue)80 CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
81 {
82     if (!univalue.isArray()) throw std::runtime_error("Script witness is not array");
83     CScriptWitness scriptwitness;
84     for (size_t i = 0; i < univalue.size(); ++i) {
85         auto bytes = CheckedParseHex(univalue[i].get_str());
86         scriptwitness.stack.push_back(std::move(bytes));
87     }
88     return scriptwitness;
89 }
90 
91 const std::map<std::string, unsigned int> FLAG_NAMES = {
92     {std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
93     {std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
94     {std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
95     {std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
96     {std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
97     {std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
98     {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
99 };
100 
AllFlags()101 std::vector<unsigned int> AllFlags()
102 {
103     std::vector<unsigned int> ret;
104 
105     for (unsigned int i = 0; i < 128; ++i) {
106         unsigned int flag = 0;
107         if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
108         if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
109         if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
110         if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
111         if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
112         if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
113         if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
114 
115         // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
116         if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
117         // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
118         if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
119 
120         ret.push_back(flag);
121     }
122 
123     return ret;
124 }
125 
126 const std::vector<unsigned int> ALL_FLAGS = AllFlags();
127 
ParseScriptFlags(const std::string & str)128 unsigned int ParseScriptFlags(const std::string& str)
129 {
130     if (str.empty()) return 0;
131 
132     unsigned int flags = 0;
133     std::vector<std::string> words;
134     boost::algorithm::split(words, str, boost::algorithm::is_any_of(","));
135 
136     for (const std::string& word : words)
137     {
138         auto it = FLAG_NAMES.find(word);
139         if (it == FLAG_NAMES.end()) throw std::runtime_error("Unknown verification flag " + word);
140         flags |= it->second;
141     }
142 
143     return flags;
144 }
145 
Test(const std::string & str)146 void Test(const std::string& str)
147 {
148     UniValue test;
149     if (!test.read(str) || !test.isObject()) throw std::runtime_error("Non-object test input");
150 
151     CMutableTransaction tx = TxFromHex(test["tx"].get_str());
152     const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
153     if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts");
154     size_t idx = test["index"].get_int64();
155     if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index");
156     unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
157     bool final = test.exists("final") && test["final"].get_bool();
158 
159     if (test.exists("success")) {
160         tx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
161         tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
162         PrecomputedTransactionData txdata;
163         txdata.Init(tx, std::vector<CTxOut>(prevouts));
164         MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
165         for (const auto flags : ALL_FLAGS) {
166             // "final": true tests are valid for all flags. Others are only valid with flags that are
167             // a subset of test_flags.
168             if (final || ((flags & test_flags) == flags)) {
169                 (void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
170             }
171         }
172     }
173 
174     if (test.exists("failure")) {
175         tx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
176         tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
177         PrecomputedTransactionData txdata;
178         txdata.Init(tx, std::vector<CTxOut>(prevouts));
179         MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
180         for (const auto flags : ALL_FLAGS) {
181             // If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
182             if ((flags & test_flags) == test_flags) {
183                 (void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
184             }
185         }
186     }
187 }
188 
189 ECCVerifyHandle handle;
190 
191 }
192 
test_one_input(const std::vector<uint8_t> & buffer)193 void test_one_input(const std::vector<uint8_t>& buffer)
194 {
195     if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return;
196     const std::string str((const char*)buffer.data(), buffer.size() - 2);
197     try {
198         Test(str);
199     } catch (const std::runtime_error&) {}
200 }
201