@@ -11,11 +11,6 @@ pub const BLOOM_SIZE_BYTES: usize = 256;
1111/// Size of the bloom filter in bits
1212pub const BLOOM_SIZE_BITS : usize = BLOOM_SIZE_BYTES * 8 ;
1313
14- /// Mask, used in accrue
15- const MASK : usize = BLOOM_SIZE_BITS - 1 ;
16- /// Number of bytes per item, used in accrue
17- const ITEM_BYTES : usize = BLOOM_SIZE_BITS . ilog2 ( ) . div_ceil ( 8 ) as usize ;
18-
1914// BLOOM_SIZE_BYTES must be a power of 2
2015#[ allow( clippy:: assertions_on_constants) ]
2116const _: ( ) = assert ! ( BLOOM_SIZE_BYTES . is_power_of_two( ) ) ;
@@ -128,7 +123,8 @@ impl Bloom {
128123 /// bloom filter data structure.
129124 #[ inline]
130125 pub fn contains_input ( & self , input : BloomInput < ' _ > ) -> bool {
131- self . contains ( & input. into ( ) )
126+ let hash = input. into_hash ( ) ;
127+ self . contains_m3_2048_hashed ( & hash)
132128 }
133129
134130 /// Compile-time version of [`contains`](Self::contains).
@@ -151,18 +147,7 @@ impl Bloom {
151147 /// Accrues the input into the bloom filter.
152148 pub fn accrue ( & mut self , input : BloomInput < ' _ > ) {
153149 let hash = input. into_hash ( ) ;
154-
155- let mut ptr = 0 ;
156-
157- for _ in 0 ..3 {
158- let mut index = 0_usize ;
159- for _ in 0 ..ITEM_BYTES {
160- index = ( index << 8 ) | hash[ ptr] as usize ;
161- ptr += 1 ;
162- }
163- index &= MASK ;
164- self . 0 [ BLOOM_SIZE_BYTES - 1 - index / 8 ] |= 1 << ( index % 8 ) ;
165- }
150+ self . m3_2048_hashed ( & hash) ;
166151 }
167152
168153 /// Accrues the input into the bloom filter.
@@ -189,6 +174,24 @@ impl Bloom {
189174 }
190175 }
191176
177+ /// Returns true if this bloom filter contains the bits set by [`m3_2048`](Self::m3_2048).
178+ fn contains_m3_2048 ( & self , bytes : & [ u8 ] ) -> bool {
179+ self . contains_m3_2048_hashed ( & keccak256 ( bytes) )
180+ }
181+
182+ /// [`contains_m3_2048`](Self::contains_m3_2048) but with a pre-hashed input.
183+ fn contains_m3_2048_hashed ( & self , hash : & B256 ) -> bool {
184+ for i in [ 0 , 2 , 4 ] {
185+ // Each adjacent hash byte pair forms one big-endian 11-bit index.
186+ // Bloom bytes are stored high-to-low, while bits within a byte are low-to-high.
187+ let bit = ( hash[ i + 1 ] as usize + ( ( hash[ i] as usize ) << 8 ) ) & 0x7FF ;
188+ if self [ BLOOM_SIZE_BYTES - 1 - bit / 8 ] & ( 1 << ( bit % 8 ) ) == 0 {
189+ return false ;
190+ }
191+ }
192+ true
193+ }
194+
192195 /// Ingests a raw log into the bloom filter.
193196 pub fn accrue_raw_log ( & mut self , address : Address , topics : & [ B256 ] ) {
194197 self . m3_2048 ( address. as_slice ( ) ) ;
@@ -214,9 +217,8 @@ impl Bloom {
214217 /// Note: This method may return false positives. This is inherent to the
215218 /// bloom filter data structure.
216219 pub fn contains_raw_log ( & self , address : Address , topics : & [ B256 ] ) -> bool {
217- let mut bloom = Self :: default ( ) ;
218- bloom. accrue_raw_log ( address, topics) ;
219- self . contains ( & bloom)
220+ self . contains_m3_2048 ( address. as_slice ( ) )
221+ && topics. iter ( ) . all ( |topic| self . contains_m3_2048 ( topic. as_slice ( ) ) )
220222 }
221223
222224 /// True if the bloom filter contains a log with given address and topics.
@@ -255,19 +257,47 @@ mod tests {
255257 ) ;
256258 let address = hex ! ( "ef2d6d194084c2de36e0dabfce45d046b37d1106" ) ;
257259 let topic = hex ! ( "02c69be41d0b7e40352fc85be1cd65eb03d40ef8427a0ca4596b1ead9a00e9fc" ) ;
260+ let address_hash = keccak256 ( address) ;
261+ let topic_hash = keccak256 ( topic) ;
258262
259263 let mut my_bloom = Bloom :: default ( ) ;
260264 assert ! ( !my_bloom. contains_input( BloomInput :: Raw ( & address) ) ) ;
261265 assert ! ( !my_bloom. contains_input( BloomInput :: Raw ( & topic) ) ) ;
266+ assert ! ( !my_bloom. contains_input( BloomInput :: Hash ( address_hash) ) ) ;
267+ assert ! ( !my_bloom. contains_input( BloomInput :: Hash ( topic_hash) ) ) ;
262268
263269 my_bloom. accrue ( BloomInput :: Raw ( & address) ) ;
264270 assert ! ( my_bloom. contains_input( BloomInput :: Raw ( & address) ) ) ;
265271 assert ! ( !my_bloom. contains_input( BloomInput :: Raw ( & topic) ) ) ;
272+ assert ! ( my_bloom. contains_input( BloomInput :: Hash ( address_hash) ) ) ;
273+ assert ! ( !my_bloom. contains_input( BloomInput :: Hash ( topic_hash) ) ) ;
266274
267275 my_bloom. accrue ( BloomInput :: Raw ( & topic) ) ;
268276 assert ! ( my_bloom. contains_input( BloomInput :: Raw ( & address) ) ) ;
269277 assert ! ( my_bloom. contains_input( BloomInput :: Raw ( & topic) ) ) ;
278+ assert ! ( my_bloom. contains_input( BloomInput :: Hash ( address_hash) ) ) ;
279+ assert ! ( my_bloom. contains_input( BloomInput :: Hash ( topic_hash) ) ) ;
270280
271281 assert_eq ! ( my_bloom, bloom) ;
272282 }
283+
284+ #[ test]
285+ #[ cfg( feature = "arbitrary" ) ]
286+ #[ cfg_attr( miri, ignore = "proptest is too slow under miri" ) ]
287+ fn contains_raw_log_matches_bloom_contains ( ) {
288+ use proptest:: { arbitrary:: any, collection:: vec} ;
289+
290+ fn contains_via_bloom ( bloom : & Bloom , address : Address , topics : & [ B256 ] ) -> bool {
291+ let mut log_bloom = Bloom :: default ( ) ;
292+ log_bloom. accrue_raw_log ( address, topics) ;
293+ bloom. contains ( & log_bloom)
294+ }
295+
296+ proptest:: proptest!( |( bloom: Bloom , address: Address , topics in vec( any:: <B256 >( ) , 0 ..8 ) ) | {
297+ proptest:: prop_assert_eq!(
298+ bloom. contains_raw_log( address, & topics) ,
299+ contains_via_bloom( & bloom, address, & topics)
300+ ) ;
301+ } ) ;
302+ }
273303}
0 commit comments