optimization: introduce `PresaltedSipHasher` for repeated hashing
What changed, and why it matters
This is a routine performance optimization for Bitcoin Core's hash tables. It replaces a function that re-computed part of its internal state on every call with a small helper class that pre-computes and reuses that state. The change is purely about speed and does not alter the final hash results. There is no indication it fixes a security bug or introduces a vulnerability.
No security action required. Treat as a normal code-quality/performance change. Reviewers may optionally verify that the cached initial state matches the original CSipHasher constructor constants and that the operator() logic is identical to the removed function body.
Security signals we found
No security-relevant signals present in the commit message or diff.
Change is explicitly described as an optimization by the author.
Existing test coverage is updated to preserve correctness invariants.
No new dependencies, network exposure, or privilege changes.
Evidence from the diff
The commit refactors the SipHash-2-4 implementation for uint256 values. It removes the free function SipHashUint256Extra(k0, k1, val, extra) and introduces a PresaltedSipHasher class that stores the key-xored initial state v[0..3] in its constructor. The operator() then continues the SipHash rounds from that cached state. Callers such as SaltedOutpointHasher now keep a PresaltedSipHasher member instead of raw k0/k1 keys. Unit and fuzz tests are updated to exercise the new API and assert equivalence with the existing CSipHasher path. The diff shows no algorithmic change, no new input handling, and no memory-safety or cryptographic boundary changes.
Changed components
src/crypto/siphash.cppsrc/crypto/siphash.hsrc/util/hasher.hsrc/util/hasher.cppsrc/test/hash_tests.cppsrc/test/fuzz/integer.cppInspect captured patch +31 / −22
diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp
index 37ed9189..a6029716 100644
--- a/src/crypto/siphash.cpp
+++ b/src/crypto/siphash.cpp
@@ -137,17 +137,12 @@ uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val)
return v0 ^ v1 ^ v2 ^ v3;
}
-uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra)
+/** Specialized implementation for efficiency */
+uint64_t PresaltedSipHasher::operator()(const uint256& val, uint32_t extra) const noexcept
{
- /* Specialized implementation for efficiency */
+ uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
uint64_t d = val.GetUint64(0);
-
- // TODO moved in followup commit
- uint64_t v0 = CSipHasher::C0 ^ k0;
- uint64_t v1 = CSipHasher::C1 ^ k1;
- uint64_t v2 = CSipHasher::C2 ^ k0;
- uint64_t v3 = CSipHasher::C3 ^ k1 ^ d;
-
+ v3 ^= d;
SIPROUND;
SIPROUND;
v0 ^= d;
diff --git a/src/crypto/siphash.h b/src/crypto/siphash.h
index 4293fdf9..a4aeb3ae 100644
--- a/src/crypto/siphash.h
+++ b/src/crypto/siphash.h
@@ -48,6 +48,20 @@ public:
* .Finalize()
*/
uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val);
-uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra);
+
+class PresaltedSipHasher
+{
+ uint64_t v[4];
+
+public:
+ explicit PresaltedSipHasher(uint64_t k0, uint64_t k1) noexcept {
+ v[0] = CSipHasher::C0 ^ k0;
+ v[1] = CSipHasher::C1 ^ k1;
+ v[2] = CSipHasher::C2 ^ k0;
+ v[3] = CSipHasher::C3 ^ k1;
+ }
+
+ uint64_t operator()(const uint256& val, uint32_t extra) const noexcept;
+};
#endif // BITCOIN_CRYPTO_SIPHASH_H
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 2e13d422..81516fd9 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -119,7 +119,7 @@ FUZZ_TARGET(integer, .init = initialize_integer)
(void)MillisToTimeval(i64);
(void)SighashToStr(uch);
(void)SipHashUint256(u64, u64, u256);
- (void)SipHashUint256Extra(u64, u64, u256, u32);
+ (void)PresaltedSipHasher(u64, u64)(u256, u32);
(void)ToLower(ch);
(void)ToUpper(ch);
{
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 46901893..d4592df7 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(siphash)
ss << TX_WITH_WITNESS(tx);
BOOST_CHECK_EQUAL(SipHashUint256(1, 2, ss.GetHash()), 0x79751e980c2a0a35ULL);
- // Check consistency between CSipHasher and SipHashUint256[Extra].
+ // Check consistency between CSipHasher and SipHashUint256 and PresaltedSipHasher.
FastRandomContext ctx;
for (int i = 0; i < 16; ++i) {
uint64_t k0 = ctx.rand64();
@@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE(siphash)
uint8_t nb[4];
WriteLE32(nb, n);
sip288.Write(nb);
- BOOST_CHECK_EQUAL(SipHashUint256Extra(k0, k1, x, n), sip288.Finalize()); // TODO modified in follow-up commit
+ BOOST_CHECK_EQUAL(PresaltedSipHasher(k0, k1)(x, n), sip288.Finalize());
}
}
diff --git a/src/util/hasher.cpp b/src/util/hasher.cpp
index c4051ae0..cac602a8 100644
--- a/src/util/hasher.cpp
+++ b/src/util/hasher.cpp
@@ -19,14 +19,15 @@ SaltedWtxidHasher::SaltedWtxidHasher() :
k0{FastRandomContext().rand64()},
k1{FastRandomContext().rand64()} {}
-SaltedOutpointHasher::SaltedOutpointHasher(bool deterministic) :
- k0{deterministic ? 0x8e819f2607a18de6 : FastRandomContext().rand64()},
- k1{deterministic ? 0xf4020d2e3983b0eb : FastRandomContext().rand64()}
+SaltedOutpointHasher::SaltedOutpointHasher(bool deterministic) : m_hasher{
+ deterministic ? 0x8e819f2607a18de6 : FastRandomContext().rand64(),
+ deterministic ? 0xf4020d2e3983b0eb : FastRandomContext().rand64()}
{}
SaltedSipHasher::SaltedSipHasher() :
m_k0{FastRandomContext().rand64()},
- m_k1{FastRandomContext().rand64()} {}
+ m_k1{FastRandomContext().rand64()}
+{}
size_t SaltedSipHasher::operator()(const std::span<const unsigned char>& script) const
{
diff --git a/src/util/hasher.h b/src/util/hasher.h
index be5d9eb1..e7e69530 100644
--- a/src/util/hasher.h
+++ b/src/util/hasher.h
@@ -60,9 +60,7 @@ public:
class SaltedOutpointHasher
{
-private:
- /** Salt */
- const uint64_t k0, k1;
+ const PresaltedSipHasher m_hasher;
public:
SaltedOutpointHasher(bool deterministic = false);
@@ -76,8 +74,9 @@ public:
*
* @see https://gcc.gnu.org/onlinedocs/gcc-13.2.0/libstdc++/manual/manual/unordered_associative.html
*/
- size_t operator()(const COutPoint& id) const noexcept {
- return SipHashUint256Extra(k0, k1, id.hash.ToUint256(), id.n);
+ size_t operator()(const COutPoint& id) const noexcept
+ {
+ return m_hasher(id.hash.ToUint256(), id.n);
}
};
Why this scored 18/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.