LCOV - code coverage report
Current view: top level - src/test - validation_block_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 78 83 94.0 %
Date: 2025-02-23 09:33:43 Functions: 11 12 91.7 %

          Line data    Source code
       1             : // Copyright (c) 2018 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <boost/test/unit_test.hpp>
       6             : 
       7             : #include "blockassembler.h"
       8             : #include "chainparams.h"
       9             : #include "consensus/merkle.h"
      10             : #include "consensus/validation.h"
      11             : #include "pow.h"
      12             : #include "random.h"
      13             : #include "test/test_pivx.h"
      14             : #include "util/blockstatecatcher.h"
      15             : #include "validation.h"
      16             : #include "validationinterface.h"
      17             : 
      18             : 
      19             : #define ASSERT_WITH_MSG(cond, msg) if (!cond) { BOOST_ERROR(msg); }
      20             : 
      21             : 
      22             : BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegTestingSetup)
      23             : 
      24           1 : struct TestSubscriber : public CValidationInterface {
      25             :     uint256 m_expected_tip;
      26             : 
      27           1 :     explicit TestSubscriber(uint256 tip) : m_expected_tip(std::move(tip)) {}
      28             : 
      29           1 :     void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
      30             :     {
      31           1 :         BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash());
      32           1 :     }
      33             : 
      34           1 :     void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)
      35             :     {
      36           1 :         BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock);
      37           1 :         BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash());
      38             : 
      39           1 :         m_expected_tip = block->GetHash();
      40           1 :     }
      41             : 
      42           0 :     void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const uint256& blockHash, int nBlockHeight, int64_t blockTime)
      43             :     {
      44           0 :         BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());
      45             : 
      46           0 :         m_expected_tip = block->hashPrevBlock;
      47           0 :     }
      48             : };
      49             : 
      50         234 : std::shared_ptr<CBlock> Block(const uint256& prev_hash)
      51             : {
      52         234 :     static int i = 0;
      53         234 :     static uint64_t time = Params().GenesisBlock().nTime;
      54             : 
      55         234 :     CScript pubKey;
      56         234 :     pubKey << i++ << OP_TRUE;
      57             : 
      58         468 :     auto ptemplate = BlockAssembler(Params(), false).CreateNewBlock(pubKey);
      59         234 :     auto pblock = std::make_shared<CBlock>(ptemplate->block);
      60         234 :     pblock->hashPrevBlock = prev_hash;
      61         234 :     pblock->nTime = ++time;
      62             : 
      63         234 :     CMutableTransaction txCoinbase(*pblock->vtx[0]);
      64         234 :     txCoinbase.vout.resize(1);
      65         468 :     pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
      66             : 
      67         468 :     return pblock;
      68             : }
      69             : 
      70             : // construct a valid block
      71         204 : const std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash)
      72             : {
      73         204 :     return FinalizeBlock(Block(prev_hash));
      74             : }
      75             : 
      76             : // construct an invalid block (but with a valid header)
      77          30 : const std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash)
      78             : {
      79          30 :     auto pblock = Block(prev_hash);
      80             : 
      81          60 :     CMutableTransaction coinbase_spend;
      82          60 :     coinbase_spend.vin.emplace_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0));
      83          30 :     coinbase_spend.vout.emplace_back(pblock->vtx[0]->vout[0]);
      84             : 
      85          60 :     CTransactionRef tx = MakeTransactionRef(coinbase_spend);
      86          30 :     pblock->vtx.emplace_back(tx);
      87             : 
      88          60 :     auto ret = FinalizeBlock(pblock);
      89          60 :     return ret;
      90             : }
      91             : 
      92         216 : 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)
      93             : {
      94         216 :     if (height <= 0 || blocks.size() >= max_size) return;
      95             : 
      96         216 :     bool gen_invalid = GetRand(100) < invalid_rate;
      97         216 :     bool gen_fork = GetRand(100) < branch_rate;
      98             : 
      99         432 :     const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
     100         216 :     blocks.emplace_back(pblock);
     101         216 :     if (!gen_invalid) {
     102         186 :         BuildChain(pblock->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
     103             :     }
     104             : 
     105         216 :     if (gen_fork) {
     106          18 :         blocks.emplace_back(GoodBlock(root));
     107          18 :         BuildChain(blocks.back()->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
     108             :     }
     109             : }
     110             : 
     111           2 : BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
     112             : {
     113             :     // build a large-ish chain that's likely to have some forks
     114           1 :     std::vector<std::shared_ptr<const CBlock>> blocks;
     115          13 :     while (blocks.size() < 50) {
     116          12 :         blocks.clear();
     117          12 :         BuildChain(Params().GenesisBlock().GetHash(), 100, 15, 10, 500, blocks);
     118             :     }
     119             : 
     120             :     // Connect the genesis block and drain any outstanding events
     121           3 :     BOOST_CHECK_MESSAGE(ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), nullptr), "Error: genesis not connected");
     122           1 :     SyncWithValidationInterfaceQueue();
     123             : 
     124             :     // subscribe to events (this subscriber will validate event ordering)
     125           3 :     const CBlockIndex* initial_tip = WITH_LOCK(cs_main, return chainActive.Tip());
     126           2 :     TestSubscriber sub(initial_tip->GetBlockHash());
     127           1 :     RegisterValidationInterface(&sub);
     128             : 
     129             :     // create a bunch of threads that repeatedly process a block generated above at random
     130             :     // this will create parallelism and randomness inside validation - the ValidationInterface
     131             :     // will subscribe to events generated during block validation and assert on ordering invariance
     132           2 :     boost::thread_group threads;
     133          11 :     for (int i = 0; i < 10; i++) {
     134       10010 :         threads.create_thread([&blocks]() {
     135       10010 :             for (int i = 0; i < 1000; i++) {
     136       20000 :                 auto block = blocks[GetRand(blocks.size() - 1)];
     137       10000 :                 ProcessNewBlock(block, nullptr);
     138             :             }
     139             : 
     140          20 :             BlockStateCatcherWrapper sc(UINT256_ZERO);
     141          10 :             sc.registerEvent();
     142             :             // to make sure that eventually we process the full chain - do it here
     143        1000 :             for (const auto& block : blocks) {
     144         990 :                 if (block->vtx.size() == 1) {
     145         880 :                     sc.get().setBlockHash(block->GetHash());
     146         880 :                     bool processed = ProcessNewBlock(block, nullptr);
     147             :                     // Future to do: "prevblk-not-found" here is the only valid reason to not check processed flag.
     148         890 :                     std::string stateReason = sc.get().state.GetRejectReason();
     149        2610 :                     if (sc.get().found && (stateReason == "duplicate" || stateReason == "prevblk-not-found" ||
     150        2570 :                                               stateReason == "bad-prevblk" || stateReason == "blk-out-of-order")) continue;
     151          10 :                     ASSERT_WITH_MSG(processed, ("Error: " + sc.get().state.GetRejectReason()).c_str());
     152             :                 }
     153             :             }
     154          10 :         });
     155             :     }
     156             : 
     157           1 :     threads.join_all();
     158           1 :     while (GetMainSignals().CallbacksPending() > 0) {
     159           0 :         MilliSleep(100);
     160             :     }
     161             : 
     162           1 :     SyncWithValidationInterfaceQueue();
     163           1 :     UnregisterValidationInterface(&sub);
     164             : 
     165           3 :     BOOST_CHECK_EQUAL(sub.m_expected_tip, WITH_LOCK(cs_main, return chainActive.Tip()->GetBlockHash()));
     166           1 : }
     167             : 
     168             : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.14