1use crate::eth::{
3 backend::{
4 db::{
5 MaybeFullDatabase, SerializableBlock, SerializableHistoricalStates,
6 SerializableTransaction, StateDb,
7 },
8 mem::cache::DiskStateCache,
9 },
10 pool::transactions::PoolTransaction,
11};
12use alloy_consensus::BlockHeader;
13#[cfg(test)]
14use alloy_consensus::Header;
15use alloy_network::Network;
16use alloy_primitives::{
17 B256, Bytes, U256,
18 map::{B256HashMap, HashMap},
19};
20use alloy_rpc_types::{
21 BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo,
22 trace::{
23 otterscan::{InternalOperation, OperationType},
24 parity::LocalizedTransactionTrace,
25 },
26};
27use anvil_core::eth::{
28 block::{Block, create_block},
29 transaction::{MaybeImpersonatedTransaction, TransactionInfo},
30};
31use foundry_evm::{
32 backend::MemDb,
33 traces::{CallKind, ParityTraceBuilder, TracingInspectorConfig},
34};
35#[cfg(test)]
36use foundry_primitives::FoundryNetwork;
37use foundry_primitives::{FoundryHeader, FoundryReceiptEnvelope, FoundryTxEnvelope};
38use parking_lot::RwLock;
39use std::{collections::VecDeque, fmt, path::PathBuf, sync::Arc, time::Duration};
40pub const DEFAULT_HISTORY_LIMIT: usize = 500;
45const MIN_HISTORY_LIMIT: usize = 10;
46const MAX_ON_DISK_HISTORY_LIMIT: usize = 3_600;
48
49pub struct InMemoryBlockStates {
51 states: B256HashMap<StateDb>,
53 on_disk_states: B256HashMap<StateDb>,
58 in_memory_limit: usize,
60 min_in_memory_limit: usize,
62 max_on_disk_limit: usize,
66 oldest_on_disk: VecDeque<B256>,
68 present: VecDeque<B256>,
70 disk_cache: DiskStateCache,
72}
73
74impl InMemoryBlockStates {
75 pub fn new(in_memory_limit: usize, on_disk_limit: usize) -> Self {
77 let in_memory_limit = in_memory_limit.max(1);
78 Self {
79 states: Default::default(),
80 on_disk_states: Default::default(),
81 in_memory_limit,
82 min_in_memory_limit: in_memory_limit.min(MIN_HISTORY_LIMIT),
83 max_on_disk_limit: on_disk_limit,
84 oldest_on_disk: Default::default(),
85 present: Default::default(),
86 disk_cache: Default::default(),
87 }
88 }
89
90 pub const fn memory_only(mut self) -> Self {
92 self.max_on_disk_limit = 0;
93 self
94 }
95
96 pub fn disk_path(mut self, path: PathBuf) -> Self {
98 self.disk_cache = self.disk_cache.with_path(path);
99 self
100 }
101
102 pub fn update_interval_mine_block_time(&mut self, block_time: Duration) {
107 let block_time = block_time.as_secs();
108 if block_time <= 2 {
112 self.in_memory_limit = DEFAULT_HISTORY_LIMIT * 3;
113 self.enforce_limits();
114 }
115 }
116
117 const fn is_memory_only(&self) -> bool {
119 self.max_on_disk_limit == 0
120 }
121
122 pub fn insert(&mut self, hash: B256, state: StateDb) {
132 if !self.is_memory_only() && self.present.len() >= self.in_memory_limit {
133 self.in_memory_limit =
135 self.in_memory_limit.saturating_sub(1).max(self.min_in_memory_limit);
136 }
137
138 self.enforce_limits();
139
140 self.states.insert(hash, state);
141 self.present.push_back(hash);
142 }
143
144 fn enforce_limits(&mut self) {
146 while self.present.len() >= self.in_memory_limit {
148 if let Some((hash, mut state)) = self
150 .present
151 .pop_front()
152 .and_then(|hash| self.states.remove(&hash).map(|state| (hash, state)))
153 {
154 if !self.is_memory_only() {
156 if state.is_persistent() {
157 self.on_disk_states.insert(hash, state);
158 self.oldest_on_disk.push_back(hash);
159 continue;
160 }
161
162 let state_snapshot = state.0.clear_into_state_snapshot();
163 if self.disk_cache.write(hash, &state_snapshot) {
164 self.on_disk_states.insert(hash, state);
166 self.oldest_on_disk.push_back(hash);
167 } else {
168 state.init_from_state_snapshot(state_snapshot);
170 self.states.insert(hash, state);
171 self.present.push_front(hash);
172 self.in_memory_limit = self.in_memory_limit.saturating_add(1);
174 break;
175 }
176 }
177 }
178 }
179
180 while !self.is_memory_only() && self.oldest_on_disk.len() >= self.max_on_disk_limit {
182 if let Some(hash) = self.oldest_on_disk.pop_front()
184 && self.on_disk_states.remove(&hash).is_some_and(|state| !state.is_persistent())
185 {
186 self.disk_cache.remove(hash);
187 }
188 }
189 }
190
191 pub fn get_state(&self, hash: &B256) -> Option<&StateDb> {
193 self.states.get(hash)
194 }
195
196 pub fn get_on_disk_state(&mut self, hash: &B256) -> Option<&StateDb> {
198 if let Some(state) = self.on_disk_states.get_mut(hash) {
199 if state.is_persistent() {
200 return Some(state);
201 }
202
203 let cached = self.disk_cache.read(*hash)?;
204 state.init_from_state_snapshot(cached);
205 return Some(state);
206 }
207
208 None
209 }
210
211 pub const fn set_cache_limit(&mut self, limit: usize) {
213 let limit = if limit == 0 { 1 } else { limit };
214 self.in_memory_limit = limit;
215 self.min_in_memory_limit =
216 if limit < MIN_HISTORY_LIMIT { limit } else { MIN_HISTORY_LIMIT };
217 }
218
219 pub fn clear(&mut self) {
221 self.states.clear();
222 self.present.clear();
223 self.oldest_on_disk.clear();
224 for (hash, state) in std::mem::take(&mut self.on_disk_states) {
225 if !state.is_persistent() {
226 self.disk_cache.remove(hash);
227 }
228 }
229 }
230
231 pub fn remove_block_states(&mut self, hashes: &[B256]) {
236 for hash in hashes {
237 self.states.remove(hash);
238 if self.on_disk_states.remove(hash).is_some_and(|state| !state.is_persistent()) {
239 self.disk_cache.remove(*hash);
240 }
241 }
242 self.present.retain(|h| !hashes.contains(h));
243 self.oldest_on_disk.retain(|h| !hashes.contains(h));
244 }
245
246 pub fn serialized_states(&mut self) -> SerializableHistoricalStates {
248 let mut states = self
250 .states
251 .iter_mut()
252 .map(|(hash, state)| (*hash, state.serialize_state()))
253 .collect::<Vec<_>>();
254
255 for (hash, state) in &mut self.on_disk_states {
257 if state.is_persistent() {
258 states.push((*hash, state.serialize_state()));
259 } else if let Some(state_snapshot) = self.disk_cache.read(*hash) {
260 states.push((*hash, state_snapshot));
261 }
262 }
263 states.sort_unstable_by_key(|(hash, _)| *hash);
264
265 SerializableHistoricalStates::new(states)
266 }
267
268 pub fn load_states(&mut self, states: SerializableHistoricalStates) {
270 for (hash, state_snapshot) in states {
271 let mut state_db = StateDb::new(MemDb::default());
272 state_db.init_from_state_snapshot(state_snapshot);
273 self.insert(hash, state_db);
274 }
275 }
276}
277
278impl fmt::Debug for InMemoryBlockStates {
279 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280 f.debug_struct("InMemoryBlockStates")
281 .field("in_memory_limit", &self.in_memory_limit)
282 .field("min_in_memory_limit", &self.min_in_memory_limit)
283 .field("max_on_disk_limit", &self.max_on_disk_limit)
284 .field("oldest_on_disk", &self.oldest_on_disk)
285 .field("present", &self.present)
286 .finish_non_exhaustive()
287 }
288}
289
290impl Default for InMemoryBlockStates {
291 fn default() -> Self {
292 Self::new(DEFAULT_HISTORY_LIMIT, MAX_ON_DISK_HISTORY_LIMIT)
294 }
295}
296
297#[derive(Clone, Debug)]
299pub struct BlockchainStorage<N: Network> {
300 pub blocks: B256HashMap<Block>,
302 pub hashes: HashMap<u64, B256>,
304 pub best_hash: B256,
306 pub best_number: u64,
308 pub genesis_hash: B256,
310 pub genesis_number: u64,
312 pub transactions: B256HashMap<MinedTransaction<N>>,
315 pub total_difficulty: U256,
317 #[cfg(feature = "monad")]
319 pub monad_block_participants: B256HashMap<foundry_evm::core::evm::MonadBlockParticipants>,
320 #[cfg(feature = "monad")]
322 pub monad_block_replay_profiles: B256HashMap<crate::eth::backend::db::MonadBlockReplayProfile>,
323}
324
325impl<N: Network> BlockchainStorage<N> {
326 #[cfg(feature = "monad")]
328 fn remove_monad_block_metadata(&mut self, block_hash: &B256) {
329 self.monad_block_participants.remove(block_hash);
330 self.monad_block_replay_profiles.remove(block_hash);
331 }
332
333 pub fn new(header: FoundryHeader) -> Self {
335 let block =
336 create_block(header, Vec::<MaybeImpersonatedTransaction<FoundryTxEnvelope>>::new());
337 let genesis_hash = block.header.hash_slow();
338 let best_hash = genesis_hash;
339 let best_number = block.header.number();
340
341 let mut blocks = B256HashMap::default();
342 blocks.insert(genesis_hash, block);
343
344 let mut hashes = HashMap::default();
345 hashes.insert(best_number, genesis_hash);
346 Self {
347 blocks,
348 hashes,
349 best_hash,
350 best_number,
351 genesis_hash,
352 genesis_number: best_number,
353 transactions: Default::default(),
354 total_difficulty: Default::default(),
355 #[cfg(feature = "monad")]
356 monad_block_participants: Default::default(),
357 #[cfg(feature = "monad")]
358 monad_block_replay_profiles: Default::default(),
359 }
360 }
361
362 pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self {
363 let mut hashes = HashMap::default();
364 hashes.insert(block_number, block_hash);
365
366 Self {
367 blocks: B256HashMap::default(),
368 hashes,
369 best_hash: block_hash,
370 best_number: block_number,
371 genesis_hash: Default::default(),
372 genesis_number: 0,
373 transactions: Default::default(),
374 total_difficulty,
375 #[cfg(feature = "monad")]
376 monad_block_participants: Default::default(),
377 #[cfg(feature = "monad")]
378 monad_block_replay_profiles: Default::default(),
379 }
380 }
381
382 pub fn unwind_to(&mut self, block_number: u64, block_hash: B256) -> Vec<Block> {
387 let mut removed = vec![];
388 let best_num: u64 = self.best_number;
389 for i in (block_number + 1)..=best_num {
390 if let Some(hash) = self.hashes.get(&i).copied() {
391 self.remove_block_transactions_by_number(i);
393
394 if let Some(block) = self.blocks.remove(&hash) {
396 removed.push(block);
397 }
398 #[cfg(feature = "monad")]
399 self.remove_monad_block_metadata(&hash);
400 self.hashes.remove(&i);
401 }
402 }
403 self.best_hash = block_hash;
404 self.best_number = block_number;
405 removed
406 }
407
408 pub fn empty() -> Self {
409 Self {
410 blocks: Default::default(),
411 hashes: Default::default(),
412 best_hash: Default::default(),
413 best_number: Default::default(),
414 genesis_hash: Default::default(),
415 genesis_number: Default::default(),
416 transactions: Default::default(),
417 total_difficulty: Default::default(),
418 #[cfg(feature = "monad")]
419 monad_block_participants: Default::default(),
420 #[cfg(feature = "monad")]
421 monad_block_replay_profiles: Default::default(),
422 }
423 }
424
425 pub fn remove_block_transactions_by_number(&mut self, num: u64) {
427 if let Some(hash) = self.hashes.get(&num).copied() {
428 self.remove_block_transactions(hash);
429 }
430 }
431
432 pub fn remove_block_transactions(&mut self, block_hash: B256) {
434 if let Some(block) = self.blocks.get_mut(&block_hash) {
435 for tx in &block.body.transactions {
436 self.transactions.remove(&tx.hash());
437 }
438 block.body.transactions.clear();
439 }
440 }
441
442 pub fn serialized_blocks(&self) -> Vec<SerializableBlock> {
444 let mut blocks = self.blocks.iter().collect::<Vec<_>>();
445 blocks.sort_unstable_by_key(|(hash, block)| {
446 let hash = **hash;
447 let number = block.header.number();
448 let is_canonical = self.hashes.get(&number).is_some_and(|canonical| *canonical == hash);
449 (number, is_canonical, hash)
450 });
451 blocks.into_iter().map(|(_, block)| block.clone().into()).collect()
452 }
453
454 pub fn insert_block(&mut self, block: Block) -> B256 {
456 let block_hash = block.header.hash_slow();
457 let block_number = block.header.number();
458 self.blocks.insert(block_hash, block);
459 self.hashes.insert(block_number, block_hash);
460
461 if block_number == self.genesis_number {
466 self.genesis_hash = block_hash;
467 }
468
469 block_hash
470 }
471
472 pub fn load_blocks(&mut self, serializable_blocks: Vec<SerializableBlock>) {
474 for serializable_block in serializable_blocks {
475 let block: Block = serializable_block.into();
476 self.insert_block(block);
477 }
478 }
479
480 pub fn hash(&self, number: BlockNumberOrTag, slots_in_an_epoch: u64) -> Option<B256> {
482 match number {
483 BlockNumberOrTag::Latest => Some(self.best_hash),
484 BlockNumberOrTag::Earliest => Some(self.genesis_hash),
485 BlockNumberOrTag::Pending => None,
486 BlockNumberOrTag::Number(num) => self.hashes.get(&num).copied(),
487 BlockNumberOrTag::Safe => {
488 if self.best_number > slots_in_an_epoch {
489 self.hashes.get(&(self.best_number - slots_in_an_epoch)).copied()
490 } else {
491 Some(self.genesis_hash)
492 }
493 }
494 BlockNumberOrTag::Finalized => {
495 if self.best_number > slots_in_an_epoch * 2 {
496 self.hashes.get(&(self.best_number - slots_in_an_epoch * 2)).copied()
497 } else {
498 Some(self.genesis_hash)
499 }
500 }
501 }
502 }
503}
504
505impl<N: Network<ReceiptEnvelope = FoundryReceiptEnvelope>> BlockchainStorage<N> {
506 pub fn serialized_transactions(&self) -> Vec<SerializableTransaction> {
507 let mut transactions = self
508 .transactions
509 .values()
510 .map(|tx: &MinedTransaction<N>| SerializableTransaction::from(tx.clone()))
511 .collect::<Vec<_>>();
512 transactions.sort_unstable_by_key(|tx| {
513 (tx.block_number, tx.info.transaction_index, tx.info.transaction_hash)
514 });
515 transactions
516 }
517
518 pub fn load_transactions(&mut self, serializable_transactions: Vec<SerializableTransaction>) {
520 for serializable_transaction in serializable_transactions {
521 let transaction: MinedTransaction<N> = serializable_transaction.into();
522 self.transactions.insert(transaction.info.transaction_hash, transaction);
523 }
524 }
525}
526
527#[derive(Clone, Debug)]
529pub struct Blockchain<N: Network> {
530 pub storage: Arc<RwLock<BlockchainStorage<N>>>,
532}
533
534impl<N: Network> Blockchain<N> {
535 pub fn new(header: FoundryHeader) -> Self {
537 Self { storage: Arc::new(RwLock::new(BlockchainStorage::new(header))) }
538 }
539
540 pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self {
541 Self {
542 storage: Arc::new(RwLock::new(BlockchainStorage::forked(
543 block_number,
544 block_hash,
545 total_difficulty,
546 ))),
547 }
548 }
549
550 pub fn hash(&self, id: BlockId, slots_in_an_epoch: u64) -> Option<B256> {
552 match id {
553 BlockId::Hash(h) => Some(h.block_hash),
554 BlockId::Number(num) => self.storage.read().hash(num, slots_in_an_epoch),
555 }
556 }
557
558 pub fn get_block_by_hash(&self, hash: &B256) -> Option<Block> {
559 self.storage.read().blocks.get(hash).cloned()
560 }
561
562 pub fn get_transaction_by_hash(&self, hash: &B256) -> Option<MinedTransaction<N>> {
563 self.storage.read().transactions.get(hash).cloned()
564 }
565
566 pub fn blocks_count(&self) -> usize {
568 self.storage.read().blocks.len()
569 }
570}
571
572pub struct MinedBlockOutcome<T> {
574 pub block_number: u64,
576 pub included: Vec<Arc<PoolTransaction<T>>>,
578 pub invalid: Vec<Arc<PoolTransaction<T>>>,
581 pub not_yet_valid: Vec<Arc<PoolTransaction<T>>>,
584}
585
586impl<T> Clone for MinedBlockOutcome<T> {
587 fn clone(&self) -> Self {
588 Self {
589 block_number: self.block_number,
590 included: self.included.clone(),
591 invalid: self.invalid.clone(),
592 not_yet_valid: self.not_yet_valid.clone(),
593 }
594 }
595}
596
597impl<T> fmt::Debug for MinedBlockOutcome<T> {
598 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599 f.debug_struct("MinedBlockOutcome")
600 .field("block_number", &self.block_number)
601 .field("included", &self.included.len())
602 .field("invalid", &self.invalid.len())
603 .field("not_yet_valid", &self.not_yet_valid.len())
604 .finish()
605 }
606}
607
608#[derive(Clone, Debug)]
610pub struct MinedTransaction<N: Network> {
611 pub info: TransactionInfo,
612 pub receipt: N::ReceiptEnvelope,
613 pub block_hash: B256,
614 pub block_number: u64,
615}
616
617impl<N: Network> MinedTransaction<N> {
618 pub fn parity_traces(&self) -> Vec<LocalizedTransactionTrace> {
620 ParityTraceBuilder::new(
621 self.info.traces.clone(),
622 None,
623 TracingInspectorConfig::default_parity(),
624 )
625 .into_localized_transaction_traces(RethTransactionInfo {
626 hash: Some(self.info.transaction_hash),
627 index: Some(self.info.transaction_index),
628 block_hash: Some(self.block_hash),
629 block_number: Some(self.block_number),
630 base_fee: None,
631 block_timestamp: None,
632 })
633 }
634
635 pub fn ots_internal_operations(&self) -> Vec<InternalOperation> {
636 self.info
637 .traces
638 .iter()
639 .filter_map(|node| {
640 let r#type = match node.trace.kind {
641 _ if node.is_selfdestruct() => OperationType::OpSelfDestruct,
642 CallKind::Call if !node.trace.value.is_zero() => OperationType::OpTransfer,
643 CallKind::Create => OperationType::OpCreate,
644 CallKind::Create2 => OperationType::OpCreate2,
645 _ => return None,
646 };
647 let (from, to, value) = if node.is_selfdestruct() {
648 (
649 node.trace.address,
650 node.trace.selfdestruct_refund_target.unwrap_or_default(),
651 node.trace.selfdestruct_transferred_value.unwrap_or_default(),
652 )
653 } else {
654 (node.trace.caller, node.trace.address, node.trace.value)
655 };
656 Some(InternalOperation { r#type, from, to, value })
657 })
658 .collect()
659 }
660}
661
662#[derive(Clone, Debug)]
664pub struct MinedTransactionReceipt<N: Network> {
665 pub inner: N::ReceiptResponse,
667 pub out: Option<Bytes>,
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674 use crate::eth::backend::{db::Db, mem::in_memory_db::StateRootDb};
675 use alloy_primitives::{Address, hex};
676 use alloy_rlp::Decodable;
677 use revm::{database::DatabaseRef, interpreter::InstructionResult, state::AccountInfo};
678 use tempo_primitives::TempoHeader;
679
680 #[test]
681 fn test_interval_update() {
682 let mut storage = InMemoryBlockStates::default();
683 storage.update_interval_mine_block_time(Duration::from_secs(1));
684 assert_eq!(storage.in_memory_limit, DEFAULT_HISTORY_LIMIT * 3);
685 }
686
687 #[test]
688 fn test_init_state_limits() {
689 let mut storage = InMemoryBlockStates::default();
690 assert_eq!(storage.in_memory_limit, DEFAULT_HISTORY_LIMIT);
691 assert_eq!(storage.min_in_memory_limit, MIN_HISTORY_LIMIT);
692 assert_eq!(storage.max_on_disk_limit, MAX_ON_DISK_HISTORY_LIMIT);
693
694 storage = storage.memory_only();
695 assert!(storage.is_memory_only());
696
697 storage = InMemoryBlockStates::new(1, 0);
698 assert!(storage.is_memory_only());
699 assert_eq!(storage.in_memory_limit, 1);
700 assert_eq!(storage.min_in_memory_limit, 1);
701 assert_eq!(storage.max_on_disk_limit, 0);
702
703 storage = InMemoryBlockStates::new(1, 2);
704 assert!(!storage.is_memory_only());
705 assert_eq!(storage.in_memory_limit, 1);
706 assert_eq!(storage.min_in_memory_limit, 1);
707 assert_eq!(storage.max_on_disk_limit, 2);
708
709 storage = InMemoryBlockStates::new(0, 0);
710 assert!(storage.is_memory_only());
711 assert_eq!(storage.in_memory_limit, 1);
712 assert_eq!(storage.min_in_memory_limit, 1);
713 assert_eq!(storage.max_on_disk_limit, 0);
714
715 storage.set_cache_limit(0);
716 assert_eq!(storage.in_memory_limit, 1);
717 assert_eq!(storage.min_in_memory_limit, 1);
718 }
719
720 #[tokio::test(flavor = "multi_thread")]
721 async fn can_read_write_cached_state() {
722 let mut storage = InMemoryBlockStates::new(1, MAX_ON_DISK_HISTORY_LIMIT);
723 let one = B256::from(U256::from(1));
724 let two = B256::from(U256::from(2));
725
726 let mut state = MemDb::default();
727 let addr = Address::random();
728 let info = AccountInfo::from_balance(U256::from(1337));
729 state.insert_account(addr, info);
730 storage.insert(one, StateDb::new(state));
731 storage.insert(two, StateDb::new(MemDb::default()));
732
733 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
735
736 assert_eq!(storage.on_disk_states.len(), 1);
737 assert!(storage.on_disk_states.contains_key(&one));
738
739 let loaded = storage.get_on_disk_state(&one).unwrap();
740
741 let acc = loaded.basic_ref(addr).unwrap().unwrap();
742 assert_eq!(acc.balance, U256::from(1337u64));
743 }
744
745 #[test]
746 fn persistent_states_do_not_use_disk_cache() {
747 let mut storage = InMemoryBlockStates::new(1, MAX_ON_DISK_HISTORY_LIMIT);
748 let one = B256::from(U256::from(1));
749 let two = B256::from(U256::from(2));
750 let address = Address::random();
751 let mut db = StateRootDb::default();
752
753 db.insert_account(address, AccountInfo::from_balance(U256::from(1)));
754 storage.insert(one, db.current_state());
755 db.set_balance(address, U256::from(2)).unwrap();
756 storage.insert(two, db.current_state());
757
758 assert!(storage.disk_cache.temp_dir.is_none());
759 assert!(storage.on_disk_states.get(&one).unwrap().is_persistent());
760 assert_eq!(
761 storage.get_on_disk_state(&one).unwrap().basic_ref(address).unwrap().unwrap().balance,
762 U256::from(1)
763 );
764 storage.remove_block_states(&[one]);
765 assert!(storage.disk_cache.temp_dir.is_none());
766 }
767
768 #[tokio::test(flavor = "multi_thread")]
769 async fn can_decrease_state_cache_size() {
770 let limit = 15;
771 let mut storage = InMemoryBlockStates::new(limit, MAX_ON_DISK_HISTORY_LIMIT);
772
773 let num_states = 30;
774 for idx in 0..num_states {
775 let mut state = MemDb::default();
776 let hash = B256::from(U256::from(idx));
777 let addr = Address::from_word(hash);
778 let balance = (idx * 2) as u64;
779 let info = AccountInfo::from_balance(U256::from(balance));
780 state.insert_account(addr, info);
781 storage.insert(hash, StateDb::new(state));
782 }
783
784 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
786
787 let on_disk_states_len = num_states - storage.min_in_memory_limit;
788
789 assert_eq!(storage.on_disk_states.len(), on_disk_states_len);
790 assert_eq!(storage.present.len(), storage.min_in_memory_limit);
791
792 for idx in 0..num_states {
793 let hash = B256::from(U256::from(idx));
794 let addr = Address::from_word(hash);
795
796 let loaded = if idx < on_disk_states_len {
797 storage.get_on_disk_state(&hash).unwrap()
798 } else {
799 storage.get_state(&hash).unwrap()
800 };
801
802 let acc = loaded.basic_ref(addr).unwrap().unwrap();
803 let balance = (idx * 2) as u64;
804 assert_eq!(acc.balance, U256::from(balance));
805 }
806 }
807
808 #[test]
809 fn test_remove_block_states_on_rollback() {
810 let mut storage = InMemoryBlockStates::new(10, MAX_ON_DISK_HISTORY_LIMIT);
811
812 let hashes: Vec<B256> = (0..5)
814 .map(|i| {
815 let hash = B256::from(U256::from(i));
816 let mut state = MemDb::default();
817 let addr = Address::from_word(hash);
818 state.insert_account(addr, AccountInfo::from_balance(U256::from(i * 100)));
819 storage.insert(hash, StateDb::new(state));
820 hash
821 })
822 .collect();
823
824 assert_eq!(storage.present.len(), 5);
825
826 let removed_hashes = &hashes[2..];
828 storage.remove_block_states(removed_hashes);
829
830 assert_eq!(storage.present.len(), 2);
832 assert!(storage.get_state(&hashes[0]).is_some());
833 assert!(storage.get_state(&hashes[1]).is_some());
834 for h in removed_hashes {
835 assert!(storage.get_state(h).is_none());
836 assert!(!storage.present.contains(h));
837 }
838 }
839
840 #[tokio::test(flavor = "multi_thread")]
841 async fn test_remove_block_states_cleans_disk_cache() {
842 let mut storage = InMemoryBlockStates::new(1, MAX_ON_DISK_HISTORY_LIMIT);
844
845 let hash_a = B256::from(U256::from(1));
846 let hash_b = B256::from(U256::from(2));
847
848 storage.insert(hash_a, StateDb::new(MemDb::default()));
849 storage.insert(hash_b, StateDb::new(MemDb::default()));
850
851 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
853
854 assert!(storage.on_disk_states.contains_key(&hash_a));
855
856 storage.remove_block_states(&[hash_a]);
858
859 assert!(!storage.on_disk_states.contains_key(&hash_a));
860 assert!(!storage.oldest_on_disk.contains(&hash_a));
861 assert!(storage.get_on_disk_state(&hash_a).is_none());
862 }
863
864 #[test]
867 fn test_storage_dump_reload_cycle() {
868 let mut dump_storage = BlockchainStorage::<FoundryNetwork>::empty();
869
870 let header = Header { gas_limit: 123456, ..Default::default() };
871 let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..];
872 let tx: MaybeImpersonatedTransaction<FoundryTxEnvelope> =
873 FoundryTxEnvelope::decode(&mut &bytes_first[..]).unwrap().into();
874 let block = create_block(header.clone().into(), vec![tx.clone()]);
875 let block_hash = block.header.hash_slow();
876 dump_storage.blocks.insert(block_hash, block);
877
878 let serialized_blocks = dump_storage.serialized_blocks();
879 let serialized_transactions = dump_storage.serialized_transactions();
880
881 let mut load_storage = BlockchainStorage::<FoundryNetwork>::empty();
882
883 load_storage.load_blocks(serialized_blocks);
884 load_storage.load_transactions(serialized_transactions);
885
886 let loaded_block = load_storage.blocks.get(&block_hash).unwrap();
887 assert_eq!(loaded_block.header.gas_limit(), header.gas_limit());
888 let loaded_tx = loaded_block.body.transactions.first().unwrap();
889 assert_eq!(loaded_tx, &tx);
890 }
891
892 #[test]
893 fn serialized_blocks_puts_canonical_block_last() {
894 let block = |timestamp| {
895 create_block(
896 Header { number: 1, timestamp, ..Default::default() }.into(),
897 Vec::<MaybeImpersonatedTransaction<FoundryTxEnvelope>>::new(),
898 )
899 };
900 let block_a = block(1);
901 let block_b = block(2);
902 let (canonical, stale) = if block_a.header.hash_slow() < block_b.header.hash_slow() {
903 (block_a, block_b)
904 } else {
905 (block_b, block_a)
906 };
907
908 let mut storage = BlockchainStorage::<FoundryNetwork>::empty();
909 let stale_hash = storage.insert_block(stale);
910 let canonical_hash = storage.insert_block(canonical);
911 assert!(canonical_hash < stale_hash);
912
913 let mut loaded = BlockchainStorage::<FoundryNetwork>::empty();
914 loaded.load_blocks(storage.serialized_blocks());
915 assert_eq!(loaded.hashes.get(&1), Some(&canonical_hash));
916 }
917
918 #[test]
919 fn serialized_transactions_are_sorted() {
920 let transaction = |block_number, transaction_index, transaction_hash| MinedTransaction::<
921 FoundryNetwork,
922 > {
923 info: TransactionInfo {
924 transaction_hash,
925 transaction_index,
926 from: Address::ZERO,
927 to: None,
928 contract_address: None,
929 traces: Vec::new(),
930 exit: InstructionResult::Stop,
931 out: None,
932 nonce: 0,
933 gas_used: 0,
934 },
935 receipt: FoundryReceiptEnvelope::Legacy(Default::default()),
936 block_hash: B256::ZERO,
937 block_number,
938 };
939 let first = B256::from(U256::from(1));
940 let second = B256::from(U256::from(2));
941 let third = B256::from(U256::from(3));
942 let fourth = B256::from(U256::from(4));
943 let mut storage = BlockchainStorage::<FoundryNetwork>::empty();
944 for transaction in [
945 transaction(2, 0, fourth),
946 transaction(1, 1, third),
947 transaction(1, 0, second),
948 transaction(1, 0, first),
949 ] {
950 storage.transactions.insert(transaction.info.transaction_hash, transaction);
951 }
952
953 let hashes = storage
954 .serialized_transactions()
955 .into_iter()
956 .map(|transaction| transaction.info.transaction_hash)
957 .collect::<Vec<_>>();
958 assert_eq!(hashes, [first, second, third, fourth]);
959 }
960
961 #[test]
962 fn serialized_historical_states_are_sorted() {
963 let hashes = [3, 1, 2].map(|number| B256::from(U256::from(number)));
964 let mut states = InMemoryBlockStates::default();
965 for hash in hashes {
966 states.insert(hash, StateDb::new(MemDb::default()));
967 }
968
969 let serialized_hashes =
970 states.serialized_states().into_iter().map(|(hash, _)| hash).collect::<Vec<_>>();
971 assert_eq!(serialized_hashes, [hashes[1], hashes[2], hashes[0]]);
972 }
973
974 #[test]
975 fn test_tempo_storage_dump_reload_cycle() {
976 let mut dump_storage = BlockchainStorage::<FoundryNetwork>::empty();
977 let header = TempoHeader {
978 general_gas_limit: 30_000_000,
979 shared_gas_limit: 1_000_000,
980 timestamp_millis_part: 123,
981 inner: Header { number: 7, gas_limit: 30_000_000, timestamp: 42, ..Default::default() },
982 consensus_context: None,
983 };
984 let block = create_block(
985 header.into(),
986 Vec::<MaybeImpersonatedTransaction<FoundryTxEnvelope>>::new(),
987 );
988 let expected_header = block.header.clone();
989 let block_hash = block.header.hash_slow();
990 dump_storage.blocks.insert(block_hash, block);
991
992 let serialized = serde_json::to_string(&dump_storage.serialized_blocks()).unwrap();
993 let blocks: Vec<SerializableBlock> = serde_json::from_str(&serialized).unwrap();
994 let mut load_storage = BlockchainStorage::<FoundryNetwork>::empty();
995 load_storage.load_blocks(blocks);
996
997 let loaded_block = load_storage.blocks.get(&block_hash).unwrap();
998 assert_eq!(loaded_block.header, expected_header);
999 assert_eq!(loaded_block.header.as_tempo().unwrap().shared_gas_limit, 1_000_000);
1000 assert_eq!(load_storage.hashes.get(&7), Some(&block_hash));
1001 }
1002
1003 #[test]
1008 fn test_load_blocks_sets_genesis_hash_with_non_zero_genesis_number() {
1009 const GENESIS_NUMBER: u64 = 73;
1010
1011 let header = Header { number: GENESIS_NUMBER, gas_limit: 123456, ..Default::default() };
1013 let block = create_block(
1014 header.into(),
1015 Vec::<MaybeImpersonatedTransaction<FoundryTxEnvelope>>::new(),
1016 );
1017 let block_hash = block.header.hash_slow();
1018 let serialized_blocks: Vec<SerializableBlock> = vec![block.into()];
1019
1020 let mut load_storage = BlockchainStorage::<FoundryNetwork>::empty();
1024 load_storage.genesis_number = GENESIS_NUMBER;
1025 let dummy_genesis_hash = B256::repeat_byte(0xab);
1026 load_storage.genesis_hash = dummy_genesis_hash;
1027
1028 load_storage.load_blocks(serialized_blocks);
1029
1030 assert_eq!(load_storage.genesis_hash, block_hash);
1031 assert_ne!(load_storage.genesis_hash, dummy_genesis_hash);
1032
1033 let mut sanity_storage = BlockchainStorage::<FoundryNetwork>::empty();
1036 sanity_storage.genesis_number = 0;
1037 sanity_storage.genesis_hash = dummy_genesis_hash;
1038
1039 let header_only_73 =
1040 Header { number: GENESIS_NUMBER, gas_limit: 123456, ..Default::default() };
1041 let block_73 = create_block(
1042 header_only_73.into(),
1043 Vec::<MaybeImpersonatedTransaction<FoundryTxEnvelope>>::new(),
1044 );
1045 sanity_storage.load_blocks(vec![block_73.into()]);
1046 assert_eq!(sanity_storage.genesis_hash, dummy_genesis_hash);
1047 }
1048}