forest/rpc/methods/eth/
bloom.rs1use super::*;
12use crate::db::EthBlockBloomStore;
13
14const BLOOM_SIZE: usize = 2048;
17
18const BLOOM_SIZE_IN_BYTES: usize = BLOOM_SIZE / 8;
20
21pub(super) const FULL_BLOOM: [u8; BLOOM_SIZE_IN_BYTES] = [0xff; BLOOM_SIZE_IN_BYTES];
23
24pub(super) const EMPTY_BLOOM: [u8; BLOOM_SIZE_IN_BYTES] = [0x0; BLOOM_SIZE_IN_BYTES];
26
27pub(crate) const COMPUTE_BLOOM_ON_MISS_ENV: &str = "FOREST_ETH_RPC_COMPUTE_BLOOM_ON_MISS";
30
31#[derive(PartialEq, Debug, Deserialize, Serialize, Default, Clone, JsonSchema, GetSize)]
32pub struct Bloom(
33 #[schemars(with = "String")]
34 #[serde(with = "crate::lotus_json::hexify_bytes")]
35 #[get_size(ignore)]
36 pub ethereum_types::Bloom,
37);
38lotus_json_with_self!(Bloom);
39
40impl Bloom {
41 pub fn accrue(&mut self, input: &[u8]) {
43 self.0.accrue(ethereum_types::BloomInput::Raw(input));
44 }
45}
46
47pub(super) fn accrue_eth_log(bloom: &mut Bloom, address: &EthAddress, topics: &[EthHash]) {
49 for topic in topics {
50 bloom.accrue(topic.0.as_bytes());
51 }
52 bloom.accrue(address.0.as_bytes());
53}
54
55fn compute_block_logs_bloom(
58 state_manager: &StateManager,
59 state_root: &Cid,
60 executed_messages: &[ExecutedMessage],
61) -> anyhow::Result<Bloom> {
62 let state_tree = state_manager.get_state_tree(state_root)?;
63 let mut resolved_eth_addrs = HashMap::default();
64 let mut bloom = Bloom::default();
65 for executed_message in executed_messages {
66 let Some(events) = &executed_message.events else {
67 continue;
68 };
69 for event in events {
70 let emitter = event.emitter();
71 let address = resolved_eth_addrs.entry(emitter).or_insert_with(|| {
72 state_tree
73 .resolve_to_deterministic_address(
74 state_manager.chain_store().db(),
75 FilecoinAddress::new_id(emitter),
76 )
77 .ok()
78 .and_then(|addr| EthAddress::from_filecoin_address(&addr).ok())
79 });
80 let Some(address) = address else {
81 continue;
82 };
83 let entries: Vec<EventEntry> = event
84 .entries()
85 .into_iter()
86 .map(|entry| {
87 let (flags, key, codec, value) = entry.into_parts();
88 EventEntry {
89 flags,
90 key,
91 codec,
92 value: value.into(),
93 }
94 })
95 .collect();
96 let Some((_data, topics)) = eth_log_from_event(&entries) else {
97 continue;
98 };
99 accrue_eth_log(&mut bloom, address, &topics);
100 }
101 }
102 Ok(bloom)
103}
104
105pub(crate) fn store_block_logs_bloom(
108 state_manager: &StateManager,
109 tipset: &Tipset,
110 state_root: &Cid,
111 executed_messages: &[ExecutedMessage],
112) -> anyhow::Result<()> {
113 let key = tipset.key().cid()?;
114 if state_manager.db().read_bloom(&key)?.is_some() {
115 return Ok(());
116 }
117 let bloom = compute_block_logs_bloom(state_manager, state_root, executed_messages)?;
118 state_manager
119 .db()
120 .write_bloom(&key, tipset.epoch(), &bloom.0.0)
121}
122
123pub(super) fn block_logs_bloom(
127 state_manager: &StateManager,
128 tipset: &Tipset,
129 state_root: &Cid,
130 executed_messages: &[ExecutedMessage],
131) -> anyhow::Result<Bloom> {
132 crate::def_is_env_truthy!(compute_bloom_on_miss, COMPUTE_BLOOM_ON_MISS_ENV);
133
134 let key = tipset.key().cid()?;
135 if let Some(bloom) = state_manager.db().read_bloom(&key)? {
136 return Ok(Bloom(ethereum_types::Bloom(bloom)));
137 }
138
139 if compute_bloom_on_miss() {
140 let bloom = compute_block_logs_bloom(state_manager, state_root, executed_messages)?;
141 state_manager
142 .db()
143 .write_bloom(&key, tipset.epoch(), &bloom.0.0)?;
144 return Ok(bloom);
145 }
146 Ok(Bloom(ethereum_types::Bloom(FULL_BLOOM)))
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_accrue_eth_log_and_block_bloom_decomposition() {
155 let empty = Bloom::default();
156 let full = Bloom(ethereum_types::Bloom(FULL_BLOOM));
157
158 assert_eq!(empty.0.0, EMPTY_BLOOM);
161
162 let addr_a = EthAddress(ethereum_types::H160::from_slice(&[0x11; ADDRESS_LENGTH]));
163 let topic_a = EthHash(ethereum_types::H256::from_slice(&[0x22; EVM_WORD_LENGTH]));
164 let addr_b = EthAddress(ethereum_types::H160::from_slice(&[0x33; ADDRESS_LENGTH]));
165 let topic_b = EthHash(ethereum_types::H256::from_slice(&[0x44; EVM_WORD_LENGTH]));
166
167 let mut bloom_a = empty.clone();
169 accrue_eth_log(&mut bloom_a, &addr_a, std::slice::from_ref(&topic_a));
170 assert_ne!(bloom_a, empty);
171 assert_ne!(bloom_a, full);
172
173 let mut bloom_b = empty;
174 accrue_eth_log(&mut bloom_b, &addr_b, std::slice::from_ref(&topic_b));
175
176 let mut combined = bloom_a.clone();
179 accrue_eth_log(&mut combined, &addr_b, std::slice::from_ref(&topic_b));
180
181 let mut expected = bloom_a.0.0;
182 for (out, b) in expected.iter_mut().zip(bloom_b.0.0.iter()) {
183 *out |= *b;
184 }
185 assert_eq!(combined.0.0, expected);
186
187 let mut twice = bloom_a.clone();
189 accrue_eth_log(&mut twice, &addr_a, std::slice::from_ref(&topic_a));
190 assert_eq!(twice, bloom_a);
191 }
192}