1use crate::eth::{
3 backend::{
4 db::{
5 MaybeFullDatabase, SerializableBlock, SerializableHistoricalStates,
6 SerializableTransaction, StateDb,
7 },
8 env::Env,
9 mem::cache::DiskStateCache,
10 },
11 pool::transactions::PoolTransaction,
12};
13use alloy_consensus::constants::EMPTY_WITHDRAWALS;
14use alloy_eips::eip7685::EMPTY_REQUESTS_HASH;
15use alloy_primitives::{
16 B256, Bytes, U256,
17 map::{B256HashMap, HashMap},
18};
19use alloy_rpc_types::{
20 BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo,
21 trace::{
22 otterscan::{InternalOperation, OperationType},
23 parity::LocalizedTransactionTrace,
24 },
25};
26use anvil_core::eth::{
27 block::{Block, PartialHeader},
28 transaction::{MaybeImpersonatedTransaction, ReceiptResponse, TransactionInfo, TypedReceipt},
29};
30use foundry_evm::{
31 backend::MemDb,
32 traces::{CallKind, ParityTraceBuilder, TracingInspectorConfig},
33};
34use parking_lot::RwLock;
35use revm::{context::Block as RevmBlock, primitives::hardfork::SpecId};
36use std::{collections::VecDeque, fmt, path::PathBuf, sync::Arc, time::Duration};
37pub const DEFAULT_HISTORY_LIMIT: usize = 500;
42const MIN_HISTORY_LIMIT: usize = 10;
43const MAX_ON_DISK_HISTORY_LIMIT: usize = 3_600;
45
46pub struct InMemoryBlockStates {
48 states: B256HashMap<StateDb>,
50 on_disk_states: B256HashMap<StateDb>,
52 in_memory_limit: usize,
54 min_in_memory_limit: usize,
56 max_on_disk_limit: usize,
60 oldest_on_disk: VecDeque<B256>,
62 present: VecDeque<B256>,
64 disk_cache: DiskStateCache,
66}
67
68impl InMemoryBlockStates {
69 pub fn new(in_memory_limit: usize, on_disk_limit: usize) -> Self {
71 Self {
72 states: Default::default(),
73 on_disk_states: Default::default(),
74 in_memory_limit,
75 min_in_memory_limit: in_memory_limit.min(MIN_HISTORY_LIMIT),
76 max_on_disk_limit: on_disk_limit,
77 oldest_on_disk: Default::default(),
78 present: Default::default(),
79 disk_cache: Default::default(),
80 }
81 }
82
83 pub fn memory_only(mut self) -> Self {
85 self.max_on_disk_limit = 0;
86 self
87 }
88
89 pub fn disk_path(mut self, path: PathBuf) -> Self {
91 self.disk_cache = self.disk_cache.with_path(path);
92 self
93 }
94
95 pub fn update_interval_mine_block_time(&mut self, block_time: Duration) {
100 let block_time = block_time.as_secs();
101 if block_time <= 2 {
105 self.in_memory_limit = DEFAULT_HISTORY_LIMIT * 3;
106 self.enforce_limits();
107 }
108 }
109
110 fn is_memory_only(&self) -> bool {
112 self.max_on_disk_limit == 0
113 }
114
115 pub fn insert(&mut self, hash: B256, state: StateDb) {
126 if !self.is_memory_only() && self.present.len() >= self.in_memory_limit {
127 self.in_memory_limit =
129 self.in_memory_limit.saturating_sub(1).max(self.min_in_memory_limit);
130 }
131
132 self.enforce_limits();
133
134 self.states.insert(hash, state);
135 self.present.push_back(hash);
136 }
137
138 fn enforce_limits(&mut self) {
140 while self.present.len() >= self.in_memory_limit {
142 if let Some((hash, mut state)) = self
144 .present
145 .pop_front()
146 .and_then(|hash| self.states.remove(&hash).map(|state| (hash, state)))
147 {
148 if !self.is_memory_only() {
150 let state_snapshot = state.0.clear_into_state_snapshot();
151 self.disk_cache.write(hash, state_snapshot);
152 self.on_disk_states.insert(hash, state);
153 self.oldest_on_disk.push_back(hash);
154 }
155 }
156 }
157
158 while !self.is_memory_only() && self.oldest_on_disk.len() >= self.max_on_disk_limit {
160 if let Some(hash) = self.oldest_on_disk.pop_front() {
162 self.on_disk_states.remove(&hash);
163 self.disk_cache.remove(hash);
164 }
165 }
166 }
167
168 pub fn get_state(&self, hash: &B256) -> Option<&StateDb> {
170 self.states.get(hash)
171 }
172
173 pub fn get_on_disk_state(&mut self, hash: &B256) -> Option<&StateDb> {
175 if let Some(state) = self.on_disk_states.get_mut(hash)
176 && let Some(cached) = self.disk_cache.read(*hash)
177 {
178 state.init_from_state_snapshot(cached);
179 return Some(state);
180 }
181
182 None
183 }
184
185 pub fn set_cache_limit(&mut self, limit: usize) {
187 self.in_memory_limit = limit;
188 }
189
190 pub fn clear(&mut self) {
192 self.states.clear();
193 self.on_disk_states.clear();
194 self.present.clear();
195 for on_disk in std::mem::take(&mut self.oldest_on_disk) {
196 self.disk_cache.remove(on_disk)
197 }
198 }
199
200 pub fn serialized_states(&mut self) -> SerializableHistoricalStates {
202 let mut states = self
204 .states
205 .iter_mut()
206 .map(|(hash, state)| (*hash, state.serialize_state()))
207 .collect::<Vec<_>>();
208
209 self.on_disk_states.iter().for_each(|(hash, _)| {
211 if let Some(state_snapshot) = self.disk_cache.read(*hash) {
212 states.push((*hash, state_snapshot));
213 }
214 });
215
216 SerializableHistoricalStates::new(states)
217 }
218
219 pub fn load_states(&mut self, states: SerializableHistoricalStates) {
221 for (hash, state_snapshot) in states {
222 let mut state_db = StateDb::new(MemDb::default());
223 state_db.init_from_state_snapshot(state_snapshot);
224 self.insert(hash, state_db);
225 }
226 }
227}
228
229impl fmt::Debug for InMemoryBlockStates {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 f.debug_struct("InMemoryBlockStates")
232 .field("in_memory_limit", &self.in_memory_limit)
233 .field("min_in_memory_limit", &self.min_in_memory_limit)
234 .field("max_on_disk_limit", &self.max_on_disk_limit)
235 .field("oldest_on_disk", &self.oldest_on_disk)
236 .field("present", &self.present)
237 .finish_non_exhaustive()
238 }
239}
240
241impl Default for InMemoryBlockStates {
242 fn default() -> Self {
243 Self::new(DEFAULT_HISTORY_LIMIT, MAX_ON_DISK_HISTORY_LIMIT)
245 }
246}
247
248#[derive(Clone, Debug)]
250pub struct BlockchainStorage {
251 pub blocks: B256HashMap<Block>,
253 pub hashes: HashMap<u64, B256>,
255 pub best_hash: B256,
257 pub best_number: u64,
259 pub genesis_hash: B256,
261 pub transactions: B256HashMap<MinedTransaction>,
264 pub total_difficulty: U256,
266}
267
268impl BlockchainStorage {
269 pub fn new(
271 env: &Env,
272 spec_id: SpecId,
273 base_fee: Option<u64>,
274 timestamp: u64,
275 genesis_number: u64,
276 ) -> Self {
277 let is_shanghai = spec_id >= SpecId::SHANGHAI;
278 let is_cancun = spec_id >= SpecId::CANCUN;
279 let is_prague = spec_id >= SpecId::PRAGUE;
280
281 let partial_header = PartialHeader {
283 timestamp,
284 base_fee,
285 gas_limit: env.evm_env.block_env.gas_limit,
286 beneficiary: env.evm_env.block_env.beneficiary,
287 difficulty: env.evm_env.block_env.difficulty,
288 blob_gas_used: env.evm_env.block_env.blob_excess_gas_and_price.as_ref().map(|_| 0),
289 excess_blob_gas: env.evm_env.block_env.blob_excess_gas(),
290 number: genesis_number,
291 parent_beacon_block_root: is_cancun.then_some(Default::default()),
292 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
293 requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
294 ..Default::default()
295 };
296 let block = Block::new::<MaybeImpersonatedTransaction>(partial_header, vec![]);
297 let genesis_hash = block.header.hash_slow();
298 let best_hash = genesis_hash;
299 let best_number = genesis_number;
300
301 let mut blocks = B256HashMap::default();
302 blocks.insert(genesis_hash, block);
303
304 let mut hashes = HashMap::default();
305 hashes.insert(best_number, genesis_hash);
306 Self {
307 blocks,
308 hashes,
309 best_hash,
310 best_number,
311 genesis_hash,
312 transactions: Default::default(),
313 total_difficulty: Default::default(),
314 }
315 }
316
317 pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self {
318 let mut hashes = HashMap::default();
319 hashes.insert(block_number, block_hash);
320
321 Self {
322 blocks: B256HashMap::default(),
323 hashes,
324 best_hash: block_hash,
325 best_number: block_number,
326 genesis_hash: Default::default(),
327 transactions: Default::default(),
328 total_difficulty,
329 }
330 }
331
332 pub fn unwind_to(&mut self, block_number: u64, block_hash: B256) -> Vec<Block> {
337 let mut removed = vec![];
338 let best_num: u64 = self.best_number;
339 for i in (block_number + 1)..=best_num {
340 if let Some(hash) = self.hashes.get(&i).copied() {
341 self.remove_block_transactions_by_number(i);
343
344 if let Some(block) = self.blocks.remove(&hash) {
346 removed.push(block);
347 }
348 self.hashes.remove(&i);
349 }
350 }
351 self.best_hash = block_hash;
352 self.best_number = block_number;
353 removed
354 }
355
356 pub fn empty() -> Self {
357 Self {
358 blocks: Default::default(),
359 hashes: Default::default(),
360 best_hash: Default::default(),
361 best_number: Default::default(),
362 genesis_hash: Default::default(),
363 transactions: Default::default(),
364 total_difficulty: Default::default(),
365 }
366 }
367
368 pub fn remove_block_transactions_by_number(&mut self, num: u64) {
370 if let Some(hash) = self.hashes.get(&num).copied() {
371 self.remove_block_transactions(hash);
372 }
373 }
374
375 pub fn remove_block_transactions(&mut self, block_hash: B256) {
377 if let Some(block) = self.blocks.get_mut(&block_hash) {
378 for tx in &block.transactions {
379 self.transactions.remove(&tx.hash());
380 }
381 block.transactions.clear();
382 }
383 }
384}
385
386impl BlockchainStorage {
387 pub fn hash(&self, number: BlockNumberOrTag) -> Option<B256> {
389 let slots_in_an_epoch = 32;
390 match number {
391 BlockNumberOrTag::Latest => Some(self.best_hash),
392 BlockNumberOrTag::Earliest => Some(self.genesis_hash),
393 BlockNumberOrTag::Pending => None,
394 BlockNumberOrTag::Number(num) => self.hashes.get(&num).copied(),
395 BlockNumberOrTag::Safe => {
396 if self.best_number > (slots_in_an_epoch) {
397 self.hashes.get(&(self.best_number - (slots_in_an_epoch))).copied()
398 } else {
399 Some(self.genesis_hash) }
401 }
402 BlockNumberOrTag::Finalized => {
403 if self.best_number > (slots_in_an_epoch * 2) {
404 self.hashes.get(&(self.best_number - (slots_in_an_epoch * 2))).copied()
405 } else {
406 Some(self.genesis_hash)
407 }
408 }
409 }
410 }
411
412 pub fn serialized_blocks(&self) -> Vec<SerializableBlock> {
413 self.blocks.values().map(|block| block.clone().into()).collect()
414 }
415
416 pub fn serialized_transactions(&self) -> Vec<SerializableTransaction> {
417 self.transactions.values().map(|tx: &MinedTransaction| tx.clone().into()).collect()
418 }
419
420 pub fn load_blocks(&mut self, serializable_blocks: Vec<SerializableBlock>) {
422 for serializable_block in &serializable_blocks {
423 let block: Block = serializable_block.clone().into();
424 let block_hash = block.header.hash_slow();
425 let block_number = block.header.number;
426 self.blocks.insert(block_hash, block);
427 self.hashes.insert(block_number, block_hash);
428 }
429 }
430
431 pub fn load_transactions(&mut self, serializable_transactions: Vec<SerializableTransaction>) {
433 for serializable_transaction in &serializable_transactions {
434 let transaction: MinedTransaction = serializable_transaction.clone().into();
435 self.transactions.insert(transaction.info.transaction_hash, transaction);
436 }
437 }
438}
439
440#[derive(Clone, Debug)]
442pub struct Blockchain {
443 pub storage: Arc<RwLock<BlockchainStorage>>,
445}
446
447impl Blockchain {
448 pub fn new(
450 env: &Env,
451 spec_id: SpecId,
452 base_fee: Option<u64>,
453 timestamp: u64,
454 genesis_number: u64,
455 ) -> Self {
456 Self {
457 storage: Arc::new(RwLock::new(BlockchainStorage::new(
458 env,
459 spec_id,
460 base_fee,
461 timestamp,
462 genesis_number,
463 ))),
464 }
465 }
466
467 pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self {
468 Self {
469 storage: Arc::new(RwLock::new(BlockchainStorage::forked(
470 block_number,
471 block_hash,
472 total_difficulty,
473 ))),
474 }
475 }
476
477 pub fn hash(&self, id: BlockId) -> Option<B256> {
479 match id {
480 BlockId::Hash(h) => Some(h.block_hash),
481 BlockId::Number(num) => self.storage.read().hash(num),
482 }
483 }
484
485 pub fn get_block_by_hash(&self, hash: &B256) -> Option<Block> {
486 self.storage.read().blocks.get(hash).cloned()
487 }
488
489 pub fn get_transaction_by_hash(&self, hash: &B256) -> Option<MinedTransaction> {
490 self.storage.read().transactions.get(hash).cloned()
491 }
492
493 pub fn blocks_count(&self) -> usize {
495 self.storage.read().blocks.len()
496 }
497}
498
499#[derive(Clone, Debug)]
501pub struct MinedBlockOutcome {
502 pub block_number: u64,
504 pub included: Vec<Arc<PoolTransaction>>,
506 pub invalid: Vec<Arc<PoolTransaction>>,
509}
510
511#[derive(Clone, Debug)]
513pub struct MinedTransaction {
514 pub info: TransactionInfo,
515 pub receipt: TypedReceipt,
516 pub block_hash: B256,
517 pub block_number: u64,
518}
519
520impl MinedTransaction {
521 pub fn parity_traces(&self) -> Vec<LocalizedTransactionTrace> {
523 ParityTraceBuilder::new(
524 self.info.traces.clone(),
525 None,
526 TracingInspectorConfig::default_parity(),
527 )
528 .into_localized_transaction_traces(RethTransactionInfo {
529 hash: Some(self.info.transaction_hash),
530 index: Some(self.info.transaction_index),
531 block_hash: Some(self.block_hash),
532 block_number: Some(self.block_number),
533 base_fee: None,
534 })
535 }
536
537 pub fn ots_internal_operations(&self) -> Vec<InternalOperation> {
538 self.info
539 .traces
540 .iter()
541 .filter_map(|node| {
542 let r#type = match node.trace.kind {
543 _ if node.is_selfdestruct() => OperationType::OpSelfDestruct,
544 CallKind::Call if !node.trace.value.is_zero() => OperationType::OpTransfer,
545 CallKind::Create => OperationType::OpCreate,
546 CallKind::Create2 => OperationType::OpCreate2,
547 _ => return None,
548 };
549 let mut from = node.trace.caller;
550 let mut to = node.trace.address;
551 let mut value = node.trace.value;
552 if node.is_selfdestruct() {
553 from = node.trace.address;
554 to = node.trace.selfdestruct_refund_target.unwrap_or_default();
555 value = node.trace.selfdestruct_transferred_value.unwrap_or_default();
556 }
557 Some(InternalOperation { r#type, from, to, value })
558 })
559 .collect()
560 }
561}
562
563#[derive(Clone, Debug)]
565pub struct MinedTransactionReceipt {
566 pub inner: ReceiptResponse,
568 pub out: Option<Bytes>,
570}
571
572#[cfg(test)]
573mod tests {
574 use super::*;
575 use crate::eth::backend::db::Db;
576 use alloy_primitives::{Address, hex};
577 use alloy_rlp::Decodable;
578 use anvil_core::eth::transaction::TypedTransaction;
579 use revm::{database::DatabaseRef, state::AccountInfo};
580
581 #[test]
582 fn test_interval_update() {
583 let mut storage = InMemoryBlockStates::default();
584 storage.update_interval_mine_block_time(Duration::from_secs(1));
585 assert_eq!(storage.in_memory_limit, DEFAULT_HISTORY_LIMIT * 3);
586 }
587
588 #[test]
589 fn test_init_state_limits() {
590 let mut storage = InMemoryBlockStates::default();
591 assert_eq!(storage.in_memory_limit, DEFAULT_HISTORY_LIMIT);
592 assert_eq!(storage.min_in_memory_limit, MIN_HISTORY_LIMIT);
593 assert_eq!(storage.max_on_disk_limit, MAX_ON_DISK_HISTORY_LIMIT);
594
595 storage = storage.memory_only();
596 assert!(storage.is_memory_only());
597
598 storage = InMemoryBlockStates::new(1, 0);
599 assert!(storage.is_memory_only());
600 assert_eq!(storage.in_memory_limit, 1);
601 assert_eq!(storage.min_in_memory_limit, 1);
602 assert_eq!(storage.max_on_disk_limit, 0);
603
604 storage = InMemoryBlockStates::new(1, 2);
605 assert!(!storage.is_memory_only());
606 assert_eq!(storage.in_memory_limit, 1);
607 assert_eq!(storage.min_in_memory_limit, 1);
608 assert_eq!(storage.max_on_disk_limit, 2);
609 }
610
611 #[tokio::test(flavor = "multi_thread")]
612 async fn can_read_write_cached_state() {
613 let mut storage = InMemoryBlockStates::new(1, MAX_ON_DISK_HISTORY_LIMIT);
614 let one = B256::from(U256::from(1));
615 let two = B256::from(U256::from(2));
616
617 let mut state = MemDb::default();
618 let addr = Address::random();
619 let info = AccountInfo::from_balance(U256::from(1337));
620 state.insert_account(addr, info);
621 storage.insert(one, StateDb::new(state));
622 storage.insert(two, StateDb::new(MemDb::default()));
623
624 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
626
627 assert_eq!(storage.on_disk_states.len(), 1);
628 assert!(storage.on_disk_states.contains_key(&one));
629
630 let loaded = storage.get_on_disk_state(&one).unwrap();
631
632 let acc = loaded.basic_ref(addr).unwrap().unwrap();
633 assert_eq!(acc.balance, U256::from(1337u64));
634 }
635
636 #[tokio::test(flavor = "multi_thread")]
637 async fn can_decrease_state_cache_size() {
638 let limit = 15;
639 let mut storage = InMemoryBlockStates::new(limit, MAX_ON_DISK_HISTORY_LIMIT);
640
641 let num_states = 30;
642 for idx in 0..num_states {
643 let mut state = MemDb::default();
644 let hash = B256::from(U256::from(idx));
645 let addr = Address::from_word(hash);
646 let balance = (idx * 2) as u64;
647 let info = AccountInfo::from_balance(U256::from(balance));
648 state.insert_account(addr, info);
649 storage.insert(hash, StateDb::new(state));
650 }
651
652 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
654
655 let on_disk_states_len = num_states - storage.min_in_memory_limit;
656
657 assert_eq!(storage.on_disk_states.len(), on_disk_states_len);
658 assert_eq!(storage.present.len(), storage.min_in_memory_limit);
659
660 for idx in 0..num_states {
661 let hash = B256::from(U256::from(idx));
662 let addr = Address::from_word(hash);
663
664 let loaded = if idx < on_disk_states_len {
665 storage.get_on_disk_state(&hash).unwrap()
666 } else {
667 storage.get_state(&hash).unwrap()
668 };
669
670 let acc = loaded.basic_ref(addr).unwrap().unwrap();
671 let balance = (idx * 2) as u64;
672 assert_eq!(acc.balance, U256::from(balance));
673 }
674 }
675
676 #[test]
679 fn test_storage_dump_reload_cycle() {
680 let mut dump_storage = BlockchainStorage::empty();
681
682 let partial_header = PartialHeader { gas_limit: 123456, ..Default::default() };
683 let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..];
684 let tx: MaybeImpersonatedTransaction =
685 TypedTransaction::decode(&mut &bytes_first[..]).unwrap().into();
686 let block =
687 Block::new::<MaybeImpersonatedTransaction>(partial_header.clone(), vec![tx.clone()]);
688 let block_hash = block.header.hash_slow();
689 dump_storage.blocks.insert(block_hash, block);
690
691 let serialized_blocks = dump_storage.serialized_blocks();
692 let serialized_transactions = dump_storage.serialized_transactions();
693
694 let mut load_storage = BlockchainStorage::empty();
695
696 load_storage.load_blocks(serialized_blocks);
697 load_storage.load_transactions(serialized_transactions);
698
699 let loaded_block = load_storage.blocks.get(&block_hash).unwrap();
700 assert_eq!(loaded_block.header.gas_limit, { partial_header.gas_limit });
701 let loaded_tx = loaded_block.transactions.first().unwrap();
702 assert_eq!(loaded_tx, &tx);
703 }
704}