From 53e2f4db0ec4d562b9dbc12e469351268551d2c3 Mon Sep 17 00:00:00 2001 From: Harshil-1603 Date: Thu, 15 Oct 2026 01:40:19 +0530 Subject: [PATCH 1/2] Prevent newly constructed Bloom filters from matching every entry Assisted-by: OpenAI gpt-6.1-sol --- bitcoin/bloom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bitcoin/bloom.py b/bitcoin/bloom.py index d0e3b765..7dfa16a8 100644 --- a/bitcoin/bloom.py +++ b/bitcoin/bloom.py @@ -107,8 +107,9 @@ def __init__(self, nElements, nFPRate, nTweak, nFlags): """ LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455 LN2 = 0.6931471805599453094172321214581765680755001343602552 - self.vData = bytearray(int(min(-1 / LN2SQUARED * nElements * math.log(nFPRate), self.MAX_BLOOM_FILTER_SIZE * 8) / 8)) - self.nHashFuncs = int(min(len(self.vData) * 8 / nElements * LN2, self.MAX_HASH_FUNCS)) + # Rounding must not produce a filter that matches every element. + self.vData = bytearray(max(1, int(min(-1 / LN2SQUARED * nElements * math.log(nFPRate), self.MAX_BLOOM_FILTER_SIZE * 8) / 8))) + self.nHashFuncs = max(1, int(min(len(self.vData) * 8 / nElements * LN2, self.MAX_HASH_FUNCS))) self.nTweak = nTweak self.nFlags = nFlags From 8d1485cac873a3dca6d12dee80543faa6a15a9df Mon Sep 17 00:00:00 2001 From: Harshil-1603 Date: Thu, 15 Oct 2026 01:40:42 +0530 Subject: [PATCH 2/2] Test small Bloom filters and quoted false-positive rates Assisted-by: OpenAI gpt-6.1-sol --- bitcoin/tests/test_bloom.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bitcoin/tests/test_bloom.py b/bitcoin/tests/test_bloom.py index 436a97ea..6cad37f6 100644 --- a/bitcoin/tests/test_bloom.py +++ b/bitcoin/tests/test_bloom.py @@ -40,6 +40,20 @@ def T(expected, seed, data): class Test_CBloomFilter(unittest.TestCase): + def test_small_and_high_false_positive_rate_filters(self): + for elements in (1, 2, 10, 100): + for rate in (0.05, 0.1, 0.5, 0.9, 0.9999): + filter = CBloomFilter(elements, rate, 0, CBloomFilter.UPDATE_ALL) + # An empty filter must not match everything, even when its + # calculated storage size or hash count rounds down to zero. + self.assertFalse(filter.contains(b'not inserted')) + filter.insert(b'inserted') + self.assertTrue(filter.contains(b'inserted')) + self.assertTrue(filter.IsWithinSizeConstraints()) + restored = CBloomFilter.deserialize(filter.serialize()) + self.assertTrue(restored.contains(b'inserted')) + self.assertEqual(restored.serialize(), filter.serialize()) + def test_create_insert_serialize(self): filter = CBloomFilter(3, 0.01, 0, CBloomFilter.UPDATE_ALL)