simular_core/
evm.rs

1//!
2//! An API to interact with an embedded Ethereum Virtual Machine.
3//!
4//! This is wrapper around [REVM](https://docs.rs/revm/latest/revm/index.html).  The implementation
5//! is a simplfied version of [Foundry's Executor](https://github.com/foundry-rs/foundry)
6//!
7
8use alloy_primitives::{Address, Bytes, U256};
9use alloy_sol_types::{decode_revert_reason, SolCall};
10use anyhow::{anyhow, bail, Result};
11use revm::{
12    db::{DatabaseCommit, DatabaseRef},
13    primitives::{
14        Account, AccountInfo, BlockEnv, Env, EnvWithHandlerCfg, ExecutionResult, HashMap as Map,
15        Log, Output, ResultAndState, TransactTo, TxEnv,
16    },
17};
18
19use crate::{
20    db::{CreateFork, StorageBackend},
21    SnapShot,
22};
23
24/// type alias for a `revm` hashmap of `Address` => `Account`
25type StateChangeSet = Map<Address, Account>;
26
27/// EVM that supports both in-memory and forked storage.
28pub struct BaseEvm {
29    backend: StorageBackend,
30    env: EnvWithHandlerCfg,
31}
32
33/// Create an EVM with the in-memory database
34impl Default for BaseEvm {
35    fn default() -> Self {
36        BaseEvm::new(None)
37    }
38}
39
40impl BaseEvm {
41    /// Create an instance of the EVM.  If fork is None it will use the in-memory database.
42    /// Otherwise it will create a forked database.
43    pub fn new(fork: Option<CreateFork>) -> Self {
44        let env = EnvWithHandlerCfg::default();
45        let backend = StorageBackend::new(fork);
46        Self { env, backend }
47    }
48
49    /// Create an instance of the EVM and load it's state from the `SnapShot`.  This
50    /// will use the in-memory database.
51    pub fn new_from_snapshot(snap: SnapShot) -> Self {
52        let env = EnvWithHandlerCfg::default();
53        let mut backend = StorageBackend::default();
54        backend.load_snapshot(snap);
55        Self { env, backend }
56    }
57
58    /// Create an account for the given `user` with an optional balance (`amount`).
59    /// This will overwrite an account if it already exists.
60    pub fn create_account(&mut self, user: Address, amount: Option<U256>) -> Result<()> {
61        let mut info = AccountInfo::default();
62        if let Some(amnt) = amount {
63            info.balance = amnt;
64        }
65        self.backend.insert_account_info(user, info);
66        Ok(())
67    }
68
69    /// Return the balance for the `caller`'s account.
70    pub fn get_balance(&mut self, caller: Address) -> Result<U256> {
71        Ok(self
72            .backend
73            .basic_ref(caller)?
74            .map(|acc| acc.balance)
75            .unwrap_or_default())
76    }
77
78    /// Set the balance for the given `address` with the given `amount`
79    pub fn set_balance(&mut self, address: Address, amount: U256) -> Result<&mut Self> {
80        let mut account = self.backend.basic_ref(address)?.unwrap_or_default();
81        account.balance = amount;
82
83        self.backend.insert_account_info(address, account);
84        Ok(self)
85    }
86
87    /// Create a snapshot of the current database. This can be used to reload state.
88    pub fn create_snapshot(&self) -> Result<SnapShot> {
89        self.backend.create_snapshot()
90    }
91
92    /// Deploy a contract returning the contract's address.
93    /// If `value` is specified, the constructor must be `payable`.
94    pub fn deploy(&mut self, caller: Address, data: Vec<u8>, value: U256) -> Result<Address> {
95        let mut env = self.build_env(Some(caller), TransactTo::create(), data.into(), value);
96        let result = self.backend.run_transact(&mut env)?;
97        let mut call_results = process_call_result(result)?;
98        self.commit(&mut call_results);
99
100        match call_results.address {
101            Some(addr) => Ok(addr),
102            _ => Err(anyhow!("deploy did not return an Address!")),
103        }
104    }
105
106    /// Transfer `value` from `caller` -> `to`
107    pub fn transfer(&mut self, caller: Address, to: Address, value: U256) -> Result<()> {
108        let _ = self.transact_commit(caller, to, vec![], value)?;
109        Ok(())
110    }
111
112    /// Same as `transact_commit`, but supports [alloy's sol types](https://docs.rs/alloy-sol-types/latest/alloy_sol_types/index.html).
113    pub fn transact_commit_sol<T: SolCall>(
114        &mut self,
115        caller: Address,
116        to: Address,
117        args: T,
118        value: U256,
119    ) -> Result<<T as SolCall>::Return> {
120        let data = args.abi_encode();
121        let result = self.transact_commit(caller, to, data, value)?;
122        T::abi_decode_returns(&result.result, true)
123            .map_err(|e| anyhow!("transact commit sol error: {:?}", e))
124    }
125
126    /// Write call to a contact.  Send a transaction where any state changes are persisted to the underlying database.
127    pub fn transact_commit(
128        &mut self,
129        caller: Address,
130        to: Address,
131        data: Vec<u8>,
132        value: U256,
133    ) -> Result<CallResult> {
134        let mut env = self.build_env(Some(caller), TransactTo::call(to), data.into(), value);
135        let result = self.backend.run_transact(&mut env)?;
136        let mut call_results = process_call_result(result)?;
137        self.commit(&mut call_results);
138
139        Ok(call_results)
140    }
141
142    /// Same as `transact_call` but supports [alloy's sol types](https://docs.rs/alloy-sol-types/latest/alloy_sol_types/index.html).
143    pub fn transact_call_sol<T: SolCall>(
144        &mut self,
145        to: Address,
146        args: T,
147        value: U256,
148    ) -> Result<<T as SolCall>::Return> {
149        let data = args.abi_encode();
150        let result = self.transact_call(to, data, value)?;
151        T::abi_decode_returns(&result.result, true)
152            .map_err(|e| anyhow!("transact call sol error: {:?}", e))
153    }
154
155    /// Read call to a contract.  Send a transaction but any state changes are NOT persisted to the
156    /// database.   
157    pub fn transact_call(&mut self, to: Address, data: Vec<u8>, value: U256) -> Result<CallResult> {
158        let mut env = self.build_env(None, TransactTo::call(to), data.into(), value);
159        let result = self.backend.run_transact(&mut env)?;
160        process_call_result(result)
161    }
162
163    /// Simulate a `transact_commit` without actually committing/changing state.
164    pub fn simulate(
165        &mut self,
166        caller: Address,
167        to: Address,
168        data: Vec<u8>,
169        value: U256,
170    ) -> Result<CallResult> {
171        let mut env = self.build_env(Some(caller), TransactTo::call(to), data.into(), value);
172        let result = self.backend.run_transact(&mut env)?;
173        process_call_result(result)
174    }
175
176    /// Advance `block.number` and `block.timestamp`. Set `interval` to the
177    /// amount of time in seconds you want to advance the timestamp. Block number
178    /// will be automatically incremented.
179    ///
180    /// Must be manually called.
181    pub fn update_block(&mut self, interval: u64) {
182        self.backend.update_block_info(interval);
183    }
184
185    fn build_env(
186        &self,
187        caller: Option<Address>,
188        transact_to: TransactTo,
189        data: Bytes,
190        value: U256,
191    ) -> EnvWithHandlerCfg {
192        let blkn = self.backend.block_number;
193        let ts = self.backend.timestamp;
194
195        let env = Env {
196            cfg: self.env.cfg.clone(),
197            block: BlockEnv {
198                basefee: U256::ZERO,
199                timestamp: U256::from(ts),
200                number: U256::from(blkn),
201                ..self.env.block.clone()
202            },
203            tx: TxEnv {
204                caller: caller.unwrap_or(Address::ZERO),
205                transact_to,
206                data,
207                value,
208                gas_price: U256::ZERO,
209                gas_priority_fee: None,
210                ..self.env.tx.clone()
211            },
212        };
213
214        EnvWithHandlerCfg::new_with_spec_id(Box::new(env), self.env.handler_cfg.spec_id)
215    }
216
217    fn commit(&mut self, result: &mut CallResult) {
218        if let Some(changes) = &result.state_changeset {
219            self.backend.commit(changes.clone());
220        }
221    }
222}
223
224/// Container for the results of a transaction
225pub struct CallResult {
226    /// The raw result of the call.
227    pub result: Bytes,
228    /// An address if the call is a TransactTo::create (deploy)
229    pub address: Option<Address>,
230    /// The gas used for the call
231    pub gas_used: u64,
232    /// Refunded gas
233    pub gas_refunded: u64,
234    /// The logs emitted during the call
235    pub logs: Vec<Log>,
236    /// Changes made to the database
237    pub state_changeset: Option<StateChangeSet>,
238}
239
240fn process_call_result(result: ResultAndState) -> Result<CallResult> {
241    let ResultAndState {
242        result: exec_result,
243        state: state_changeset,
244    } = result;
245
246    let (gas_refunded, gas_used, out, logs) = match exec_result {
247        ExecutionResult::Success {
248            gas_used,
249            gas_refunded,
250            output,
251            logs,
252            ..
253        } => (gas_refunded, gas_used, output, logs),
254        ExecutionResult::Revert { gas_used, output } => match decode_revert_reason(&output) {
255            Some(reason) => bail!("Reverted: {:?}. Gas used: {:?}", reason, gas_used),
256            _ => bail!("Reverted with no reason. Gas used: {:?}", gas_used),
257        },
258        ExecutionResult::Halt { reason, gas_used } => {
259            bail!("Halted: {:?}. Gas used: {:?}", reason, gas_used)
260        }
261    };
262
263    match out {
264        Output::Call(result) => Ok(CallResult {
265            result,
266            gas_used,
267            gas_refunded,
268            logs,
269            address: None,
270            state_changeset: Some(state_changeset),
271        }),
272        Output::Create(data, address) => Ok(CallResult {
273            result: data.clone(),
274            address,
275            gas_used,
276            logs,
277            gas_refunded,
278            state_changeset: Some(state_changeset),
279        }),
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use crate::ContractAbi;
286    use crate::{generate_random_addresses, BaseEvm};
287    use alloy_dyn_abi::DynSolValue;
288    use alloy_primitives::{Address, U256};
289    use alloy_sol_types::{sol, SolConstructor};
290    use rstest::*;
291
292    sol! {
293        struct ChangeIt {
294            address owner;
295            uint256 value;
296        }
297
298        contract TestContract {
299            address public owner;
300            uint256 public value;
301
302            constructor(uint256 _value) payable;
303
304            // returns the previous value
305            function increment() public returns (uint256);
306
307            // increment by 'input' (overload). Return input and new value
308            function increment(uint256 _input) public returns (uint256, uint256);
309
310            // change value and owner. requires og owner to call
311            function changeIt(ChangeIt calldata _input) public returns (bool);
312
313            function deposit() public payable;
314        }
315    }
316
317    sol! {
318        contract BlockMeta {
319            function getMeta() external view returns (uint, uint);
320        }
321    }
322
323    #[fixture]
324    fn contract_bytecode() -> Vec<u8> {
325        let raw: &str = "608060405260405161032c38038061032c8339810160408190526100\
326        229161003c565b600155600080546001600160a01b03191633179055610055565b6000602\
327        0828403121561004e57600080fd5b5051919050565b6102c8806100646000396000f3fe60\
328        80604052600436106100555760003560e01c80633fa4f2451461005a57806361fa423b146\
329        100835780637cf5dab0146100b35780638da5cb5b146100e8578063d09de08a1461012057\
330        8063d0e30db014610135575b600080fd5b34801561006657600080fd5b506100706001548\
331        1565b6040519081526020015b60405180910390f35b34801561008f57600080fd5b506100\
332        a361009e36600461020a565b610137565b604051901515815260200161007a565b3480156\
333        100bf57600080fd5b506100d36100ce366004610222565b6101c8565b6040805192835260\
334        208301919091520161007a565b3480156100f457600080fd5b50600054610108906001600\
335        160a01b031681565b6040516001600160a01b03909116815260200161007a565b34801561\
336        012c57600080fd5b506100706101ec565b005b600080546001600160a01b0316331461018\
337        e5760405162461bcd60e51b81526020600482015260156024820152743737ba103a343290\
338        31bab93932b73a1037bbb732b960591b604482015260640160405180910390fd5b61019b6\
339        02083018361023b565b600080546001600160a01b0319166001600160a01b039290921691\
340        90911790555060200135600190815590565b60008082600160008282546101dd919061026\
341        b565b90915550506001549293915050565b6001805460009180836101ff828561026b565b\
342        909155509092915050565b60006040828403121561021c57600080fd5b50919050565b600\
343        06020828403121561023457600080fd5b5035919050565b60006020828403121561024d57\
344        600080fd5b81356001600160a01b038116811461026457600080fd5b9392505050565b808\
345        2018082111561028c57634e487b7160e01b600052601160045260246000fd5b9291505056\
346        fea264697066735822122073a633ec59ee8e261bbdfefdc6d54f1d47dd6ccd6dcab4aa1eb\
347        37b62d24b4c1b64736f6c63430008140033";
348
349        hex::decode(raw).expect("failed to decode bytecode")
350    }
351
352    #[fixture]
353    fn meta_bytecode() -> Vec<u8> {
354        let raw: &str = "6080604052348015600f57600080fd5b50607c80601d6000396000f\
355        3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063a79af2ce\
356        14602d575b600080fd5b6040805142815243602082015281519081900390910190f3fea2646\
357        9706673582212202c76d8081bf4b8745cf50463d5b4f48aadbd688456ec111406e9010a51d4\
358        56ba64736f6c63430008150033";
359        hex::decode(raw).expect("failed to decode meta bytecode")
360    }
361
362    #[test]
363    fn balances() {
364        let zero = U256::from(0);
365        let one_eth = U256::from(1e18);
366
367        let mut evm = BaseEvm::default();
368        let bob = Address::repeat_byte(23);
369
370        evm.create_account(bob, None).unwrap();
371        assert!(evm.get_balance(bob).unwrap() == zero);
372
373        evm.set_balance(bob, one_eth).unwrap();
374        assert!(evm.get_balance(bob).unwrap() == one_eth);
375    }
376
377    #[test]
378    fn simple_transfers() {
379        let one_eth = U256::from(1e18);
380        let addresses = generate_random_addresses(2);
381        let bob = addresses[0];
382        let alice = addresses[1];
383
384        let mut evm = BaseEvm::new(None);
385        evm.create_account(bob, Some(U256::from(2e18))).unwrap();
386        evm.create_account(alice, None).unwrap();
387
388        assert!(evm.transfer(alice, bob, one_eth).is_err()); // alice has nothing to transfer...yet
389        assert!(evm.transfer(bob, alice, one_eth).is_ok());
390
391        assert!(evm.get_balance(bob).unwrap() == one_eth);
392        assert!(evm.get_balance(alice).unwrap() == one_eth);
393
394        let s = evm.create_snapshot();
395        println!("{:?}", s);
396    }
397
398    #[rstest]
399    fn no_sol_test_contract(contract_bytecode: Vec<u8>) {
400        let zero = U256::from(0);
401        let owner = Address::repeat_byte(12);
402        let mut evm = BaseEvm::default();
403        evm.create_account(owner, Some(U256::from(1e18))).unwrap();
404
405        let mut test_contract_abi = ContractAbi::from_human_readable(vec![
406            "constructor(uint256)",
407            "function owner() (address)",
408            "function value() (uint256)",
409            "function increment() (uint256)",
410            "function increment(uint256) (uint256, uint256)",
411        ]);
412        test_contract_abi.bytecode = Some(contract_bytecode.into());
413
414        let (args, _) = test_contract_abi.encode_constructor("(1)").unwrap();
415        let contract_address = evm.deploy(owner, args, U256::from(0)).unwrap();
416
417        // Check owner call
418        let (enc_owner_call, _, de1) = test_contract_abi.encode_function("owner", "()").unwrap();
419        let o1 = evm
420            .transact_call(contract_address, enc_owner_call, zero)
421            .unwrap();
422        assert!(DynSolValue::Address(owner) == de1.unwrap().abi_decode(&o1.result).unwrap());
423
424        // do increment()
425        let (enc_inc_0, _, de2) = test_contract_abi
426            .encode_function("increment", "()")
427            .unwrap();
428        let o2 = evm
429            .transact_commit(owner, contract_address, enc_inc_0, zero)
430            .unwrap();
431        assert!(
432            DynSolValue::Uint(U256::from(1), 256) == de2.unwrap().abi_decode(&o2.result).unwrap()
433        );
434
435        // check the value
436        let (enc_value_call, _, de3) = test_contract_abi.encode_function("value", "()").unwrap();
437        let o3 = evm
438            .transact_call(contract_address, enc_value_call, zero)
439            .unwrap();
440        assert!(
441            DynSolValue::Uint(U256::from(2), 256) == de3.unwrap().abi_decode(&o3.result).unwrap()
442        );
443
444        // do increment(value)
445        let (enc_inc_1, _, de4) = test_contract_abi
446            .encode_function("increment", "(2)")
447            .unwrap();
448        let o4 = evm
449            .transact_commit(owner, contract_address, enc_inc_1, zero)
450            .unwrap();
451        assert!(
452            DynSolValue::Tuple(vec![
453                DynSolValue::Uint(U256::from(2), 256),
454                DynSolValue::Uint(U256::from(4), 256)
455            ]) == de4.unwrap().abi_decode(&o4.result).unwrap()
456        );
457
458        // simulate increment
459        let (enc_inc_sim, _, des) = test_contract_abi
460            .encode_function("increment", "()")
461            .unwrap();
462        let os = evm
463            .simulate(owner, contract_address, enc_inc_sim, zero)
464            .unwrap();
465        assert!(
466            DynSolValue::Uint(U256::from(4), 256) == des.unwrap().abi_decode(&os.result).unwrap()
467        );
468
469        // make sure value didn't change from 'simulate'
470        let (enc_value_call1, _, de5) = test_contract_abi.encode_function("value", "()").unwrap();
471        let o5 = evm
472            .transact_call(contract_address, enc_value_call1, zero)
473            .unwrap();
474        assert!(
475            DynSolValue::Uint(U256::from(4), 256) == de5.unwrap().abi_decode(&o5.result).unwrap()
476        );
477    }
478
479    #[rstest]
480    fn sol_calls_on_test_contract(mut contract_bytecode: Vec<u8>) {
481        let zero = U256::from(0);
482        let owner = Address::repeat_byte(12);
483        let new_owner = Address::repeat_byte(33);
484
485        let mut evm = BaseEvm::default();
486
487        evm.create_account(owner, Some(U256::from(1e18))).unwrap();
488
489        let encode_constructor_args = TestContract::constructorCall {
490            _value: U256::from(1),
491        }
492        .abi_encode();
493        contract_bytecode.extend(encode_constructor_args);
494
495        let contract_address = evm
496            .deploy(owner, contract_bytecode, U256::from(1e18))
497            .unwrap();
498
499        let owner_back = evm
500            .transact_call_sol(contract_address, TestContract::ownerCall {}, zero)
501            .unwrap()
502            ._0;
503
504        assert!(owner == owner_back);
505
506        // try increment()
507        assert_eq!(
508            U256::from(1),
509            evm.transact_commit_sol(
510                owner,
511                contract_address,
512                TestContract::increment_0Call {},
513                zero,
514            )
515            .unwrap()
516            ._0
517        );
518
519        // try increment(value)
520        let rt = evm
521            .transact_commit_sol(
522                owner,
523                contract_address,
524                TestContract::increment_1Call {
525                    _input: U256::from(3),
526                },
527                zero,
528            )
529            .unwrap();
530        let inp = rt._0;
531        let nv = rt._1;
532
533        assert_eq!(U256::from(3), inp);
534        assert_eq!(U256::from(5), nv);
535
536        assert_eq!(
537            U256::from(5),
538            evm.transact_call_sol(contract_address, TestContract::valueCall {}, zero)
539                .unwrap()
540                ._0
541        );
542
543        assert_eq!(
544            owner,
545            evm.transact_call_sol(contract_address, TestContract::ownerCall {}, zero)
546                .unwrap()
547                ._0
548        );
549
550        // test revert on wrong owner
551        assert!(evm
552            .transact_commit_sol(
553                new_owner,
554                contract_address,
555                TestContract::changeItCall {
556                    _input: ChangeIt {
557                        owner: new_owner,
558                        value: zero,
559                    },
560                },
561                zero,
562            )
563            .is_err());
564
565        assert!(evm
566            .transact_commit_sol(
567                owner,
568                contract_address,
569                TestContract::changeItCall {
570                    _input: ChangeIt {
571                        owner: new_owner,
572                        value: zero,
573                    },
574                },
575                zero,
576            )
577            .is_ok());
578
579        assert_eq!(
580            U256::from(0),
581            evm.transact_call_sol(contract_address, TestContract::valueCall {}, zero)
582                .unwrap()
583                ._0
584        );
585
586        assert_eq!(
587            new_owner,
588            evm.transact_call_sol(contract_address, TestContract::ownerCall {}, zero)
589                .unwrap()
590                ._0
591        );
592
593        assert_eq!(U256::from(1e18), evm.get_balance(contract_address).unwrap());
594    }
595
596    #[rstest]
597    fn snapshots_with_memdb(mut contract_bytecode: Vec<u8>) {
598        let zero = U256::from(0);
599        let owner = Address::repeat_byte(12);
600
601        let mut evm = BaseEvm::default();
602
603        evm.create_account(owner, Some(U256::from(1e18))).unwrap();
604
605        let encode_constructor_args = TestContract::constructorCall {
606            _value: U256::from(0),
607        }
608        .abi_encode();
609        contract_bytecode.extend(encode_constructor_args);
610
611        let contract_address = evm
612            .deploy(owner, contract_bytecode, U256::from(1e18))
613            .unwrap();
614
615        let snap = evm.create_snapshot().unwrap();
616
617        let mut evm2 = BaseEvm::new_from_snapshot(snap);
618
619        assert_eq!(
620            U256::from(1e18),
621            evm2.get_balance(contract_address).unwrap()
622        );
623        assert_eq!(zero, evm2.get_balance(owner).unwrap());
624
625        assert_eq!(
626            U256::from(0),
627            evm2.transact_call_sol(contract_address, TestContract::valueCall {}, zero)
628                .unwrap()
629                ._0
630        );
631
632        assert_eq!(
633            owner,
634            evm2.transact_call_sol(contract_address, TestContract::ownerCall {}, zero)
635                .unwrap()
636                ._0
637        );
638    }
639
640    #[rstest]
641    fn updates_block_meta(meta_bytecode: Vec<u8>) {
642        const INTERVAL: u64 = 15; // update time interval
643
644        let owner = Address::repeat_byte(12);
645        let mut evm = BaseEvm::default();
646        evm.create_account(owner, Some(U256::from(1e18))).unwrap();
647        let addr = evm.deploy(owner, meta_bytecode, U256::from(0)).unwrap();
648
649        let tx1 = evm
650            .transact_call_sol(addr, BlockMeta::getMetaCall {}, U256::from(0))
651            .unwrap();
652        assert_eq!(U256::from(1), tx1._1);
653
654        let start = tx1._0;
655        evm.update_block(INTERVAL);
656        evm.update_block(INTERVAL);
657        evm.update_block(INTERVAL);
658
659        let tx2 = evm
660            .transact_call_sol(addr, BlockMeta::getMetaCall {}, U256::from(0))
661            .unwrap();
662
663        let expected_time = start + U256::from(45);
664        let expected_block = U256::from(4);
665
666        // advances block number and timestamp
667        assert_eq!(expected_block, tx2._1);
668        assert_eq!(expected_time, tx2._0);
669
670        let snap = evm.create_snapshot().unwrap();
671        assert_eq!(snap.block_num, 4);
672        assert_eq!(U256::from(snap.timestamp), expected_time);
673
674        // reload new evm and meta
675        let mut evm2 = BaseEvm::new_from_snapshot(snap);
676        let tx3 = evm2
677            .transact_call_sol(addr, BlockMeta::getMetaCall {}, U256::from(0))
678            .unwrap();
679        assert_eq!(expected_block, tx3._1);
680        assert_eq!(expected_time, tx3._0);
681    }
682}