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