Bitcoin Core  24.99.0
P2P Digital Currency
headers_sync_chainwork_tests.cpp
Go to the documentation of this file.
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 
18  void FindProofOfWork(CBlockHeader& starting_header);
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 
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.push_back(CBlockHeader());
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);
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);
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 
uint256 ArithToUint256(const arith_uint256 &a)
node::NodeContext m_node
Definition: bitcoin-gui.cpp:37
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: validation.cpp:121
const CChainParams & Params()
Return the currently selected parameters.
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition: block.h:22
uint32_t nNonce
Definition: block.h:30
uint32_t nBits
Definition: block.h:29
uint32_t nTime
Definition: block.h:28
int32_t nVersion
Definition: block.h:25
uint256 hashPrevBlock
Definition: block.h:26
uint256 hashMerkleRoot
Definition: block.h:27
uint256 GetHash() const
Definition: block.cpp:11
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:152
HeadersSyncState:
Definition: headerssync.h:101
@ FINAL
We're done syncing with this peer and can discard any remaining state.
@ PRESYNC
PRESYNC means the peer has not yet demonstrated their chain has sufficient work and we're only buildi...
@ REDOWNLOAD
REDOWNLOAD means the peer has given us a high-enough-work chain, and now we're redownloading the head...
256-bit unsigned big integer.
256-bit opaque blob.
Definition: uint256.h:119
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(headers_sync_state)
#define BOOST_CHECK(expr)
Definition: object.cpp:16
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params &params)
Check whether a block hash satisfies the proof-of-work requirement specified by nBits.
Definition: pow.cpp:125
void FindProofOfWork(CBlockHeader &starting_header)
Search for a nonce to meet (regtest) proof of work.
void GenerateHeaders(std::vector< CBlockHeader > &headers, size_t count, const uint256 &starting_hash, const int nVersion, int prev_time, const uint256 &merkle_root, const uint32_t nBits)
Generate headers in a chain that build off a given starting hash, using the given nVersion,...
Identical to TestingSetup, but chain set to regtest.
Definition: setup_common.h:114
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:54
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:305
static int count
Definition: tests.c:33