Merge #8173: Use SipHash for node eviction (cont'd)

eebc232 test: Add more test vectors for siphash (Wladimir J. van der Laan)
8884830 Use C++11 thread-safe static initializers (Pieter Wuille)
c31b24f Use 64-bit SipHash of netgroups in eviction (Pieter Wuille)
9bf156b Support SipHash with arbitrary byte writes (Pieter Wuille)
053930f Avoid recalculating vchKeyedNetGroup in eviction logic. (Patrick Strateman)
This commit is contained in:
Pieter Wuille 2016-06-08 18:05:01 +02:00
commit 4286f43025
No known key found for this signature in database
GPG Key ID: DBA1A67379A1A931
8 changed files with 152 additions and 62 deletions

View File

@ -56,11 +56,7 @@ void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
SaltedTxidHasher::SaltedTxidHasher() SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
{
GetRandBytes((unsigned char*)&k0, sizeof(k0));
GetRandBytes((unsigned char*)&k1, sizeof(k1));
}
CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { } CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { }

View File

@ -269,7 +269,7 @@ class SaltedTxidHasher
{ {
private: private:
/** Salt */ /** Salt */
uint64_t k0, k1; const uint64_t k0, k1;
public: public:
SaltedTxidHasher(); SaltedTxidHasher();

View File

@ -100,12 +100,15 @@ CSipHasher::CSipHasher(uint64_t k0, uint64_t k1)
v[2] = 0x6c7967656e657261ULL ^ k0; v[2] = 0x6c7967656e657261ULL ^ k0;
v[3] = 0x7465646279746573ULL ^ k1; v[3] = 0x7465646279746573ULL ^ k1;
count = 0; count = 0;
tmp = 0;
} }
CSipHasher& CSipHasher::Write(uint64_t data) CSipHasher& CSipHasher::Write(uint64_t data)
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
assert(count % 8 == 0);
v3 ^= data; v3 ^= data;
SIPROUND; SIPROUND;
SIPROUND; SIPROUND;
@ -116,7 +119,35 @@ CSipHasher& CSipHasher::Write(uint64_t data)
v[2] = v2; v[2] = v2;
v[3] = v3; v[3] = v3;
count++; count += 8;
return *this;
}
CSipHasher& CSipHasher::Write(const unsigned char* data, size_t size)
{
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
uint64_t t = tmp;
int c = count;
while (size--) {
t |= ((uint64_t)(*(data++))) << (8 * (c % 8));
c++;
if ((c & 7) == 0) {
v3 ^= t;
SIPROUND;
SIPROUND;
v0 ^= t;
t = 0;
}
}
v[0] = v0;
v[1] = v1;
v[2] = v2;
v[3] = v3;
count = c;
tmp = t;
return *this; return *this;
} }
@ -124,10 +155,12 @@ uint64_t CSipHasher::Finalize() const
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
v3 ^= ((uint64_t)count) << 59; uint64_t t = tmp | (((uint64_t)count) << 56);
v3 ^= t;
SIPROUND; SIPROUND;
SIPROUND; SIPROUND;
v0 ^= ((uint64_t)count) << 59; v0 ^= t;
v2 ^= 0xFF; v2 ^= 0xFF;
SIPROUND; SIPROUND;
SIPROUND; SIPROUND;

View File

@ -171,19 +171,38 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]); void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]);
/** SipHash-2-4, using a uint64_t-based (rather than byte-based) interface */ /** SipHash-2-4 */
class CSipHasher class CSipHasher
{ {
private: private:
uint64_t v[4]; uint64_t v[4];
uint64_t tmp;
int count; int count;
public: public:
/** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */
CSipHasher(uint64_t k0, uint64_t 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); CSipHasher& Write(uint64_t data);
/** Hash arbitrary bytes. */
CSipHasher& Write(const unsigned char* data, size_t size);
/** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */
uint64_t Finalize() const; 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 SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val);
#endif // BITCOIN_HASH_H #endif // BITCOIN_HASH_H

View File

@ -4783,11 +4783,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LOCK(cs_vNodes); LOCK(cs_vNodes);
// Use deterministic randomness to send to the same nodes for 24 hours // Use deterministic randomness to send to the same nodes for 24 hours
// at a time so the addrKnowns of the chosen nodes prevent repeats // at a time so the addrKnowns of the chosen nodes prevent repeats
static uint64_t salt0 = 0, salt1 = 0; static const uint64_t salt0 = GetRand(std::numeric_limits<uint64_t>::max());
while (salt0 == 0 && salt1 == 0) { static const uint64_t salt1 = GetRand(std::numeric_limits<uint64_t>::max());
GetRandBytes((unsigned char*)&salt0, sizeof(salt0));
GetRandBytes((unsigned char*)&salt1, sizeof(salt1));
}
uint64_t hashAddr = addr.GetHash(); uint64_t hashAddr = addr.GetHash();
multimap<uint64_t, CNode*> mapMix; multimap<uint64_t, CNode*> mapMix;
const CSipHasher hasher = CSipHasher(salt0, salt1).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); const CSipHasher hasher = CSipHasher(salt0, salt1).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60));

View File

@ -14,6 +14,7 @@
#include "clientversion.h" #include "clientversion.h"
#include "consensus/consensus.h" #include "consensus/consensus.h"
#include "crypto/common.h" #include "crypto/common.h"
#include "crypto/sha256.h"
#include "hash.h" #include "hash.h"
#include "primitives/transaction.h" #include "primitives/transaction.h"
#include "scheduler.h" #include "scheduler.h"
@ -838,6 +839,7 @@ struct NodeEvictionCandidate
int64_t nTimeConnected; int64_t nTimeConnected;
int64_t nMinPingUsecTime; int64_t nMinPingUsecTime;
CAddress addr; CAddress addr;
uint64_t nKeyedNetGroup;
}; };
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@ -850,36 +852,8 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
return a.nTimeConnected > b.nTimeConnected; return a.nTimeConnected > b.nTimeConnected;
} }
class CompareNetGroupKeyed static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
{ return a.nKeyedNetGroup < b.nKeyedNetGroup;
std::vector<unsigned char> vchSecretKey;
public:
CompareNetGroupKeyed()
{
vchSecretKey.resize(32, 0);
GetRandBytes(vchSecretKey.data(), vchSecretKey.size());
}
bool operator()(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
{
std::vector<unsigned char> vchGroupA, vchGroupB;
CSHA256 hashA, hashB;
std::vector<unsigned char> vchA(32), vchB(32);
vchGroupA = a.addr.GetGroup();
vchGroupB = b.addr.GetGroup();
hashA.Write(begin_ptr(vchGroupA), vchGroupA.size());
hashB.Write(begin_ptr(vchGroupB), vchGroupB.size());
hashA.Write(begin_ptr(vchSecretKey), vchSecretKey.size());
hashB.Write(begin_ptr(vchSecretKey), vchSecretKey.size());
hashA.Finalize(begin_ptr(vchA));
hashB.Finalize(begin_ptr(vchB));
return vchA < vchB;
}
}; };
/** Try to find a connection to evict when the node is full. /** Try to find a connection to evict when the node is full.
@ -902,7 +876,7 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
continue; continue;
if (node->fDisconnect) if (node->fDisconnect)
continue; continue;
NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime, node->addr}; NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime, node->addr, node->nKeyedNetGroup};
vEvictionCandidates.push_back(candidate); vEvictionCandidates.push_back(candidate);
} }
} }
@ -912,9 +886,8 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
// Protect connections with certain characteristics // Protect connections with certain characteristics
// Deterministically select 4 peers to protect by netgroup. // Deterministically select 4 peers to protect by netgroup.
// An attacker cannot predict which netgroups will be protected. // An attacker cannot predict which netgroups will be protected
static CompareNetGroupKeyed comparerNetGroupKeyed; std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed);
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), comparerNetGroupKeyed);
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
if (vEvictionCandidates.empty()) return false; if (vEvictionCandidates.empty()) return false;
@ -935,24 +908,24 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
// Identify the network group with the most connections and youngest member. // Identify the network group with the most connections and youngest member.
// (vEvictionCandidates is already sorted by reverse connect time) // (vEvictionCandidates is already sorted by reverse connect time)
std::vector<unsigned char> naMostConnections; uint64_t naMostConnections;
unsigned int nMostConnections = 0; unsigned int nMostConnections = 0;
int64_t nMostConnectionsTime = 0; int64_t nMostConnectionsTime = 0;
std::map<std::vector<unsigned char>, std::vector<NodeEvictionCandidate> > mapAddrCounts; std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapAddrCounts;
BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) { BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) {
mapAddrCounts[node.addr.GetGroup()].push_back(node); mapAddrCounts[node.nKeyedNetGroup].push_back(node);
int64_t grouptime = mapAddrCounts[node.addr.GetGroup()][0].nTimeConnected; int64_t grouptime = mapAddrCounts[node.nKeyedNetGroup][0].nTimeConnected;
size_t groupsize = mapAddrCounts[node.addr.GetGroup()].size(); size_t groupsize = mapAddrCounts[node.nKeyedNetGroup].size();
if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) {
nMostConnections = groupsize; nMostConnections = groupsize;
nMostConnectionsTime = grouptime; nMostConnectionsTime = grouptime;
naMostConnections = node.addr.GetGroup(); naMostConnections = node.nKeyedNetGroup;
} }
} }
// Reduce to the network group with the most connections // Reduce to the network group with the most connections
vEvictionCandidates = mapAddrCounts[naMostConnections]; vEvictionCandidates = std::move(mapAddrCounts[naMostConnections]);
// Do not disconnect peers if there is only one unprotected connection from their network group. // Do not disconnect peers if there is only one unprotected connection from their network group.
// This step excessively favors netgroup diversity, and should be removed once more protective criteria are established. // This step excessively favors netgroup diversity, and should be removed once more protective criteria are established.
@ -2346,6 +2319,8 @@ unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAX
CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) :
ssSend(SER_NETWORK, INIT_PROTO_VERSION), ssSend(SER_NETWORK, INIT_PROTO_VERSION),
addr(addrIn),
nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)),
addrKnown(5000, 0.001), addrKnown(5000, 0.001),
filterInventoryKnown(50000, 0.000001) filterInventoryKnown(50000, 0.000001)
{ {
@ -2358,7 +2333,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
nRecvBytes = 0; nRecvBytes = 0;
nTimeConnected = GetTime(); nTimeConnected = GetTime();
nTimeOffset = 0; nTimeOffset = 0;
addr = addrIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
nVersion = 0; nVersion = 0;
strSubVer = ""; strSubVer = "";
@ -2625,3 +2599,13 @@ bool CBanDB::Read(banmap_t& banSet)
int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) {
return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
} }
/* static */ uint64_t CNode::CalculateKeyedNetGroup(const CAddress& ad)
{
static const uint64_t k0 = GetRand(std::numeric_limits<uint64_t>::max());
static const uint64_t k1 = GetRand(std::numeric_limits<uint64_t>::max());
std::vector<unsigned char> vchNetGroup(ad.GetGroup());
return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize();
}

View File

@ -335,7 +335,7 @@ public:
int64_t nLastRecv; int64_t nLastRecv;
int64_t nTimeConnected; int64_t nTimeConnected;
int64_t nTimeOffset; int64_t nTimeOffset;
CAddress addr; const CAddress addr;
std::string addrName; std::string addrName;
CService addrLocal; CService addrLocal;
int nVersion; int nVersion;
@ -362,6 +362,8 @@ public:
CBloomFilter* pfilter; CBloomFilter* pfilter;
int nRefCount; int nRefCount;
NodeId id; NodeId id;
const uint64_t nKeyedNetGroup;
protected: protected:
// Denial-of-service detection/prevention // Denial-of-service detection/prevention
@ -450,6 +452,8 @@ private:
CNode(const CNode&); CNode(const CNode&);
void operator=(const CNode&); void operator=(const CNode&);
static uint64_t CalculateKeyedNetGroup(const CAddress& ad);
public: public:
NodeId GetId() const { NodeId GetId() const {

View File

@ -47,17 +47,58 @@ BOOST_AUTO_TEST_CASE(murmurhash3)
#undef T #undef T
} }
/*
SipHash-2-4 output with
k = 00 01 02 ...
and
in = (empty string)
in = 00 (1 byte)
in = 00 01 (2 bytes)
in = 00 01 02 (3 bytes)
...
in = 00 01 02 ... 3e (63 bytes)
from: https://131002.net/siphash/siphash24.c
*/
uint64_t siphash_4_2_testvec[] = {
0x726fdb47dd0e0e31, 0x74f839c593dc67fd, 0x0d6c8009d9a94f5a, 0x85676696d7fb7e2d,
0xcf2794e0277187b7, 0x18765564cd99a68d, 0xcbc9466e58fee3ce, 0xab0200f58b01d137,
0x93f5f5799a932462, 0x9e0082df0ba9e4b0, 0x7a5dbbc594ddb9f3, 0xf4b32f46226bada7,
0x751e8fbc860ee5fb, 0x14ea5627c0843d90, 0xf723ca908e7af2ee, 0xa129ca6149be45e5,
0x3f2acc7f57c29bdb, 0x699ae9f52cbe4794, 0x4bc1b3f0968dd39c, 0xbb6dc91da77961bd,
0xbed65cf21aa2ee98, 0xd0f2cbb02e3b67c7, 0x93536795e3a33e88, 0xa80c038ccd5ccec8,
0xb8ad50c6f649af94, 0xbce192de8a85b8ea, 0x17d835b85bbb15f3, 0x2f2e6163076bcfad,
0xde4daaaca71dc9a5, 0xa6a2506687956571, 0xad87a3535c49ef28, 0x32d892fad841c342,
0x7127512f72f27cce, 0xa7f32346f95978e3, 0x12e0b01abb051238, 0x15e034d40fa197ae,
0x314dffbe0815a3b4, 0x027990f029623981, 0xcadcd4e59ef40c4d, 0x9abfd8766a33735c,
0x0e3ea96b5304a7d0, 0xad0c42d6fc585992, 0x187306c89bc215a9, 0xd4a60abcf3792b95,
0xf935451de4f21df2, 0xa9538f0419755787, 0xdb9acddff56ca510, 0xd06c98cd5c0975eb,
0xe612a3cb9ecba951, 0xc766e62cfcadaf96, 0xee64435a9752fe72, 0xa192d576b245165a,
0x0a8787bf8ecb74b2, 0x81b3e73d20b49b6f, 0x7fa8220ba3b2ecea, 0x245731c13ca42499,
0xb78dbfaf3a8d83bd, 0xea1ad565322a1a0b, 0x60e61c23a3795013, 0x6606d7e446282b93,
0x6ca4ecb15c5f91e1, 0x9f626da15c9625f3, 0xe51b38608ef25f57, 0x958a324ceb064572
};
BOOST_AUTO_TEST_CASE(siphash) BOOST_AUTO_TEST_CASE(siphash)
{ {
CSipHasher hasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); CSipHasher hasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x726fdb47dd0e0e31ull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x726fdb47dd0e0e31ull);
hasher.Write(0x0706050403020100ULL); static const unsigned char t0[1] = {0};
hasher.Write(t0, 1);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x74f839c593dc67fdull);
static const unsigned char t1[7] = {1,2,3,4,5,6,7};
hasher.Write(t1, 7);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x93f5f5799a932462ull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x93f5f5799a932462ull);
hasher.Write(0x0F0E0D0C0B0A0908ULL); hasher.Write(0x0F0E0D0C0B0A0908ULL);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x3f2acc7f57c29bdbull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x3f2acc7f57c29bdbull);
hasher.Write(0x1716151413121110ULL); static const unsigned char t2[2] = {16,17};
BOOST_CHECK_EQUAL(hasher.Finalize(), 0xb8ad50c6f649af94ull); hasher.Write(t2, 2);
hasher.Write(0x1F1E1D1C1B1A1918ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x4bc1b3f0968dd39cull);
static const unsigned char t3[9] = {18,19,20,21,22,23,24,25,26};
hasher.Write(t3, 9);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x2f2e6163076bcfadull);
static const unsigned char t4[5] = {27,28,29,30,31};
hasher.Write(t4, 5);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x7127512f72f27cceull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x7127512f72f27cceull);
hasher.Write(0x2726252423222120ULL); hasher.Write(0x2726252423222120ULL);
BOOST_CHECK_EQUAL(hasher.Finalize(), 0x0e3ea96b5304a7d0ull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x0e3ea96b5304a7d0ull);
@ -65,6 +106,22 @@ BOOST_AUTO_TEST_CASE(siphash)
BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull); BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull);
BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256S("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")), 0x7127512f72f27cceull); BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256S("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")), 0x7127512f72f27cceull);
// Check test vectors from spec, one byte at a time
CSipHasher hasher2(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
for (uint8_t x=0; x<ARRAYLEN(siphash_4_2_testvec); ++x)
{
BOOST_CHECK_EQUAL(hasher2.Finalize(), siphash_4_2_testvec[x]);
hasher2.Write(&x, 1);
}
// Check test vectors from spec, eight bytes at a time
CSipHasher hasher3(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
for (uint8_t x=0; x<ARRAYLEN(siphash_4_2_testvec); x+=8)
{
BOOST_CHECK_EQUAL(hasher3.Finalize(), siphash_4_2_testvec[x]);
hasher3.Write(uint64_t(x)|(uint64_t(x+1)<<8)|(uint64_t(x+2)<<16)|(uint64_t(x+3)<<24)|
(uint64_t(x+4)<<32)|(uint64_t(x+5)<<40)|(uint64_t(x+6)<<48)|(uint64_t(x+7)<<56));
}
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()