From f7753fcaea0b79abcf72e0675e1313cc6198c84c Mon Sep 17 00:00:00 2001 From: Chris Stewart Date: Tue, 2 Jul 2019 10:39:22 -0500 Subject: [PATCH] Add case for empty bloom filter (#561) --- .../bitcoins/core/bloom/BloomFilterTest.scala | 7 +++ .../org/bitcoins/core/bloom/BloomFilter.scala | 54 +++++++++++-------- .../core/protocol/CompactSizeUInt.scala | 1 + 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/core-test/src/test/scala/org/bitcoins/core/bloom/BloomFilterTest.scala b/core-test/src/test/scala/org/bitcoins/core/bloom/BloomFilterTest.scala index 2ffada1ab7..a9db74c837 100644 --- a/core-test/src/test/scala/org/bitcoins/core/bloom/BloomFilterTest.scala +++ b/core-test/src/test/scala/org/bitcoins/core/bloom/BloomFilterTest.scala @@ -277,4 +277,11 @@ class BloomFilterTest extends BitcoinSUnitTest { } } + + it must "be able to instantiate a empty bloom filter" in { + val empty = BloomFilter.empty + + empty.data must be(ByteVector.empty) + BloomFilter.fromBytes(empty.bytes) must be(empty) + } } diff --git a/core/src/main/scala/org/bitcoins/core/bloom/BloomFilter.scala b/core/src/main/scala/org/bitcoins/core/bloom/BloomFilter.scala index e4479eb6aa..2582526156 100644 --- a/core/src/main/scala/org/bitcoins/core/bloom/BloomFilter.scala +++ b/core/src/main/scala/org/bitcoins/core/bloom/BloomFilter.scala @@ -316,6 +316,12 @@ object BloomFilter extends Factory[BloomFilter] { /** Max hashFunc size as per [[https://bitcoin.org/en/developer-reference#filterload]] */ val maxHashFuncs = UInt32(50) + val empty: BloomFilter = BloomFilterImpl(CompactSizeUInt.zero, + ByteVector.empty, + UInt32.zero, + UInt32.zero, + BloomUpdateAll) + /** * Creates a bloom filter based on the number of elements to be inserted into the filter * and the desired false positive rate. @@ -348,28 +354,34 @@ object BloomFilter extends Factory[BloomFilter] { falsePositiveRate: Double, tweak: UInt32, flags: BloomFlag): BloomFilter = { - import scala.math._ - //m = number of bits in the array - //n = number of elements in the array - //from https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#bloom-filter-format - val optimalFilterSize: Double = (-1 / pow(log(2), 2) * numElements * log( - falsePositiveRate)) / 8 - logger.debug("optimalFilterSize " + optimalFilterSize) - //BIP37 places limitations on the filter size, namely it cannot be > 36,000 bytes - val actualFilterSize: Int = - max(1, min(optimalFilterSize, maxSize.toInt * 8)).toInt - logger.debug("actualFilterSize: " + actualFilterSize) - val optimalHashFuncs: Double = (actualFilterSize * 8 / numElements * log(2)) - //BIP37 places a limit on the amount of hashFuncs we can use, which is 50 - val actualHashFuncs: Int = - max(1, min(optimalHashFuncs, maxHashFuncs.toInt)).toInt + if (numElements == 0) { + BloomFilter.empty + } else { + import scala.math._ + //m = number of bits in the array + //n = number of elements in the array + //from https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#bloom-filter-format + val optimalFilterSize: Double = (-1 / pow(log(2), 2) * numElements * log( + falsePositiveRate)) / 8 + logger.debug("optimalFilterSize " + optimalFilterSize) + //BIP37 places limitations on the filter size, namely it cannot be > 36,000 bytes + val actualFilterSize: Int = + max(1, min(optimalFilterSize, maxSize.toInt * 8)).toInt + logger.debug("actualFilterSize: " + actualFilterSize) + val optimalHashFuncs: Double = (actualFilterSize * 8 / numElements * log( + 2)) + //BIP37 places a limit on the amount of hashFuncs we can use, which is 50 + val actualHashFuncs: Int = + max(1, min(optimalHashFuncs, maxHashFuncs.toInt)).toInt + + val emptyByteArray = ByteVector(Array.fill(actualFilterSize)(0.toByte)) + BloomFilter(filterSize = CompactSizeUInt(UInt64(actualFilterSize)), + data = emptyByteArray, + hashFuncs = UInt32(actualHashFuncs), + tweak = tweak, + flags = flags) + } - val emptyByteArray = ByteVector(Array.fill(actualFilterSize)(0.toByte)) - BloomFilter(CompactSizeUInt(UInt64(actualFilterSize)), - emptyByteArray, - UInt32(actualHashFuncs), - tweak, - flags) } def apply( diff --git a/core/src/main/scala/org/bitcoins/core/protocol/CompactSizeUInt.scala b/core/src/main/scala/org/bitcoins/core/protocol/CompactSizeUInt.scala index f3a4f9f692..4878a22b35 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/CompactSizeUInt.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/CompactSizeUInt.scala @@ -40,6 +40,7 @@ object CompactSizeUInt extends Factory[CompactSizeUInt] { private case class CompactSizeUIntImpl(num: UInt64, override val size: Long) extends CompactSizeUInt + val zero: CompactSizeUInt = CompactSizeUInt(UInt64.zero) override def fromBytes(bytes: ByteVector): CompactSizeUInt = { parseCompactSizeUInt(bytes) }