Branch data Line data Source code
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 : 0 : /** Search for a nonce to meet (regtest) proof of work */
18 : 0 : 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 : 0 : void HeadersGeneratorSetup::FindProofOfWork(CBlockHeader& starting_header)
30 : : {
31 : 0 : while (!CheckProofOfWork(starting_header.GetHash(), starting_header.nBits, Params().GetConsensus())) {
32 : 0 : ++(starting_header.nNonce);
33 : : }
34 : 0 : }
35 : :
36 : 0 : 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 : 0 : uint256 prev_hash = starting_hash;
41 : :
42 : 0 : while (headers.size() < count) {
43 : 0 : headers.push_back(CBlockHeader());
44 : 0 : CBlockHeader& next_header = headers.back();;
45 : 0 : next_header.nVersion = nVersion;
46 : 0 : next_header.hashPrevBlock = prev_hash;
47 : 0 : next_header.hashMerkleRoot = merkle_root;
48 : 0 : next_header.nTime = prev_time+1;
49 : 0 : next_header.nBits = nBits;
50 : :
51 : 0 : FindProofOfWork(next_header);
52 : 0 : prev_hash = next_header.GetHash();
53 : 0 : prev_time = next_header.nTime;
54 : : }
55 : 0 : return;
56 : : }
57 : :
58 : 0 : 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 : 0 : BOOST_AUTO_TEST_CASE(headers_sync_state)
69 : : {
70 : 0 : std::vector<CBlockHeader> first_chain;
71 : 0 : std::vector<CBlockHeader> second_chain;
72 : :
73 : 0 : std::unique_ptr<HeadersSyncState> hss;
74 : 0 :
75 : 0 : const int target_blocks = 15000;
76 : 0 : 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 : 0 : GenerateHeaders(first_chain, target_blocks-1, Params().GenesisBlock().GetHash(),
81 : 0 : Params().GenesisBlock().nVersion, Params().GenesisBlock().nTime,
82 : 0 : ArithToUint256(0), Params().GenesisBlock().nBits);
83 : 0 :
84 : 0 : GenerateHeaders(second_chain, target_blocks-2, Params().GenesisBlock().GetHash(),
85 : 0 : Params().GenesisBlock().nVersion, Params().GenesisBlock().nTime,
86 : 0 : ArithToUint256(1), Params().GenesisBlock().nBits);
87 : :
88 : 0 : const CBlockIndex* chain_start = WITH_LOCK(::cs_main, return m_node.chainman->m_blockman.LookupBlockIndex(Params().GenesisBlock().GetHash()));
89 : 0 : std::vector<CBlockHeader> headers_batch;
90 : :
91 : : // Feed the first chain to HeadersSyncState, by delivering 1 header
92 : : // initially and then the rest.
93 : 0 : headers_batch.insert(headers_batch.end(), std::next(first_chain.begin()), first_chain.end());
94 : :
95 : 0 : hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
96 : 0 : (void)hss->ProcessNextHeaders({first_chain.front()}, true);
97 : : // Pretend the first header is still "full", so we don't abort.
98 : 0 : 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 : 0 : BOOST_CHECK(result.success);
103 : 0 : BOOST_CHECK(result.request_more);
104 : 0 : BOOST_CHECK(hss->GetState() == HeadersSyncState::State::REDOWNLOAD);
105 : :
106 : : // Try to sneakily feed back the second chain.
107 : 0 : result = hss->ProcessNextHeaders(second_chain, true);
108 : 0 : BOOST_CHECK(!result.success); // foiled!
109 : 0 : BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
110 : :
111 : : // Now try again, this time feeding the first chain twice.
112 : 0 : hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
113 : 0 : (void)hss->ProcessNextHeaders(first_chain, true);
114 : 0 : BOOST_CHECK(hss->GetState() == HeadersSyncState::State::REDOWNLOAD);
115 : :
116 : 0 : result = hss->ProcessNextHeaders(first_chain, true);
117 : 0 : BOOST_CHECK(result.success);
118 : 0 : BOOST_CHECK(!result.request_more);
119 : : // All headers should be ready for acceptance:
120 : 0 : BOOST_CHECK(result.pow_validated_headers.size() == first_chain.size());
121 : : // Nothing left for the sync logic to do:
122 : 0 : 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 : 0 : hss.reset(new HeadersSyncState(0, Params().GetConsensus(), chain_start, chain_work));
127 : 0 : BOOST_CHECK(hss->GetState() == HeadersSyncState::State::PRESYNC);
128 : : // Pretend just the first message is "full", so we don't abort.
129 : 0 : (void)hss->ProcessNextHeaders({second_chain.front()}, true);
130 : 0 : BOOST_CHECK(hss->GetState() == HeadersSyncState::State::PRESYNC);
131 : :
132 : 0 : headers_batch.clear();
133 : 0 : 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 : 0 : result = hss->ProcessNextHeaders(headers_batch, false);
138 : 0 : BOOST_CHECK(hss->GetState() == HeadersSyncState::State::FINAL);
139 : 0 : BOOST_CHECK(result.pow_validated_headers.empty());
140 : 0 : BOOST_CHECK(!result.request_more);
141 : : // Nevertheless, no validation errors should have been detected with the
142 : : // chain:
143 : 0 : BOOST_CHECK(result.success);
144 : 0 : }
145 : :
146 : 0 : BOOST_AUTO_TEST_SUITE_END()
|