@@ -40,7 +40,6 @@ typedef std::vector<unsigned char> valtype;
40
40
extern UniValue read_json (const std::string& jsondata);
41
41
42
42
static std::map<std::string, unsigned int > mapFlagNames = {
43
- {std::string (" NONE" ), (unsigned int )SCRIPT_VERIFY_NONE},
44
43
{std::string (" P2SH" ), (unsigned int )SCRIPT_VERIFY_P2SH},
45
44
{std::string (" STRICTENC" ), (unsigned int )SCRIPT_VERIFY_STRICTENC},
46
45
{std::string (" DERSIG" ), (unsigned int )SCRIPT_VERIFY_DERSIG},
@@ -63,9 +62,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
63
62
64
63
unsigned int ParseScriptFlags (std::string strFlags)
65
64
{
66
- if (strFlags.empty ()) {
67
- return 0 ;
68
- }
65
+ if (strFlags.empty () | strFlags == " NONE" ) return 0 ;
69
66
unsigned int flags = 0 ;
70
67
std::vector<std::string> words;
71
68
boost::algorithm::split (words, strFlags, boost::algorithm::is_any_of (" ," ));
@@ -96,14 +93,84 @@ std::string FormatScriptFlags(unsigned int flags)
96
93
return ret.substr (0 , ret.size () - 1 );
97
94
}
98
95
96
+ /*
97
+ * Check that the input scripts of a transaction are valid/invalid as expected.
98
+ */
99
+ bool CheckTxScripts (const CTransaction& tx, const std::map<COutPoint, CScript>& map_prevout_scriptPubKeys,
100
+ const std::map<COutPoint, int64_t >& map_prevout_values, unsigned int flags,
101
+ const PrecomputedTransactionData& txdata, const std::string& strTest, bool expect_valid)
102
+ {
103
+ bool tx_valid = true ;
104
+ ScriptError err = expect_valid ? SCRIPT_ERR_UNKNOWN_ERROR : SCRIPT_ERR_OK;
105
+ for (unsigned int i = 0 ; i < tx.vin .size () && tx_valid; ++i) {
106
+ const CTxIn input = tx.vin [i];
107
+ const CAmount amount = map_prevout_values.count (input.prevout ) ? map_prevout_values.at (input.prevout ) : 0 ;
108
+ try {
109
+ tx_valid = VerifyScript (input.scriptSig , map_prevout_scriptPubKeys.at (input.prevout ),
110
+ &input.scriptWitness , flags, TransactionSignatureChecker (&tx, i, amount, txdata), &err);
111
+ } catch (...) {
112
+ BOOST_ERROR (" Bad test: " << strTest);
113
+ return true ; // The test format is bad and an error is thrown. Return true to silence further error.
114
+ }
115
+ if (expect_valid) {
116
+ BOOST_CHECK_MESSAGE (tx_valid, strTest);
117
+ BOOST_CHECK_MESSAGE ((err == SCRIPT_ERR_OK), ScriptErrorString (err));
118
+ err = SCRIPT_ERR_UNKNOWN_ERROR;
119
+ }
120
+ }
121
+ if (!expect_valid) {
122
+ BOOST_CHECK_MESSAGE (!tx_valid, strTest);
123
+ BOOST_CHECK_MESSAGE ((err != SCRIPT_ERR_OK), ScriptErrorString (err));
124
+ }
125
+ return (tx_valid == expect_valid);
126
+ }
127
+
128
+ /*
129
+ * Trim or fill flags to make the combination valid:
130
+ * WITNESS must be used with P2SH
131
+ * CLEANSTACK must be used WITNESS and P2SH
132
+ */
133
+
134
+ unsigned int TrimFlags (unsigned int flags)
135
+ {
136
+ // WITNESS requires P2SH
137
+ if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~(unsigned int )SCRIPT_VERIFY_WITNESS;
138
+
139
+ // CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH)
140
+ if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~(unsigned int )SCRIPT_VERIFY_CLEANSTACK;
141
+ return flags;
142
+ }
143
+
144
+ unsigned int FillFlags (unsigned int flags)
145
+ {
146
+ // CLEANSTACK implies WITNESS
147
+ if (flags & SCRIPT_VERIFY_CLEANSTACK) flags |= SCRIPT_VERIFY_WITNESS;
148
+
149
+ // WITNESS implies P2SH (and transitively CLEANSTACK implies P2SH)
150
+ if (flags & SCRIPT_VERIFY_WITNESS) flags |= SCRIPT_VERIFY_P2SH;
151
+ return flags;
152
+ }
153
+
154
+ // Return valid flags that are all except one flag for each flag
155
+ std::vector<unsigned int > ExcludeIndividualFlags (unsigned int flags)
156
+ {
157
+ std::vector<unsigned int > flags_combos;
158
+ for (unsigned int i = 0 ; i < mapFlagNames.size (); ++i) {
159
+ const unsigned int flags_excluding_i = TrimFlags (flags & ~(1U << i));
160
+ if (flags != flags_excluding_i && std::find (flags_combos.begin (), flags_combos.end (), flags_excluding_i) != flags_combos.end ()) {
161
+ flags_combos.push_back (flags_excluding_i);
162
+ }
163
+ }
164
+ return flags_combos;
165
+ }
166
+
99
167
BOOST_FIXTURE_TEST_SUITE (transaction_tests, BasicTestingSetup)
100
168
101
169
BOOST_AUTO_TEST_CASE(tx_valid)
102
170
{
103
171
// Read tests from test/data/tx_valid.json
104
172
UniValue tests = read_json (std::string (json_tests::tx_valid, json_tests::tx_valid + sizeof (json_tests::tx_valid)));
105
173
106
- ScriptError err;
107
174
for (unsigned int idx = 0 ; idx < tests.size (); idx++) {
108
175
UniValue test = tests[idx];
109
176
std::string strTest = test.write ();
@@ -153,24 +220,10 @@ BOOST_AUTO_TEST_CASE(tx_valid)
153
220
BOOST_CHECK (state.IsValid ());
154
221
155
222
PrecomputedTransactionData txdata (tx);
156
- for (unsigned int i = 0 ; i < tx.vin .size (); i++)
157
- {
158
- if (!mapprevOutScriptPubKeys.count (tx.vin [i].prevout ))
159
- {
160
- BOOST_ERROR (" Bad test: " << strTest);
161
- break ;
162
- }
223
+ unsigned int verify_flags = ParseScriptFlags (test[2 ].get_str ());
163
224
164
- CAmount amount = 0 ;
165
- if (mapprevOutValues.count (tx.vin [i].prevout )) {
166
- amount = mapprevOutValues[tx.vin [i].prevout ];
167
- }
168
- unsigned int verify_flags = ~ParseScriptFlags (test[2 ].get_str());
169
- const CScriptWitness *witness = &tx.vin [i].scriptWitness ;
170
- BOOST_CHECK_MESSAGE (VerifyScript (tx.vin [i].scriptSig , mapprevOutScriptPubKeys[tx.vin [i].prevout ],
171
- witness, verify_flags, TransactionSignatureChecker (&tx, i, amount, txdata), &err),
172
- strTest);
173
- BOOST_CHECK_MESSAGE (err == SCRIPT_ERR_OK, ScriptErrorString (err));
225
+ if (!CheckTxScripts (tx, mapprevOutScriptPubKeys, mapprevOutValues, ~verify_flags, txdata, strTest, /* expect_valid */ true )) {
226
+ BOOST_ERROR (" Tx unexpectedly failed: " << strTest);
174
227
}
175
228
}
176
229
}
@@ -181,9 +234,6 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
181
234
// Read tests from test/data/tx_invalid.json
182
235
UniValue tests = read_json (std::string (json_tests::tx_invalid, json_tests::tx_invalid + sizeof (json_tests::tx_invalid)));
183
236
184
- // Initialize to SCRIPT_ERR_OK. The tests expect err to be changed to a
185
- // value other than SCRIPT_ERR_OK.
186
- ScriptError err = SCRIPT_ERR_OK;
187
237
for (unsigned int idx = 0 ; idx < tests.size (); idx++) {
188
238
UniValue test = tests[idx];
189
239
std::string strTest = test.write ();
@@ -235,25 +285,11 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
235
285
}
236
286
237
287
PrecomputedTransactionData txdata (tx);
238
- for (unsigned int i = 0 ; i < tx.vin .size () && fValid ; i++)
239
- {
240
- if (!mapprevOutScriptPubKeys.count (tx.vin [i].prevout ))
241
- {
242
- BOOST_ERROR (" Bad test: " << strTest);
243
- break ;
244
- }
288
+ unsigned int verify_flags = ParseScriptFlags (test[2 ].get_str ());
245
289
246
- unsigned int verify_flags = ParseScriptFlags (test[2 ].get_str ());
247
- CAmount amount = 0 ;
248
- if (mapprevOutValues.count (tx.vin [i].prevout )) {
249
- amount = mapprevOutValues[tx.vin [i].prevout ];
250
- }
251
- const CScriptWitness *witness = &tx.vin [i].scriptWitness ;
252
- fValid = VerifyScript (tx.vin [i].scriptSig , mapprevOutScriptPubKeys[tx.vin [i].prevout ],
253
- witness, verify_flags, TransactionSignatureChecker (&tx, i, amount, txdata), &err);
290
+ if (!CheckTxScripts (tx, mapprevOutScriptPubKeys, mapprevOutValues, verify_flags, txdata, strTest, /* expect_valid */ false )) {
291
+ BOOST_ERROR (" Tx unexpectedly passed: " << strTest);
254
292
}
255
- BOOST_CHECK_MESSAGE (!fValid , strTest);
256
- BOOST_CHECK_MESSAGE (err != SCRIPT_ERR_OK, ScriptErrorString (err));
257
293
}
258
294
}
259
295
}
0 commit comments