mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-09 19:37:27 -03:00
Merge 0859fc22f0
into 66aa6a47bd
This commit is contained in:
commit
51f7cec79f
3 changed files with 191 additions and 141 deletions
|
@ -103,6 +103,7 @@ add_executable(test_bitcoin
|
||||||
rpc_tests.cpp
|
rpc_tests.cpp
|
||||||
sanity_tests.cpp
|
sanity_tests.cpp
|
||||||
scheduler_tests.cpp
|
scheduler_tests.cpp
|
||||||
|
script_assets_tests.cpp
|
||||||
script_p2sh_tests.cpp
|
script_p2sh_tests.cpp
|
||||||
script_parse_tests.cpp
|
script_parse_tests.cpp
|
||||||
script_segwit_tests.cpp
|
script_segwit_tests.cpp
|
||||||
|
@ -194,7 +195,7 @@ function(add_boost_test source_file)
|
||||||
COMMAND test_bitcoin --run_test=${test_suite_name} --catch_system_error=no --log_level=test_suite -- DEBUG_LOG_OUT
|
COMMAND test_bitcoin --run_test=${test_suite_name} --catch_system_error=no --log_level=test_suite -- DEBUG_LOG_OUT
|
||||||
)
|
)
|
||||||
set_property(TEST ${test_suite_name} PROPERTY
|
set_property(TEST ${test_suite_name} PROPERTY
|
||||||
SKIP_REGULAR_EXPRESSION "no test cases matching filter"
|
SKIP_REGULAR_EXPRESSION "no test cases matching filter" "skipping script_assets_test"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
189
src/test/script_assets_tests.cpp
Normal file
189
src/test/script_assets_tests.cpp
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
// Copyright (c) 2011-present 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 <primitives/transaction.h>
|
||||||
|
#include <script/interpreter.h>
|
||||||
|
#include <script/script.h>
|
||||||
|
#include <script/sigcache.h>
|
||||||
|
#include <script/sign.h>
|
||||||
|
#include <script/signingprovider.h>
|
||||||
|
#include <span.h>
|
||||||
|
#include <streams.h>
|
||||||
|
#include <test/util/json.h>
|
||||||
|
#include <util/check.h>
|
||||||
|
#include <util/fs.h>
|
||||||
|
#include <util/strencodings.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <univalue.h>
|
||||||
|
|
||||||
|
unsigned int ParseScriptFlags(std::string strFlags);
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(script_assets_tests)
|
||||||
|
|
||||||
|
/* Wrapper around ProduceSignature to combine two scriptsigs */
|
||||||
|
SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction& tx, const SignatureData& scriptSig1, const SignatureData& scriptSig2)
|
||||||
|
{
|
||||||
|
SignatureData data;
|
||||||
|
data.MergeSignatureData(scriptSig1);
|
||||||
|
data.MergeSignatureData(scriptSig2);
|
||||||
|
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(tx, 0, txout.nValue, SIGHASH_DEFAULT), txout.scriptPubKey, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
CScript ToScript(const T& byte_container)
|
||||||
|
{
|
||||||
|
auto span{MakeUCharSpan(byte_container)};
|
||||||
|
return {span.begin(), span.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
static CScript ScriptFromHex(const std::string& str)
|
||||||
|
{
|
||||||
|
return ToScript(*Assert(TryParseHex(str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static CMutableTransaction TxFromHex(const std::string& str)
|
||||||
|
{
|
||||||
|
CMutableTransaction tx;
|
||||||
|
SpanReader{ParseHex(str)} >> TX_NO_WITNESS(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;
|
||||||
|
SpanReader{ParseHex(univalue[i].get_str())} >> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, SignatureCache& signature_cache)
|
||||||
|
{
|
||||||
|
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"].getInt<int64_t>();
|
||||||
|
uint32_t 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, signature_cache, 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, signature_cache, 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.
|
||||||
|
SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
|
||||||
|
|
||||||
|
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;
|
||||||
|
std::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.data(), 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], signature_cache);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -1362,11 +1362,6 @@ CScript ToScript(const T& byte_container)
|
||||||
return {span.begin(), span.end()};
|
return {span.begin(), span.end()};
|
||||||
}
|
}
|
||||||
|
|
||||||
static CScript ScriptFromHex(const std::string& str)
|
|
||||||
{
|
|
||||||
return ToScript(*Assert(TryParseHex(str)));
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(script_byte_array_u8_vector_equivalence)
|
BOOST_AUTO_TEST_CASE(script_byte_array_u8_vector_equivalence)
|
||||||
{
|
{
|
||||||
const CScript scriptPubKey1 = CScript() << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"_hex_v_u8 << OP_CHECKSIG;
|
const CScript scriptPubKey1 = CScript() << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"_hex_v_u8 << OP_CHECKSIG;
|
||||||
|
@ -1497,141 +1492,6 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps)
|
||||||
BOOST_CHECK(!script.HasValidOps());
|
BOOST_CHECK(!script.HasValidOps());
|
||||||
}
|
}
|
||||||
|
|
||||||
static CMutableTransaction TxFromHex(const std::string& str)
|
|
||||||
{
|
|
||||||
CMutableTransaction tx;
|
|
||||||
SpanReader{ParseHex(str)} >> TX_NO_WITNESS(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;
|
|
||||||
SpanReader{ParseHex(univalue[i].get_str())} >> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, SignatureCache& signature_cache)
|
|
||||||
{
|
|
||||||
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"].getInt<int64_t>();
|
|
||||||
uint32_t 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, signature_cache, 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, signature_cache, 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.
|
|
||||||
SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
|
|
||||||
|
|
||||||
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;
|
|
||||||
std::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.data(), 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], signature_cache);
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors)
|
BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors)
|
||||||
{
|
{
|
||||||
UniValue tests;
|
UniValue tests;
|
||||||
|
|
Loading…
Reference in a new issue