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. | |||||
deadalnix: This still doesn't test what it used to. This unit test is not there to check the CLEANSTACK… | |||||
{ | |||||
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 |
This still doesn't test what it used to. This unit test is not there to check the CLEANSTACK behavior, but the cache behavior. We used to check that an invalid tx with a set of flag will not invalidate block acceptance given it is valid with consensus enforced flags.