Repository: bitcoin/bitcoin
File: src/crypto/siphash.h (C/C++)

Code before:
// Copyright (c) 2016-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CRYPTO_SIPHASH_H
#define BITCOIN_CRYPTO_SIPHASH_H

#include <cstdint>
#include <span>

class uint256;

/** SipHash-2-4 */
class CSipHasher
{
private:
    uint64_t v[4];
    uint64_t tmp;
    uint8_t count; // 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
     *  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.
     */
    CSipHasher& Write(uint64_t data);
    /** Hash arbitrary bytes. */
    CSipHasher& Write(std::span<const unsigned char> data);
    /** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */
    uint64_t Finalize() const;
};

/** Optimized SipHash-2-4 implementation for uint256.
 *
 *  It is identical to:
 *    SipHasher(k0, k1)
 *      .Write(val.GetUint64(0))
 *      .Write(val.GetUint64(1))
 *      .Write(val.GetUint64(2))
 *      .Write(val.GetUint64(3))
 *      .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);

#endif // BITCOIN_CRYPTO_SIPHASH_H


Review comment (refactor):
In commit "optimization: Introduce PresaltedSipHasher for repeated hashing" (8568340be2c679713703edc3d418272ac78d1147)

I can see this code is just being moved, not duplicated again, but since this does duplicate the CSipHasher constructor code, would seem good to just move and define once in a single place. 

Would suggest a change like the following (this change also makes PresaltedSipHasher state constant as suggested earlier):

```diff
--- a/src/crypto/siphash.cpp
+++ b/src/crypto/siphash.cpp
@@ -15,12 +15,8 @@
     v2 = std::rotl(v2, 32); \
 } while (0)
 
-CSipHasher::CSipHasher(uint64_t k0, uint64_t k1)
+CSipHasher::CSipHasher(uint64_t k0, uint64_t k1) : SipHashState{k0, k1}
 {
-    v[0] = C0 ^ k0;
-    v[1] = C1 ^ k1;
-    v[2] = C2 ^ k0;
-    v[3] = C3 ^ k1;
     count = 0;
     tmp = 0;
 }
@@ -136,7 +132,7 @@ uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val)
 /* 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;
--- a/src/crypto/siphash.h
+++ b/src/crypto/siphash.h
@@ -10,19 +10,27 @@
 #include <span.h>
 #include <uint256.h>
 
-/** SipHash-2-4 */
-class CSipHasher
+class SipHashState
 {
-private:
-    uint64_t v[4];
-    uint64_t tmp;
-    uint8_t count; // Only the low 8 bits of the input size matter.
-
 public:
+    SipHashState(uint64_t k0, uint64_t k1)  noexcept : v{C0 ^ k0, C1 ^ k1, C2 ^ k0, C3 ^ k1} {}
+
+    uint64_t v[4];
+
     static constexpr uint64_t C0{0x736f6d6570736575ULL};
     static constexpr uint64_t C1{0x646f72616e646f6dULL};
     static constexpr uint64_t C2{0x6c7967656e657261ULL};
     static constexpr uint64_t C3{0x7465646279746573ULL};
+};
+
+/** SipHash-2-4 */
+class CSipHasher : public SipHashState
+{
+private:
+    uint64_t tmp;
+    uint8_t count; // 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);
@@ -51,15 +59,10 @@ uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val);
 
 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} {}
 
     uint64_t operator()(const uint256& val, uint32_t extra) const noexcept;
 };
```








Code after:
     *  This function can only be used when a multiple of 8 bytes have been written so far.
     */
    CSipHasher& Write(uint64_t data);
    /** Hash arbitrary bytes. */
    CSipHasher& Write(std::span<const unsigned char> data);
    /** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */
    uint64_t Finalize() const;
};

/**
 * Optimized SipHash-2-4 implementation for uint256.
 *
 * This class caches the initial SipHash v[0..3] state derived from (k0, k1)
 * and implements a specialized hashing path for uint256 values, with or
 * without an extra 32-bit word. The internal state is immutable, so
 * PresaltedSipHasher instances can be reused for multiple hashes with the
 * same key.
 */
class PresaltedSipHasher
{
    const SipHashState m_state;

public:
    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;
};

#endif // BITCOIN_CRYPTO_SIPHASH_H