From 6eb5ba569141b722a5e27ef36e04994886769c44 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] refactor: extract shared `SipHash` state into `SipHashState` Split the repeated `SipHash` v[0..3] initialization into a small `SipHashState` helper that is used by both `CSipHasher` and `PresaltedSipHasher`. Added explanatory comments to clarify behavior, documenting the equivalence of `PresaltedSipHasher` `operator()` overloads to `CSipHasher` usage. Co-authored-by: Ryan Ofsky --- src/crypto/siphash.cpp | 50 ++++++++++++++++++------------------------ src/crypto/siphash.h | 45 +++++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp index 49cc234993e..89dbad6f44f 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 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 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 b81b9557dec..2f28473a4f1 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 #include #include 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} {} - /** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */ + std::array v{}; +}; + +/** 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; };