alloy_primitives/bits/
bloom.rs1use crate::{Address, B256, Log, LogData, keccak256};
6
7pub const BLOOM_BITS_PER_ITEM: usize = 3;
9pub const BLOOM_SIZE_BYTES: usize = 256;
11pub const BLOOM_SIZE_BITS: usize = BLOOM_SIZE_BYTES * 8;
13
14const MASK: usize = BLOOM_SIZE_BITS - 1;
16const ITEM_BYTES: usize = BLOOM_SIZE_BITS.ilog2().div_ceil(8) as usize;
18
19#[allow(clippy::assertions_on_constants)]
21const _: () = assert!(BLOOM_SIZE_BYTES.is_power_of_two());
22
23#[derive(Clone, Copy, Debug)]
25pub enum BloomInput<'a> {
26 Raw(&'a [u8]),
28 Hash(B256),
30}
31
32impl BloomInput<'_> {
33 #[inline]
35 pub fn into_hash(self) -> B256 {
36 match self {
37 BloomInput::Raw(raw) => keccak256(raw),
38 BloomInput::Hash(hash) => hash,
39 }
40 }
41}
42
43impl From<BloomInput<'_>> for Bloom {
44 #[inline]
45 fn from(input: BloomInput<'_>) -> Self {
46 let mut bloom = Self::ZERO;
47 bloom.accrue(input);
48 bloom
49 }
50}
51
52wrap_fixed_bytes!(
53 #[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
55 #[cfg_attr(feature = "rkyv", rkyv(derive(Copy, Clone, Hash, PartialEq, Eq)))]
56 pub struct Bloom<256>;
57);
58
59impl<'a> FromIterator<&'a (Address, LogData)> for Bloom {
60 fn from_iter<T: IntoIterator<Item = &'a (Address, LogData)>>(iter: T) -> Self {
61 let mut bloom = Self::ZERO;
62 bloom.extend(iter);
63 bloom
64 }
65}
66
67impl<'a> Extend<&'a (Address, LogData)> for Bloom {
68 fn extend<T: IntoIterator<Item = &'a (Address, LogData)>>(&mut self, iter: T) {
69 for (address, log_data) in iter {
70 self.accrue_raw_log(*address, log_data.topics())
71 }
72 }
73}
74
75impl<'a> FromIterator<&'a Log> for Bloom {
76 #[inline]
77 fn from_iter<T: IntoIterator<Item = &'a Log>>(logs: T) -> Self {
78 let mut bloom = Self::ZERO;
79 bloom.extend(logs);
80 bloom
81 }
82}
83
84impl<'a> Extend<&'a Log> for Bloom {
85 #[inline]
86 fn extend<T: IntoIterator<Item = &'a Log>>(&mut self, logs: T) {
87 for log in logs {
88 self.accrue_log(log)
89 }
90 }
91}
92
93impl<'a, 'b> FromIterator<&'a BloomInput<'b>> for Bloom {
94 #[inline]
95 fn from_iter<T: IntoIterator<Item = &'a BloomInput<'b>>>(inputs: T) -> Self {
96 let mut bloom = Self::ZERO;
97 bloom.extend(inputs);
98 bloom
99 }
100}
101
102impl<'a, 'b> Extend<&'a BloomInput<'b>> for Bloom {
103 #[inline]
104 fn extend<T: IntoIterator<Item = &'a BloomInput<'b>>>(&mut self, inputs: T) {
105 for input in inputs {
106 self.accrue(*input);
107 }
108 }
109}
110
111impl Bloom {
112 #[inline]
114 pub const fn data(&self) -> &[u8; BLOOM_SIZE_BYTES] {
115 &self.0.0
116 }
117
118 #[inline]
120 pub const fn data_mut(&mut self) -> &mut [u8; BLOOM_SIZE_BYTES] {
121 &mut self.0.0
122 }
123
124 #[inline]
130 pub fn contains_input(&self, input: BloomInput<'_>) -> bool {
131 self.contains(&input.into())
132 }
133
134 pub const fn const_contains(self, other: Self) -> bool {
139 self.0.const_covers(other.0)
140 }
141
142 pub fn contains(&self, other: &Self) -> bool {
148 self.0.covers(&other.0)
149 }
150
151 pub fn accrue(&mut self, input: BloomInput<'_>) {
153 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 }
166 }
167
168 pub fn accrue_bloom(&mut self, bloom: &Self) {
170 *self |= *bloom;
171 }
172
173 pub fn m3_2048(&mut self, bytes: &[u8]) {
181 self.m3_2048_hashed(&keccak256(bytes));
182 }
183
184 pub fn m3_2048_hashed(&mut self, hash: &B256) {
186 for i in [0, 2, 4] {
187 let bit = (hash[i + 1] as usize + ((hash[i] as usize) << 8)) & 0x7FF;
188 self[BLOOM_SIZE_BYTES - 1 - bit / 8] |= 1 << (bit % 8);
189 }
190 }
191
192 pub fn accrue_raw_log(&mut self, address: Address, topics: &[B256]) {
194 self.m3_2048(address.as_slice());
195 for topic in topics.iter() {
196 self.m3_2048(topic.as_slice());
197 }
198 }
199
200 pub fn accrue_log(&mut self, log: &Log) {
202 self.accrue_raw_log(log.address, log.topics())
203 }
204
205 pub fn contains_raw_log(&self, address: Address, topics: &[B256]) -> bool {
210 let mut bloom = Self::default();
211 bloom.accrue_raw_log(address, topics);
212 self.contains(&bloom)
213 }
214
215 pub fn contains_log(&self, log: &Log) -> bool {
220 self.contains_raw_log(log.address, log.topics())
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 use crate::hex;
228
229 #[test]
230 fn works() {
231 let bloom = bloom!(
232 "00000000000000000000000000000000
233 00000000100000000000000000000000
234 00000000000000000000000000000000
235 00000000000000000000000000000000
236 00000000000000000000000000000000
237 00000000000000000000000000000000
238 00000002020000000000000000000000
239 00000000000000000000000800000000
240 10000000000000000000000000000000
241 00000000000000000000001000000000
242 00000000000000000000000000000000
243 00000000000000000000000000000000
244 00000000000000000000000000000000
245 00000000000000000000000000000000
246 00000000000000000000000000000000
247 00000000000000000000000000000000"
248 );
249 let address = hex!("ef2d6d194084c2de36e0dabfce45d046b37d1106");
250 let topic = hex!("02c69be41d0b7e40352fc85be1cd65eb03d40ef8427a0ca4596b1ead9a00e9fc");
251
252 let mut my_bloom = Bloom::default();
253 assert!(!my_bloom.contains_input(BloomInput::Raw(&address)));
254 assert!(!my_bloom.contains_input(BloomInput::Raw(&topic)));
255
256 my_bloom.accrue(BloomInput::Raw(&address));
257 assert!(my_bloom.contains_input(BloomInput::Raw(&address)));
258 assert!(!my_bloom.contains_input(BloomInput::Raw(&topic)));
259
260 my_bloom.accrue(BloomInput::Raw(&topic));
261 assert!(my_bloom.contains_input(BloomInput::Raw(&address)));
262 assert!(my_bloom.contains_input(BloomInput::Raw(&topic)));
263
264 assert_eq!(my_bloom, bloom);
265 }
266}