From 0a48b680f88f91f039e286890bfbf17b8f89b40d Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sun, 18 Aug 2013 20:21:06 -0700 Subject: [PATCH] Performance optimization for bloom filters. This reduces a peer's ability to attack network resources by using a full bloom filter, but without reducing the usability of bloom filters. It sets a default match everything filter for peers and it generalizes a prior optimization to cover more cases. --- src/bloom.cpp | 26 ++++++++++++++++++++++++-- src/bloom.h | 9 ++++++--- src/main.cpp | 3 ++- src/net.h | 2 +- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/bloom.cpp b/src/bloom.cpp index d9ec2efa8..6bdffdbb3 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -23,6 +23,8 @@ vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM // The ideal number of hash functions is filter size * ln(2) / number of elements // Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits // See http://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas +isFull(false), +isEmpty(false), nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), nTweak(nTweakIn), nFlags(nFlagsIn) @@ -37,7 +39,7 @@ inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector< void CBloomFilter::insert(const vector& vKey) { - if (vData.size() == 1 && vData[0] == 0xff) + if (isFull) return; for (unsigned int i = 0; i < nHashFuncs; i++) { @@ -45,6 +47,7 @@ void CBloomFilter::insert(const vector& vKey) // Sets bit nIndex of vData vData[nIndex >> 3] |= bit_mask[7 & nIndex]; } + isEmpty = false; } void CBloomFilter::insert(const COutPoint& outpoint) @@ -63,8 +66,10 @@ void CBloomFilter::insert(const uint256& hash) bool CBloomFilter::contains(const vector& vKey) const { - if (vData.size() == 1 && vData[0] == 0xff) + if (isFull) return true; + if (isEmpty) + return false; for (unsigned int i = 0; i < nHashFuncs; i++) { unsigned int nIndex = Hash(i, vKey); @@ -99,6 +104,10 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& ha bool fFound = false; // Match if the filter contains the hash of tx // for finding tx when they appear in a block + if (isFull) + return true; + if (isEmpty) + return false; if (contains(hash)) fFound = true; @@ -158,3 +167,16 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& ha return false; } + +void CBloomFilter::UpdateEmptyFull() +{ + bool full = true; + bool empty = true; + for (unsigned int i = 0; i < vData.size(); i++) + { + full &= vData[i] == 0xff; + empty &= vData[i] == 0; + } + isFull = full; + isEmpty = empty; +} diff --git a/src/bloom.h b/src/bloom.h index 389ae748e..f482bfcc1 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -42,6 +42,8 @@ class CBloomFilter { private: std::vector vData; + bool isFull; + bool isEmpty; unsigned int nHashFuncs; unsigned int nTweak; unsigned char nFlags; @@ -57,9 +59,7 @@ public: // It should generally always be a random value (and is largely only exposed for unit testing) // nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK) CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak, unsigned char nFlagsIn); - // Using a filter initialized with this results in undefined behavior - // Should only be used for deserialization - CBloomFilter() {} + CBloomFilter() : isFull(true) {} IMPLEMENT_SERIALIZE ( @@ -83,6 +83,9 @@ public: // Also adds any outputs which match the filter to the filter (to match their spending txes) bool IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash); + + // Checks for empty and full filters to avoid wasting cpu + void UpdateEmptyFull(); }; #endif /* BITCOIN_BLOOM_H */ diff --git a/src/main.cpp b/src/main.cpp index af1f3eae7..7f0515118 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3698,6 +3698,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); + filter.UpdateEmptyFull(); } pfrom->fRelayTxes = true; } @@ -3727,7 +3728,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { LOCK(pfrom->cs_filter); delete pfrom->pfilter; - pfrom->pfilter = NULL; + pfrom->pfilter = new CBloomFilter(); pfrom->fRelayTxes = true; } diff --git a/src/net.h b/src/net.h index af66eed07..6f315f74f 100644 --- a/src/net.h +++ b/src/net.h @@ -253,7 +253,7 @@ public: nMisbehavior = 0; fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); - pfilter = NULL; + pfilter = new CBloomFilter(); // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound)