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