diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 2f1ff564388..05c7788d74b 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -193,13 +193,11 @@ static void SHA512(benchmark::Bench& bench) static void SipHash_32b(benchmark::Bench& bench) { FastRandomContext rng{/*fDeterministic=*/true}; - auto k0{rng.rand64()}, k1{rng.rand64()}; + PresaltedSipHasher presalted_sip_hasher(rng.rand64(), rng.rand64()); auto val{rng.rand256()}; auto i{0U}; bench.run([&] { - ankerl::nanobench::doNotOptimizeAway(SipHashUint256(k0, k1, val)); - ++k0; - ++k1; + ankerl::nanobench::doNotOptimizeAway(presalted_sip_hasher(val)); ++i; val.data()[i % uint256::size()] ^= i & 0xFF; }); diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index cf6da55aa9a..55c8f8a056f 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -42,7 +42,8 @@ void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const { uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const { static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids"); - return SipHashUint256(shorttxidk0, shorttxidk1, wtxid.ToUint256()) & 0xffffffffffffL; + PresaltedSipHasher hasher(shorttxidk0, shorttxidk1); // TODO extract + return hasher(wtxid.ToUint256()) & 0xffffffffffffL; } /* Reconstructing a compact block is in the hot-path for block relay, diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp index a60297162a8..49cc234993e 100644 --- a/src/crypto/siphash.cpp +++ b/src/crypto/siphash.cpp @@ -96,16 +96,11 @@ uint64_t CSipHasher::Finalize() const return v0 ^ v1 ^ v2 ^ v3; } -uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val) +uint64_t PresaltedSipHasher::operator()(const uint256& val) 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; diff --git a/src/crypto/siphash.h b/src/crypto/siphash.h index a4aeb3ae479..b81b9557dec 100644 --- a/src/crypto/siphash.h +++ b/src/crypto/siphash.h @@ -37,18 +37,15 @@ public: uint64_t Finalize() const; }; -/** Optimized SipHash-2-4 implementation for uint256. +/** + * 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() + * 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. */ -uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val); - class PresaltedSipHasher { uint64_t v[4]; @@ -61,6 +58,7 @@ public: v[3] = CSipHasher::C3 ^ k1; } + uint64_t operator()(const uint256& val) const noexcept; uint64_t operator()(const uint256& val, uint32_t extra) const noexcept; }; diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index 81516fd9f87..89c29dbcb70 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -118,7 +118,7 @@ FUZZ_TARGET(integer, .init = initialize_integer) } (void)MillisToTimeval(i64); (void)SighashToStr(uch); - (void)SipHashUint256(u64, u64, u256); + (void)PresaltedSipHasher(u64, u64)(u256); (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 d4592df7eeb..a5059a8fe8c 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(siphash) hasher.Write(0x2F2E2D2C2B2A2928ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull); - BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256{"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"}), 0x7127512f72f27cceull); + BOOST_CHECK_EQUAL(PresaltedSipHasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL)(uint256{"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"}), 0x7127512f72f27cceull); // Check test vectors from spec, one byte at a time CSipHasher hasher2(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); @@ -128,9 +128,9 @@ BOOST_AUTO_TEST_CASE(siphash) // and the test would be affected by default tx version bumps if not fixed. tx.version = 1; ss << TX_WITH_WITNESS(tx); - BOOST_CHECK_EQUAL(SipHashUint256(1, 2, ss.GetHash()), 0x79751e980c2a0a35ULL); + BOOST_CHECK_EQUAL(PresaltedSipHasher(1, 2)(ss.GetHash()), 0x79751e980c2a0a35ULL); - // Check consistency between CSipHasher and SipHashUint256 and PresaltedSipHasher. + // Check consistency between CSipHasher and PresaltedSipHasher. FastRandomContext ctx; for (int i = 0; i < 16; ++i) { uint64_t k0 = ctx.rand64(); @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(siphash) CSipHasher sip256(k0, k1); sip256.Write(x); - BOOST_CHECK_EQUAL(SipHashUint256(k0, k1, x), sip256.Finalize()); // TODO modified in follow-up commit + BOOST_CHECK_EQUAL(PresaltedSipHasher(k0, k1)(x), sip256.Finalize()); CSipHasher sip288 = sip256; uint32_t n = ctx.rand32(); diff --git a/src/util/hasher.cpp b/src/util/hasher.cpp index cac602a893f..3d5ffcf89b4 100644 --- a/src/util/hasher.cpp +++ b/src/util/hasher.cpp @@ -7,17 +7,20 @@ #include #include -SaltedUint256Hasher::SaltedUint256Hasher() : - k0{FastRandomContext().rand64()}, - k1{FastRandomContext().rand64()} {} +SaltedUint256Hasher::SaltedUint256Hasher() : m_hasher{ + FastRandomContext().rand64(), + FastRandomContext().rand64()} +{} -SaltedTxidHasher::SaltedTxidHasher() : - k0{FastRandomContext().rand64()}, - k1{FastRandomContext().rand64()} {} +SaltedTxidHasher::SaltedTxidHasher() : m_hasher{ + FastRandomContext().rand64(), + FastRandomContext().rand64()} +{} -SaltedWtxidHasher::SaltedWtxidHasher() : - k0{FastRandomContext().rand64()}, - k1{FastRandomContext().rand64()} {} +SaltedWtxidHasher::SaltedWtxidHasher() : m_hasher{ + FastRandomContext().rand64(), + FastRandomContext().rand64()} +{} SaltedOutpointHasher::SaltedOutpointHasher(bool deterministic) : m_hasher{ deterministic ? 0x8e819f2607a18de6 : FastRandomContext().rand64(), diff --git a/src/util/hasher.h b/src/util/hasher.h index e7e69530cc5..cdf2250bf3c 100644 --- a/src/util/hasher.h +++ b/src/util/hasher.h @@ -17,47 +17,43 @@ class SaltedUint256Hasher { -private: - /** Salt */ - const uint64_t k0, k1; + const PresaltedSipHasher m_hasher; public: SaltedUint256Hasher(); - size_t operator()(const uint256& hash) const { - return SipHashUint256(k0, k1, hash); + size_t operator()(const uint256& hash) const + { + return m_hasher(hash); } }; class SaltedTxidHasher { -private: - /** Salt */ - const uint64_t k0, k1; + const PresaltedSipHasher m_hasher; public: SaltedTxidHasher(); - size_t operator()(const Txid& txid) const { - return SipHashUint256(k0, k1, txid.ToUint256()); + size_t operator()(const Txid& txid) const + { + return m_hasher(txid.ToUint256()); } }; class SaltedWtxidHasher { -private: - /** Salt */ - const uint64_t k0, k1; + const PresaltedSipHasher m_hasher; public: SaltedWtxidHasher(); - size_t operator()(const Wtxid& wtxid) const { - return SipHashUint256(k0, k1, wtxid.ToUint256()); + size_t operator()(const Wtxid& wtxid) const + { + return m_hasher(wtxid.ToUint256()); } }; - class SaltedOutpointHasher { const PresaltedSipHasher m_hasher; @@ -80,8 +76,7 @@ public: } }; -struct FilterHeaderHasher -{ +struct FilterHeaderHasher { size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } };