diff options
Diffstat (limited to 'src/test/transaction_tests.cpp')
-rw-r--r-- | src/test/transaction_tests.cpp | 71 |
1 files changed, 47 insertions, 24 deletions
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 8f1d99b199..9bbf9567f5 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -20,6 +20,7 @@ #include <script/signingprovider.h> #include <script/standard.h> #include <streams.h> +#include <test/util/script.h> #include <test/util/transaction_utils.h> #include <util/strencodings.h> #include <util/string.h> @@ -59,6 +60,9 @@ static std::map<std::string, unsigned int> mapFlagNames = { {std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE}, {std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE}, {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT}, + {std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE}, + {std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS}, + {std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION}, }; unsigned int ParseScriptFlags(std::string strFlags) @@ -78,6 +82,16 @@ unsigned int ParseScriptFlags(std::string strFlags) return flags; } +// Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in mapFlagNames. +bool CheckMapFlagNames() +{ + unsigned int standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS}; + for (const auto& pair : mapFlagNames) { + standard_flags_missing &= ~(pair.second); + } + return standard_flags_missing == 0; +} + std::string FormatScriptFlags(unsigned int flags) { if (flags == 0) { @@ -108,7 +122,7 @@ bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>& const CAmount amount = map_prevout_values.count(input.prevout) ? map_prevout_values.at(input.prevout) : 0; try { tx_valid = VerifyScript(input.scriptSig, map_prevout_scriptPubKeys.at(input.prevout), - &input.scriptWitness, flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err); + &input.scriptWitness, flags, TransactionSignatureChecker(&tx, i, amount, txdata, MissingDataBehavior::ASSERT_FAIL), &err); } catch (...) { BOOST_ERROR("Bad test: " << strTest); return true; // The test format is bad and an error is thrown. Return true to silence further error. @@ -139,6 +153,7 @@ unsigned int TrimFlags(unsigned int flags) // CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH) if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~(unsigned int)SCRIPT_VERIFY_CLEANSTACK; + Assert(IsValidFlagCombination(flags)); return flags; } @@ -149,17 +164,21 @@ unsigned int FillFlags(unsigned int flags) // WITNESS implies P2SH (and transitively CLEANSTACK implies P2SH) if (flags & SCRIPT_VERIFY_WITNESS) flags |= SCRIPT_VERIFY_P2SH; + Assert(IsValidFlagCombination(flags)); return flags; } -// Return valid flags that are all except one flag for each flag -std::vector<unsigned int> ExcludeIndividualFlags(unsigned int flags) +// Exclude each possible script verify flag from flags. Returns a set of these flag combinations +// that are valid and without duplicates. For example: if flags=1111 and the 4 possible flags are +// 0001, 0010, 0100, and 1000, this should return the set {0111, 1011, 1101, 1110}. +// Assumes that mapFlagNames contains all script verify flags. +std::set<unsigned int> ExcludeIndividualFlags(unsigned int flags) { - std::vector<unsigned int> flags_combos; - for (unsigned int i = 0; i < mapFlagNames.size(); ++i) { - const unsigned int flags_excluding_i = TrimFlags(flags & ~(1U << i)); - if (flags != flags_excluding_i && std::find(flags_combos.begin(), flags_combos.end(), flags_excluding_i) != flags_combos.end()) { - flags_combos.push_back(flags_excluding_i); + std::set<unsigned int> flags_combos; + for (const auto& pair : mapFlagNames) { + const unsigned int flags_excluding_one = TrimFlags(flags & ~(pair.second)); + if (flags != flags_excluding_one) { + flags_combos.insert(flags_excluding_one); } } return flags_combos; @@ -169,6 +188,7 @@ BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(tx_valid) { + BOOST_CHECK_MESSAGE(CheckMapFlagNames(), "mapFlagNames is missing a script verification flag"); // Read tests from test/data/tx_valid.json UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid))); @@ -228,16 +248,15 @@ BOOST_AUTO_TEST_CASE(tx_valid) BOOST_ERROR("Bad test flags: " << strTest); } - if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~verify_flags, txdata, strTest, /* expect_valid */ true)) { - BOOST_ERROR("Tx unexpectedly failed: " << strTest); - } + BOOST_CHECK_MESSAGE(CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~verify_flags, txdata, strTest, /* expect_valid */ true), + "Tx unexpectedly failed: " << strTest); // Backwards compatibility of script verification flags: Removing any flag(s) should not invalidate a valid transaction - for (size_t i = 0; i < mapFlagNames.size(); ++i) { + for (const auto& [name, flag] : mapFlagNames) { // Removing individual flags - unsigned int flags = TrimFlags(~(verify_flags | (1U << i))); + unsigned int flags = TrimFlags(~(verify_flags | flag)); if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ true)) { - BOOST_ERROR("Tx unexpectedly failed with flag " << ToString(i) << " unset: " << strTest); + BOOST_ERROR("Tx unexpectedly failed with flag " << name << " unset: " << strTest); } // Removing random combinations of flags flags = TrimFlags(~(verify_flags | (unsigned int)InsecureRandBits(mapFlagNames.size()))); @@ -247,7 +266,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) } // Check that flags are maximal: transaction should fail if any unset flags are set. - for (auto flags_excluding_one: ExcludeIndividualFlags(verify_flags)) { + for (auto flags_excluding_one : ExcludeIndividualFlags(verify_flags)) { if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~flags_excluding_one, txdata, strTest, /* expect_valid */ false)) { BOOST_ERROR("Too many flags unset: " << strTest); } @@ -314,27 +333,31 @@ BOOST_AUTO_TEST_CASE(tx_invalid) PrecomputedTransactionData txdata(tx); unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); - // Not using FillFlags() in the main test, in order to detect invalid verifyFlags combination - if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, verify_flags, txdata, strTest, /* expect_valid */ false)) { - BOOST_ERROR("Tx unexpectedly passed: " << strTest); + // Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags. + if (verify_flags != FillFlags(verify_flags)) { + BOOST_ERROR("Bad test flags: " << strTest); } + // Not using FillFlags() in the main test, in order to detect invalid verifyFlags combination + BOOST_CHECK_MESSAGE(CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, verify_flags, txdata, strTest, /* expect_valid */ false), + "Tx unexpectedly passed: " << strTest); + // Backwards compatibility of script verification flags: Adding any flag(s) should not validate an invalid transaction - for (size_t i = 0; i < mapFlagNames.size(); i++) { - unsigned int flags = FillFlags(verify_flags | (1U << i)); + for (const auto& [name, flag] : mapFlagNames) { + unsigned int flags = FillFlags(verify_flags | flag); // Adding individual flags if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ false)) { - BOOST_ERROR("Tx unexpectedly passed with flag " << ToString(i) << " set: " << strTest); + BOOST_ERROR("Tx unexpectedly passed with flag " << name << " set: " << strTest); } // Adding random combinations of flags flags = FillFlags(verify_flags | (unsigned int)InsecureRandBits(mapFlagNames.size())); if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ false)) { - BOOST_ERROR("Tx unexpectedly passed with random flags " << ToString(flags) << ": " << strTest); + BOOST_ERROR("Tx unexpectedly passed with random flags " << name << ": " << strTest); } } // Check that flags are minimal: transaction should succeed if any set flags are unset. - for (auto flags_excluding_one: ExcludeIndividualFlags(verify_flags)) { + for (auto flags_excluding_one : ExcludeIndividualFlags(verify_flags)) { if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags_excluding_one, txdata, strTest, /* expect_valid */ true)) { BOOST_ERROR("Too many flags set: " << strTest); } @@ -427,7 +450,7 @@ static void CheckWithFlag(const CTransactionRef& output, const CMutableTransacti { ScriptError error; CTransaction inputi(input); - bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue), &error); + bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &error); assert(ret == success); } |