headers_sync_chainwork_tests.cpp raw

   1  // Copyright (c) 2022 The Limenka 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 <chain.h>
   6  #include <chainparams.h>
   7  #include <consensus/params.h>
   8  #include <headerssync.h>
   9  #include <pow.h>
  10  #include <test/util/setup_common.h>
  11  #include <validation.h>
  12  #include <vector>
  13  
  14  #include <boost/test/unit_test.hpp>
  15  
  16  struct HeadersGeneratorSetup : public RegTestingSetup {
  17      /** Search for a nonce to meet (regtest) proof of work */
  18      void FindProofOfWork(CBlockHeader& starting_header);
  19      /**
  20       * Generate headers in a chain that build off a given starting hash, using
  21       * the given nVersion, advancing time by 1 second from the starting
  22       * prev_time, and with a fixed merkle root hash.
  23       */
  24      void GenerateHeaders(std::vector<CBlockHeader>& headers, size_t count,
  25              const uint256& starting_hash, const int nVersion, int prev_time,
  26              const uint256& merkle_root, const uint32_t nBits);
  27  };
  28  
  29  void HeadersGeneratorSetup::FindProofOfWork(CBlockHeader& starting_header)
  30  {
  31      while (!CheckProofOfWork(starting_header.GetHash(), starting_header.nBits, Params().GetConsensus())) {
  32          ++(starting_header.nNonce);
  33      }
  34  }
  35  
  36  void HeadersGeneratorSetup::GenerateHeaders(std::vector<CBlockHeader>& headers,
  37          size_t count, const uint256& starting_hash, const int nVersion, int prev_time,
  38          const uint256& merkle_root, const uint32_t nBits)
  39  {
  40      uint256 prev_hash = starting_hash;
  41  
  42      while (headers.size() < count) {
  43          headers.emplace_back();
  44          CBlockHeader& next_header = headers.back();;
  45          next_header.nVersion = nVersion;
  46          next_header.hashPrevBlock = prev_hash;
  47          next_header.hashMerkleRoot = merkle_root;
  48          next_header.nTime = prev_time+1;
  49          next_header.nBits = nBits;
  50  
  51          FindProofOfWork(next_header);
  52          prev_hash = next_header.GetHash();
  53          prev_time = next_header.nTime;
  54      }
  55      return;
  56  }
  57  
  58  BOOST_FIXTURE_TEST_SUITE(headers_sync_chainwork_tests, HeadersGeneratorSetup)
  59  
  60  // In this test, we construct two sets of headers from genesis, one with
  61  // sufficient proof of work and one without.
  62  // 1. We deliver the first set of headers and verify that the headers sync state
  63  //    updates to the REDOWNLOAD phase successfully.
  64  // 2. Then we deliver the second set of headers and verify that they fail
  65  //    processing (presumably due to commitments not matching).
  66  // 3. Finally, we verify that repeating with the first set of headers in both
  67  //    phases is successful.
  68  BOOST_AUTO_TEST_CASE(headers_sync_state)
  69  {
  70      std::vector<CBlockHeader> first_chain;
  71      std::vector<CBlockHeader> second_chain;
  72  
  73      std::unique_ptr<HeadersSyncState> hss;
  74  
  75      const int target_blocks = 15000;
  76      arith_uint256 chain_work = target_blocks*2;
  77  
  78      // Generate headers for two different chains (using differing merkle roots
  79      // to ensure the headers are different).
  80      GenerateHeaders(first_chain, target_blocks-1, Params().GenesisBlock().GetHash(),
  81              Params().GenesisBlock().nVersion, Params().GenesisBlock().nTime,
  82              ArithToUint256(0), Params().GenesisBlock().nBits);
  83  
  84      GenerateHeaders(second_chain, target_blocks-2, Params().GenesisBlock().GetHash(),
  85              Params().GenesisBlock().nVersion, Params().GenesisBlock().nTime,
  86              ArithToUint256(1), Params().GenesisBlock().nBits);
  87  
  88      const CBlockIndex* chain_start = WITH_LOCK(::cs_main, return m_node.chainman->m_blockman.LookupBlockIndex(Params().GenesisBlock().GetHash()));
  89      std::vector<CBlockHeader> headers_batch;
  90  
  91      // Feed the first chain to HeadersSyncState, by delivering 1 header
  92      // initially and then the rest.
  93      headers_batch.insert(headers_batch.end(), std::next(first_chain.begin()), first_chain.end());
  94  
  95      hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
  96      (void)hss->ProcessNextHeaders({first_chain.front()}, true);
  97      // Pretend the first header is still "full", so we don't abort.
  98      auto result = hss->ProcessNextHeaders(headers_batch, true);
  99  
 100      // This chain should look valid, and we should have met the proof-of-work
 101      // requirement.
 102      BOOST_CHECK(result.success);
 103      BOOST_CHECK(result.request_more);
 104      BOOST_CHECK(hss->GetState() == HeadersSyncState::State::REDOWNLOAD);
 105  
 106      // Try to sneakily feed back the second chain.
 107      result = hss->ProcessNextHeaders(second_chain, true);
 108      BOOST_CHECK(!result.success); // foiled!
 109      BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
 110  
 111      // Now try again, this time feeding the first chain twice.
 112      hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
 113      (void)hss->ProcessNextHeaders(first_chain, true);
 114      BOOST_CHECK(hss->GetState() == HeadersSyncState::State::REDOWNLOAD);
 115  
 116      result = hss->ProcessNextHeaders(first_chain, true);
 117      BOOST_CHECK(result.success);
 118      BOOST_CHECK(!result.request_more);
 119      // All headers should be ready for acceptance:
 120      BOOST_CHECK(result.pow_validated_headers.size() == first_chain.size());
 121      // Nothing left for the sync logic to do:
 122      BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
 123  
 124      // Finally, verify that just trying to process the second chain would not
 125      // succeed (too little work)
 126      hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
 127      BOOST_CHECK(hss->GetState() == HeadersSyncState::State::PRESYNC);
 128       // Pretend just the first message is "full", so we don't abort.
 129      (void)hss->ProcessNextHeaders({second_chain.front()}, true);
 130      BOOST_CHECK(hss->GetState() == HeadersSyncState::State::PRESYNC);
 131  
 132      headers_batch.clear();
 133      headers_batch.insert(headers_batch.end(), std::next(second_chain.begin(), 1), second_chain.end());
 134      // Tell the sync logic that the headers message was not full, implying no
 135      // more headers can be requested. For a low-work-chain, this should causes
 136      // the sync to end with no headers for acceptance.
 137      result = hss->ProcessNextHeaders(headers_batch, false);
 138      BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
 139      BOOST_CHECK(result.pow_validated_headers.empty());
 140      BOOST_CHECK(!result.request_more);
 141      // Nevertheless, no validation errors should have been detected with the
 142      // chain:
 143      BOOST_CHECK(result.success);
 144  }
 145  
 146  BOOST_AUTO_TEST_SUITE_END()
 147