1use std::collections::{HashMap, HashSet};
4use std::fmt;
5use std::sync::Arc;
6
7use alloy_eips::BlockId;
8use alloy_network::AnyNetwork;
9use alloy_primitives::{Address, B256, U256};
10use alloy_provider::Provider;
11
12use crate::cache::{
13 EvmCache, StorageBatchConfig, StorageBatchFetchFn, StorageFetchStrategy,
14 provider_storage_fetcher,
15};
16use crate::errors::StorageFetchResult;
17use crate::freshness::{SlotFetch, SlotOutcome};
18use crate::state_update::{StateDiff, StateUpdate};
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub struct StorageSlotRequest {
23 address: Address,
24 slot: U256,
25}
26
27impl StorageSlotRequest {
28 pub const fn new(address: Address, slot: U256) -> Self {
30 Self { address, slot }
31 }
32
33 pub const fn address(self) -> Address {
35 self.address
36 }
37
38 pub const fn slot(self) -> U256 {
40 self.slot
41 }
42}
43
44impl From<(Address, U256)> for StorageSlotRequest {
45 fn from((address, slot): (Address, U256)) -> Self {
46 Self::new(address, slot)
47 }
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
52pub struct PreparedStorageValue {
53 address: Address,
54 slot: U256,
55 value: U256,
56}
57
58impl PreparedStorageValue {
59 pub const fn new(address: Address, slot: U256, value: U256) -> Self {
61 Self {
62 address,
63 slot,
64 value,
65 }
66 }
67
68 pub const fn address(self) -> Address {
70 self.address
71 }
72
73 pub const fn slot(self) -> U256 {
75 self.slot
76 }
77
78 pub const fn value(self) -> U256 {
80 self.value
81 }
82}
83
84#[derive(Clone, Debug, PartialEq, Eq)]
90pub struct PreparedStoragePatch {
91 block_hash: B256,
92 values: Vec<PreparedStorageValue>,
93}
94
95impl EvmCache {
96 pub fn apply_prepared_storage_patch(
104 &mut self,
105 patch: &PreparedStoragePatch,
106 ) -> Result<StateDiff, PreparedStoragePatchError> {
107 let mut identities = HashSet::with_capacity(patch.values.len());
108 for value in &patch.values {
109 if !identities.insert((value.address, value.slot)) {
110 return Err(PreparedStoragePatchError::DuplicateSlot {
111 address: value.address,
112 slot: value.slot,
113 });
114 }
115 }
116
117 let cache_hash = match self.block() {
118 BlockId::Hash(hash) => Some(hash.block_hash),
119 BlockId::Number(_) => None,
120 };
121 if cache_hash != Some(patch.block_hash) {
122 return Err(PreparedStoragePatchError::BaselineMismatch {
123 prepared: patch.block_hash,
124 cache: cache_hash,
125 });
126 }
127
128 let updates: Vec<_> = patch
129 .values
130 .iter()
131 .map(|value| StateUpdate::slot(value.address, value.slot, value.value))
132 .collect();
133 Ok(self.apply_updates(&updates))
134 }
135}
136
137impl PreparedStoragePatch {
138 pub fn new(block_hash: B256, values: impl IntoIterator<Item = PreparedStorageValue>) -> Self {
140 Self {
141 block_hash,
142 values: values.into_iter().collect(),
143 }
144 }
145
146 pub const fn block_hash(&self) -> B256 {
148 self.block_hash
149 }
150
151 pub fn values(&self) -> &[PreparedStorageValue] {
153 &self.values
154 }
155}
156
157#[derive(Clone, Debug, PartialEq, Eq)]
159pub struct StorageRoundRequest {
160 block_hash: B256,
161 verify: Vec<StorageSlotRequest>,
162 probe: Vec<StorageSlotRequest>,
163}
164
165impl StorageRoundRequest {
166 pub fn new<V, P, VI, PI>(block_hash: B256, verify: V, probe: P) -> Self
169 where
170 V: IntoIterator<Item = VI>,
171 P: IntoIterator<Item = PI>,
172 VI: Into<StorageSlotRequest>,
173 PI: Into<StorageSlotRequest>,
174 {
175 Self {
176 block_hash,
177 verify: verify.into_iter().map(Into::into).collect(),
178 probe: probe.into_iter().map(Into::into).collect(),
179 }
180 }
181
182 pub const fn block_hash(&self) -> B256 {
184 self.block_hash
185 }
186
187 pub fn verify(&self) -> &[StorageSlotRequest] {
189 &self.verify
190 }
191
192 pub fn probe(&self) -> &[StorageSlotRequest] {
194 &self.probe
195 }
196}
197
198#[derive(Clone, Debug, PartialEq, Eq)]
200pub struct StorageRoundFetch {
201 block_hash: B256,
202 verified: Vec<SlotOutcome>,
203 probed: Vec<SlotOutcome>,
204 patch: PreparedStoragePatch,
205}
206
207impl StorageRoundFetch {
208 pub const fn block_hash(&self) -> B256 {
210 self.block_hash
211 }
212
213 pub fn verified(&self) -> &[SlotOutcome] {
215 &self.verified
216 }
217
218 pub fn probed(&self) -> &[SlotOutcome] {
220 &self.probed
221 }
222
223 pub const fn patch(&self) -> &PreparedStoragePatch {
225 &self.patch
226 }
227
228 pub fn into_patch(self) -> PreparedStoragePatch {
230 self.patch
231 }
232
233 pub fn into_parts(self) -> (Vec<SlotOutcome>, Vec<SlotOutcome>, PreparedStoragePatch) {
235 (self.verified, self.probed, self.patch)
236 }
237}
238
239#[derive(Clone)]
241pub struct StorageRoundFetcher {
242 fetcher: StorageBatchFetchFn,
243}
244
245impl fmt::Debug for StorageRoundFetcher {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 f.debug_struct("StorageRoundFetcher")
248 .finish_non_exhaustive()
249 }
250}
251
252impl StorageRoundFetcher {
253 pub fn new(fetcher: StorageBatchFetchFn) -> Self {
255 Self { fetcher }
256 }
257
258 pub fn from_provider<P>(
261 provider: Arc<P>,
262 batch_config: StorageBatchConfig,
263 strategy: StorageFetchStrategy,
264 ) -> Self
265 where
266 P: Provider<AnyNetwork> + 'static,
267 {
268 Self::new(provider_storage_fetcher(provider, batch_config, strategy))
269 }
270
271 pub fn fetch(
278 &self,
279 request: &StorageRoundRequest,
280 ) -> Result<StorageRoundFetch, StorageRoundFetchError> {
281 let mut requested = HashSet::with_capacity(request.verify.len() + request.probe.len());
282 for slot in request.verify.iter().chain(&request.probe) {
283 if !requested.insert((slot.address, slot.slot)) {
284 return Err(StorageRoundFetchError::DuplicateRequest {
285 address: slot.address,
286 slot: slot.slot,
287 });
288 }
289 }
290
291 let provider_requests: Vec<_> = request
292 .verify
293 .iter()
294 .chain(&request.probe)
295 .map(|slot| (slot.address, slot.slot))
296 .collect();
297 let response = (self.fetcher)(
298 provider_requests,
299 BlockId::from((request.block_hash, Some(true))),
300 );
301
302 let mut returned: HashMap<(Address, U256), StorageFetchResult<U256>> =
303 HashMap::with_capacity(response.len());
304 for (address, slot, value) in response {
305 if !requested.contains(&(address, slot)) {
306 return Err(StorageRoundFetchError::UnexpectedResult { address, slot });
307 }
308 if returned.insert((address, slot), value).is_some() {
309 return Err(StorageRoundFetchError::DuplicateResult { address, slot });
310 }
311 }
312
313 for &(address, slot) in &requested {
314 if !returned.contains_key(&(address, slot)) {
315 return Err(StorageRoundFetchError::MissingResult { address, slot });
316 }
317 }
318
319 let mut patch = Vec::with_capacity(request.verify.len());
320 let verified = take_outcomes(&request.verify, &mut returned, Some(&mut patch));
321 let probed = take_outcomes(&request.probe, &mut returned, None);
322 Ok(StorageRoundFetch {
323 block_hash: request.block_hash,
324 verified,
325 probed,
326 patch: PreparedStoragePatch::new(request.block_hash, patch),
327 })
328 }
329}
330
331fn take_outcomes(
332 requests: &[StorageSlotRequest],
333 returned: &mut HashMap<(Address, U256), StorageFetchResult<U256>>,
334 mut patch: Option<&mut Vec<PreparedStorageValue>>,
335) -> Vec<SlotOutcome> {
336 requests
337 .iter()
338 .map(|request| {
339 let fetched = returned
340 .remove(&(request.address, request.slot))
341 .expect("provider response completeness validated above");
342 let fetch = match fetched {
343 Ok(value) => {
344 if let Some(values) = patch.as_deref_mut() {
345 values.push(PreparedStorageValue::new(
346 request.address,
347 request.slot,
348 value,
349 ));
350 }
351 if value == U256::ZERO {
352 SlotFetch::Zero
353 } else {
354 SlotFetch::Value(value)
355 }
356 }
357 Err(error) => SlotFetch::FetchFailed {
358 reason: error.to_string(),
359 },
360 };
361 SlotOutcome {
362 address: request.address,
363 slot: request.slot,
364 fetch,
365 }
366 })
367 .collect()
368}
369
370#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
372#[non_exhaustive]
373pub enum StorageRoundFetchError {
374 #[error("duplicate storage-round request for ({address}, {slot})")]
376 DuplicateRequest {
377 address: Address,
379 slot: U256,
381 },
382 #[error("storage provider returned unexpected slot ({address}, {slot})")]
384 UnexpectedResult {
385 address: Address,
387 slot: U256,
389 },
390 #[error("storage provider returned duplicate slot ({address}, {slot})")]
392 DuplicateResult {
393 address: Address,
395 slot: U256,
397 },
398 #[error("storage provider omitted requested slot ({address}, {slot})")]
400 MissingResult {
401 address: Address,
403 slot: U256,
405 },
406}
407
408#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
410#[non_exhaustive]
411pub enum PreparedStoragePatchError {
412 #[error("prepared storage patch contains duplicate slot ({address}, {slot})")]
414 DuplicateSlot {
415 address: Address,
417 slot: U256,
419 },
420 #[error("prepared storage patch baseline mismatch: prepared {prepared}, cache {cache:?}")]
422 BaselineMismatch {
423 prepared: B256,
425 cache: Option<B256>,
427 },
428}