Skip to content

Commit 0894748

Browse files
committed
Merge bitcoin/bitcoin#30918: fuzz: Add check in p2p_headers_presync that chain work never exceeds minimum work
284bd17 add check that chainwork doesn't exceed minimum work (marcofleon) 9aa5d1c add clarification in comment (marcofleon) Pull request description: A followup to bitcoin/bitcoin#30661 The added assertion just makes sure that the fuzz test is working as intended. If we're sure that the total work of the test chain is never more than minimum chain work, then we can be sure that the later assertion failure would actually mean that a bug in the headers presync logic was found. This PR also addresses: bitcoin/bitcoin#30661 (comment) bitcoin/bitcoin#30661 (comment) bitcoin/bitcoin#30661 (comment) ACKs for top commit: instagibbs: reACK 284bd17 maflcko: review ACK 284bd17 achow101: ACK 284bd17 Tree-SHA512: 76a9dffea4b6e13499c636d6ad26af06135319d25117c0eb40cf8dfcfdca6a4549c9b4d2ba835192ca355e0f8d476227aeabf8bdb68770def72a9fb521533fe5
2 parents f57a675 + 284bd17 commit 0894748

File tree

2 files changed

+26
-9
lines changed

2 files changed

+26
-9
lines changed

src/net_processing.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
7474
//! Whether or not the internal RNG behaves deterministically (this is
7575
//! a test-only option).
7676
bool deterministic_rng{false};
77-
//! Number of headers sent in one getheaders message result.
77+
//! Number of headers sent in one getheaders message result (this is
78+
//! a test-only option).
7879
uint32_t max_headers_result{MAX_HEADERS_RESULTS};
7980
};
8081

src/test/fuzz/p2p_headers_presync.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <test/util/net.h>
1111
#include <test/util/script.h>
1212
#include <test/util/setup_common.h>
13+
#include <uint256.h>
1314
#include <validation.h>
1415

1516
namespace {
@@ -20,9 +21,7 @@ class HeadersSyncSetup : public TestingSetup
2021
std::vector<CNode*> m_connections;
2122

2223
public:
23-
HeadersSyncSetup(const ChainType chain_type = ChainType::MAIN,
24-
TestOpts opts = {})
25-
: TestingSetup(chain_type, opts)
24+
HeadersSyncSetup(const ChainType chain_type, TestOpts opts) : TestingSetup(chain_type, opts)
2625
{
2726
PeerManager::Options peerman_opts;
2827
node::ApplyArgsManOptions(*m_node.args, peerman_opts);
@@ -116,9 +115,9 @@ CBlock ConsumeBlock(FuzzedDataProvider& fuzzed_data_provider, const uint256& pre
116115
return block;
117116
}
118117

119-
void FinalizeHeader(CBlockHeader& header)
118+
void FinalizeHeader(CBlockHeader& header, const ChainstateManager& chainman)
120119
{
121-
while (!CheckProofOfWork(header.GetHash(), header.nBits, Params().GetConsensus())) {
120+
while (!CheckProofOfWork(header.GetHash(), header.nBits, chainman.GetParams().GetConsensus())) {
122121
++(header.nNonce);
123122
}
124123
}
@@ -144,17 +143,20 @@ FUZZ_TARGET(p2p_headers_presync, .init = initialize)
144143

145144
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
146145

147-
CBlockHeader base{Params().GenesisBlock()};
146+
CBlockHeader base{chainman.GetParams().GenesisBlock()};
148147
SetMockTime(base.nTime);
149148

150149
// The chain is just a single block, so this is equal to 1
151150
size_t original_index_size{WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size())};
151+
arith_uint256 total_work{WITH_LOCK(cs_main, return chainman.m_best_header->nChainWork)};
152+
153+
std::vector<CBlockHeader> all_headers;
152154

153155
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)
154156
{
155157
auto finalized_block = [&]() {
156158
CBlock block = ConsumeBlock(fuzzed_data_provider, base.GetHash(), base.nBits);
157-
FinalizeHeader(block);
159+
FinalizeHeader(block, chainman);
158160
return block;
159161
};
160162

@@ -167,10 +169,12 @@ FUZZ_TARGET(p2p_headers_presync, .init = initialize)
167169
headers.resize(FUZZ_MAX_HEADERS_RESULTS);
168170
for (CBlock& header : headers) {
169171
header = ConsumeHeader(fuzzed_data_provider, base.GetHash(), base.nBits);
170-
FinalizeHeader(header);
172+
FinalizeHeader(header, chainman);
171173
base = header;
172174
}
173175

176+
all_headers.insert(all_headers.end(), headers.begin(), headers.end());
177+
174178
auto headers_msg = NetMsg::Make(NetMsgType::HEADERS, TX_WITH_WITNESS(headers));
175179
g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
176180
},
@@ -179,16 +183,28 @@ FUZZ_TARGET(p2p_headers_presync, .init = initialize)
179183
auto block = finalized_block();
180184
CBlockHeaderAndShortTxIDs cmpct_block{block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
181185

186+
all_headers.push_back(block);
187+
182188
auto headers_msg = NetMsg::Make(NetMsgType::CMPCTBLOCK, TX_WITH_WITNESS(cmpct_block));
183189
g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
184190
},
185191
[&]() NO_THREAD_SAFETY_ANALYSIS {
186192
// Send a block
187193
auto block = finalized_block();
188194

195+
all_headers.push_back(block);
196+
189197
auto headers_msg = NetMsg::Make(NetMsgType::BLOCK, TX_WITH_WITNESS(block));
190198
g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
191199
});
200+
201+
// This is a conservative overestimate, as base is only moved forward when sending headers. In theory,
202+
// the longest chain generated by this test is 1600 (FUZZ_MAX_HEADERS_RESULTS * 100) headers. In that case,
203+
// this variable will accurately reflect the chain's total work.
204+
total_work += CalculateClaimedHeadersWork(all_headers);
205+
206+
// This test should never create a chain with more work than MinimumChainWork.
207+
assert(total_work < chainman.MinimumChainWork());
192208
}
193209

194210
// The headers/blocks sent in this test should never be stored, as the chains don't have the work required

0 commit comments

Comments
 (0)