Skip to content

Commit 9afc233

Browse files
committed
perf: pre-calculate hash of header once , apply #7272 for pre-sync feature
1 parent c4da8de commit 9afc233

6 files changed

Lines changed: 71 additions & 49 deletions

File tree

src/headerssync.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,11 @@ void HeadersSyncState::Finalize()
6666
* Validate and store commitments, and compare total chainwork to our target to
6767
* see if we can switch to REDOWNLOAD mode. */
6868
HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
69-
std::vector<CBlockHeader>& received_headers, const bool full_headers_message)
69+
std::vector<CBlockHeader>& received_headers, const std::vector<uint256>& hashes, const bool full_headers_message)
7070
{
7171
ProcessingResult ret;
7272

73+
Assume(received_headers.size() == hashes.size());
7374
Assume(!received_headers.empty());
7475
if (received_headers.empty()) return ret;
7576

@@ -80,7 +81,7 @@ HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
8081
// During PRESYNC, we minimally validate block headers and
8182
// occasionally add commitments to them, until we reach our work
8283
// threshold (at which point m_download_state is updated to REDOWNLOAD).
83-
ret.success = ValidateAndStoreHeadersCommitments(received_headers);
84+
ret.success = ValidateAndStoreHeadersCommitments(received_headers, hashes);
8485
if (ret.success) {
8586
if (full_headers_message || m_download_state == State::REDOWNLOAD) {
8687
// A full headers message means the peer may have more to give us;
@@ -101,8 +102,8 @@ HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
101102
// gets big enough (meaning that we've checked enough commitments),
102103
// we'll return a batch of headers to the caller for processing.
103104
ret.success = true;
104-
for (const auto& hdr : received_headers) {
105-
if (!ValidateAndStoreRedownloadedHeader(hdr)) {
105+
for (size_t i = 0; i < received_headers.size(); ++i) {
106+
if (!ValidateAndStoreRedownloadedHeader(received_headers[i], hashes[i])) {
106107
// Something went wrong -- the peer gave us an unexpected chain.
107108
// We could consider looking at the reason for failure and
108109
// punishing the peer, but for now just give up on sync.
@@ -136,12 +137,14 @@ HeadersSyncState::ProcessingResult HeadersSyncState::ProcessNextHeaders(const
136137
return ret;
137138
}
138139

139-
bool HeadersSyncState::ValidateAndStoreHeadersCommitments(const std::vector<CBlockHeader>& headers)
140+
bool HeadersSyncState::ValidateAndStoreHeadersCommitments(const std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes)
140141
{
141142
// The caller should not give us an empty set of headers.
142143
Assume(headers.size() > 0);
143144
if (headers.size() == 0) return true;
144145

146+
Assume(headers.size() == hashes.size());
147+
145148
Assume(m_download_state == State::PRESYNC);
146149
if (m_download_state != State::PRESYNC) return false;
147150

@@ -156,8 +159,8 @@ bool HeadersSyncState::ValidateAndStoreHeadersCommitments(const std::vector<CBlo
156159

157160
// If it does connect, (minimally) validate and occasionally store
158161
// commitments.
159-
for (const auto& hdr : headers) {
160-
if (!ValidateAndProcessSingleHeader(hdr)) {
162+
for (size_t i = 0; i < headers.size(); ++i) {
163+
if (!ValidateAndProcessSingleHeader(headers[i], hashes[i])) {
161164
return false;
162165
}
163166
}
@@ -174,7 +177,7 @@ bool HeadersSyncState::ValidateAndStoreHeadersCommitments(const std::vector<CBlo
174177
return true;
175178
}
176179

177-
bool HeadersSyncState::ValidateAndProcessSingleHeader(const CBlockHeader& current)
180+
bool HeadersSyncState::ValidateAndProcessSingleHeader(const CBlockHeader& current, const uint256& hash)
178181
{
179182
Assume(m_download_state == State::PRESYNC);
180183
if (m_download_state != State::PRESYNC) return false;
@@ -194,7 +197,7 @@ bool HeadersSyncState::ValidateAndProcessSingleHeader(const CBlockHeader& curren
194197

195198
if (next_height % HEADER_COMMITMENT_PERIOD == m_commit_offset) {
196199
// Add a commitment.
197-
m_header_commitments.push_back(m_hasher(current.GetHash()) & 1);
200+
m_header_commitments.push_back(m_hasher(hash) & 1);
198201
if (m_header_commitments.size() > m_max_commitments) {
199202
// The peer's chain is too long; give up.
200203
// It's possible the chain grew since we started the sync; so
@@ -212,7 +215,7 @@ bool HeadersSyncState::ValidateAndProcessSingleHeader(const CBlockHeader& curren
212215
return true;
213216
}
214217

215-
bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& header)
218+
bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& header, const uint256& hash)
216219
{
217220
Assume(m_download_state == State::REDOWNLOAD);
218221
if (m_download_state != State::REDOWNLOAD) return false;
@@ -260,7 +263,7 @@ bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& he
260263
// we've run out of commitments.
261264
return false;
262265
}
263-
bool commitment = m_hasher(header.GetHash()) & 1;
266+
bool commitment = m_hasher(hash) & 1;
264267
bool expected_commitment = m_header_commitments.front();
265268
m_header_commitments.pop_front();
266269
if (commitment != expected_commitment) {
@@ -272,7 +275,7 @@ bool HeadersSyncState::ValidateAndStoreRedownloadedHeader(const CBlockHeader& he
272275
// Store this header for later processing.
273276
m_redownloaded_headers.push_back(header);
274277
m_redownload_buffer_last_height = next_height;
275-
m_redownload_buffer_last_hash = header.GetHash();
278+
m_redownload_buffer_last_hash = hash;
276279

277280
return true;
278281
}

src/headerssync.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class HeadersSyncState {
166166
* NextHeadersRequestLocator and send a getheaders message using it.
167167
*/
168168
ProcessingResult ProcessNextHeaders(const std::vector<CBlockHeader>&
169-
received_headers, bool full_headers_message);
169+
received_headers, const std::vector<uint256>& hashes, bool full_headers_message);
170170

171171
/** Issue the next GETHEADERS message to our peer.
172172
*
@@ -188,14 +188,14 @@ class HeadersSyncState {
188188
* processed headers.
189189
* On failure, this invokes Finalize() and returns false.
190190
*/
191-
bool ValidateAndStoreHeadersCommitments(const std::vector<CBlockHeader>& headers);
191+
bool ValidateAndStoreHeadersCommitments(const std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes);
192192

193193
/** In PRESYNC, process and update state for a single header */
194-
bool ValidateAndProcessSingleHeader(const CBlockHeader& current);
194+
bool ValidateAndProcessSingleHeader(const CBlockHeader& current, const uint256& hash);
195195

196196
/** In REDOWNLOAD, check a header's commitment (if applicable) and add to
197197
* buffer for later processing */
198-
bool ValidateAndStoreRedownloadedHeader(const CBlockHeader& header);
198+
bool ValidateAndStoreRedownloadedHeader(const CBlockHeader& header, const uint256& hash);
199199

200200
/** Return a set of headers that satisfy our proof-of-work threshold */
201201
std::vector<CBlockHeader> PopHeadersReadyForAcceptance();

src/net_processing.cpp

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -772,14 +772,15 @@ class PeerManagerImpl final : public PeerManager
772772
*/
773773
void ProcessHeadersMessage(CNode& pfrom, Peer& peer,
774774
std::vector<CBlockHeader>&& headers,
775+
std::vector<uint256>&& hashes,
775776
bool via_compact_block, bool uses_compressed)
776777
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
777778
[[nodiscard]] MessageProcessingResult ProcessPlatformBanMessage(NodeId node, std::string_view msg_type, CDataStream& vRecv)
778779
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
779780

780781
/** Various helpers for headers processing, invoked by ProcessHeadersMessage() */
781782
/** Return true if headers are continuous and have valid proof-of-work (DoS points assigned on failure) */
782-
bool CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams, CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
783+
bool CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, const Consensus::Params& consensusParams, CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
783784
/** Calculate an anti-DoS work threshold for headers chains */
784785
arith_uint256 GetAntiDoSWorkThreshold();
785786
/** Deal with state tracking and headers sync for peers that send the
@@ -788,7 +789,7 @@ class PeerManagerImpl final : public PeerManager
788789
void HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer, const std::vector<CBlockHeader>& headers)
789790
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex);
790791
/** Return true if the headers connect to each other, false otherwise */
791-
bool CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers) const;
792+
bool CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes) const;
792793
/** Try to continue a low-work headers sync that has already begun.
793794
* Assumes the caller has already verified the headers connect, and has
794795
* checked that each header satisfies the proof-of-work target included in
@@ -808,7 +809,7 @@ class PeerManagerImpl final : public PeerManager
808809
* acceptance by the caller).
809810
*/
810811
bool IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom,
811-
std::vector<CBlockHeader>& headers, bool uses_compressed)
812+
std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, bool uses_compressed)
812813
EXCLUSIVE_LOCKS_REQUIRED(peer.m_headers_sync_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
813814
/** Check work on a headers chain to be processed, and if insufficient,
814815
* initiate our anti-DoS headers sync mechanism.
@@ -823,7 +824,7 @@ class PeerManagerImpl final : public PeerManager
823824
*/
824825
bool TryLowWorkHeadersSync(Peer& peer, CNode& pfrom,
825826
const CBlockIndex* chain_start_header,
826-
std::vector<CBlockHeader>& headers, bool uses_compressed)
827+
std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, bool uses_compressed)
827828
EXCLUSIVE_LOCKS_REQUIRED(!peer.m_headers_sync_mutex, !m_peer_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
828829

829830
/** Return true if the given header is an ancestor of
@@ -3196,16 +3197,16 @@ void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, const CBlock& block, c
31963197
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCKTXN, resp));
31973198
}
31983199

3199-
bool PeerManagerImpl::CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams, CNode& pfrom)
3200+
bool PeerManagerImpl::CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, const Consensus::Params& consensusParams, CNode& pfrom)
32003201
{
32013202
// Do these headers have proof-of-work matching what's claimed?
3202-
if (!HasValidProofOfWork(headers, consensusParams)) {
3203+
if (!HasValidProofOfWork(headers, hashes, consensusParams)) {
32033204
Misbehaving(pfrom.GetId(), 100, "header with invalid proof of work");
32043205
return false;
32053206
}
32063207

32073208
// Are these headers connected to each other?
3208-
if (!CheckHeadersAreContinuous(headers)) {
3209+
if (!CheckHeadersAreContinuous(headers, hashes)) {
32093210
Misbehaving(pfrom.GetId(), 20, "non-continuous headers sequence");
32103211
return false;
32113212
}
@@ -3266,22 +3267,21 @@ void PeerManagerImpl::HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer,
32663267
}
32673268
}
32683269

3269-
bool PeerManagerImpl::CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers) const
3270+
bool PeerManagerImpl::CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes) const
32703271
{
3271-
uint256 hashLastBlock;
3272-
for (const CBlockHeader& header : headers) {
3273-
if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
3272+
assert(headers.size() == hashes.size());
3273+
for (size_t i = 1; i < headers.size(); ++i) {
3274+
if (headers[i].hashPrevBlock != hashes[i - 1]) {
32743275
return false;
32753276
}
3276-
hashLastBlock = header.GetHash();
32773277
}
32783278
return true;
32793279
}
32803280

3281-
bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom, std::vector<CBlockHeader>& headers, bool uses_compressed)
3281+
bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom, std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, bool uses_compressed)
32823282
{
32833283
if (peer.m_headers_sync) {
3284-
auto result = peer.m_headers_sync->ProcessNextHeaders(headers, headers.size() == GetHeadersLimit(pfrom, uses_compressed));
3284+
auto result = peer.m_headers_sync->ProcessNextHeaders(headers, hashes, headers.size() == GetHeadersLimit(pfrom, uses_compressed));
32853285
if (result.request_more) {
32863286
auto locator = peer.m_headers_sync->NextHeadersRequestLocator();
32873287
// If we were instructed to ask for a locator, it should not be empty.
@@ -3365,7 +3365,7 @@ bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfro
33653365
return false;
33663366
}
33673367

3368-
bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlockIndex* chain_start_header, std::vector<CBlockHeader>& headers, bool uses_compressed)
3368+
bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlockIndex* chain_start_header, std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, bool uses_compressed)
33693369
{
33703370
// Calculate the total work on this chain.
33713371
arith_uint256 total_work = chain_start_header->nChainWork + CalculateHeadersWork(headers);
@@ -3397,7 +3397,7 @@ bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlo
33973397
// Now a HeadersSyncState object for tracking this synchronization
33983398
// is created, process the headers using it as normal. Failures are
33993399
// handled inside of IsContinuationOfLowWorkHeadersSync.
3400-
(void)IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers, uses_compressed);
3400+
(void)IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers, hashes, uses_compressed);
34013401
} else {
34023402
LogPrint(BCLog::NET, "Ignoring low-work chain (height=%u) from peer=%d\n", chain_start_header->nHeight + headers.size(), pfrom.GetId());
34033403
}
@@ -3568,8 +3568,10 @@ void PeerManagerImpl::UpdatePeerStateForReceivedHeaders(CNode& pfrom,
35683568

35693569
void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
35703570
std::vector<CBlockHeader>&& headers,
3571+
std::vector<uint256>&& hashes,
35713572
bool via_compact_block, bool uses_compressed)
35723573
{
3574+
Assume(headers.size() == hashes.size());
35733575
size_t nCount = headers.size();
35743576

35753577
if (nCount == 0) {
@@ -3590,7 +3592,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
35903592
// We'll rely on headers having valid proof-of-work further down, as an
35913593
// anti-DoS criteria (note: this check is required before passing any
35923594
// headers into HeadersSyncState).
3593-
if (!CheckHeadersPoW(headers, m_chainparams.GetConsensus(), pfrom)) {
3595+
if (!CheckHeadersPoW(headers, hashes, m_chainparams.GetConsensus(), pfrom)) {
35943596
// Misbehaving() calls are handled within CheckHeadersPoW(), so we can
35953597
// just return. (Note that even if a header is announced via compact
35963598
// block, the header itself should be valid, so this type of error can
@@ -3612,7 +3614,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
36123614
{
36133615
LOCK(peer.m_headers_sync_mutex);
36143616

3615-
already_validated_work = IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers, uses_compressed);
3617+
already_validated_work = IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers, hashes, uses_compressed);
36163618

36173619
// The headers we passed in may have been:
36183620
// - untouched, perhaps if no headers-sync was in progress, or some
@@ -3654,7 +3656,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
36543656
const CBlockIndex *last_received_header{nullptr};
36553657
{
36563658
LOCK(cs_main);
3657-
last_received_header = m_chainman.m_blockman.LookupBlockIndex(headers.back().GetHash());
3659+
last_received_header = m_chainman.m_blockman.LookupBlockIndex(hashes.back());
36583660
if (IsAncestorOfBestHeaderOrTip(last_received_header)) {
36593661
already_validated_work = true;
36603662
}
@@ -3671,7 +3673,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
36713673
// Do anti-DoS checks to determine if we should process or store for later
36723674
// processing.
36733675
if (!already_validated_work && TryLowWorkHeadersSync(peer, pfrom,
3674-
chain_start_header, headers, uses_compressed)) {
3676+
chain_start_header, headers, hashes, uses_compressed)) {
36753677
// If we successfully started a low-work headers sync, then there
36763678
// should be no headers to process any further.
36773679
Assume(headers.empty());
@@ -5364,7 +5366,7 @@ void PeerManagerImpl::ProcessMessage(
53645366
// the peer if the header turns out to be for an invalid block.
53655367
// Note that if a peer tries to build on an invalid chain, that
53665368
// will be detected and the peer will be disconnected/discouraged.
5367-
return ProcessHeadersMessage(pfrom, *peer, {cmpctblock.header}, /*via_compact_block=*/true, UsesCompressedHeaders(*peer));
5369+
return ProcessHeadersMessage(pfrom, *peer, {cmpctblock.header}, {cmpctblock.header.GetHash()}, /*via_compact_block=*/true, UsesCompressedHeaders(*peer));
53685370
}
53695371

53705372
if (fBlockReconstructed) {
@@ -5507,7 +5509,14 @@ void PeerManagerImpl::ProcessMessage(
55075509
}
55085510
}
55095511

5510-
ProcessHeadersMessage(pfrom, *peer, std::move(headers), /*via_compact_block=*/false, msg_type == NetMsgType::HEADERS2);
5512+
// Pre-compute each header's hash once so downstream PRESYNC/REDOWNLOAD/
5513+
// CheckHeadersPoW/CheckHeadersAreContinuous reuse it instead of
5514+
// recomputing X11 multiple times per header.
5515+
std::vector<uint256> hashes;
5516+
hashes.reserve(headers.size());
5517+
for (const CBlockHeader& h : headers) hashes.push_back(h.GetHash());
5518+
5519+
ProcessHeadersMessage(pfrom, *peer, std::move(headers), std::move(hashes), /*via_compact_block=*/false, msg_type == NetMsgType::HEADERS2);
55115520

55125521
// Check if the headers presync progress needs to be reported to validation.
55135522
// This needs to be done without holding the m_headers_presync_mutex lock.

0 commit comments

Comments
 (0)