1use crate::eth::{
3 backend::{
4 db::{
5 MaybeFullDatabase, SerializableBlock, SerializableHistoricalStates,
6 SerializableTransaction, StateDb,
7 },
8 mem::cache::DiskStateCache,
9 },
10 error::BlockchainError,
11 pool::transactions::PoolTransaction,
12};
13use alloy_consensus::constants::EMPTY_WITHDRAWALS;
14use alloy_eips::eip7685::EMPTY_REQUESTS_HASH;
15use alloy_primitives::{
16 map::{B256HashMap, HashMap},
17 Bytes, B256, U256, U64,
18};
19use alloy_rpc_types::{
20 trace::{
21 geth::{
22 FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
23 GethDebugTracingOptions, GethTrace, NoopFrame,
24 },
25 otterscan::{InternalOperation, OperationType},
26 parity::LocalizedTransactionTrace,
27 },
28 BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo,
29};
30use anvil_core::eth::{
31 block::{Block, PartialHeader},
32 transaction::{MaybeImpersonatedTransaction, ReceiptResponse, TransactionInfo, TypedReceipt},
33};
34use anvil_rpc::error::RpcError;
35use foundry_evm::{
36 backend::MemDb,
37 revm::primitives::Env,
38 traces::{
39 CallKind, FourByteInspector, GethTraceBuilder, ParityTraceBuilder, TracingInspectorConfig,
40 },
41};
42use parking_lot::RwLock;
43use revm::primitives::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.block.gas_limit.to::<u64>(),
290 beneficiary: env.block.coinbase,
291 difficulty: env.block.difficulty,
292 blob_gas_used: env.block.blob_excess_gas_and_price.as_ref().map(|_| 0),
293 excess_blob_gas: env.block.get_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: U64 = U64::from(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(U64::from(block_number), block_hash);
324
325 Self {
326 blocks: B256HashMap::default(),
327 hashes,
328 best_hash: block_hash,
329 best_number: U64::from(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.try_into().unwrap_or(0);
342 for i in (block_number + 1)..=best_num {
343 if let Some(hash) = self.hashes.remove(&U64::from(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 = U64::from(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(&(U64::from(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 = U64::from(32u64);
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(&U64::from(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 * U64::from(2)) {
401 self.hashes
402 .get(&(self.best_number - (slots_in_an_epoch * U64::from(2))))
403 .copied()
404 } else {
405 Some(self.genesis_hash)
406 }
407 }
408 }
409 }
410
411 pub fn serialized_blocks(&self) -> Vec<SerializableBlock> {
412 self.blocks.values().map(|block| block.clone().into()).collect()
413 }
414
415 pub fn serialized_transactions(&self) -> Vec<SerializableTransaction> {
416 self.transactions.values().map(|tx: &MinedTransaction| tx.clone().into()).collect()
417 }
418
419 pub fn load_blocks(&mut self, serializable_blocks: Vec<SerializableBlock>) {
421 for serializable_block in &serializable_blocks {
422 let block: Block = serializable_block.clone().into();
423 let block_hash = block.header.hash_slow();
424 let block_number = block.header.number;
425 self.blocks.insert(block_hash, block);
426 self.hashes.insert(U64::from(block_number), block_hash);
427 }
428 }
429
430 pub fn load_transactions(&mut self, serializable_transactions: Vec<SerializableTransaction>) {
432 for serializable_transaction in &serializable_transactions {
433 let transaction: MinedTransaction = serializable_transaction.clone().into();
434 self.transactions.insert(transaction.info.transaction_hash, transaction);
435 }
436 }
437}
438
439#[derive(Clone)]
441pub struct Blockchain {
442 pub storage: Arc<RwLock<BlockchainStorage>>,
444}
445
446impl Blockchain {
447 pub fn new(
449 env: &Env,
450 spec_id: SpecId,
451 base_fee: Option<u64>,
452 timestamp: u64,
453 genesis_number: u64,
454 ) -> Self {
455 Self {
456 storage: Arc::new(RwLock::new(BlockchainStorage::new(
457 env,
458 spec_id,
459 base_fee,
460 timestamp,
461 genesis_number,
462 ))),
463 }
464 }
465
466 pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self {
467 Self {
468 storage: Arc::new(RwLock::new(BlockchainStorage::forked(
469 block_number,
470 block_hash,
471 total_difficulty,
472 ))),
473 }
474 }
475
476 pub fn hash(&self, id: BlockId) -> Option<B256> {
478 match id {
479 BlockId::Hash(h) => Some(h.block_hash),
480 BlockId::Number(num) => self.storage.read().hash(num),
481 }
482 }
483
484 pub fn get_block_by_hash(&self, hash: &B256) -> Option<Block> {
485 self.storage.read().blocks.get(hash).cloned()
486 }
487
488 pub fn get_transaction_by_hash(&self, hash: &B256) -> Option<MinedTransaction> {
489 self.storage.read().transactions.get(hash).cloned()
490 }
491
492 pub fn blocks_count(&self) -> usize {
494 self.storage.read().blocks.len()
495 }
496}
497
498#[derive(Clone, Debug)]
500pub struct MinedBlockOutcome {
501 pub block_number: U64,
503 pub included: Vec<Arc<PoolTransaction>>,
505 pub invalid: Vec<Arc<PoolTransaction>>,
508}
509
510#[derive(Clone, Debug)]
512pub struct MinedTransaction {
513 pub info: TransactionInfo,
514 pub receipt: TypedReceipt,
515 pub block_hash: B256,
516 pub block_number: u64,
517}
518
519impl MinedTransaction {
520 pub fn parity_traces(&self) -> Vec<LocalizedTransactionTrace> {
522 ParityTraceBuilder::new(
523 self.info.traces.clone(),
524 None,
525 TracingInspectorConfig::default_parity(),
526 )
527 .into_localized_transaction_traces(RethTransactionInfo {
528 hash: Some(self.info.transaction_hash),
529 index: Some(self.info.transaction_index),
530 block_hash: Some(self.block_hash),
531 block_number: Some(self.block_number),
532 base_fee: None,
533 })
534 }
535
536 pub fn ots_internal_operations(&self) -> Vec<InternalOperation> {
537 self.info
538 .traces
539 .iter()
540 .filter_map(|node| {
541 let r#type = match node.trace.kind {
542 _ if node.is_selfdestruct() => OperationType::OpSelfDestruct,
543 CallKind::Call if !node.trace.value.is_zero() => OperationType::OpTransfer,
544 CallKind::Create => OperationType::OpCreate,
545 CallKind::Create2 => OperationType::OpCreate2,
546 _ => return None,
547 };
548 let mut from = node.trace.caller;
549 let mut to = node.trace.address;
550 let mut value = node.trace.value;
551 if node.is_selfdestruct() {
552 from = node.trace.address;
553 to = node.trace.selfdestruct_refund_target.unwrap_or_default();
554 value = node.trace.selfdestruct_transferred_value.unwrap_or_default();
555 }
556 Some(InternalOperation { r#type, from, to, value })
557 })
558 .collect()
559 }
560
561 pub fn geth_trace(&self, opts: GethDebugTracingOptions) -> Result<GethTrace, BlockchainError> {
562 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
563
564 if let Some(tracer) = tracer {
565 match tracer {
566 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
567 GethDebugBuiltInTracerType::FourByteTracer => {
568 let inspector = FourByteInspector::default();
569 return Ok(FourByteFrame::from(inspector).into());
570 }
571 GethDebugBuiltInTracerType::CallTracer => {
572 return match tracer_config.into_call_config() {
573 Ok(call_config) => Ok(GethTraceBuilder::new(self.info.traces.clone())
574 .geth_call_traces(call_config, self.receipt.cumulative_gas_used())
575 .into()),
576 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
577 };
578 }
579 GethDebugBuiltInTracerType::PreStateTracer |
580 GethDebugBuiltInTracerType::NoopTracer |
581 GethDebugBuiltInTracerType::MuxTracer |
582 GethDebugBuiltInTracerType::FlatCallTracer => {}
583 },
584 GethDebugTracerType::JsTracer(_code) => {}
585 }
586
587 return Ok(NoopFrame::default().into());
588 }
589
590 Ok(GethTraceBuilder::new(self.info.traces.clone())
592 .geth_traces(
593 self.receipt.cumulative_gas_used(),
594 self.info.out.clone().unwrap_or_default(),
595 config,
596 )
597 .into())
598 }
599}
600
601#[derive(Clone, Debug)]
603pub struct MinedTransactionReceipt {
604 pub inner: ReceiptResponse,
606 pub out: Option<Bytes>,
608}
609
610#[cfg(test)]
611mod tests {
612 use super::*;
613 use crate::eth::backend::db::Db;
614 use alloy_primitives::{hex, Address};
615 use alloy_rlp::Decodable;
616 use anvil_core::eth::transaction::TypedTransaction;
617 use foundry_evm::{
618 backend::MemDb,
619 revm::{
620 db::DatabaseRef,
621 primitives::{AccountInfo, U256},
622 },
623 };
624
625 #[test]
626 fn test_interval_update() {
627 let mut storage = InMemoryBlockStates::default();
628 storage.update_interval_mine_block_time(Duration::from_secs(1));
629 assert_eq!(storage.in_memory_limit, DEFAULT_HISTORY_LIMIT * 3);
630 }
631
632 #[test]
633 fn test_init_state_limits() {
634 let mut storage = InMemoryBlockStates::default();
635 assert_eq!(storage.in_memory_limit, DEFAULT_HISTORY_LIMIT);
636 assert_eq!(storage.min_in_memory_limit, MIN_HISTORY_LIMIT);
637 assert_eq!(storage.max_on_disk_limit, MAX_ON_DISK_HISTORY_LIMIT);
638
639 storage = storage.memory_only();
640 assert!(storage.is_memory_only());
641
642 storage = InMemoryBlockStates::new(1, 0);
643 assert!(storage.is_memory_only());
644 assert_eq!(storage.in_memory_limit, 1);
645 assert_eq!(storage.min_in_memory_limit, 1);
646 assert_eq!(storage.max_on_disk_limit, 0);
647
648 storage = InMemoryBlockStates::new(1, 2);
649 assert!(!storage.is_memory_only());
650 assert_eq!(storage.in_memory_limit, 1);
651 assert_eq!(storage.min_in_memory_limit, 1);
652 assert_eq!(storage.max_on_disk_limit, 2);
653 }
654
655 #[tokio::test(flavor = "multi_thread")]
656 async fn can_read_write_cached_state() {
657 let mut storage = InMemoryBlockStates::new(1, MAX_ON_DISK_HISTORY_LIMIT);
658 let one = B256::from(U256::from(1));
659 let two = B256::from(U256::from(2));
660
661 let mut state = MemDb::default();
662 let addr = Address::random();
663 let info = AccountInfo::from_balance(U256::from(1337));
664 state.insert_account(addr, info);
665 storage.insert(one, StateDb::new(state));
666 storage.insert(two, StateDb::new(MemDb::default()));
667
668 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
670
671 assert_eq!(storage.on_disk_states.len(), 1);
672 assert!(storage.on_disk_states.contains_key(&one));
673
674 let loaded = storage.get(&one).unwrap();
675
676 let acc = loaded.basic_ref(addr).unwrap().unwrap();
677 assert_eq!(acc.balance, U256::from(1337u64));
678 }
679
680 #[tokio::test(flavor = "multi_thread")]
681 async fn can_decrease_state_cache_size() {
682 let limit = 15;
683 let mut storage = InMemoryBlockStates::new(limit, MAX_ON_DISK_HISTORY_LIMIT);
684
685 let num_states = 30;
686 for idx in 0..num_states {
687 let mut state = MemDb::default();
688 let hash = B256::from(U256::from(idx));
689 let addr = Address::from_word(hash);
690 let balance = (idx * 2) as u64;
691 let info = AccountInfo::from_balance(U256::from(balance));
692 state.insert_account(addr, info);
693 storage.insert(hash, StateDb::new(state));
694 }
695
696 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
698
699 assert_eq!(storage.on_disk_states.len(), num_states - storage.min_in_memory_limit);
700 assert_eq!(storage.present.len(), storage.min_in_memory_limit);
701
702 for idx in 0..num_states {
703 let hash = B256::from(U256::from(idx));
704 let addr = Address::from_word(hash);
705 let loaded = storage.get(&hash).unwrap();
706 let acc = loaded.basic_ref(addr).unwrap().unwrap();
707 let balance = (idx * 2) as u64;
708 assert_eq!(acc.balance, U256::from(balance));
709 }
710 }
711
712 #[test]
715 fn test_storage_dump_reload_cycle() {
716 let mut dump_storage = BlockchainStorage::empty();
717
718 let partial_header = PartialHeader { gas_limit: 123456, ..Default::default() };
719 let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..];
720 let tx: MaybeImpersonatedTransaction =
721 TypedTransaction::decode(&mut &bytes_first[..]).unwrap().into();
722 let block =
723 Block::new::<MaybeImpersonatedTransaction>(partial_header.clone(), vec![tx.clone()]);
724 let block_hash = block.header.hash_slow();
725 dump_storage.blocks.insert(block_hash, block);
726
727 let serialized_blocks = dump_storage.serialized_blocks();
728 let serialized_transactions = dump_storage.serialized_transactions();
729
730 let mut load_storage = BlockchainStorage::empty();
731
732 load_storage.load_blocks(serialized_blocks);
733 load_storage.load_transactions(serialized_transactions);
734
735 let loaded_block = load_storage.blocks.get(&block_hash).unwrap();
736 assert_eq!(loaded_block.header.gas_limit, { partial_header.gas_limit });
737 let loaded_tx = loaded_block.transactions.first().unwrap();
738 assert_eq!(loaded_tx, &tx);
739 }
740}