Skip to main content

signet_test_utils/
evm.rs

1use std::sync::Arc;
2
3use crate::{
4    contracts::{
5        counter::{COUNTER_BYTECODE, COUNTER_TEST_ADDRESS},
6        reverts::{REVERT_BYTECODE, REVERT_TEST_ADDRESS},
7        system::{
8            HOST_ORDERS_BYTECODE, HOST_PASSAGE_BYTECODE, RU_ORDERS_BYTECODE, RU_PASSAGE_BYTECODE,
9        },
10        token::{allowances_slot_for, balance_slot_for, deploy_wbtc_at, deploy_weth_at},
11    },
12    users::TEST_USERS,
13};
14use alloy::{
15    consensus::constants::ETH_TO_WEI,
16    primitives::{Address, Bytes, KECCAK256_EMPTY, U256},
17};
18use signet_constants::test_utils::*;
19use signet_sim::{AcctInfo, BlockBuild, HostEnv, RollupEnv, StateSource};
20use trevm::{
21    helpers::Ctx,
22    revm::{
23        context::CfgEnv,
24        database::in_memory_db::InMemoryDB,
25        inspector::NoOpInspector,
26        primitives::hardfork::SpecId,
27        state::{Account, AccountInfo, Bytecode, EvmState, EvmStorageSlot},
28        Database, DatabaseCommit, Inspector,
29    },
30    Cfg, NoopBlock,
31};
32
33/// Create a new Signet EVM with an in-memory database for testing.
34///
35/// Performs initial setup to
36/// - Deploy [`RU_ORDERS`] and and [`RU_PASSAGE`] system contracts
37/// - Deploy a [`COUNTER`] contract for testing at [`COUNTER_TEST_ADDRESS`].
38/// - Deploy Token contracts for WBTC and WETH with their respective bytecodes
39///   and storage.
40/// - Deploy a `Revert` contract for testing at [`REVERT_TEST_ADDRESS`].
41/// - Fund the [`TEST_USERS`] with 1000 ETH each.
42///
43/// [`COUNTER`]: crate::contracts::counter::Counter
44pub fn test_signet_evm() -> signet_evm::EvmNeedsBlock<InMemoryDB> {
45    test_signet_evm_with_inspector(NoOpInspector)
46}
47
48/// Create a new Signet EVM with an in-memory database for testing.
49///
50/// Performs initial setup to
51/// - Deploy [`RU_ORDERS`] and and [`RU_PASSAGE`] system contracts
52/// - Deploy a [`COUNTER`] contract for testing at [`COUNTER_TEST_ADDRESS`].
53/// - Deploy Token contracts for WBTC and WETH with their respective bytecodes
54///   and storage.
55/// - Deploy a `Revert` contract for testing at [`REVERT_TEST_ADDRESS`].
56/// - Fund the [`TEST_USERS`] with 1000 ETH each.
57///
58/// [`COUNTER`]: crate::contracts::counter::Counter
59pub fn test_signet_evm_with_inspector<I>(inspector: I) -> signet_evm::EvmNeedsBlock<InMemoryDB, I>
60where
61    I: Inspector<Ctx<InMemoryDB>>,
62{
63    let mut db = InMemoryDB::default();
64    setup_rollup_db(&mut db).unwrap();
65
66    signet_evm::signet_evm_with_inspector(db, inspector, TEST_SYS).fill_cfg(&TestCfg)
67}
68
69/// Test configuration for the Signet EVM.
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub struct TestCfg;
72
73impl Cfg for TestCfg {
74    fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) {
75        let CfgEnv { chain_id, spec, .. } = cfg_env;
76
77        *chain_id = RU_CHAIN_ID;
78        *spec = SpecId::default();
79    }
80}
81
82/// Test configuration for the Host EVM.
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub struct HostTestCfg;
85
86impl Cfg for HostTestCfg {
87    fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) {
88        let CfgEnv { chain_id, spec, .. } = cfg_env;
89
90        *chain_id = HOST_CHAIN_ID;
91        *spec = SpecId::default();
92    }
93}
94
95/// Create a rollup EVM environment for testing the simulator
96pub fn rollup_sim_env() -> RollupEnv<Arc<InMemoryDB>, NoOpInspector> {
97    let mut ru_db = InMemoryDB::default();
98
99    setup_rollup_db(&mut ru_db).unwrap();
100
101    let ru_db = Arc::new(ru_db);
102
103    RollupEnv::new(ru_db, TEST_SYS, &TestCfg, &NoopBlock)
104}
105
106/// Create a host EVM environment for testing.
107pub fn host_sim_env() -> HostEnv<Arc<InMemoryDB>, NoOpInspector> {
108    let mut host_db = InMemoryDB::default();
109    setup_host_db(&mut host_db).unwrap();
110    let host_db = Arc::new(host_db);
111
112    HostEnv::new(host_db, TEST_SYS, &HostTestCfg, &NoopBlock)
113}
114
115/// Async state source adapter for sync in-memory databases.
116///
117/// Wraps an `Arc<InMemoryDB>` and implements [`StateSource`] by delegating to sync
118/// `DatabaseRef` methods. Suitable for tests where no real I/O occurs.
119#[derive(Clone)]
120pub struct SyncAsyncSource(pub Arc<InMemoryDB>);
121
122impl StateSource for SyncAsyncSource {
123    type Error = <InMemoryDB as trevm::revm::DatabaseRef>::Error;
124
125    async fn account_details(&self, address: &Address) -> Result<AcctInfo, Self::Error> {
126        use trevm::revm::DatabaseRef;
127        let info = self.0.basic_ref(*address)?.unwrap_or_default();
128        let has_code = info.code_hash() != trevm::revm::primitives::KECCAK_EMPTY;
129        Ok(AcctInfo { nonce: info.nonce, balance: info.balance, has_code })
130    }
131}
132
133/// Create a [`BlockBuild`] simulator environment for testing.
134pub fn test_sim_env(
135    deadline: tokio::time::Instant,
136) -> BlockBuild<Arc<InMemoryDB>, Arc<InMemoryDB>, SyncAsyncSource, SyncAsyncSource> {
137    let ru_evm = rollup_sim_env();
138    let host_evm = host_sim_env();
139
140    let mut ru_async_db = InMemoryDB::default();
141    setup_rollup_db(&mut ru_async_db).unwrap();
142    let mut host_async_db = InMemoryDB::default();
143    setup_host_db(&mut host_async_db).unwrap();
144
145    BlockBuild::new(
146        ru_evm,
147        host_evm,
148        deadline,
149        10,
150        Default::default(),
151        50_000_000,
152        50_000_000,
153        SyncAsyncSource(Arc::new(ru_async_db)),
154        SyncAsyncSource(Arc::new(host_async_db)),
155    )
156}
157
158fn modify_account<Db, F>(db: &mut Db, addr: Address, f: F) -> Result<AccountInfo, Db::Error>
159where
160    F: FnOnce(&mut AccountInfo),
161    Db: Database + DatabaseCommit,
162{
163    let mut acct: AccountInfo = db.basic(addr)?.unwrap_or_default();
164    let old = acct.clone();
165    f(&mut acct);
166
167    let mut acct: Account = acct.into();
168    acct.mark_touch();
169
170    let changes: EvmState = [(addr, acct)].into_iter().collect();
171    db.commit(changes);
172    Ok(old)
173}
174
175/// Set the bytecode at the given address in the database.
176fn set_bytecode_at<Db: Database + DatabaseCommit>(
177    db: &mut Db,
178    addr: Address,
179    code: Bytes,
180) -> Result<(), Db::Error> {
181    modify_account(db, addr, |acct| {
182        acct.set_code(Bytecode::new_legacy(code));
183    })
184    .map(|_| ())
185    .inspect(|_| {
186        assert_ne!(db.basic(addr).unwrap().unwrap().code_hash, KECCAK256_EMPTY);
187    })
188}
189
190fn set_balance_of<Db: Database + DatabaseCommit>(
191    db: &mut Db,
192    addr: Address,
193    balance: U256,
194) -> Result<(), Db::Error> {
195    modify_account(db, addr, |acct| {
196        acct.balance = balance;
197    })
198    .map(|_| ())
199    .inspect(|_| {
200        assert_eq!(db.basic(addr).unwrap().unwrap().balance, balance);
201    })
202}
203
204fn set_storage_at<Db: Database + DatabaseCommit>(
205    db: &mut Db,
206    addr: Address,
207    slot: U256,
208    value: U256,
209) -> Result<(), Db::Error> {
210    let mut account: Account = db.basic(addr)?.unwrap_or_default().into();
211    let mut changes = EvmState::default();
212    account.storage.insert(slot, EvmStorageSlot::new(value, 1));
213    account.mark_touch();
214    changes.insert(addr, account);
215    db.commit(changes);
216    assert_eq!(db.storage(addr, slot).unwrap(), value);
217    Ok(())
218}
219
220fn setup_db<Db: Database + DatabaseCommit>(db: &mut Db, rollup: bool) -> Result<(), Db::Error> {
221    let (weth, wbtc, orders, orders_bytecode, passage, passage_bytecode);
222    if rollup {
223        weth = RU_WETH;
224        wbtc = RU_WBTC;
225        orders = TEST_SYS.ru_orders();
226        orders_bytecode = RU_ORDERS_BYTECODE;
227        passage = TEST_SYS.ru_passage();
228        passage_bytecode = RU_PASSAGE_BYTECODE;
229    } else {
230        weth = HOST_WETH;
231        wbtc = HOST_WBTC;
232        orders = TEST_SYS.host_orders();
233        orders_bytecode = HOST_ORDERS_BYTECODE;
234        passage = TEST_SYS.host_passage();
235        passage_bytecode = HOST_PASSAGE_BYTECODE;
236    }
237
238    // Deploy WETH and WBTC
239    deploy_weth_at(db, weth)?;
240    deploy_wbtc_at(db, wbtc)?;
241
242    // Set the bytecode for system contracts
243    set_bytecode_at(db, orders, orders_bytecode)?;
244    set_bytecode_at(db, passage, passage_bytecode)?;
245
246    set_bytecode_at(db, COUNTER_TEST_ADDRESS, COUNTER_BYTECODE)?;
247
248    // Set the bytecode for the Revert contract
249    set_bytecode_at(db, REVERT_TEST_ADDRESS, REVERT_BYTECODE)?;
250
251    let max_approve = U256::MAX;
252    let token_balance = U256::from(1000 * ETH_TO_WEI);
253
254    // increment the balance for each test signer
255    TEST_USERS.iter().copied().for_each(|user| {
256        set_balance_of(db, user, U256::from(1000 * ETH_TO_WEI)).unwrap();
257
258        set_storage_at(db, weth, balance_slot_for(user), token_balance).unwrap();
259        set_storage_at(db, weth, allowances_slot_for(user, orders), max_approve).unwrap();
260
261        set_storage_at(db, wbtc, balance_slot_for(user), token_balance).unwrap();
262        set_storage_at(db, wbtc, allowances_slot_for(user, orders), max_approve).unwrap();
263    });
264
265    Ok(())
266}
267
268fn setup_rollup_db<Db: Database + DatabaseCommit>(db: &mut Db) -> Result<(), Db::Error> {
269    setup_db(db, true)
270}
271
272fn setup_host_db<Db: Database + DatabaseCommit>(db: &mut Db) -> Result<(), Db::Error> {
273    setup_db(db, false)
274}