Skip to content

Commit 0b72ede

Browse files
committed
perf: pre-calculate hash of header once , apply #7272 for pre-sync feature
1 parent ef9894d commit 0b72ede

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
@@ -771,14 +771,15 @@ class PeerManagerImpl final : public PeerManager
771771
*/
772772
void ProcessHeadersMessage(CNode& pfrom, Peer& peer,
773773
std::vector<CBlockHeader>&& headers,
774+
std::vector<uint256>&& hashes,
774775
bool via_compact_block, bool uses_compressed)
775776
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
776777
[[nodiscard]] MessageProcessingResult ProcessPlatformBanMessage(NodeId node, std::string_view msg_type, CDataStream& vRecv)
777778
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
778779

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

828829
/** Return true if the given header is an ancestor of
@@ -3162,16 +3163,16 @@ void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, const CBlock& block, c
31623163
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCKTXN, resp));
31633164
}
31643165

3165-
bool PeerManagerImpl::CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams, CNode& pfrom)
3166+
bool PeerManagerImpl::CheckHeadersPoW(const std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, const Consensus::Params& consensusParams, CNode& pfrom)
31663167
{
31673168
// Do these headers have proof-of-work matching what's claimed?
3168-
if (!HasValidProofOfWork(headers, consensusParams)) {
3169+
if (!HasValidProofOfWork(headers, hashes, consensusParams)) {
31693170
Misbehaving(pfrom.GetId(), 100, "header with invalid proof of work");
31703171
return false;
31713172
}
31723173

31733174
// Are these headers connected to each other?
3174-
if (!CheckHeadersAreContinuous(headers)) {
3175+
if (!CheckHeadersAreContinuous(headers, hashes)) {
31753176
Misbehaving(pfrom.GetId(), 20, "non-continuous headers sequence");
31763177
return false;
31773178
}
@@ -3232,22 +3233,21 @@ void PeerManagerImpl::HandleFewUnconnectingHeaders(CNode& pfrom, Peer& peer,
32323233
}
32333234
}
32343235

3235-
bool PeerManagerImpl::CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers) const
3236+
bool PeerManagerImpl::CheckHeadersAreContinuous(const std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes) const
32363237
{
3237-
uint256 hashLastBlock;
3238-
for (const CBlockHeader& header : headers) {
3239-
if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
3238+
assert(headers.size() == hashes.size());
3239+
for (size_t i = 1; i < headers.size(); ++i) {
3240+
if (headers[i].hashPrevBlock != hashes[i - 1]) {
32403241
return false;
32413242
}
3242-
hashLastBlock = header.GetHash();
32433243
}
32443244
return true;
32453245
}
32463246

3247-
bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom, std::vector<CBlockHeader>& headers, bool uses_compressed)
3247+
bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfrom, std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, bool uses_compressed)
32483248
{
32493249
if (peer.m_headers_sync) {
3250-
auto result = peer.m_headers_sync->ProcessNextHeaders(headers, headers.size() == GetHeadersLimit(pfrom, uses_compressed));
3250+
auto result = peer.m_headers_sync->ProcessNextHeaders(headers, hashes, headers.size() == GetHeadersLimit(pfrom, uses_compressed));
32513251
if (result.request_more) {
32523252
auto locator = peer.m_headers_sync->NextHeadersRequestLocator();
32533253
// If we were instructed to ask for a locator, it should not be empty.
@@ -3331,7 +3331,7 @@ bool PeerManagerImpl::IsContinuationOfLowWorkHeadersSync(Peer& peer, CNode& pfro
33313331
return false;
33323332
}
33333333

3334-
bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlockIndex* chain_start_header, std::vector<CBlockHeader>& headers, bool uses_compressed)
3334+
bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlockIndex* chain_start_header, std::vector<CBlockHeader>& headers, const std::vector<uint256>& hashes, bool uses_compressed)
33353335
{
33363336
// Calculate the total work on this chain.
33373337
arith_uint256 total_work = chain_start_header->nChainWork + CalculateHeadersWork(headers);
@@ -3363,7 +3363,7 @@ bool PeerManagerImpl::TryLowWorkHeadersSync(Peer& peer, CNode& pfrom, const CBlo
33633363
// Now a HeadersSyncState object for tracking this synchronization
33643364
// is created, process the headers using it as normal. Failures are
33653365
// handled inside of IsContinuationOfLowWorkHeadersSync.
3366-
(void)IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers, uses_compressed);
3366+
(void)IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers, hashes, uses_compressed);
33673367
} else {
33683368
LogPrint(BCLog::NET, "Ignoring low-work chain (height=%u) from peer=%d\n", chain_start_header->nHeight + headers.size(), pfrom.GetId());
33693369
}
@@ -3534,8 +3534,10 @@ void PeerManagerImpl::UpdatePeerStateForReceivedHeaders(CNode& pfrom,
35343534

35353535
void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
35363536
std::vector<CBlockHeader>&& headers,
3537+
std::vector<uint256>&& hashes,
35373538
bool via_compact_block, bool uses_compressed)
35383539
{
3540+
Assume(headers.size() == hashes.size());
35393541
size_t nCount = headers.size();
35403542

35413543
if (nCount == 0) {
@@ -3556,7 +3558,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
35563558
// We'll rely on headers having valid proof-of-work further down, as an
35573559
// anti-DoS criteria (note: this check is required before passing any
35583560
// headers into HeadersSyncState).
3559-
if (!CheckHeadersPoW(headers, m_chainparams.GetConsensus(), pfrom)) {
3561+
if (!CheckHeadersPoW(headers, hashes, m_chainparams.GetConsensus(), pfrom)) {
35603562
// Misbehaving() calls are handled within CheckHeadersPoW(), so we can
35613563
// just return. (Note that even if a header is announced via compact
35623564
// block, the header itself should be valid, so this type of error can
@@ -3578,7 +3580,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
35783580
{
35793581
LOCK(peer.m_headers_sync_mutex);
35803582

3581-
already_validated_work = IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers, uses_compressed);
3583+
already_validated_work = IsContinuationOfLowWorkHeadersSync(peer, pfrom, headers, hashes, uses_compressed);
35823584

35833585
// The headers we passed in may have been:
35843586
// - untouched, perhaps if no headers-sync was in progress, or some
@@ -3620,7 +3622,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
36203622
const CBlockIndex *last_received_header{nullptr};
36213623
{
36223624
LOCK(cs_main);
3623-
last_received_header = m_chainman.m_blockman.LookupBlockIndex(headers.back().GetHash());
3625+
last_received_header = m_chainman.m_blockman.LookupBlockIndex(hashes.back());
36243626
if (IsAncestorOfBestHeaderOrTip(last_received_header)) {
36253627
already_validated_work = true;
36263628
}
@@ -3637,7 +3639,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
36373639
// Do anti-DoS checks to determine if we should process or store for later
36383640
// processing.
36393641
if (!already_validated_work && TryLowWorkHeadersSync(peer, pfrom,
3640-
chain_start_header, headers, uses_compressed)) {
3642+
chain_start_header, headers, hashes, uses_compressed)) {
36413643
// If we successfully started a low-work headers sync, then there
36423644
// should be no headers to process any further.
36433645
Assume(headers.empty());
@@ -5330,7 +5332,7 @@ void PeerManagerImpl::ProcessMessage(
53305332
// the peer if the header turns out to be for an invalid block.
53315333
// Note that if a peer tries to build on an invalid chain, that
53325334
// will be detected and the peer will be disconnected/discouraged.
5333-
return ProcessHeadersMessage(pfrom, *peer, {cmpctblock.header}, /*via_compact_block=*/true, UsesCompressedHeaders(*peer));
5335+
return ProcessHeadersMessage(pfrom, *peer, {cmpctblock.header}, {cmpctblock.header.GetHash()}, /*via_compact_block=*/true, UsesCompressedHeaders(*peer));
53345336
}
53355337

53365338
if (fBlockReconstructed) {
@@ -5473,7 +5475,14 @@ void PeerManagerImpl::ProcessMessage(
54735475
}
54745476
}
54755477

5476-
ProcessHeadersMessage(pfrom, *peer, std::move(headers), /*via_compact_block=*/false, msg_type == NetMsgType::HEADERS2);
5478+
// Pre-compute each header's hash once so downstream PRESYNC/REDOWNLOAD/
5479+
// CheckHeadersPoW/CheckHeadersAreContinuous reuse it instead of
5480+
// recomputing X11 multiple times per header.
5481+
std::vector<uint256> hashes;
5482+
hashes.reserve(headers.size());
5483+
for (const CBlockHeader& h : headers) hashes.push_back(h.GetHash());
5484+
5485+
ProcessHeadersMessage(pfrom, *peer, std::move(headers), std::move(hashes), /*via_compact_block=*/false, msg_type == NetMsgType::HEADERS2);
54775486

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

0 commit comments

Comments
 (0)