Changeset View
Changeset View
Standalone View
Standalone View
src/test/txvalidationcache_tests.cpp
Show First 20 Lines • Show All 183 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. | // Test that invalidity under a set of flags doesn't preclude validity under | ||||
// other (eg consensus) flags. | |||||
{ | |||||
// 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); | ||||
} | |||||
// And if we produce a block with this tx, it should be valid (LOW_S not | // Create a transaction we can spend | ||||
// enabled yet), even though there's no cache entry. | // 1. Sign | ||||
{ | |||||
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); | |||||
// 2. Spend into UTXOs we can respend later | |||||
{ | |||||
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 | ||||
// test validity with P2SH. | // test validity with P2SH. | ||||
{ | { | ||||
CMutableTransaction invalid_under_p2sh_tx; | CMutableTransaction invalid_under_p2sh_tx; | ||||
invalid_under_p2sh_tx.nVersion = 1; | invalid_under_p2sh_tx.nVersion = 1; | ||||
invalid_under_p2sh_tx.vin.resize(1); | invalid_under_p2sh_tx.vin.resize(1); | ||||
invalid_under_p2sh_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 0); | invalid_under_p2sh_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 0); | ||||
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; | ||||
LOCK(cs_main); | |||||
ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, | ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, | ||||
SCRIPT_VERIFY_P2SH, true, false); | SCRIPT_VERIFY_P2SH, true, false); | ||||
} | } | ||||
// 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; | ||||
Show All 9 Lines | // Test CHECKLOCKTIMEVERIFY | ||||
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.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)); | ||||
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; | invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; | ||||
LOCK(cs_main); | |||||
ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, | ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, | ||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, | ||||
true); | true); | ||||
// 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; | ||||
Show All 21 Lines | // TEST CHECKSEQUENCEVERIFY | ||||
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.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)); | ||||
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; | invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; | ||||
LOCK(cs_main); | |||||
ValidateCheckInputsForAllFlags( | ValidateCheckInputsForAllFlags( | ||||
invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true); | invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true); | ||||
// 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); | ||||
Show All 27 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 |