/ src / test / headers_sync_chainwork_tests.cpp
headers_sync_chainwork_tests.cpp
  1  // Copyright (c) 2022 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 <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()