Changeset View
Changeset View
Standalone View
Standalone View
src/test/txvalidationcache_tests.cpp
Show First 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | void ValidateCheckInputsForAllFlags(const CMutableTransaction &mutableTx, | ||||
PrecomputedTransactionData txdata(tx); | PrecomputedTransactionData txdata(tx); | ||||
// If we add many more flags, this loop can get too expensive, but we can | // If we add many more flags, this loop can get too expensive, but we can | ||||
// rewrite in the future to randomly pick a set of flags to evaluate. | // rewrite in the future to randomly pick a set of flags to evaluate. | ||||
for (uint32_t test_flags = 0; test_flags < (1U << 17); test_flags += 1) { | for (uint32_t test_flags = 0; test_flags < (1U << 17); test_flags += 1) { | ||||
CValidationState state; | CValidationState state; | ||||
// Make sure the mandatory flags are enabled. | // Make sure the mandatory flags are enabled. | ||||
test_flags |= MANDATORY_SCRIPT_VERIFY_FLAGS; | test_flags |= MANDATORY_SCRIPT_VERIFY_FLAGS; | ||||
LOCK(cs_main); | |||||
bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, | bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, | ||||
true, add_to_cache, txdata, nullptr); | true, add_to_cache, txdata, 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); | ||||
if (expected_return_value && upgraded_nop) { | if (expected_return_value && upgraded_nop) { | ||||
// If the script flag being tested corresponds to an upgraded NOP, | // If the script flag being tested corresponds to an upgraded NOP, | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | mutableSpend_tx.vout[1].scriptPubKey = | ||||
<< ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; | << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; | ||||
mutableSpend_tx.vout[2].nValue = 11 * CENT; | mutableSpend_tx.vout[2].nValue = 11 * CENT; | ||||
mutableSpend_tx.vout[2].scriptPubKey = | mutableSpend_tx.vout[2].scriptPubKey = | ||||
CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP | CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP | ||||
<< ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; | << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; | ||||
mutableSpend_tx.vout[3].nValue = 11 * CENT; | mutableSpend_tx.vout[3].nValue = 11 * CENT; | ||||
mutableSpend_tx.vout[3].scriptPubKey = p2sh_scriptPubKey; | mutableSpend_tx.vout[3].scriptPubKey = p2sh_scriptPubKey; | ||||
// Sign, and push an extra element on the stack. | // This block tests that invalidity under a set of flags doesn't preclude | ||||
// validity under other another set of flags. | |||||
// | |||||
// This test specifically tests CLEANSTACK being enforced or not; | |||||
// and that invalid checks are not cached so as to not cause a consensus | |||||
// failure | |||||
// when receiving a block with a non-standard transaction. | |||||
{ | |||||
// Create a transaction which will fail cleanstack | |||||
{ | { | ||||
std::vector<uint8_t> vchSig; | std::vector<uint8_t> vchSig; | ||||
uint256 hash = SignatureHash( | uint256 hash = SignatureHash( | ||||
p2pk_scriptPubKey, CTransaction(mutableSpend_tx), 0, | p2pk_scriptPubKey, CTransaction(mutableSpend_tx), 0, | ||||
SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue); | SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue); | ||||
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); | BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); | ||||
vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); | vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); | ||||
mutableSpend_tx.vin[0].scriptSig << OP_TRUE << vchSig; | mutableSpend_tx.vin[0].scriptSig = CScript() << OP_1 << vchSig; | ||||
} | } | ||||
const CTransaction dirtystack_tx(mutableSpend_tx); | |||||
const CTransaction spend_tx(mutableSpend_tx); | CValidationState state; | ||||
PrecomputedTransactionData ptd_spend_tx(dirtystack_tx); | |||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// Test that invalidity under a set of flags doesn't preclude validity under | // Invalid under cleanstack | ||||
// other (eg consensus) flags. | BOOST_CHECK(!CheckInputs(dirtystack_tx, state, pcoinsTip.get(), true, | ||||
// spend_tx is invalid according to DERSIG | |||||
{ | |||||
CValidationState state; | |||||
PrecomputedTransactionData ptd_spend_tx(spend_tx); | |||||
BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, | |||||
MANDATORY_SCRIPT_VERIFY_FLAGS | | MANDATORY_SCRIPT_VERIFY_FLAGS | | ||||
SCRIPT_VERIFY_CLEANSTACK, | SCRIPT_VERIFY_CLEANSTACK, | ||||
true, true, ptd_spend_tx, nullptr)); | true, true, ptd_spend_tx, nullptr)); | ||||
// Valid without cleanstack, but don't cache for the next assertion | |||||
BOOST_CHECK(CheckInputs(dirtystack_tx, state, pcoinsTip.get(), false, | |||||
MANDATORY_SCRIPT_VERIFY_FLAGS, true, true, | |||||
ptd_spend_tx, 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(spend_tx, state, pcoinsTip.get(), true, | BOOST_CHECK(CheckInputs(dirtystack_tx, state, pcoinsTip.get(), true, | ||||
MANDATORY_SCRIPT_VERIFY_FLAGS | | MANDATORY_SCRIPT_VERIFY_FLAGS | | ||||
SCRIPT_VERIFY_CLEANSTACK, | SCRIPT_VERIFY_CLEANSTACK, | ||||
true, true, ptd_spend_tx, &scriptchecks)); | true, true, ptd_spend_tx, &scriptchecks)); | ||||
BOOST_CHECK_EQUAL(scriptchecks.size(), 1); | BOOST_CHECK_EQUAL(scriptchecks.size(), 1); | ||||
// Test that CheckInputs returns true iff cleanstack-enforcing flags are | // Test that CheckInputs returns false if cleanstack is enforced for | ||||
// not present. Don't add these checks to the cache, so that we can test | // all combinations of flags | ||||
// later that block validation works fine in the absence of cached | ValidateCheckInputsForAllFlags(dirtystack_tx, SCRIPT_VERIFY_CLEANSTACK, | ||||
// successes. | |||||
ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_CLEANSTACK, | |||||
false, false); | false, false); | ||||
} | |||||
// The following tests will try to spend from a pk2h transaction. | |||||
// Therefor we need to first create one first. | |||||
// Create a spendable transaction. It is the same transaction as above, | |||||
// except it passes CLEANSTACK; which is now enforced at the consensus | |||||
// level. | |||||
// NOTE: This test previously used block acceptance to test CLEANSTACK being | |||||
// ON/OFF. | |||||
{ | |||||
std::vector<uint8_t> vchSig; | |||||
uint256 hash = SignatureHash( | |||||
p2pk_scriptPubKey, CTransaction(mutableSpend_tx), 0, | |||||
SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue); | |||||
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); | |||||
vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); | |||||
mutableSpend_tx.vin[0].scriptSig = CScript() << vchSig; | |||||
} | |||||
const CTransaction spend_tx(mutableSpend_tx); | |||||
// And if we produce a block with this tx, it should be valid (LOW_S not | // Spend the transaction so we have a UTXO we can respend later | ||||
// enabled yet), even though there's no cache entry. | { | ||||
CValidationState state; | |||||
PrecomputedTransactionData ptd_spend_tx(spend_tx); | |||||
LOCK(cs_main); | |||||
// Valid under cleanstack, but don't cache or the next step | |||||
// check won't be able to be tested. | |||||
BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, | |||||
MANDATORY_SCRIPT_VERIFY_FLAGS | | |||||
SCRIPT_VERIFY_CLEANSTACK, | |||||
true, false, ptd_spend_tx, nullptr)); | |||||
// If we call again asking for scriptchecks (as happens in | |||||
// ConnectBlock), we should add a script check object for this. | |||||
std::vector<CScriptCheck> scriptchecks; | |||||
BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, | |||||
MANDATORY_SCRIPT_VERIFY_FLAGS | | |||||
SCRIPT_VERIFY_CLEANSTACK, | |||||
true, true, ptd_spend_tx, &scriptchecks)); | |||||
BOOST_CHECK_EQUAL(scriptchecks.size(), 1); | |||||
// And if we produce a block with this tx, it should be valid. | |||||
CBlock block; | CBlock block; | ||||
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); | block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); | ||||
BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); | BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); | ||||
BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); | BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); | ||||
} | } | ||||
// Test P2SH: construct a transaction that is valid without P2SH, and then | // Test P2SH: construct a transaction that is valid without P2SH, and then | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | // Test CHECKLOCKTIMEVERIFY | ||||
// 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); | ||||
LOCK(cs_main); | |||||
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
MANDATORY_SCRIPT_VERIFY_FLAGS | | MANDATORY_SCRIPT_VERIFY_FLAGS | | ||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, | ||||
true, true, txdata, nullptr)); | true, true, txdata, nullptr)); | ||||
} | } | ||||
// TEST CHECKSEQUENCEVERIFY | // TEST CHECKSEQUENCEVERIFY | ||||
{ | { | ||||
Show All 20 Lines | // TEST CHECKSEQUENCEVERIFY | ||||
// 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); | ||||
LOCK(cs_main); | |||||
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, | ||||
MANDATORY_SCRIPT_VERIFY_FLAGS | | MANDATORY_SCRIPT_VERIFY_FLAGS | | ||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, | SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, | ||||
true, true, txdata, nullptr)); | true, true, txdata, nullptr)); | ||||
} | } | ||||
// TODO: add tests for remaining script flags | // TODO: add tests for remaining script flags | ||||
Show All 17 Lines | // TODO: add tests for remaining script flags | ||||
spend_tx.vout[0].scriptPubKey, sigdata); | spend_tx.vout[0].scriptPubKey, sigdata); | ||||
UpdateTransaction(tx, 0, sigdata); | UpdateTransaction(tx, 0, sigdata); | ||||
ProduceSignature( | ProduceSignature( | ||||
MutableTransactionSignatureCreator(&keystore, &tx, 1, 11 * CENT, | MutableTransactionSignatureCreator(&keystore, &tx, 1, 11 * CENT, | ||||
SigHashType().withForkId()), | SigHashType().withForkId()), | ||||
spend_tx.vout[3].scriptPubKey, sigdata); | spend_tx.vout[3].scriptPubKey, sigdata); | ||||
UpdateTransaction(tx, 1, sigdata); | UpdateTransaction(tx, 1, sigdata); | ||||
LOCK(cs_main); | |||||
// This should be valid under all script flags | // This should be valid under all script flags | ||||
ValidateCheckInputsForAllFlags(tx, 0, true, false); | ValidateCheckInputsForAllFlags(tx, 0, true, false); | ||||
// 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(); | ||||
Show All 23 Lines |