diff --git a/src/test/finalization_tests.cpp b/src/test/finalization_tests.cpp index 7ca824eca..b24d341ae 100644 --- a/src/test/finalization_tests.cpp +++ b/src/test/finalization_tests.cpp @@ -1,88 +1,93 @@ // Copyright (c) 2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "chainparams.h" #include "config.h" #include "test/test_bitcoin.h" #include "validation.h" #include BOOST_FIXTURE_TEST_SUITE(finalization_tests, TestChain100Setup) BOOST_AUTO_TEST_CASE(finalizationDelay) { CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; CBlock block; - LOCK(cs_main); - - // We should have no finalized block because the 100 blocks generated by the - // test setup are too close to "now"; - BOOST_CHECK_MESSAGE(GetFinalizedBlock() == nullptr, - "No block finalized (tip at height " - << chainActive.Tip()->nHeight << ")"); + { + LOCK(cs_main); + // We should have no finalized block because the 100 blocks generated by + // the test setup are too close to "now"; + BOOST_CHECK_MESSAGE(GetFinalizedBlock() == nullptr, + "No block finalized (tip at height " + << chainActive.Tip()->nHeight << ")"); + } // Create maxreorgdepth blocks. Auto-finalization will not occur because // the delay is not expired int64_t mockedTime = GetTime(); for (uint32_t i = 0; i < DEFAULT_MAX_REORG_DEPTH; i++) { block = CreateAndProcessBlock({}, p2pk_scriptPubKey); + LOCK(cs_main); // These blocks are too recent. BOOST_CHECK_MESSAGE(GetFinalizedBlock() == nullptr, "No block finalized (tip at height " << chainActive.Tip()->nHeight << ")"); } // Make the finalization time to expire mockedTime += DEFAULT_MIN_FINALIZATION_DELAY + 1; SetMockTime(mockedTime); // Next maxreorgdepth blocks should cause auto-finalization CBlockIndex *blockToFinalize = chainActive.Tip()->GetAncestor( chainActive.Tip()->nHeight - DEFAULT_MAX_REORG_DEPTH); for (uint32_t i = 0; i < DEFAULT_MAX_REORG_DEPTH; i++) { blockToFinalize = chainActive.Next(blockToFinalize); block = CreateAndProcessBlock({}, p2pk_scriptPubKey); + LOCK(cs_main); BOOST_CHECK_MESSAGE(GetFinalizedBlock() == blockToFinalize, "Block finalized at height " << blockToFinalize->nHeight << " (tip at height " << chainActive.Tip()->nHeight << ")"); } // Next blocks won't cause auto-finalization because the delay is not // expired for (uint32_t i = 0; i < DEFAULT_MAX_REORG_DEPTH; i++) { block = CreateAndProcessBlock({}, p2pk_scriptPubKey); + LOCK(cs_main); // These blocks are finalized. BOOST_CHECK_MESSAGE(GetFinalizedBlock() == blockToFinalize, "Finalized block remains unchanged at height " << blockToFinalize->nHeight << " (tip at height " << chainActive.Tip()->nHeight << ")"); } // Make the finalization time to expire mockedTime += DEFAULT_MIN_FINALIZATION_DELAY + 1; SetMockTime(mockedTime); blockToFinalize = chainActive.Tip()->GetAncestor( chainActive.Tip()->nHeight - DEFAULT_MAX_REORG_DEPTH); // Create some more blocks. // Finalization should start moving again. for (uint32_t i = 0; i < DEFAULT_MAX_REORG_DEPTH; i++) { blockToFinalize = chainActive.Next(blockToFinalize); block = CreateAndProcessBlock({}, p2pk_scriptPubKey); + LOCK(cs_main); BOOST_CHECK_MESSAGE(GetFinalizedBlock() == blockToFinalize, "Block finalized at height " << blockToFinalize->nHeight << " (tip at height " << chainActive.Tip()->nHeight << ")"); } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index fe4912603..73fb16ea3 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -1,438 +1,437 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "config.h" #include "consensus/validation.h" #include "key.h" #include "keystore.h" #include "miner.h" #include "pubkey.h" #include "random.h" #include "script/scriptcache.h" #include "script/sighashtype.h" #include "script/sign.h" #include "script/standard.h" #include "test/sigutil.h" #include "test/test_bitcoin.h" #include "txmempool.h" #include "utiltime.h" #include "validation.h" #include BOOST_AUTO_TEST_SUITE(txvalidationcache_tests) static bool ToMemPool(const CMutableTransaction &tx) { LOCK(cs_main); CValidationState state; return AcceptToMemoryPool(GetConfig(), g_mempool, state, MakeTransactionRef(tx), false, nullptr, true, Amount::zero()); } BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) { // Make sure skipping validation of transctions that were validated going // into the memory pool does not allow double-spends in blocks to pass // validation when they should not. CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; // Create a double-spend of mature coinbase txn: std::vector spends; spends.resize(2); for (int i = 0; i < 2; i++) { spends[i].nVersion = 1; spends[i].vin.resize(1); spends[i].vin[0].prevout = COutPoint(coinbaseTxns[0].GetId(), 0); spends[i].vout.resize(1); spends[i].vout[0].nValue = 11 * CENT; spends[i].vout[0].scriptPubKey = scriptPubKey; // Sign: std::vector vchSig; uint256 hash = SignatureHash(scriptPubKey, CTransaction(spends[i]), 0, SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue); BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig)); vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); spends[i].vin[0].scriptSig << vchSig; } CBlock block; // Test 1: block with both of those transactions should be rejected. block = CreateAndProcessBlock(spends, scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash()); // Test 2: ... and should be rejected if spend1 is in the memory pool BOOST_CHECK(ToMemPool(spends[0])); block = CreateAndProcessBlock(spends, scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash()); g_mempool.clear(); // Test 3: ... and should be rejected if spend2 is in the memory pool BOOST_CHECK(ToMemPool(spends[1])); block = CreateAndProcessBlock(spends, scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash()); g_mempool.clear(); // Final sanity test: first spend in mempool, second in block, that's OK: std::vector oneSpend; oneSpend.push_back(spends[0]); BOOST_CHECK(ToMemPool(spends[1])); block = CreateAndProcessBlock(oneSpend, scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); // spends[1] should have been removed from the mempool when the block with // spends[0] is accepted: BOOST_CHECK_EQUAL(g_mempool.size(), 0); } // Run CheckInputs (using pcoinsTip) on the given transaction, for all script // flags. Test that CheckInputs passes for all flags that don't overlap with the // failing_flags argument, but otherwise fails. // CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may // get reassigned) have an interaction with DISCOURAGE_UPGRADABLE_NOPS: if the // script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain // CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain // OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution // should fail. // Capture this interaction with the upgraded_nop argument: set it when // evaluating any script flag that is implemented as an upgraded NOP code. void ValidateCheckInputsForAllFlags(const CMutableTransaction &mutableTx, uint32_t failing_flags, bool add_to_cache, bool upgraded_nop) { const CTransaction tx(mutableTx); PrecomputedTransactionData txdata(tx); // 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. for (uint32_t test_flags = 0; test_flags < (1U << 17); test_flags += 1) { CValidationState state; // Make sure the mandatory flags are enabled. test_flags |= MANDATORY_SCRIPT_VERIFY_FLAGS; bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, nullptr); // CheckInputs should succeed iff test_flags doesn't intersect with // failing_flags bool expected_return_value = !(test_flags & failing_flags); if (expected_return_value && upgraded_nop) { // If the script flag being tested corresponds to an upgraded NOP, // then script execution should fail if DISCOURAGE_UPGRADABLE_NOPS // is set. expected_return_value = !(test_flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS); } BOOST_CHECK_EQUAL(ret, expected_return_value); // Test the caching if (ret && add_to_cache) { // Check that we get a cache hit if the tx was valid std::vector scriptchecks; BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK(scriptchecks.empty()); } else { // Check that we get script executions to check, if the transaction // was invalid, or we didn't add to cache. std::vector scriptchecks; BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); } } } BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) { // Test that passing CheckInputs with one set of script flags doesn't imply // that we would pass again with a different set of flags. { LOCK(cs_main); InitScriptExecutionCache(); } CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey)); CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); CBasicKeyStore keystore; keystore.AddKey(coinbaseKey); keystore.AddCScript(p2pk_scriptPubKey); CMutableTransaction mutableFunding_tx; // Needed when spending the output of this transaction CScript nulldummyPubKeyScript; // Create a funding transaction that can fail NULLDUMMY checks. This is for // testing consensus vs non-standard rules in `checkinputs_test`. { mutableFunding_tx.nVersion = 1; mutableFunding_tx.vin.resize(1); mutableFunding_tx.vin[0].prevout = COutPoint(coinbaseTxns[0].GetId(), 0); mutableFunding_tx.vout.resize(1); mutableFunding_tx.vout[0].nValue = 50 * COIN; CKey dummyKey; dummyKey.MakeNewKey(true); nulldummyPubKeyScript << OP_1 << ToByteVector(coinbaseKey.GetPubKey()) << ToByteVector(dummyKey.GetPubKey()) << OP_2 << OP_CHECKMULTISIG; mutableFunding_tx.vout[0].scriptPubKey = nulldummyPubKeyScript; std::vector nullDummyVchSig; uint256 nulldummySigHash = SignatureHash( p2pk_scriptPubKey, CTransaction(mutableFunding_tx), 0, SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue); BOOST_CHECK(coinbaseKey.SignECDSA(nulldummySigHash, nullDummyVchSig)); nullDummyVchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); mutableFunding_tx.vin[0].scriptSig << nullDummyVchSig; } const CTransaction funding_tx = CTransaction(mutableFunding_tx); // Spend the funding transaction by mining it into a block { - LOCK(cs_main); CBlock block = CreateAndProcessBlock({funding_tx}, p2pk_scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); } // flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, // SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed // pubkey thing // Create 2 outputs that match the three scripts above, spending the first // coinbase tx. CMutableTransaction mutableSpend_tx; mutableSpend_tx.nVersion = 1; mutableSpend_tx.vin.resize(1); mutableSpend_tx.vin[0].prevout = COutPoint(funding_tx.GetId(), 0); mutableSpend_tx.vout.resize(4); mutableSpend_tx.vout[0].nValue = 11 * CENT; mutableSpend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey; mutableSpend_tx.vout[1].nValue = 11 * CENT; mutableSpend_tx.vout[1].scriptPubKey = CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; mutableSpend_tx.vout[2].nValue = 11 * CENT; mutableSpend_tx.vout[2].scriptPubKey = CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; mutableSpend_tx.vout[3].nValue = 11 * CENT; mutableSpend_tx.vout[3].scriptPubKey = p2sh_scriptPubKey; // Sign the main transaction that we spend from. { std::vector vchSig; uint256 hash = SignatureHash( nulldummyPubKeyScript, CTransaction(mutableSpend_tx), 0, SigHashType().withForkId(), funding_tx.vout[0].nValue); coinbaseKey.SignECDSA(hash, vchSig); vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); // The last item on the stack will be dropped by CHECKMULTISIG This is // to check nulldummy enforcement. It is OP_1 instead of OP_0. mutableSpend_tx.vin[0].scriptSig << OP_1 << vchSig; } const CTransaction spend_tx(mutableSpend_tx); // Test that invalidity under a set of flags doesn't preclude validity under // other (eg consensus) flags. // spend_tx is invalid according to NULLDUMMY { LOCK(cs_main); CValidationState state; PrecomputedTransactionData ptd_spend_tx(spend_tx); BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_NULLDUMMY, true, true, ptd_spend_tx, nullptr)); // If we call again asking for scriptchecks (as happens in // ConnectBlock), we should add a script check object for this -- we're // not caching invalidity (if that changes, delete this test case). std::vector scriptchecks; BOOST_CHECK( CheckInputs(spend_tx, state, pcoinsTip.get(), true, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_NULLDUMMY, true, true, ptd_spend_tx, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), 1); // 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 // later that block validation works fine in the absence of cached // successes. ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_NULLDUMMY, false, false); } // And if we produce a block with this tx, it should be valid (LOW_S not // enabled yet), even though there's no cache entry. CBlock block; block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); LOCK(cs_main); // Test P2SH: construct a transaction that is valid without P2SH, and then // test validity with P2SH. { CMutableTransaction invalid_under_p2sh_tx; invalid_under_p2sh_tx.nVersion = 1; invalid_under_p2sh_tx.vin.resize(1); invalid_under_p2sh_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 0); invalid_under_p2sh_tx.vout.resize(1); invalid_under_p2sh_tx.vout[0].nValue = 11 * CENT; invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; std::vector vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end()); invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2; ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, SCRIPT_VERIFY_P2SH, true, false); } // Test CHECKLOCKTIMEVERIFY { CMutableTransaction invalid_with_cltv_tx; invalid_with_cltv_tx.nVersion = 1; invalid_with_cltv_tx.nLockTime = 100; 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].nSequence = 0; invalid_with_cltv_tx.vout.resize(1); invalid_with_cltv_tx.vout[0].nValue = 11 * CENT; invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; // Sign std::vector vchSig; uint256 hash = SignatureHash( spend_tx.vout[1].scriptPubKey, CTransaction(invalid_with_cltv_tx), 0, SigHashType().withForkId(), spend_tx.vout[1].nValue); BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig)); vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true); // Make it valid, and check again invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; CTransaction transaction(invalid_with_cltv_tx); PrecomputedTransactionData txdata(transaction); BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); } // TEST CHECKSEQUENCEVERIFY { CMutableTransaction invalid_with_csv_tx; invalid_with_csv_tx.nVersion = 2; 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].nSequence = 100; invalid_with_csv_tx.vout.resize(1); invalid_with_csv_tx.vout[0].nValue = 11 * CENT; invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; // Sign std::vector vchSig; uint256 hash = SignatureHash( spend_tx.vout[2].scriptPubKey, CTransaction(invalid_with_csv_tx), 0, SigHashType().withForkId(), spend_tx.vout[2].nValue); BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig)); vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; ValidateCheckInputsForAllFlags( invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true); // Make it valid, and check again invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; CTransaction transaction(invalid_with_csv_tx); PrecomputedTransactionData txdata(transaction); BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); } // TODO: add tests for remaining script flags { // Test a transaction with multiple inputs. CMutableTransaction tx; tx.nVersion = 1; tx.vin.resize(2); tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 0); tx.vin[1].prevout = COutPoint(spend_tx.GetId(), 3); tx.vout.resize(1); tx.vout[0].nValue = 22 * CENT; tx.vout[0].scriptPubKey = p2pk_scriptPubKey; // Sign SignatureData sigdata; ProduceSignature( MutableTransactionSignatureCreator(&keystore, &tx, 0, 11 * CENT, SigHashType().withForkId()), spend_tx.vout[0].scriptPubKey, sigdata); UpdateTransaction(tx, 0, sigdata); ProduceSignature( MutableTransactionSignatureCreator(&keystore, &tx, 1, 11 * CENT, SigHashType().withForkId()), spend_tx.vout[3].scriptPubKey, sigdata); UpdateTransaction(tx, 1, sigdata); // This should be valid under all script flags ValidateCheckInputsForAllFlags(tx, 0, true, false); // Check that if the second input is invalid, but the first input is // valid, the transaction is not cached. // Invalidate vin[1] tx.vin[1].scriptSig = CScript(); CValidationState state; CTransaction transaction(tx); PrecomputedTransactionData txdata(transaction); // This transaction is now invalid because the second signature is // missing. BOOST_CHECK(!CheckInputs(transaction, state, pcoinsTip.get(), true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, true, txdata, nullptr)); // Make sure this transaction was not cached (ie becausethe first input // was valid) std::vector scriptchecks; BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, true, txdata, &scriptchecks)); // Should get 2 script checks back -- caching is on a whole-transaction // basis. BOOST_CHECK_EQUAL(scriptchecks.size(), 2); } } BOOST_AUTO_TEST_SUITE_END()