refactor: extract shared `SipHash` state into `SipHashState`
What changed, and why it matters
This commit is a straightforward code cleanup: it pulls the repeated starting-state setup for Bitcoin's SipHash implementation into a shared helper class called SipHashState. The actual math and behavior of the hash functions do not change. There is no security bug being fixed here.
No security action needed; review as normal refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors CSipHasher and PresaltedSipHasher so both use a new SipHashState helper to initialize the SipHash v[0..3] constants from the key. Member variables are renamed (v→m_state.v, tmp→m_tmp, count→m_count) and constants C0..C3 move from CSipHasher into SipHashState. The hashing rounds, byte handling, and finalization logic are preserved exactly. Comments are added to document that PresaltedSipHasher overloads are equivalent to corresponding CSipHasher usage.
Changed components
src/crypto/siphash.cppsrc/crypto/siphash.hInspect captured patch +47 / −48
diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp
index 49cc2349..89dbad6f 100644
--- a/src/crypto/siphash.cpp
+++ b/src/crypto/siphash.cpp
@@ -19,41 +19,33 @@
v2 = std::rotl(v2, 32); \
} while (0)
-CSipHasher::CSipHasher(uint64_t k0, uint64_t k1)
-{
- v[0] = C0 ^ k0;
- v[1] = C1 ^ k1;
- v[2] = C2 ^ k0;
- v[3] = C3 ^ k1;
- count = 0;
- tmp = 0;
-}
+CSipHasher::CSipHasher(uint64_t k0, uint64_t k1) : m_state{k0, k1} {}
CSipHasher& CSipHasher::Write(uint64_t data)
{
- uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
+ uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
- assert(count % 8 == 0);
+ assert(m_count % 8 == 0);
v3 ^= data;
SIPROUND;
SIPROUND;
v0 ^= data;
- v[0] = v0;
- v[1] = v1;
- v[2] = v2;
- v[3] = v3;
+ m_state.v[0] = v0;
+ m_state.v[1] = v1;
+ m_state.v[2] = v2;
+ m_state.v[3] = v3;
- count += 8;
+ m_count += 8;
return *this;
}
CSipHasher& CSipHasher::Write(std::span<const unsigned char> data)
{
- uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
- uint64_t t = tmp;
- uint8_t c = count;
+ uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
+ uint64_t t = m_tmp;
+ uint8_t c = m_count;
while (data.size() > 0) {
t |= uint64_t{data.front()} << (8 * (c % 8));
@@ -68,21 +60,21 @@ CSipHasher& CSipHasher::Write(std::span<const unsigned char> data)
data = data.subspan(1);
}
- v[0] = v0;
- v[1] = v1;
- v[2] = v2;
- v[3] = v3;
- count = c;
- tmp = t;
+ m_state.v[0] = v0;
+ m_state.v[1] = v1;
+ m_state.v[2] = v2;
+ m_state.v[3] = v3;
+ m_count = c;
+ m_tmp = t;
return *this;
}
uint64_t CSipHasher::Finalize() const
{
- uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
+ uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
- uint64_t t = tmp | (((uint64_t)count) << 56);
+ uint64_t t = m_tmp | (((uint64_t)m_count) << 56);
v3 ^= t;
SIPROUND;
@@ -98,7 +90,7 @@ uint64_t CSipHasher::Finalize() const
uint64_t PresaltedSipHasher::operator()(const uint256& val) const noexcept
{
- uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
+ uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
uint64_t d = val.GetUint64(0);
v3 ^= d;
@@ -135,7 +127,7 @@ uint64_t PresaltedSipHasher::operator()(const uint256& val) const noexcept
/** Specialized implementation for efficiency */
uint64_t PresaltedSipHasher::operator()(const uint256& val, uint32_t extra) const noexcept
{
- uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
+ uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
uint64_t d = val.GetUint64(0);
v3 ^= d;
SIPROUND;
diff --git a/src/crypto/siphash.h b/src/crypto/siphash.h
index b81b9557..2f28473a 100644
--- a/src/crypto/siphash.h
+++ b/src/crypto/siphash.h
@@ -5,28 +5,34 @@
#ifndef BITCOIN_CRYPTO_SIPHASH_H
#define BITCOIN_CRYPTO_SIPHASH_H
+#include <array>
#include <cstdint>
#include <span>
class uint256;
-/** SipHash-2-4 */
-class CSipHasher
+/** Shared SipHash internal state v[0..3], initialized from (k0, k1). */
+class SipHashState
{
-private:
- uint64_t v[4];
- uint64_t tmp;
- uint8_t count; // Only the low 8 bits of the input size matter.
+ static constexpr uint64_t C0{0x736f6d6570736575ULL}, C1{0x646f72616e646f6dULL}, C2{0x6c7967656e657261ULL}, C3{0x7465646279746573ULL};
public:
- static constexpr uint64_t C0{0x736f6d6570736575ULL};
- static constexpr uint64_t C1{0x646f72616e646f6dULL};
- static constexpr uint64_t C2{0x6c7967656e657261ULL};
- static constexpr uint64_t C3{0x7465646279746573ULL};
+ explicit SipHashState(uint64_t k0, uint64_t k1) noexcept : v{C0 ^ k0, C1 ^ k1, C2 ^ k0, C3 ^ k1} {}
+
+ std::array<uint64_t, 4> v{};
+};
- /** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */
+/** General SipHash-2-4 implementation. */
+class CSipHasher
+{
+ SipHashState m_state;
+ uint64_t m_tmp{0};
+ uint8_t m_count{0}; //!< Only the low 8 bits of the input size matter.
+
+public:
+ /** Construct a SipHash calculator initialized with 128-bit key (k0, k1). */
CSipHasher(uint64_t k0, uint64_t k1);
- /** Hash a 64-bit integer worth of data
+ /** Hash a 64-bit integer worth of data.
* It is treated as if this was the little-endian interpretation of 8 bytes.
* This function can only be used when a multiple of 8 bytes have been written so far.
*/
@@ -48,17 +54,18 @@ public:
*/
class PresaltedSipHasher
{
- uint64_t v[4];
+ const SipHashState m_state;
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;
- }
+ explicit PresaltedSipHasher(uint64_t k0, uint64_t k1) noexcept : m_state{k0, k1} {}
+ /** Equivalent to CSipHasher(k0, k1).Write(val).Finalize(). */
uint64_t operator()(const uint256& val) const noexcept;
+
+ /**
+ * Equivalent to CSipHasher(k0, k1).Write(val).Write(extra).Finalize(),
+ * with `extra` encoded as 4 little-endian bytes.
+ */
uint64_t operator()(const uint256& val, uint32_t extra) const noexcept;
};
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.