From 118d22ddb4ba6af6cd54204dda579c2ff9a70c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Tue, 30 Sep 2025 19:03:02 -0400 Subject: [PATCH] optimization: cache `PresaltedSipHasher` in `CBlockHeaderAndShortTxIDs` Replaces separate `shorttxidk0`/`shorttxidk1` members with a cached `PresaltedSipHasher`, so `GetShortID()` reuses the precomputed `SipHash` state instead of rebuilding it on every call. `CBlockHeaderAndShortTxIDs` was never intended to be used before `FillShortTxIDSelector()` runs; doing so already relied on indeterminate salt values. The new `Assert(m_hasher)` just makes this invariant explicit and fails fast if the object is used in an uninitialized state. --- src/blockencodings.cpp | 23 +++++++++++++---------- src/blockencodings.h | 6 +++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index 55c8f8a056f..d48ba400a1e 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -17,11 +17,14 @@ #include -CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, const uint64_t nonce) : - nonce(nonce), - shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) { +CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, uint64_t nonce) + : nonce(nonce), + shorttxids(block.vtx.size() - 1), + prefilledtxn(1), + header(block) +{ FillShortTxIDSelector(); - //TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase + // TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase prefilledtxn[0] = {0, block.vtx[0]}; for (size_t i = 1; i < block.vtx.size(); i++) { const CTransaction& tx = *block.vtx[i]; @@ -29,21 +32,21 @@ CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, const } } -void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const { +void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const +{ DataStream stream{}; stream << header << nonce; CSHA256 hasher; hasher.Write((unsigned char*)&(*stream.begin()), stream.end() - stream.begin()); uint256 shorttxidhash; hasher.Finalize(shorttxidhash.begin()); - shorttxidk0 = shorttxidhash.GetUint64(0); - shorttxidk1 = shorttxidhash.GetUint64(1); + m_hasher.emplace(shorttxidhash.GetUint64(0), shorttxidhash.GetUint64(1)); } -uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const { +uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const +{ static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids"); - PresaltedSipHasher hasher(shorttxidk0, shorttxidk1); // TODO extract - return hasher(wtxid.ToUint256()) & 0xffffffffffffL; + return (*Assert(m_hasher))(wtxid.ToUint256()) & 0xffffffffffffL; } /* Reconstructing a compact block is in the hot-path for block relay, diff --git a/src/blockencodings.h b/src/blockencodings.h index 133724b64e8..124df50a3d3 100644 --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_BLOCKENCODINGS_H #define BITCOIN_BLOCKENCODINGS_H +#include #include #include @@ -87,8 +88,7 @@ typedef enum ReadStatus_t } ReadStatus; class CBlockHeaderAndShortTxIDs { -private: - mutable uint64_t shorttxidk0, shorttxidk1; + mutable std::optional m_hasher; uint64_t nonce; void FillShortTxIDSelector() const; @@ -112,7 +112,7 @@ public: /** * @param[in] nonce This should be randomly generated, and is used for the siphash secret key */ - CBlockHeaderAndShortTxIDs(const CBlock& block, const uint64_t nonce); + CBlockHeaderAndShortTxIDs(const CBlock& block, uint64_t nonce); uint64_t GetShortID(const Wtxid& wtxid) const;