diff options
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/fuzz/script_assets_test_minimizer.cpp | 200 | ||||
-rw-r--r-- | src/test/fuzz/script_sigcache.cpp | 18 | ||||
-rw-r--r-- | src/test/fuzz/signature_checker.cpp | 7 | ||||
-rw-r--r-- | src/test/key_tests.cpp | 28 | ||||
-rw-r--r-- | src/test/script_tests.cpp | 138 | ||||
-rw-r--r-- | src/test/transaction_tests.cpp | 1 |
6 files changed, 383 insertions, 9 deletions
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp new file mode 100644 index 0000000000..d20fa43d68 --- /dev/null +++ b/src/test/fuzz/script_assets_test_minimizer.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/fuzz/fuzz.h> + +#include <primitives/transaction.h> +#include <pubkey.h> +#include <script/interpreter.h> +#include <serialize.h> +#include <streams.h> +#include <univalue.h> +#include <util/strencodings.h> + +#include <boost/algorithm/string.hpp> +#include <cstdint> +#include <string> +#include <vector> + +// This fuzz "test" can be used to minimize test cases for script_assets_test in +// src/test/script_tests.cpp. While it written as a fuzz test, and can be used as such, +// fuzzing the inputs is unlikely to construct useful test cases. +// +// Instead, it is primarily intended to be run on a test set that was generated +// externally, for example using test/functional/feature_taproot.py's --dumptests mode. +// The minimized set can then be concatenated together, surrounded by '[' and ']', +// and used as the script_assets_test.json input to the script_assets_test unit test: +// +// (normal build) +// $ mkdir dump +// $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot --dumptests; done +// $ ... +// +// (fuzz test build) +// $ mkdir dump-min +// $ ./src/test/fuzz/script_assets_test_minimizer -merge=1 dump-min/ dump/ +// $ (echo -en '[\n'; cat dump-min/* | head -c -2; echo -en '\n]') >script_assets_test.json + +namespace { + +std::vector<unsigned char> CheckedParseHex(const std::string& str) +{ + if (str.size() && !IsHex(str)) throw std::runtime_error("Non-hex input '" + str + "'"); + return ParseHex(str); +} + +CScript ScriptFromHex(const std::string& str) +{ + std::vector<unsigned char> data = CheckedParseHex(str); + return CScript(data.begin(), data.end()); +} + +CMutableTransaction TxFromHex(const std::string& str) +{ + CMutableTransaction tx; + try { + VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, CheckedParseHex(str), 0) >> tx; + } catch (const std::ios_base::failure&) { + throw std::runtime_error("Tx deserialization failure"); + } + return tx; +} + +std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue) +{ + if (!univalue.isArray()) throw std::runtime_error("Prevouts must be array"); + std::vector<CTxOut> prevouts; + for (size_t i = 0; i < univalue.size(); ++i) { + CTxOut txout; + try { + VectorReader(SER_DISK, 0, CheckedParseHex(univalue[i].get_str()), 0) >> txout; + } catch (const std::ios_base::failure&) { + throw std::runtime_error("Prevout invalid format"); + } + prevouts.push_back(std::move(txout)); + } + return prevouts; +} + +CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue) +{ + if (!univalue.isArray()) throw std::runtime_error("Script witness is not array"); + CScriptWitness scriptwitness; + for (size_t i = 0; i < univalue.size(); ++i) { + auto bytes = CheckedParseHex(univalue[i].get_str()); + scriptwitness.stack.push_back(std::move(bytes)); + } + return scriptwitness; +} + +const std::map<std::string, unsigned int> FLAG_NAMES = { + {std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH}, + {std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG}, + {std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY}, + {std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY}, + {std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY}, + {std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS}, + {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT}, +}; + +std::vector<unsigned int> AllFlags() +{ + std::vector<unsigned int> ret; + + for (unsigned int i = 0; i < 128; ++i) { + unsigned int flag = 0; + if (i & 1) flag |= SCRIPT_VERIFY_P2SH; + if (i & 2) flag |= SCRIPT_VERIFY_DERSIG; + if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY; + if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; + if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; + if (i & 32) flag |= SCRIPT_VERIFY_WITNESS; + if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT; + + // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH + if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue; + // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS + if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue; + + ret.push_back(flag); + } + + return ret; +} + +const std::vector<unsigned int> ALL_FLAGS = AllFlags(); + +unsigned int ParseScriptFlags(const std::string& str) +{ + if (str.empty()) return 0; + + unsigned int flags = 0; + std::vector<std::string> words; + boost::algorithm::split(words, str, boost::algorithm::is_any_of(",")); + + for (const std::string& word : words) + { + auto it = FLAG_NAMES.find(word); + if (it == FLAG_NAMES.end()) throw std::runtime_error("Unknown verification flag " + word); + flags |= it->second; + } + + return flags; +} + +void Test(const std::string& str) +{ + UniValue test; + if (!test.read(str) || !test.isObject()) throw std::runtime_error("Non-object test input"); + + CMutableTransaction tx = TxFromHex(test["tx"].get_str()); + const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]); + if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts"); + size_t idx = test["index"].get_int64(); + if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index"); + unsigned int test_flags = ParseScriptFlags(test["flags"].get_str()); + bool final = test.exists("final") && test["final"].get_bool(); + + if (test.exists("success")) { + tx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str()); + tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]); + PrecomputedTransactionData txdata; + txdata.Init(tx, std::vector<CTxOut>(prevouts)); + MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata); + for (const auto flags : ALL_FLAGS) { + // "final": true tests are valid for all flags. Others are only valid with flags that are + // a subset of test_flags. + if (final || ((flags & test_flags) == flags)) { + (void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); + } + } + } + + if (test.exists("failure")) { + tx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str()); + tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]); + PrecomputedTransactionData txdata; + txdata.Init(tx, std::vector<CTxOut>(prevouts)); + MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata); + for (const auto flags : ALL_FLAGS) { + // If a test is supposed to fail with test_flags, it should also fail with any superset thereof. + if ((flags & test_flags) == test_flags) { + (void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); + } + } + } +} + +ECCVerifyHandle handle; + +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return; + const std::string str((const char*)buffer.data(), buffer.size() - 2); + try { + Test(str); + } catch (const std::runtime_error&) {} +} diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp index 434a47b702..87af71897b 100644 --- a/src/test/fuzz/script_sigcache.cpp +++ b/src/test/fuzz/script_sigcache.cpp @@ -35,11 +35,19 @@ void test_one_input(const std::vector<uint8_t>& buffer) const bool store = fuzzed_data_provider.ConsumeBool(); PrecomputedTransactionData tx_data; CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data}; - const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider); - if (pub_key) { - const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider); - if (!random_bytes.empty()) { - (void)caching_transaction_signature_checker.VerifySignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider)); + if (fuzzed_data_provider.ConsumeBool()) { + const auto random_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(64); + const XOnlyPubKey pub_key(ConsumeUInt256(fuzzed_data_provider)); + if (random_bytes.size() == 64) { + (void)caching_transaction_signature_checker.VerifySchnorrSignature(random_bytes, pub_key, ConsumeUInt256(fuzzed_data_provider)); + } + } else { + const auto random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider); + const auto pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider); + if (pub_key) { + if (!random_bytes.empty()) { + (void)caching_transaction_signature_checker.VerifyECDSASignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider)); + } } } } diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp index 3aaeb66649..e121c89665 100644 --- a/src/test/fuzz/signature_checker.cpp +++ b/src/test/fuzz/signature_checker.cpp @@ -28,7 +28,12 @@ public: { } - bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override + bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override + { + return m_fuzzed_data_provider.ConsumeBool(); + } + + bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override { return m_fuzzed_data_provider.ConsumeBool(); } diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 4e4c44266a..3362b8d17c 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -264,4 +264,32 @@ BOOST_AUTO_TEST_CASE(pubkey_unserialize) } } +BOOST_AUTO_TEST_CASE(bip340_test_vectors) +{ + static const std::vector<std::pair<std::array<std::string, 3>, bool>> VECTORS = { + {{"F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", "0000000000000000000000000000000000000000000000000000000000000000", "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"}, true}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"}, true}, + {{"DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"}, true}, + {{"25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"}, true}, + {{"D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703", "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4"}, true}, + {{"EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2"}, false}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD"}, false}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6"}, false}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051"}, false}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197"}, false}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false}, + {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"}, false}, + {{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false} + }; + + for (const auto& test : VECTORS) { + auto pubkey = ParseHex(test.first[0]); + auto msg = ParseHex(test.first[1]); + auto sig = ParseHex(test.first[2]); + BOOST_CHECK_EQUAL(XOnlyPubKey(pubkey).VerifySchnorr(uint256(msg), sig), test.second); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 0830743d61..a2efd8ac07 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -5,10 +5,12 @@ #include <test/data/script_tests.json.h> #include <core_io.h> +#include <fs.h> #include <key.h> #include <rpc/util.h> #include <script/script.h> #include <script/script_error.h> +#include <script/sigcache.h> #include <script/sign.h> #include <script/signingprovider.h> #include <streams.h> @@ -1339,13 +1341,41 @@ BOOST_AUTO_TEST_CASE(script_GetScriptAsm) BOOST_CHECK_EQUAL(derSig + "83 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "83")) << vchPubKey)); } -static CScript -ScriptFromHex(const char* hex) +static CScript ScriptFromHex(const std::string& str) { - std::vector<unsigned char> data = ParseHex(hex); + std::vector<unsigned char> data = ParseHex(str); return CScript(data.begin(), data.end()); } +static CMutableTransaction TxFromHex(const std::string& str) +{ + CMutableTransaction tx; + VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx; + return tx; +} + +static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue) +{ + assert(univalue.isArray()); + std::vector<CTxOut> prevouts; + for (size_t i = 0; i < univalue.size(); ++i) { + CTxOut txout; + VectorReader(SER_DISK, 0, ParseHex(univalue[i].get_str()), 0) >> txout; + prevouts.push_back(std::move(txout)); + } + return prevouts; +} + +static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue) +{ + assert(univalue.isArray()); + CScriptWitness scriptwitness; + for (size_t i = 0; i < univalue.size(); ++i) { + auto bytes = ParseHex(univalue[i].get_str()); + scriptwitness.stack.push_back(std::move(bytes)); + } + return scriptwitness; +} BOOST_AUTO_TEST_CASE(script_FindAndDelete) { @@ -1610,5 +1640,107 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags) BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS); } +static std::vector<unsigned int> AllConsensusFlags() +{ + std::vector<unsigned int> ret; + + for (unsigned int i = 0; i < 128; ++i) { + unsigned int flag = 0; + if (i & 1) flag |= SCRIPT_VERIFY_P2SH; + if (i & 2) flag |= SCRIPT_VERIFY_DERSIG; + if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY; + if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; + if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; + if (i & 32) flag |= SCRIPT_VERIFY_WITNESS; + if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT; + + // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH + if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue; + // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS + if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue; + + ret.push_back(flag); + } + + return ret; +} + +/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */ +static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags(); + +static void AssetTest(const UniValue& test) +{ + BOOST_CHECK(test.isObject()); + + CMutableTransaction mtx = TxFromHex(test["tx"].get_str()); + const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]); + BOOST_CHECK(prevouts.size() == mtx.vin.size()); + size_t idx = test["index"].get_int64(); + unsigned int test_flags = ParseScriptFlags(test["flags"].get_str()); + bool fin = test.exists("final") && test["final"].get_bool(); + + if (test.exists("success")) { + mtx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str()); + mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]); + CTransaction tx(mtx); + PrecomputedTransactionData txdata; + txdata.Init(tx, std::vector<CTxOut>(prevouts)); + CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata); + for (const auto flags : ALL_CONSENSUS_FLAGS) { + // "final": true tests are valid for all flags. Others are only valid with flags that are + // a subset of test_flags. + if (fin || ((flags & test_flags) == flags)) { + bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); + BOOST_CHECK(ret); + } + } + } + + if (test.exists("failure")) { + mtx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str()); + mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]); + CTransaction tx(mtx); + PrecomputedTransactionData txdata; + txdata.Init(tx, std::vector<CTxOut>(prevouts)); + CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata); + for (const auto flags : ALL_CONSENSUS_FLAGS) { + // If a test is supposed to fail with test_flags, it should also fail with any superset thereof. + if ((flags & test_flags) == test_flags) { + bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr); + BOOST_CHECK(!ret); + } + } + } +} + +BOOST_AUTO_TEST_CASE(script_assets_test) +{ + // See src/test/fuzz/script_assets_test_minimizer.cpp for information on how to generate + // the script_assets_test.json file used by this test. + + const char* dir = std::getenv("DIR_UNIT_TEST_DATA"); + BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test"); + if (dir == nullptr) return; + auto path = fs::path(dir) / "script_assets_test.json"; + bool exists = fs::exists(path); + BOOST_WARN_MESSAGE(exists, "File $DIR_UNIT_TEST_DATA/script_assets_test.json not found, skipping script_assets_test"); + if (!exists) return; + fs::ifstream file(path); + BOOST_CHECK(file.is_open()); + file.seekg(0, std::ios::end); + size_t length = file.tellg(); + file.seekg(0, std::ios::beg); + std::string data(length, '\0'); + file.read(&data[0], data.size()); + UniValue tests = read_json(data); + BOOST_CHECK(tests.isArray()); + BOOST_CHECK(tests.size() > 0); + + for (size_t i = 0; i < tests.size(); i++) { + AssetTest(tests[i]); + } + file.close(); +} + #endif BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 94b5dba913..b7ee280336 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -57,6 +57,7 @@ static std::map<std::string, unsigned int> mapFlagNames = { {std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM}, {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}, }; unsigned int ParseScriptFlags(std::string strFlags) |