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()