LCOV - code coverage report
Current view: top level - src/test - validation_block_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 191 198 96.5 %
Date: 2020-09-26 01:30:44 Functions: 44 45 97.8 %

          Line data    Source code
       1             : // Copyright (c) 2018-2020 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <boost/test/unit_test.hpp>
       6             : 
       7             : #include <chainparams.h>
       8             : #include <consensus/merkle.h>
       9             : #include <consensus/validation.h>
      10             : #include <miner.h>
      11             : #include <pow.h>
      12             : #include <random.h>
      13             : #include <script/standard.h>
      14             : #include <test/util/setup_common.h>
      15             : #include <util/time.h>
      16             : #include <validation.h>
      17             : #include <validationinterface.h>
      18             : 
      19             : #include <thread>
      20             : 
      21          89 : static const std::vector<unsigned char> V_OP_TRUE{OP_TRUE};
      22             : 
      23             : namespace validation_block_tests {
      24           6 : struct MinerTestingSetup : public RegTestingSetup {
      25             :     std::shared_ptr<CBlock> Block(const uint256& prev_hash);
      26             :     std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash);
      27             :     std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash);
      28             :     std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock);
      29             :     void BuildChain(const uint256& root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector<std::shared_ptr<const CBlock>>& blocks);
      30             : };
      31             : } // namespace validation_block_tests
      32             : 
      33          89 : BOOST_FIXTURE_TEST_SUITE(validation_block_tests, MinerTestingSetup)
      34             : 
      35             : struct TestSubscriber final : public CValidationInterface {
      36             :     uint256 m_expected_tip;
      37             : 
      38           2 :     explicit TestSubscriber(uint256 tip) : m_expected_tip(tip) {}
      39             : 
      40          26 :     void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override
      41             :     {
      42          26 :         BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash());
      43          26 :     }
      44             : 
      45          26 :     void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override
      46             :     {
      47          26 :         BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock);
      48          26 :         BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash());
      49             : 
      50          26 :         m_expected_tip = block->GetHash();
      51          26 :     }
      52             : 
      53           0 :     void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override
      54             :     {
      55           0 :         BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());
      56           0 :         BOOST_CHECK_EQUAL(m_expected_tip, pindex->GetBlockHash());
      57             : 
      58           0 :         m_expected_tip = block->hashPrevBlock;
      59           0 :     }
      60             : };
      61             : 
      62         932 : std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
      63             : {
      64             :     static int i = 0;
      65         932 :     static uint64_t time = Params().GenesisBlock().nTime;
      66             : 
      67         932 :     CScript pubKey;
      68         932 :     pubKey << i++ << OP_TRUE;
      69             : 
      70         932 :     auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(pubKey);
      71         932 :     auto pblock = std::make_shared<CBlock>(ptemplate->block);
      72         932 :     pblock->hashPrevBlock = prev_hash;
      73         932 :     pblock->nTime = ++time;
      74             : 
      75         932 :     pubKey.clear();
      76             :     {
      77         932 :         WitnessV0ScriptHash witness_program;
      78         932 :         CSHA256().Write(&V_OP_TRUE[0], V_OP_TRUE.size()).Finalize(witness_program.begin());
      79         932 :         pubKey << OP_0 << ToByteVector(witness_program);
      80         932 :     }
      81             : 
      82             :     // Make the coinbase transaction with two outputs:
      83             :     // One zero-value one that has a unique pubkey to make sure that blocks at the same height can have a different hash
      84             :     // Another one that has the coinbase reward in a P2WSH with OP_TRUE as witness program to make it easy to spend
      85         932 :     CMutableTransaction txCoinbase(*pblock->vtx[0]);
      86         932 :     txCoinbase.vout.resize(2);
      87         932 :     txCoinbase.vout[1].scriptPubKey = pubKey;
      88         932 :     txCoinbase.vout[1].nValue = txCoinbase.vout[0].nValue;
      89         932 :     txCoinbase.vout[0].nValue = 0;
      90         932 :     txCoinbase.vin[0].scriptWitness.SetNull();
      91         932 :     pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
      92             : 
      93             :     return pblock;
      94         932 : }
      95             : 
      96         932 : std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock> pblock)
      97             : {
      98         932 :     LOCK(cs_main); // For LookupBlockIndex
      99         932 :     GenerateCoinbaseCommitment(*pblock, LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
     100             : 
     101         932 :     pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
     102             : 
     103        1894 :     while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
     104         962 :         ++(pblock->nNonce);
     105             :     }
     106             : 
     107         932 :     return pblock;
     108         932 : }
     109             : 
     110             : // construct a valid block
     111         895 : std::shared_ptr<const CBlock> MinerTestingSetup::GoodBlock(const uint256& prev_hash)
     112             : {
     113         895 :     return FinalizeBlock(Block(prev_hash));
     114           0 : }
     115             : 
     116             : // construct an invalid block (but with a valid header)
     117          37 : std::shared_ptr<const CBlock> MinerTestingSetup::BadBlock(const uint256& prev_hash)
     118             : {
     119          37 :     auto pblock = Block(prev_hash);
     120             : 
     121          37 :     CMutableTransaction coinbase_spend;
     122          37 :     coinbase_spend.vin.push_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0));
     123          37 :     coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]);
     124             : 
     125          37 :     CTransactionRef tx = MakeTransactionRef(coinbase_spend);
     126          37 :     pblock->vtx.push_back(tx);
     127             : 
     128          37 :     auto ret = FinalizeBlock(pblock);
     129          37 :     return ret;
     130          37 : }
     131             : 
     132         174 : void MinerTestingSetup::BuildChain(const uint256& root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector<std::shared_ptr<const CBlock>>& blocks)
     133             : {
     134         174 :     if (height <= 0 || blocks.size() >= max_size) return;
     135             : 
     136         174 :     bool gen_invalid = InsecureRandRange(100) < invalid_rate;
     137         174 :     bool gen_fork = InsecureRandRange(100) < branch_rate;
     138             : 
     139         174 :     const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
     140         174 :     blocks.push_back(pblock);
     141         174 :     if (!gen_invalid) {
     142         137 :         BuildChain(pblock->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
     143         137 :     }
     144             : 
     145         174 :     if (gen_fork) {
     146          19 :         blocks.push_back(GoodBlock(root));
     147          19 :         BuildChain(blocks.back()->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
     148          19 :     }
     149         174 : }
     150             : 
     151          95 : BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
     152             : {
     153             :     // build a large-ish chain that's likely to have some forks
     154           1 :     std::vector<std::shared_ptr<const CBlock>> blocks;
     155          19 :     while (blocks.size() < 50) {
     156          18 :         blocks.clear();
     157          18 :         BuildChain(Params().GenesisBlock().GetHash(), 100, 15, 10, 500, blocks);
     158             :     }
     159             : 
     160           1 :     bool ignored;
     161           1 :     BlockValidationState state;
     162           1 :     std::vector<CBlockHeader> headers;
     163          58 :     std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr<const CBlock> b) { return b->GetBlockHeader(); });
     164             : 
     165             :     // Process all the headers so we understand the toplogy of the chain
     166           2 :     BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders(headers, state, Params()));
     167             : 
     168             :     // Connect the genesis block and drain any outstanding events
     169           2 :     BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
     170           1 :     SyncWithValidationInterfaceQueue();
     171             : 
     172             :     // subscribe to events (this subscriber will validate event ordering)
     173             :     const CBlockIndex* initial_tip = nullptr;
     174             :     {
     175           1 :         LOCK(cs_main);
     176           1 :         initial_tip = ::ChainActive().Tip();
     177           1 :     }
     178           1 :     auto sub = std::make_shared<TestSubscriber>(initial_tip->GetBlockHash());
     179           1 :     RegisterSharedValidationInterface(sub);
     180             : 
     181             :     // create a bunch of threads that repeatedly process a block generated above at random
     182             :     // this will create parallelism and randomness inside validation - the ValidationInterface
     183             :     // will subscribe to events generated during block validation and assert on ordering invariance
     184           1 :     std::vector<std::thread> threads;
     185          11 :     for (int i = 0; i < 10; i++) {
     186          20 :         threads.emplace_back([&]() {
     187          10 :             bool ignored;
     188          10 :             FastRandomContext insecure;
     189       10010 :             for (int i = 0; i < 1000; i++) {
     190       10000 :                 auto block = blocks[insecure.randrange(blocks.size() - 1)];
     191       20000 :                 Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, &ignored);
     192       10000 :             }
     193             : 
     194             :             // to make sure that eventually we process the full chain - do it here
     195         580 :             for (auto block : blocks) {
     196         570 :                 if (block->vtx.size() == 1) {
     197         960 :                     bool processed = Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, &ignored);
     198         480 :                     assert(processed);
     199         480 :                 }
     200         570 :             }
     201          10 :         });
     202             :     }
     203             : 
     204          11 :     for (auto& t : threads) {
     205          10 :         t.join();
     206             :     }
     207           1 :     SyncWithValidationInterfaceQueue();
     208             : 
     209           1 :     UnregisterSharedValidationInterface(sub);
     210             : 
     211           1 :     LOCK(cs_main);
     212           1 :     BOOST_CHECK_EQUAL(sub->m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
     213           1 : }
     214             : 
     215             : /**
     216             :  * Test that mempool updates happen atomically with reorgs.
     217             :  *
     218             :  * This prevents RPC clients, among others, from retrieving immediately-out-of-date mempool data
     219             :  * during large reorgs.
     220             :  *
     221             :  * The test verifies this by creating a chain of `num_txs` blocks, matures their coinbases, and then
     222             :  * submits txns spending from their coinbase to the mempool. A fork chain is then processed,
     223             :  * invalidating the txns and evicting them from the mempool.
     224             :  *
     225             :  * We verify that the mempool updates atomically by polling it continuously
     226             :  * from another thread during the reorg and checking that its size only changes
     227             :  * once. The size changing exactly once indicates that the polling thread's
     228             :  * view of the mempool is either consistent with the chain state before reorg,
     229             :  * or consistent with the chain state after the reorg, and not just consistent
     230             :  * with some intermediate state during the reorg.
     231             :  */
     232          95 : BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
     233             : {
     234           1 :     bool ignored;
     235         741 :     auto ProcessBlock = [&](std::shared_ptr<const CBlock> block) -> bool {
     236        1480 :         return Assert(m_node.chainman)->ProcessNewBlock(Params(), block, /* fForceProcessing */ true, /* fNewBlock */ &ignored);
     237           0 :     };
     238             : 
     239             :     // Process all mined blocks
     240           1 :     BOOST_REQUIRE(ProcessBlock(std::make_shared<CBlock>(Params().GenesisBlock())));
     241           1 :     auto last_mined = GoodBlock(Params().GenesisBlock().GetHash());
     242           1 :     BOOST_REQUIRE(ProcessBlock(last_mined));
     243             : 
     244             :     // Run the test multiple times
     245           4 :     for (int test_runs = 3; test_runs > 0; --test_runs) {
     246           3 :         BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
     247             : 
     248             :         // Later on split from here
     249           3 :         const uint256 split_hash{last_mined->hashPrevBlock};
     250             : 
     251             :         // Create a bunch of transactions to spend the miner rewards of the
     252             :         // most recent blocks
     253           3 :         std::vector<CTransactionRef> txs;
     254          69 :         for (int num_txs = 22; num_txs > 0; --num_txs) {
     255          66 :             CMutableTransaction mtx;
     256          66 :             mtx.vin.push_back(CTxIn{COutPoint{last_mined->vtx[0]->GetHash(), 1}, CScript{}});
     257          66 :             mtx.vin[0].scriptWitness.stack.push_back(V_OP_TRUE);
     258          66 :             mtx.vout.push_back(last_mined->vtx[0]->vout[1]);
     259          66 :             mtx.vout[0].nValue -= 1000;
     260          66 :             txs.push_back(MakeTransactionRef(mtx));
     261             : 
     262          66 :             last_mined = GoodBlock(last_mined->GetHash());
     263          66 :             BOOST_REQUIRE(ProcessBlock(last_mined));
     264          66 :         }
     265             : 
     266             :         // Mature the inputs of the txs
     267         303 :         for (int j = COINBASE_MATURITY; j > 0; --j) {
     268         300 :             last_mined = GoodBlock(last_mined->GetHash());
     269         300 :             BOOST_REQUIRE(ProcessBlock(last_mined));
     270             :         }
     271             : 
     272             :         // Mine a reorg (and hold it back) before adding the txs to the mempool
     273           3 :         const uint256 tip_init{last_mined->GetHash()};
     274             : 
     275           3 :         std::vector<std::shared_ptr<const CBlock>> reorg;
     276           3 :         last_mined = GoodBlock(split_hash);
     277           3 :         reorg.push_back(last_mined);
     278         372 :         for (size_t j = COINBASE_MATURITY + txs.size() + 1; j > 0; --j) {
     279         369 :             last_mined = GoodBlock(last_mined->GetHash());
     280         369 :             reorg.push_back(last_mined);
     281             :         }
     282             : 
     283             :         // Add the txs to the tx pool
     284             :         {
     285           3 :             LOCK(cs_main);
     286           3 :             TxValidationState state;
     287           3 :             std::list<CTransactionRef> plTxnReplaced;
     288          69 :             for (const auto& tx : txs) {
     289          66 :                 BOOST_REQUIRE(AcceptToMemoryPool(
     290             :                     *m_node.mempool,
     291             :                     state,
     292             :                     tx,
     293             :                     &plTxnReplaced,
     294             :                     /* bypass_limits */ false,
     295             :                     /* nAbsurdFee */ 0));
     296             :             }
     297           3 :         }
     298             : 
     299             :         // Check that all txs are in the pool
     300             :         {
     301           3 :             LOCK(m_node.mempool->cs);
     302           3 :             BOOST_CHECK_EQUAL(m_node.mempool->mapTx.size(), txs.size());
     303           3 :         }
     304             : 
     305             :         // Run a thread that simulates an RPC caller that is polling while
     306             :         // validation is doing a reorg
     307           6 :         std::thread rpc_thread{[&]() {
     308             :             // This thread is checking that the mempool either contains all of
     309             :             // the transactions invalidated by the reorg, or none of them, and
     310             :             // not some intermediate amount.
     311           3 :             while (true) {
     312     6092758 :                 LOCK(m_node.mempool->cs);
     313     6092758 :                 if (m_node.mempool->mapTx.size() == 0) {
     314             :                     // We are done with the reorg
     315           3 :                     break;
     316             :                 }
     317             :                 // Internally, we might be in the middle of the reorg, but
     318             :                 // externally the reorg to the most-proof-of-work chain should
     319             :                 // be atomic. So the caller assumes that the returned mempool
     320             :                 // is consistent. That is, it has all txs that were there
     321             :                 // before the reorg.
     322     6092755 :                 assert(m_node.mempool->mapTx.size() == txs.size());
     323     6092755 :                 continue;
     324     6092758 :             }
     325           3 :             LOCK(cs_main);
     326             :             // We are done with the reorg, so the tip must have changed
     327           3 :             assert(tip_init != ::ChainActive().Tip()->GetBlockHash());
     328           3 :         }};
     329             : 
     330             :         // Submit the reorg in this thread to invalidate and remove the txs from the tx pool
     331         375 :         for (const auto& b : reorg) {
     332         372 :             ProcessBlock(b);
     333             :         }
     334             :         // Check that the reorg was eventually successful
     335           3 :         BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
     336             : 
     337             :         // We can join the other thread, which returns when the reorg was successful
     338           3 :         rpc_thread.join();
     339           3 :     }
     340           1 : }
     341             : 
     342          95 : BOOST_AUTO_TEST_CASE(witness_commitment_index)
     343             : {
     344           1 :     CScript pubKey;
     345           1 :     pubKey << 1 << OP_TRUE;
     346           1 :     auto ptemplate = BlockAssembler(*m_node.mempool, Params()).CreateNewBlock(pubKey);
     347           1 :     CBlock pblock = ptemplate->block;
     348             : 
     349           1 :     CTxOut witness;
     350           1 :     witness.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
     351           1 :     witness.scriptPubKey[0] = OP_RETURN;
     352           1 :     witness.scriptPubKey[1] = 0x24;
     353           1 :     witness.scriptPubKey[2] = 0xaa;
     354           1 :     witness.scriptPubKey[3] = 0x21;
     355           1 :     witness.scriptPubKey[4] = 0xa9;
     356           1 :     witness.scriptPubKey[5] = 0xed;
     357             : 
     358             :     // A witness larger than the minimum size is still valid
     359           1 :     CTxOut min_plus_one = witness;
     360           1 :     min_plus_one.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT + 1);
     361             : 
     362           1 :     CTxOut invalid = witness;
     363           1 :     invalid.scriptPubKey[0] = OP_VERIFY;
     364             : 
     365           1 :     CMutableTransaction txCoinbase(*pblock.vtx[0]);
     366           1 :     txCoinbase.vout.resize(4);
     367           1 :     txCoinbase.vout[0] = witness;
     368           1 :     txCoinbase.vout[1] = witness;
     369           1 :     txCoinbase.vout[2] = min_plus_one;
     370           1 :     txCoinbase.vout[3] = invalid;
     371           1 :     pblock.vtx[0] = MakeTransactionRef(std::move(txCoinbase));
     372             : 
     373           1 :     BOOST_CHECK_EQUAL(GetWitnessCommitmentIndex(pblock), 2);
     374           1 : }
     375          89 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.15