optimization: cache `PresaltedSipHasher` in `CBlockHeaderAndShortTxIDs`
What changed, and why it matters
This is a small performance cleanup, not a security fix. It replaces two stored hash-key numbers with a pre-initialized SipHash object so the same hashing state can be reused instead of rebuilt each time. The added assertion only makes an existing assumption explicit: the object must be initialized before use. There is no vulnerability being patched.
No security action needed. Treat as a normal code-quality/optimization change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors CBlockHeaderAndShortTxIDs to cache a std::optional
Changed components
src/blockencodings.cppsrc/blockencodings.hCBlockHeaderAndShortTxIDscompact block short transaction ID generationInspect captured patch +16 / −13
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 55c8f8a0..d48ba400 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -17,11 +17,14 @@
#include <unordered_map>
-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 133724b6..124df50a 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_BLOCKENCODINGS_H
#define BITCOIN_BLOCKENCODINGS_H
+#include <crypto/siphash.h>
#include <primitives/block.h>
#include <functional>
@@ -87,8 +88,7 @@ typedef enum ReadStatus_t
} ReadStatus;
class CBlockHeaderAndShortTxIDs {
-private:
- mutable uint64_t shorttxidk0, shorttxidk1;
+ mutable std::optional<PresaltedSipHasher> 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;
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.