Changeset View
Changeset View
Standalone View
Standalone View
src/test/txvalidationcache_tests.cpp
Show All 26 Lines | BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) { | ||||
// into the memory pool does not allow double-spends in blocks to pass | // into the memory pool does not allow double-spends in blocks to pass | ||||
// validation when they should not. | // validation when they should not. | ||||
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) | CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) | ||||
<< OP_CHECKSIG; | << OP_CHECKSIG; | ||||
const auto ToMemPool = [this](const CMutableTransaction &tx) { | const auto ToMemPool = [this](const CMutableTransaction &tx) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CValidationState state; | TxValidationState state; | ||||
return AcceptToMemoryPool( | return AcceptToMemoryPool( | ||||
GetConfig(), *m_node.mempool, state, MakeTransactionRef(tx), | GetConfig(), *m_node.mempool, state, MakeTransactionRef(tx), | ||||
nullptr /* pfMissingInputs */, true /* bypass_limits */, | true /* bypass_limits */, Amount::zero() /* nAbsurdFee */); | ||||
Amount::zero() /* nAbsurdFee */); | |||||
}; | }; | ||||
// Create a double-spend of mature coinbase txn: | // Create a double-spend of mature coinbase txn: | ||||
std::vector<CMutableTransaction> spends; | std::vector<CMutableTransaction> spends; | ||||
spends.resize(2); | spends.resize(2); | ||||
for (int i = 0; i < 2; i++) { | for (int i = 0; i < 2; i++) { | ||||
spends[i].nVersion = 1; | spends[i].nVersion = 1; | ||||
spends[i].vin.resize(1); | spends[i].vin.resize(1); | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | block = CreateAndProcessBlock(oneSpend, scriptPubKey); | ||||
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash()); | BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash()); | ||||
} | } | ||||
// spends[1] should have been removed from the mempool when the block with | // spends[1] should have been removed from the mempool when the block with | ||||
// spends[0] is accepted: | // spends[0] is accepted: | ||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U); | BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U); | ||||
} | } | ||||
static inline bool | static inline bool | ||||
CheckInputs(const CTransaction &tx, CValidationState &state, | CheckInputs(const CTransaction &tx, TxValidationState &state, | ||||
const CCoinsViewCache &view, bool fScriptChecks, | const CCoinsViewCache &view, bool fScriptChecks, | ||||
const uint32_t flags, bool sigCacheStore, bool scriptCacheStore, | const uint32_t flags, bool sigCacheStore, bool scriptCacheStore, | ||||
const PrecomputedTransactionData &txdata, int &nSigChecksOut, | const PrecomputedTransactionData &txdata, int &nSigChecksOut, | ||||
std::vector<CScriptCheck> *pvChecks, | std::vector<CScriptCheck> *pvChecks, | ||||
CheckInputsLimiter *pBlockLimitSigChecks = nullptr) | CheckInputsLimiter *pBlockLimitSigChecks = nullptr) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
// nSigChecksTxLimiter need to outlive this function call, because test | // nSigChecksTxLimiter need to outlive this function call, because test | ||||
// cases are using pvChecks, so the verification is done asynchronously. | // cases are using pvChecks, so the verification is done asynchronously. | ||||
Show All 20 Lines | ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, | ||||
uint32_t required_flags, bool add_to_cache, | uint32_t required_flags, bool add_to_cache, | ||||
int expected_sigchecks) | int expected_sigchecks) | ||||
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; | TxValidationState 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; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) { | ||||
// Test that invalidity under a set of flags doesn't preclude validity under | // Test that invalidity under a set of flags doesn't preclude validity under | ||||
// other (eg consensus) flags. | // other (eg consensus) flags. | ||||
// 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; | TxValidationState state; | ||||
PrecomputedTransactionData ptd_spend_tx(tx); | PrecomputedTransactionData ptd_spend_tx(tx); | ||||
int nSigChecksDummy; | int nSigChecksDummy; | ||||
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, nSigChecksDummy, nullptr)); | ptd_spend_tx, nSigChecksDummy, nullptr)); | ||||
// If we call again asking for scriptchecks (as happens in | // If we call again asking for scriptchecks (as happens in | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | // Test CHECKLOCKTIMEVERIFY | ||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), | ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), | ||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | | ||||
SCRIPT_ENABLE_REPLAY_PROTECTION, | SCRIPT_ENABLE_REPLAY_PROTECTION, | ||||
SCRIPT_ENABLE_SIGHASH_FORKID, true, 1); | SCRIPT_ENABLE_SIGHASH_FORKID, true, 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; | TxValidationState state; | ||||
CTransaction transaction(invalid_with_cltv_tx); | CTransaction transaction(invalid_with_cltv_tx); | ||||
PrecomputedTransactionData txdata(transaction); | PrecomputedTransactionData txdata(transaction); | ||||
int nSigChecksRet; | int nSigChecksRet; | ||||
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | ||||
txdata, nSigChecksRet, nullptr)); | txdata, nSigChecksRet, nullptr)); | ||||
Show All 22 Lines | // TEST CHECKSEQUENCEVERIFY | ||||
ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), | ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), | ||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | | SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | | ||||
SCRIPT_ENABLE_REPLAY_PROTECTION, | SCRIPT_ENABLE_REPLAY_PROTECTION, | ||||
SCRIPT_ENABLE_SIGHASH_FORKID, true, 1); | SCRIPT_ENABLE_SIGHASH_FORKID, true, 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; | TxValidationState state; | ||||
CTransaction transaction(invalid_with_csv_tx); | CTransaction transaction(invalid_with_csv_tx); | ||||
PrecomputedTransactionData txdata(transaction); | PrecomputedTransactionData txdata(transaction); | ||||
int nSigChecksRet; | int nSigChecksRet; | ||||
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | ||||
txdata, nSigChecksRet, nullptr)); | txdata, nSigChecksRet, nullptr)); | ||||
Show All 39 Lines | // TODO: add tests for remaining script flags | ||||
ValidateCheckInputsForAllFlags( | ValidateCheckInputsForAllFlags( | ||||
CTransaction(tx), SCRIPT_ENABLE_REPLAY_PROTECTION, | CTransaction(tx), SCRIPT_ENABLE_REPLAY_PROTECTION, | ||||
SCRIPT_ENABLE_SIGHASH_FORKID | SCRIPT_VERIFY_P2SH, true, 2); | SCRIPT_ENABLE_SIGHASH_FORKID | SCRIPT_VERIFY_P2SH, true, 2); | ||||
{ | { | ||||
// Try checking this valid transaction with sigchecks limiter | // Try checking this valid transaction with sigchecks limiter | ||||
// supplied. Each input consumes 1 sigcheck. | // supplied. Each input consumes 1 sigcheck. | ||||
CValidationState state; | TxValidationState state; | ||||
CTransaction transaction(tx); | CTransaction transaction(tx); | ||||
PrecomputedTransactionData txdata(transaction); | PrecomputedTransactionData txdata(transaction); | ||||
const uint32_t flags = | const uint32_t flags = | ||||
STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENFORCE_SIGCHECKS; | STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENFORCE_SIGCHECKS; | ||||
int nSigChecksDummy; | int nSigChecksDummy; | ||||
/** | /** | ||||
* Parallel validation initially works (no cached value), but | * Parallel validation initially works (no cached value), but | ||||
Show All 10 Lines | // TODO: add tests for remaining script flags | ||||
// the second check (the first input) fails due to the limiter. | // the second check (the first input) fails due to the limiter. | ||||
BOOST_CHECK(!scriptchecks1[0]()); | BOOST_CHECK(!scriptchecks1[0]()); | ||||
BOOST_CHECK_EQUAL(scriptchecks1[0].GetScriptError(), | BOOST_CHECK_EQUAL(scriptchecks1[0].GetScriptError(), | ||||
ScriptError::SIGCHECKS_LIMIT_EXCEEDED); | ScriptError::SIGCHECKS_LIMIT_EXCEEDED); | ||||
BOOST_CHECK(!sigchecklimiter1.check()); | BOOST_CHECK(!sigchecklimiter1.check()); | ||||
// Serial validation fails with the limiter. | // Serial validation fails with the limiter. | ||||
CheckInputsLimiter sigchecklimiter2(1); | CheckInputsLimiter sigchecklimiter2(1); | ||||
CValidationState state2; | TxValidationState state2; | ||||
BOOST_CHECK(!CheckInputs(transaction, state2, pcoinsTip.get(), true, | BOOST_CHECK(!CheckInputs(transaction, state2, pcoinsTip.get(), true, | ||||
flags, true, true, txdata, nSigChecksDummy, | flags, true, true, txdata, nSigChecksDummy, | ||||
nullptr, &sigchecklimiter2)); | nullptr, &sigchecklimiter2)); | ||||
BOOST_CHECK(!sigchecklimiter2.check()); | BOOST_CHECK(!sigchecklimiter2.check()); | ||||
BOOST_CHECK_EQUAL(state2.GetRejectReason(), | BOOST_CHECK_EQUAL(state2.GetRejectReason(), | ||||
"non-mandatory-script-verify-flag (Validation " | "non-mandatory-script-verify-flag (Validation " | ||||
"resources exceeded (SigChecks))"); | "resources exceeded (SigChecks))"); | ||||
Show All 25 Lines | // TODO: add tests for remaining script flags | ||||
BOOST_CHECK(scriptchecks5.empty()); | BOOST_CHECK(scriptchecks5.empty()); | ||||
BOOST_CHECK(sigchecklimiter5.check()); | BOOST_CHECK(sigchecklimiter5.check()); | ||||
/** | /** | ||||
* Going back to the lower limit, we now fail immediately due to the | * Going back to the lower limit, we now fail immediately due to the | ||||
* caching. | * caching. | ||||
*/ | */ | ||||
CheckInputsLimiter sigchecklimiter6(1); | CheckInputsLimiter sigchecklimiter6(1); | ||||
CValidationState state6; | TxValidationState state6; | ||||
BOOST_CHECK(!CheckInputs(transaction, state6, pcoinsTip.get(), true, | BOOST_CHECK(!CheckInputs(transaction, state6, pcoinsTip.get(), true, | ||||
flags, true, true, txdata, nSigChecksDummy, | flags, true, true, txdata, nSigChecksDummy, | ||||
nullptr, &sigchecklimiter6)); | nullptr, &sigchecklimiter6)); | ||||
BOOST_CHECK_EQUAL(state6.GetRejectReason(), "too-many-sigchecks"); | BOOST_CHECK_EQUAL(state6.GetRejectReason(), "too-many-sigchecks"); | ||||
BOOST_CHECK_EQUAL(state6.GetReason(), | BOOST_CHECK_EQUAL(state6.GetResult(), | ||||
ValidationInvalidReason::CONSENSUS); | TxValidationResult::TX_CONSENSUS); | ||||
BOOST_CHECK(!sigchecklimiter6.check()); | BOOST_CHECK(!sigchecklimiter6.check()); | ||||
// even in parallel validation, immediate fail from the cache. | // even in parallel validation, immediate fail from the cache. | ||||
std::vector<CScriptCheck> scriptchecks7; | std::vector<CScriptCheck> scriptchecks7; | ||||
CheckInputsLimiter sigchecklimiter7(1); | CheckInputsLimiter sigchecklimiter7(1); | ||||
CValidationState state7; | TxValidationState state7; | ||||
BOOST_CHECK(!CheckInputs(transaction, state7, pcoinsTip.get(), true, | BOOST_CHECK(!CheckInputs(transaction, state7, pcoinsTip.get(), true, | ||||
flags, true, true, txdata, nSigChecksDummy, | flags, true, true, txdata, nSigChecksDummy, | ||||
&scriptchecks7, &sigchecklimiter7)); | &scriptchecks7, &sigchecklimiter7)); | ||||
BOOST_CHECK_EQUAL(state7.GetRejectReason(), "too-many-sigchecks"); | BOOST_CHECK_EQUAL(state7.GetRejectReason(), "too-many-sigchecks"); | ||||
BOOST_CHECK_EQUAL(state6.GetReason(), | BOOST_CHECK_EQUAL(state6.GetResult(), | ||||
ValidationInvalidReason::CONSENSUS); | TxValidationResult::TX_CONSENSUS); | ||||
BOOST_CHECK(!sigchecklimiter7.check()); | BOOST_CHECK(!sigchecklimiter7.check()); | ||||
BOOST_CHECK(scriptchecks7.empty()); | BOOST_CHECK(scriptchecks7.empty()); | ||||
} | } | ||||
// 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; | TxValidationState 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. | ||||
int nSigChecksDummy; | int nSigChecksDummy; | ||||
BOOST_CHECK(!CheckInputs(transaction, state, pcoinsTip.get(), true, | BOOST_CHECK(!CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | STANDARD_SCRIPT_VERIFY_FLAGS, true, true, | ||||
▲ Show 20 Lines • Show All 109 Lines • Show Last 20 Lines |