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