Skip to content

Commit a7098a2

Browse files
glozowjl2012
andcommitted
[refactor] use CheckTxScripts, TrimFlags, FillFlags
Co-authored-by: Johnson Lau <[email protected]>
1 parent 7a77727 commit a7098a2

File tree

1 file changed

+78
-42
lines changed

1 file changed

+78
-42
lines changed

src/test/transaction_tests.cpp

Lines changed: 78 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ typedef std::vector<unsigned char> valtype;
4040
extern UniValue read_json(const std::string& jsondata);
4141

4242
static std::map<std::string, unsigned int> mapFlagNames = {
43-
{std::string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE},
4443
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
4544
{std::string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC},
4645
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
@@ -63,9 +62,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
6362

6463
unsigned int ParseScriptFlags(std::string strFlags)
6564
{
66-
if (strFlags.empty()) {
67-
return 0;
68-
}
65+
if (strFlags.empty() | strFlags == "NONE") return 0;
6966
unsigned int flags = 0;
7067
std::vector<std::string> words;
7168
boost::algorithm::split(words, strFlags, boost::algorithm::is_any_of(","));
@@ -96,14 +93,84 @@ std::string FormatScriptFlags(unsigned int flags)
9693
return ret.substr(0, ret.size() - 1);
9794
}
9895

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+
99167
BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
100168

101169
BOOST_AUTO_TEST_CASE(tx_valid)
102170
{
103171
// Read tests from test/data/tx_valid.json
104172
UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
105173

106-
ScriptError err;
107174
for (unsigned int idx = 0; idx < tests.size(); idx++) {
108175
UniValue test = tests[idx];
109176
std::string strTest = test.write();
@@ -153,24 +220,10 @@ BOOST_AUTO_TEST_CASE(tx_valid)
153220
BOOST_CHECK(state.IsValid());
154221

155222
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());
163224

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);
174227
}
175228
}
176229
}
@@ -181,9 +234,6 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
181234
// Read tests from test/data/tx_invalid.json
182235
UniValue tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
183236

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;
187237
for (unsigned int idx = 0; idx < tests.size(); idx++) {
188238
UniValue test = tests[idx];
189239
std::string strTest = test.write();
@@ -235,25 +285,11 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
235285
}
236286

237287
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());
245289

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);
254292
}
255-
BOOST_CHECK_MESSAGE(!fValid, strTest);
256-
BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
257293
}
258294
}
259295
}

0 commit comments

Comments
 (0)