Branch data Line data Source code
1 : : // Copyright (c) 2011-present 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 <primitives/transaction.h>
6 : : #include <script/interpreter.h>
7 : : #include <script/script.h>
8 : : #include <script/sigcache.h>
9 : : #include <script/sign.h>
10 : : #include <script/signingprovider.h>
11 : : #include <span.h>
12 : : #include <streams.h>
13 : : #include <test/util/json.h>
14 : : #include <util/check.h>
15 : : #include <util/fs.h>
16 : : #include <util/strencodings.h>
17 : :
18 : : #include <cstdint>
19 : : #include <cstdlib>
20 : : #include <fstream>
21 : : #include <utility>
22 : : #include <vector>
23 : :
24 : : #include <boost/test/unit_test.hpp>
25 : :
26 : : #include <univalue.h>
27 : :
28 : : unsigned int ParseScriptFlags(std::string strFlags);
29 : :
30 : : BOOST_AUTO_TEST_SUITE(script_assets_tests)
31 : :
32 : : template <typename T>
33 : 0 : CScript ToScript(const T& byte_container)
34 : : {
35 : 0 : auto span{MakeUCharSpan(byte_container)};
36 : 0 : return {span.begin(), span.end()};
37 : : }
38 : :
39 : 0 : static CScript ScriptFromHex(const std::string& str)
40 : : {
41 [ # # ]: 0 : return ToScript(*Assert(TryParseHex(str)));
42 : : }
43 : :
44 : 0 : static CMutableTransaction TxFromHex(const std::string& str)
45 : : {
46 : 0 : CMutableTransaction tx;
47 [ # # # # ]: 0 : SpanReader{ParseHex(str)} >> TX_NO_WITNESS(tx);
48 : 0 : return tx;
49 : 0 : }
50 : :
51 : 0 : static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
52 : : {
53 [ # # ]: 0 : assert(univalue.isArray());
54 : 0 : std::vector<CTxOut> prevouts;
55 [ # # ]: 0 : for (size_t i = 0; i < univalue.size(); ++i) {
56 : 0 : CTxOut txout;
57 [ # # # # : 0 : SpanReader{ParseHex(univalue[i].get_str())} >> txout;
# # # # ]
58 [ # # ]: 0 : prevouts.push_back(std::move(txout));
59 : 0 : }
60 : 0 : return prevouts;
61 : 0 : }
62 : :
63 : 0 : static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
64 : : {
65 [ # # ]: 0 : assert(univalue.isArray());
66 : 0 : CScriptWitness scriptwitness;
67 [ # # ]: 0 : for (size_t i = 0; i < univalue.size(); ++i) {
68 [ # # # # : 0 : auto bytes = ParseHex(univalue[i].get_str());
# # ]
69 [ # # ]: 0 : scriptwitness.stack.push_back(std::move(bytes));
70 : 0 : }
71 : 0 : return scriptwitness;
72 : 0 : }
73 : :
74 : 139 : static std::vector<unsigned int> AllConsensusFlags()
75 : : {
76 : 139 : std::vector<unsigned int> ret;
77 : :
78 [ + + ]: 17931 : for (unsigned int i = 0; i < 128; ++i) {
79 : 17792 : unsigned int flag = 0;
80 [ + + ]: 17792 : if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
81 [ + + ]: 17792 : if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
82 [ + + ]: 17792 : if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
83 [ + + ]: 17792 : if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
84 [ + + ]: 17792 : if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
85 [ + + ]: 17792 : if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
86 [ + + ]: 17792 : if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
87 : :
88 : : // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
89 [ + + ]: 17792 : if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
90 : : // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
91 [ + + ]: 13344 : if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
92 : :
93 [ + - ]: 8896 : ret.push_back(flag);
94 : : }
95 : :
96 : 139 : return ret;
97 : 0 : }
98 : :
99 : : /** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
100 : : static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
101 : :
102 : 0 : static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
103 : : {
104 [ # # ]: 0 : BOOST_CHECK(test.isObject());
105 : :
106 [ # # # # : 0 : CMutableTransaction mtx = TxFromHex(test["tx"].get_str());
# # ]
107 [ # # # # : 0 : const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
# # ]
108 [ # # # # : 0 : BOOST_CHECK(prevouts.size() == mtx.vin.size());
# # ]
109 [ # # # # : 0 : size_t idx = test["index"].getInt<int64_t>();
# # ]
110 [ # # # # : 0 : uint32_t test_flags{ParseScriptFlags(test["flags"].get_str())};
# # # # #
# ]
111 [ # # # # : 0 : bool fin = test.exists("final") && test["final"].get_bool();
# # # # #
# # # # #
# # ]
112 : :
113 [ # # # # ]: 0 : if (test.exists("success")) {
114 [ # # # # : 0 : mtx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
# # # # #
# # # ]
115 [ # # # # : 0 : mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
# # # # #
# ]
116 [ # # ]: 0 : CTransaction tx(mtx);
117 : 0 : PrecomputedTransactionData txdata;
118 [ # # # # ]: 0 : txdata.Init(tx, std::vector<CTxOut>(prevouts));
119 : 0 : CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, signature_cache, txdata);
120 : :
121 [ # # ]: 0 : for (const auto flags : ALL_CONSENSUS_FLAGS) {
122 : : // "final": true tests are valid for all flags. Others are only valid with flags that are
123 : : // a subset of test_flags.
124 [ # # # # ]: 0 : if (fin || ((flags & test_flags) == flags)) {
125 [ # # ]: 0 : bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
126 [ # # # # ]: 0 : BOOST_CHECK(ret);
127 : : }
128 : : }
129 : 0 : }
130 : :
131 [ # # # # ]: 0 : if (test.exists("failure")) {
132 [ # # # # : 0 : mtx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
# # # # #
# # # ]
133 [ # # # # : 0 : mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
# # # # #
# ]
134 [ # # ]: 0 : CTransaction tx(mtx);
135 : 0 : PrecomputedTransactionData txdata;
136 [ # # # # ]: 0 : txdata.Init(tx, std::vector<CTxOut>(prevouts));
137 : 0 : CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, signature_cache, txdata);
138 : :
139 [ # # ]: 0 : for (const auto flags : ALL_CONSENSUS_FLAGS) {
140 : : // If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
141 [ # # ]: 0 : if ((flags & test_flags) == test_flags) {
142 [ # # ]: 0 : bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
143 [ # # # # ]: 0 : BOOST_CHECK(!ret);
144 : : }
145 : : }
146 : 0 : }
147 : 0 : }
148 : :
149 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(script_assets_test)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
150 : : {
151 : : // See src/test/fuzz/script_assets_test_minimizer.cpp for information on how to generate
152 : : // the script_assets_test.json file used by this test.
153 : 1 : SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
154 : :
155 : 1 : const char* dir = std::getenv("DIR_UNIT_TEST_DATA");
156 [ + - + - : 2 : BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test");
- + ]
157 [ - + ]: 1 : if (dir == nullptr) return;
158 [ # # # # ]: 0 : auto path = fs::path(dir) / "script_assets_test.json";
159 [ # # ]: 0 : bool exists = fs::exists(path);
160 [ # # # # : 0 : BOOST_WARN_MESSAGE(exists, "File $DIR_UNIT_TEST_DATA/script_assets_test.json not found, skipping script_assets_test");
# # ]
161 [ # # ]: 0 : if (!exists) return;
162 [ # # ]: 0 : std::ifstream file{path};
163 [ # # # # : 0 : BOOST_CHECK(file.is_open());
# # ]
164 [ # # ]: 0 : file.seekg(0, std::ios::end);
165 [ # # ]: 0 : size_t length = file.tellg();
166 [ # # ]: 0 : file.seekg(0, std::ios::beg);
167 [ # # # # ]: 0 : std::string data(length, '\0');
168 [ # # ]: 0 : file.read(data.data(), data.size());
169 [ # # ]: 0 : UniValue tests = read_json(data);
170 [ # # # # : 0 : BOOST_CHECK(tests.isArray());
# # ]
171 [ # # # # ]: 0 : BOOST_CHECK(tests.size() > 0);
172 : :
173 [ # # ]: 0 : for (size_t i = 0; i < tests.size(); i++) {
174 [ # # # # ]: 0 : AssetTest(tests[i], signature_cache);
175 : : }
176 [ # # ]: 0 : file.close();
177 : 1 : }
178 : :
179 : : BOOST_AUTO_TEST_SUITE_END()
|