Changeset View
Changeset View
Standalone View
Standalone View
src/test/txvalidationcache_tests.cpp
Show First 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | |||||
// script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain | // script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain | ||||
// CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain | // CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain | ||||
// OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution | // OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution | ||||
// should fail. | // should fail. | ||||
// Capture this interaction with the upgraded_nop argument: set it when | // Capture this interaction with the upgraded_nop argument: set it when | ||||
// evaluating any script flag that is implemented as an upgraded NOP code. | // evaluating any script flag that is implemented as an upgraded NOP code. | ||||
static void | static void | ||||
ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, | ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, | ||||
uint32_t required_flags, bool add_to_cache) | uint32_t required_flags, bool add_to_cache, | ||||
ScriptExecutionMetrics expected_metrics) | |||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
PrecomputedTransactionData txdata(tx); | PrecomputedTransactionData txdata(tx); | ||||
MMIXLinearCongruentialGenerator lcg; | MMIXLinearCongruentialGenerator lcg; | ||||
for (int i = 0; i < 4096; i++) { | for (int i = 0; i < 4096; i++) { | ||||
uint32_t test_flags = lcg.next() | required_flags; | uint32_t test_flags = lcg.next() | required_flags; | ||||
CValidationState state; | CValidationState state; | ||||
// Filter out incompatible flag choices | // Filter out incompatible flag choices | ||||
if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { | if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { | ||||
// CLEANSTACK requires P2SH, see VerifyScript() in | // CLEANSTACK requires P2SH, see VerifyScript() in | ||||
// script/interpreter.cpp | // script/interpreter.cpp | ||||
test_flags |= SCRIPT_VERIFY_P2SH; | test_flags |= SCRIPT_VERIFY_P2SH; | ||||
} | } | ||||
bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, | ScriptExecutionMetrics metricsDirect(0xf00d); | ||||
true, add_to_cache, txdata, nullptr); | bool ret = | ||||
CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, | |||||
add_to_cache, txdata, metricsDirect, nullptr); | |||||
// CheckInputs should succeed iff test_flags doesn't intersect with | // CheckInputs should succeed iff test_flags doesn't intersect with | ||||
// failing_flags | // failing_flags | ||||
bool expected_return_value = !(test_flags & failing_flags); | bool expected_return_value = !(test_flags & failing_flags); | ||||
BOOST_CHECK_EQUAL(ret, expected_return_value); | BOOST_CHECK_EQUAL(ret, expected_return_value); | ||||
if (ret) { | |||||
ScriptExecutionMetrics metricsExpectedForFlags = expected_metrics; | |||||
if (!(test_flags & SCRIPT_REPORT_SIGCHECKS)) { | |||||
metricsExpectedForFlags.nSigChecks = 0; | |||||
} | |||||
BOOST_CHECK(metricsExpectedForFlags == metricsDirect); | |||||
} | |||||
// Test the caching | // Test the caching | ||||
if (ret && add_to_cache) { | if (ret && add_to_cache) { | ||||
// Check that we get a cache hit if the tx was valid | // Check that we get a cache hit if the tx was valid | ||||
std::vector<CScriptCheck> scriptchecks; | std::vector<CScriptCheck> scriptchecks; | ||||
ScriptExecutionMetrics metricsCached(0xbeef); | |||||
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, | BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, | ||||
test_flags, true, add_to_cache, txdata, | test_flags, true, add_to_cache, txdata, | ||||
&scriptchecks)); | metricsCached, &scriptchecks)); | ||||
BOOST_CHECK(metricsCached == metricsDirect); | |||||
BOOST_CHECK(scriptchecks.empty()); | BOOST_CHECK(scriptchecks.empty()); | ||||
} else { | } else { | ||||
// Check that we get script executions to check, if the transaction | // Check that we get script executions to check, if the transaction | ||||
// was invalid, or we didn't add to cache. | // was invalid, or we didn't add to cache. | ||||
std::vector<CScriptCheck> scriptchecks; | std::vector<CScriptCheck> scriptchecks; | ||||
ScriptExecutionMetrics metricsUncached(0xbabe); | |||||
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, | BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, | ||||
test_flags, true, add_to_cache, txdata, | test_flags, true, add_to_cache, txdata, | ||||
&scriptchecks)); | metricsUncached, &scriptchecks)); | ||||
BOOST_CHECK(!ret || metricsUncached == ScriptExecutionMetrics()); | |||||
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); | BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) { | BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) { | ||||
// Test that passing CheckInputs with one set of script flags doesn't imply | // Test that passing CheckInputs with one set of script flags doesn't imply | ||||
// that we would pass again with a different set of flags. | // that we would pass again with a different set of flags. | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) { | ||||
// spend_tx is invalid according to DISCOURAGE_UPGRADABLE_NOPS | // spend_tx is invalid according to DISCOURAGE_UPGRADABLE_NOPS | ||||
{ | { | ||||
const CTransaction tx(spend_tx); | const CTransaction tx(spend_tx); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CValidationState state; | CValidationState state; | ||||
PrecomputedTransactionData ptd_spend_tx(tx); | PrecomputedTransactionData ptd_spend_tx(tx); | ||||
ScriptExecutionMetrics metricsDummy; | |||||
BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip.get(), true, | BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | ||||
ptd_spend_tx, nullptr)); | ptd_spend_tx, metricsDummy, nullptr)); | ||||
// If we call again asking for scriptchecks (as happens in | // If we call again asking for scriptchecks (as happens in | ||||
// ConnectBlock), we should add a script check object for this -- we're | // ConnectBlock), we should add a script check object for this -- we're | ||||
// not caching invalidity (if that changes, delete this test case). | // not caching invalidity (if that changes, delete this test case). | ||||
std::vector<CScriptCheck> scriptchecks; | std::vector<CScriptCheck> scriptchecks; | ||||
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, | BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | ||||
ptd_spend_tx, &scriptchecks)); | ptd_spend_tx, metricsDummy, &scriptchecks)); | ||||
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U); | BOOST_CHECK_EQUAL(scriptchecks.size(), 1U); | ||||
// Test that CheckInputs returns true iff cleanstack-enforcing flags are | // Test that CheckInputs returns true iff cleanstack-enforcing flags are | ||||
// not present. Don't add these checks to the cache, so that we can test | // not present. Don't add these checks to the cache, so that we can test | ||||
// later that block validation works fine in the absence of cached | // later that block validation works fine in the absence of cached | ||||
// successes. | // successes. | ||||
ValidateCheckInputsForAllFlags( | ValidateCheckInputsForAllFlags(tx, | ||||
tx, SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 0, false); | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS, | ||||
0, false, ScriptExecutionMetrics(0)); | |||||
} | } | ||||
// And if we produce a block with this tx, it should be valid, even though | // And if we produce a block with this tx, it should be valid, even though | ||||
// there's no cache entry. | // there's no cache entry. | ||||
CBlock block; | CBlock block; | ||||
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); | block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Show All 10 Lines | // test validity with P2SH. | ||||
invalid_under_p2sh_tx.vout.resize(1); | invalid_under_p2sh_tx.vout.resize(1); | ||||
invalid_under_p2sh_tx.vout[0].nValue = 11 * CENT; | invalid_under_p2sh_tx.vout[0].nValue = 11 * CENT; | ||||
invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; | invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; | ||||
std::vector<uint8_t> vchSig2(p2pk_scriptPubKey.begin(), | std::vector<uint8_t> vchSig2(p2pk_scriptPubKey.begin(), | ||||
p2pk_scriptPubKey.end()); | p2pk_scriptPubKey.end()); | ||||
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2; | invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2; | ||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), | ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), | ||||
SCRIPT_VERIFY_P2SH, 0, true); | SCRIPT_VERIFY_P2SH, 0, true, | ||||
ScriptExecutionMetrics(0)); | |||||
} | } | ||||
// Test CHECKLOCKTIMEVERIFY | // Test CHECKLOCKTIMEVERIFY | ||||
{ | { | ||||
CMutableTransaction invalid_with_cltv_tx; | CMutableTransaction invalid_with_cltv_tx; | ||||
invalid_with_cltv_tx.nVersion = 1; | invalid_with_cltv_tx.nVersion = 1; | ||||
invalid_with_cltv_tx.nLockTime = 100; | invalid_with_cltv_tx.nLockTime = 100; | ||||
invalid_with_cltv_tx.vin.resize(1); | invalid_with_cltv_tx.vin.resize(1); | ||||
invalid_with_cltv_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 1); | invalid_with_cltv_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 1); | ||||
invalid_with_cltv_tx.vin[0].nSequence = 0; | invalid_with_cltv_tx.vin[0].nSequence = 0; | ||||
invalid_with_cltv_tx.vout.resize(1); | invalid_with_cltv_tx.vout.resize(1); | ||||
invalid_with_cltv_tx.vout[0].nValue = 11 * CENT; | invalid_with_cltv_tx.vout[0].nValue = 11 * CENT; | ||||
invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; | invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; | ||||
// Sign | // Sign | ||||
std::vector<uint8_t> vchSig; | std::vector<uint8_t> vchSig; | ||||
uint256 hash = SignatureHash( | uint256 hash = SignatureHash( | ||||
spend_tx.vout[1].scriptPubKey, CTransaction(invalid_with_cltv_tx), | spend_tx.vout[1].scriptPubKey, CTransaction(invalid_with_cltv_tx), | ||||
0, SigHashType().withForkId(), spend_tx.vout[1].nValue); | 0, SigHashType().withForkId(), spend_tx.vout[1].nValue); | ||||
BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig)); | BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig)); | ||||
vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); | vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); | ||||
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; | invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; | ||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), | ValidateCheckInputsForAllFlags( | ||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | | CTransaction(invalid_with_cltv_tx), | ||||
SCRIPT_ENABLE_REPLAY_PROTECTION, | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | SCRIPT_ENABLE_REPLAY_PROTECTION, | ||||
SCRIPT_ENABLE_SIGHASH_FORKID, true); | SCRIPT_ENABLE_SIGHASH_FORKID, true, ScriptExecutionMetrics(1)); | ||||
// Make it valid, and check again | // Make it valid, and check again | ||||
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; | invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; | ||||
CValidationState state; | CValidationState state; | ||||
CTransaction transaction(invalid_with_cltv_tx); | CTransaction transaction(invalid_with_cltv_tx); | ||||
PrecomputedTransactionData txdata(transaction); | PrecomputedTransactionData txdata(transaction); | ||||
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | ScriptExecutionMetrics metricsRet; | ||||
STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | BOOST_CHECK( | ||||
txdata, nullptr)); | CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_REPORT_SIGCHECKS, | |||||
true, true, txdata, metricsRet, nullptr)); | |||||
BOOST_CHECK(metricsRet == ScriptExecutionMetrics(1)); | |||||
} | } | ||||
// TEST CHECKSEQUENCEVERIFY | // TEST CHECKSEQUENCEVERIFY | ||||
{ | { | ||||
CMutableTransaction invalid_with_csv_tx; | CMutableTransaction invalid_with_csv_tx; | ||||
invalid_with_csv_tx.nVersion = 2; | invalid_with_csv_tx.nVersion = 2; | ||||
invalid_with_csv_tx.vin.resize(1); | invalid_with_csv_tx.vin.resize(1); | ||||
invalid_with_csv_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 2); | invalid_with_csv_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 2); | ||||
invalid_with_csv_tx.vin[0].nSequence = 100; | invalid_with_csv_tx.vin[0].nSequence = 100; | ||||
invalid_with_csv_tx.vout.resize(1); | invalid_with_csv_tx.vout.resize(1); | ||||
invalid_with_csv_tx.vout[0].nValue = 11 * CENT; | invalid_with_csv_tx.vout[0].nValue = 11 * CENT; | ||||
invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; | invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; | ||||
// Sign | // Sign | ||||
std::vector<uint8_t> vchSig; | std::vector<uint8_t> vchSig; | ||||
uint256 hash = SignatureHash( | uint256 hash = SignatureHash( | ||||
spend_tx.vout[2].scriptPubKey, CTransaction(invalid_with_csv_tx), 0, | spend_tx.vout[2].scriptPubKey, CTransaction(invalid_with_csv_tx), 0, | ||||
SigHashType().withForkId(), spend_tx.vout[2].nValue); | SigHashType().withForkId(), spend_tx.vout[2].nValue); | ||||
BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig)); | BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig)); | ||||
vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); | vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); | ||||
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; | invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; | ||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), | ValidateCheckInputsForAllFlags( | ||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | | CTransaction(invalid_with_csv_tx), | ||||
SCRIPT_ENABLE_REPLAY_PROTECTION, | SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | SCRIPT_ENABLE_REPLAY_PROTECTION, | ||||
SCRIPT_ENABLE_SIGHASH_FORKID, true); | SCRIPT_ENABLE_SIGHASH_FORKID, true, ScriptExecutionMetrics(1)); | ||||
// Make it valid, and check again | // Make it valid, and check again | ||||
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; | invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; | ||||
CValidationState state; | CValidationState state; | ||||
CTransaction transaction(invalid_with_csv_tx); | CTransaction transaction(invalid_with_csv_tx); | ||||
PrecomputedTransactionData txdata(transaction); | PrecomputedTransactionData txdata(transaction); | ||||
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | ScriptExecutionMetrics metricsRet; | ||||
STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | BOOST_CHECK( | ||||
txdata, nullptr)); | CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_REPORT_SIGCHECKS, | |||||
true, true, txdata, metricsRet, nullptr)); | |||||
BOOST_CHECK(metricsRet == ScriptExecutionMetrics(1)); | |||||
} | } | ||||
// TODO: add tests for remaining script flags | // TODO: add tests for remaining script flags | ||||
{ | { | ||||
// Test a transaction with multiple inputs. | // Test a transaction with multiple inputs. | ||||
CMutableTransaction tx; | CMutableTransaction tx; | ||||
Show All 24 Lines | // TODO: add tests for remaining script flags | ||||
spend_tx.vout[3].scriptPubKey, sigdata)); | spend_tx.vout[3].scriptPubKey, sigdata)); | ||||
UpdateInput(tx.vin[1], sigdata); | UpdateInput(tx.vin[1], sigdata); | ||||
} | } | ||||
// This should be valid under all script flags that support our sighash | // This should be valid under all script flags that support our sighash | ||||
// convention. | // convention. | ||||
ValidateCheckInputsForAllFlags( | ValidateCheckInputsForAllFlags( | ||||
CTransaction(tx), SCRIPT_ENABLE_REPLAY_PROTECTION, | CTransaction(tx), SCRIPT_ENABLE_REPLAY_PROTECTION, | ||||
SCRIPT_ENABLE_SIGHASH_FORKID | SCRIPT_VERIFY_P2SH, true); | SCRIPT_ENABLE_SIGHASH_FORKID | SCRIPT_VERIFY_P2SH, true, | ||||
ScriptExecutionMetrics(2)); | |||||
// Check that if the second input is invalid, but the first input is | // Check that if the second input is invalid, but the first input is | ||||
// valid, the transaction is not cached. | // valid, the transaction is not cached. | ||||
// Invalidate vin[1] | // Invalidate vin[1] | ||||
tx.vin[1].scriptSig = CScript(); | tx.vin[1].scriptSig = CScript(); | ||||
CValidationState state; | CValidationState state; | ||||
CTransaction transaction(tx); | CTransaction transaction(tx); | ||||
PrecomputedTransactionData txdata(transaction); | PrecomputedTransactionData txdata(transaction); | ||||
// This transaction is now invalid because the second signature is | // This transaction is now invalid because the second signature is | ||||
// missing. | // missing. | ||||
ScriptExecutionMetrics metricsDummy; | |||||
BOOST_CHECK( | BOOST_CHECK( | ||||
!CheckInputs(transaction, state, pcoinsTip.get(), true, | !CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_REPORT_SIGCHECKS, | STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_REPORT_SIGCHECKS, | ||||
true, true, txdata, nullptr)); | true, true, txdata, metricsDummy, nullptr)); | ||||
// Make sure this transaction was not cached (ie becausethe first input | // Make sure this transaction was not cached (ie becausethe first input | ||||
// was valid) | // was valid) | ||||
std::vector<CScriptCheck> scriptchecks; | std::vector<CScriptCheck> scriptchecks; | ||||
BOOST_CHECK( | BOOST_CHECK( | ||||
CheckInputs(transaction, state, pcoinsTip.get(), true, | CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_REPORT_SIGCHECKS, | STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_REPORT_SIGCHECKS, | ||||
true, true, txdata, &scriptchecks)); | true, true, txdata, metricsDummy, &scriptchecks)); | ||||
// Should get 2 script checks back -- caching is on a whole-transaction | // Should get 2 script checks back -- caching is on a whole-transaction | ||||
// basis. | // basis. | ||||
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U); | BOOST_CHECK_EQUAL(scriptchecks.size(), 2U); | ||||
// Execute the first check, and check its result | // Execute the first check, and check its result | ||||
BOOST_CHECK(scriptchecks[0]()); | BOOST_CHECK(scriptchecks[0]()); | ||||
BOOST_CHECK_EQUAL(scriptchecks[0].GetScriptError(), ScriptError::OK); | BOOST_CHECK_EQUAL(scriptchecks[0].GetScriptError(), ScriptError::OK); | ||||
BOOST_CHECK_EQUAL( | BOOST_CHECK_EQUAL( | ||||
▲ Show 20 Lines • Show All 105 Lines • Show Last 20 Lines |