Skip to main content

tycho_simulation/evm/engine_db/
mod.rs

1use std::{collections::HashMap, fmt::Debug};
2
3use alloy::primitives::{Address, U160};
4use revm::{primitives::KECCAK_EMPTY, state::AccountInfo, DatabaseRef};
5use tycho_client::feed::BlockHeader;
6use tycho_common::simulation::errors::SimulationError;
7
8use crate::evm::{
9    engine_db::{
10        engine_db_interface::EngineDatabaseInterface,
11        tycho_db::{PreCachedDB, PreCachedDBError},
12    },
13    simulation::SimulationEngine,
14    tycho_models::{AccountUpdate, ChangeType, ResponseAccount},
15};
16
17pub mod engine_db_interface;
18pub mod simulation_db;
19pub mod tycho_db;
20pub mod utils;
21
22pub static SHARED_TYCHO_DB: std::sync::LazyLock<PreCachedDB> = std::sync::LazyLock::new(|| {
23    PreCachedDB::new().unwrap_or_else(|err| panic!("Failed to create PreCachedDB: {err}"))
24});
25
26/// Creates a simulation engine.
27///
28/// # Parameters
29///
30/// - `trace`: Whether to trace calls. Only meant for debugging purposes, might print a lot of data
31///   to stdout.
32pub fn create_engine<D: EngineDatabaseInterface + Clone + Debug>(
33    db: D,
34    trace: bool,
35) -> Result<SimulationEngine<D>, SimulationError>
36where
37    <D as EngineDatabaseInterface>::Error: Debug,
38    <D as DatabaseRef>::Error: Debug,
39{
40    let engine = SimulationEngine::new(db.clone(), trace);
41
42    let zero_account_info =
43        AccountInfo { balance: Default::default(), nonce: 0, code_hash: KECCAK_EMPTY, code: None };
44
45    // Accounts necessary for enabling pre-compilation are initialized by default.
46    engine
47        .state
48        .init_account(Address::ZERO, zero_account_info.clone(), None, false)
49        .map_err(|e| {
50            SimulationError::FatalError(format!("Failed to init zero address: {:?}", e))
51        })?;
52
53    engine
54        .state
55        .init_account(Address::from(U160::from(4)), zero_account_info.clone(), None, false)
56        .map_err(|e| {
57            SimulationError::FatalError(format!(
58                "Failed to init ecrecover precompile address: {:?}",
59                e
60            ))
61        })?;
62
63    Ok(engine)
64}
65
66pub fn update_engine(
67    db: PreCachedDB,
68    block: Option<BlockHeader>,
69    vm_storage: Option<HashMap<Address, ResponseAccount>>,
70    account_updates: HashMap<Address, AccountUpdate>,
71) -> Result<Vec<AccountUpdate>, PreCachedDBError> {
72    if let Some(block) = block {
73        let mut vm_updates: Vec<AccountUpdate> = Vec::new();
74
75        for account_update in account_updates.values() {
76            vm_updates.push(account_update.clone());
77        }
78
79        if let Some(vm_storage_values) = vm_storage {
80            for vm_storage_values in vm_storage_values.values() {
81                // ResponseAccount objects to AccountUpdate objects as required by the update method
82                vm_updates.push(AccountUpdate {
83                    address: vm_storage_values.address,
84                    chain: vm_storage_values.chain,
85                    slots: vm_storage_values.slots.clone(),
86                    balance: Some(vm_storage_values.native_balance),
87                    code: Some(vm_storage_values.code.clone()),
88                    change: ChangeType::Creation,
89                });
90            }
91        }
92
93        if !vm_updates.is_empty() {
94            db.update(vm_updates.clone(), Some(block))?;
95        }
96
97        Ok(vm_updates)
98    } else {
99        Ok(vec![])
100    }
101}