verbs_rs/env/
mod.rs

1//! Simulation environments
2//!
3//! A simulation environment wraps a local
4//! EVM & database with tracking of simulation
5//! events. It also provides functionality to
6//! deploy contracts, interact contracts and
7//! process queues of transactions.
8//!
9
10mod utils;
11mod validator;
12
13use crate::contract::{Event, Transaction};
14use crate::utils::Eth;
15use crate::{ForkDb, LocalDB, RequestCache, DB};
16use alloy_primitives::{Address, FixedBytes, B256, U256};
17use alloy_sol_types::SolCall;
18use log::debug;
19use rand::Rng;
20use revm::primitives::{AccountInfo, Bytecode, ExecutionResult, Log, ResultAndState, TxEnv};
21use revm::{ContextWithHandlerCfg, Evm, Handler};
22pub use utils::{decode_event, process_events, RevertError};
23pub use validator::{GasPriorityValidator, RandomValidator, Validator};
24
25/// Simulation environment
26///
27/// Environment wrapping an in-memory EVM and
28/// functionality to update the state of the
29/// environment.
30pub struct Env<D: DB, V: Validator> {
31    /// Local EVM state
32    pub evm_state: Option<ContextWithHandlerCfg<(), D>>,
33    /// Events/updates in the last block
34    pub last_events: Vec<Event>,
35    /// History of events/updates over the
36    /// lifetime of the environment
37    pub event_history: Vec<Event>,
38    /// Validator responsbile for transaction ordering
39    pub validator: V,
40}
41
42/// EVM update methods
43trait CallEVM {
44    /// Execute a transaction, and update the EVM state
45    fn execute(&mut self, tx: TxEnv) -> ExecutionResult;
46    /// Execute a transaction without updating the EVM
47    fn call(&mut self, tx: TxEnv) -> ResultAndState;
48}
49
50impl<'a, D: DB> CallEVM for Evm<'a, (), D> {
51    fn execute(&mut self, tx: TxEnv) -> ExecutionResult {
52        self.context.evm.env.tx = tx;
53
54        match self.transact_commit() {
55            Ok(val) => val,
56            Err(e) => match e {
57                revm::primitives::EVMError::Transaction(t) => {
58                    panic!("Call failed: Invalid transaction {:?}", t)
59                }
60                revm::primitives::EVMError::Header(h) => {
61                    panic!("Call failed: Invalid header {:?}", h)
62                }
63                revm::primitives::EVMError::Database(d) => {
64                    panic!("Call failed: Database error {:?}", d)
65                }
66                revm::primitives::EVMError::Custom(c) => {
67                    panic!("Call failed: Custom error {:?}", c)
68                }
69            },
70        }
71    }
72
73    fn call(&mut self, tx: TxEnv) -> ResultAndState {
74        self.context.evm.env.tx = tx;
75
76        match self.transact() {
77            Ok(val) => val,
78            Err(e) => match e {
79                revm::primitives::EVMError::Transaction(t) => {
80                    panic!("Call failed: Invalid transaction {:?}", t)
81                }
82                revm::primitives::EVMError::Header(h) => {
83                    panic!("Call failed: Invalid header {:?}", h)
84                }
85                revm::primitives::EVMError::Database(d) => {
86                    panic!("Call failed: Database error {:?}", d)
87                }
88                revm::primitives::EVMError::Custom(c) => {
89                    panic!("Call failed: Custom error {:?}", c)
90                }
91            },
92        }
93    }
94}
95
96impl<V: Validator> Env<ForkDb, V> {
97    /// Initialise an environment with a forked DB
98    ///
99    /// Initialise a simulation environment with
100    /// a database that can request values from
101    /// a remote fork of the blockchain. During EVM
102    /// execution, if a contract or storage value
103    /// does not exist locally the db will attempt
104    /// to request it from the remote endpoint.
105    ///
106    /// # Arguments
107    ///
108    /// * `node_url` - Url of service to make db requests
109    /// * `block_number` - Block number to fork from, if None
110    ///    latest available block will be used.
111    ///
112    pub fn init(node_url: &str, block_number: Option<u64>, validator: V) -> Self {
113        let db = ForkDb::new(node_url, block_number);
114        let timestamp = db.block.timestamp.as_u128();
115        let block_number = match db.block.number {
116            Some(n) => U256::try_from(n.as_u64()).unwrap(),
117            None => U256::ZERO,
118        };
119
120        let evm = Evm::builder()
121            .with_db(db)
122            .modify_cfg_env(|cfg| {
123                cfg.limit_contract_code_size = Some(0x1000000);
124                cfg.disable_eip3607 = true;
125            })
126            .modify_block_env(|block| {
127                block.gas_limit = U256::MAX;
128                block.timestamp = U256::try_from(timestamp).unwrap();
129                block.number = block_number;
130            })
131            .build();
132
133        let context = evm.into_context_with_handler_cfg();
134
135        Self {
136            evm_state: Some(context),
137            last_events: Vec::new(),
138            event_history: Vec::new(),
139            validator,
140        }
141    }
142
143    /// Get history of data requests made by the DB
144    ///
145    /// Get a history of requests for data from the
146    /// remote fork. These requests can then be inserted
147    /// into an empty DB to speed up future simulations.
148    ///
149    pub fn get_request_history(&self) -> &RequestCache {
150        match &self.evm_state {
151            Some(e) => &e.context.evm.db.requests,
152            None => panic!("No EVM state set"),
153        }
154    }
155}
156
157impl<V: Validator> Env<LocalDB, V> {
158    /// Initialise a simulation with an in-memory DB
159    ///
160    /// Initialises a simulation environment with an
161    /// empty in-memory database.
162    ///
163    /// # Arguments
164    ///
165    /// - `timestamp` - Timestamp to initialise the
166    ///   the simulation/EVM with
167    /// - `block_number` - Block number initialise the
168    ///   the simulation/EVM with
169    ///
170    pub fn init(timestamp: U256, block_number: U256, validator: V) -> Self {
171        let evm = Evm::builder()
172            .with_db(LocalDB::new())
173            .modify_cfg_env(|cfg| {
174                cfg.limit_contract_code_size = Some(0x1000000);
175                cfg.disable_eip3607 = true;
176            })
177            .modify_block_env(|block| {
178                block.gas_limit = U256::MAX;
179                block.timestamp = timestamp;
180                block.number = block_number;
181            })
182            .build();
183
184        let start_balance = U256::to_weth(10_000);
185
186        let mut env = Self {
187            evm_state: Some(evm.into_context_with_handler_cfg()),
188            last_events: Vec::new(),
189            event_history: Vec::new(),
190            validator,
191        };
192
193        env.insert_account(Address::ZERO, start_balance);
194
195        env
196    }
197}
198
199impl<D: DB, V: Validator> Env<D, V> {
200    fn evm(&mut self) -> Evm<(), D> {
201        let state = self.evm_state.take();
202
203        match state {
204            Some(s) => {
205                let ContextWithHandlerCfg { context, cfg } = s;
206                Evm {
207                    context,
208                    handler: Handler::new(cfg),
209                }
210            }
211            None => panic!("No EVM state set (this should not happen!)"),
212        }
213    }
214
215    /// Get a mutable reference to the stored evm-state
216    pub fn evm_state(&mut self) -> &mut ContextWithHandlerCfg<(), D> {
217        match &mut self.evm_state {
218            Some(e) => e,
219            None => panic!("No EVM state set (this should not happen!)"),
220        }
221    }
222
223    /// Increment block number, time and prevarando
224    pub fn increment_time<R: Rng>(&mut self, rng: &mut R, interval: u64) {
225        let state = self.evm_state();
226        state.context.evm.env.block.timestamp += U256::from(interval);
227        state.context.evm.env.block.number += U256::from(1);
228        state.context.evm.env.block.prevrandao = Some(FixedBytes(rng.gen::<[u8; 32]>()));
229    }
230
231    /// Insert a user account into the DB
232    ///
233    /// # Arguments
234    ///
235    /// - `address` - Address to create the account at
236    /// - `start_balance` - Starting balance of Eth of the account
237    ///
238    pub fn insert_account(&mut self, address: Address, start_balance: U256) {
239        self.evm_state().context.evm.db.insert_account_info(
240            address,
241            AccountInfo::new(start_balance, 0, B256::default(), Bytecode::default()),
242        );
243    }
244
245    /// Insert multiple accounts into the DB
246    ///
247    /// # Arguments
248    ///
249    /// - `start_balance` - Starting balance of Eth of the account
250    /// - `addresses` - Vector of addresses to create accounts at
251    ///
252    pub fn insert_accounts(&mut self, start_balance: u128, addresses: Vec<Address>) {
253        let start_balance = U256::from(start_balance);
254        for address in addresses {
255            self.insert_account(address, start_balance);
256        }
257    }
258
259    /// Deploy a contract to the EVM
260    ///
261    /// # Arguments
262    ///
263    /// - `deployer` - Address of contract deployer
264    /// - `contract_name` - Name of the contract, only used for
265    ///   error messaging
266    /// - `data` - Deployment bytecode, and abi encoded arguments
267    ///   if required
268    ///
269    pub fn deploy_contract(
270        &mut self,
271        deployer: Address,
272        contract_name: &str,
273        data: Vec<u8>,
274    ) -> Address {
275        let tx = utils::init_create_transaction(deployer, data);
276        let mut evm = self.evm();
277        let result = evm.execute(tx);
278        let output = utils::deployment_output(contract_name, result);
279        let deploy_address = match output {
280            revm::primitives::Output::Create(_, address) => address.unwrap(),
281            _ => panic!("Deployment of {} failed", contract_name),
282        };
283        debug!("Deployed {} to {}", contract_name, deploy_address);
284        self.evm_state = Some(evm.into_context_with_handler_cfg());
285        deploy_address
286    }
287
288    /// Execute a contract function with ABI encoded arguments
289    ///
290    /// # Arguments
291    ///
292    /// - `callee` - Contract caller address
293    /// - `contract` -  Address of the contract to call
294    /// - `encoded_args` - ABI encoded function arguments and
295    ///   function selector
296    /// - `value` - Value attached to the transaction
297    ///
298    pub fn direct_execute_raw(
299        &mut self,
300        callee: Address,
301        contract: Address,
302        encoded_args: Vec<u8>,
303        value: U256,
304    ) -> Result<ExecutionResult, RevertError> {
305        let tx = utils::init_call_transaction(callee, contract, encoded_args, value);
306        let mut evm = self.evm();
307        let execution_result = evm.execute(tx);
308        self.evm_state = Some(evm.into_context_with_handler_cfg());
309        utils::result_to_raw_output(callee, execution_result)
310    }
311
312    /// Execute a contract function for a specific ABI
313    ///
314    /// # Arguments
315    ///
316    /// - `callee` - Function caller address
317    /// - `contract` - Address of the contract
318    /// - `call_args` - Function arguments wrapped in a
319    ///   [SolCall] object
320    /// - `value` - Value attached to the transaction
321    pub fn direct_execute<T: SolCall>(
322        &mut self,
323        callee: Address,
324        contract: Address,
325        call_args: T,
326        value: U256,
327    ) -> Result<(<T as SolCall>::Return, Vec<Log>), utils::RevertError> {
328        let function_name = T::SIGNATURE;
329        let call_args = call_args.abi_encode();
330        let tx = utils::init_call_transaction(callee, contract, call_args, value);
331        let mut evm = self.evm();
332        let execution_result = evm.execute(tx);
333        let (output, events) = utils::result_to_output(function_name, callee, execution_result)?;
334        let output_data = output.into_data();
335        let decoded = T::abi_decode_returns(&output_data, true);
336        let decoded = match decoded {
337            Ok(x) => x,
338            Err(e) => panic!("Decoding error from {} {:?}", function_name, e),
339        };
340        self.evm_state = Some(evm.into_context_with_handler_cfg());
341        Ok((decoded, events))
342    }
343
344    /// Call a contract function without committing changes
345    ///
346    /// # Arguments
347    ///
348    /// - `callee` - Address of the function caller
349    /// - `contract` - Address of the contract
350    /// - `encoded_args` - ABI encoded function selector and arguments
351    /// - `value` - Value attached to the transaction
352    pub fn direct_call_raw(
353        &mut self,
354        callee: Address,
355        contract: Address,
356        encoded_args: Vec<u8>,
357        value: U256,
358    ) -> Result<ExecutionResult, RevertError> {
359        let tx = utils::init_call_transaction(callee, contract, encoded_args, value);
360        let mut evm = self.evm();
361        let result = evm.call(tx);
362        self.evm_state = Some(evm.into_context_with_handler_cfg());
363        utils::result_to_raw_output(callee, result.result)
364    }
365
366    /// Call a contract function without committing changes
367    ///
368    /// # Arguments
369    ///
370    /// - `callee` - Address of the function caller
371    /// - `contract` - Address of the contract
372    /// - `call_args` - Function arguments wrapped in a
373    ///   [SolCall] object
374    /// - `value` - Value attached to the transaction
375    pub fn direct_call<T: SolCall>(
376        &mut self,
377        callee: Address,
378        contract: Address,
379        call_args: T,
380        value: U256,
381    ) -> Result<(<T as SolCall>::Return, Vec<Log>), utils::RevertError> {
382        let function_name = T::SIGNATURE;
383        let call_args = call_args.abi_encode();
384        let tx = utils::init_call_transaction(callee, contract, call_args, value);
385        let mut evm = self.evm();
386        let execution_result = evm.call(tx);
387        let (output, events) =
388            utils::result_to_output(function_name, callee, execution_result.result)?;
389        let output_data = output.into_data();
390        let decoded = T::abi_decode_returns(&output_data, true);
391        let decoded = match decoded {
392            Ok(x) => x,
393            Err(_) => panic!("Decoding error from {}", function_name),
394        };
395        self.evm_state = Some(evm.into_context_with_handler_cfg());
396        Ok((decoded, events))
397    }
398
399    /// Execute a function from a [Transaction] object
400    ///
401    /// This function is used during simulation execution
402    /// to process [Transaction] submitted for execution by
403    /// agents.
404    ///
405    /// # Arguments
406    ///
407    /// - `transaction` - Struct containing function call parameters
408    /// - `step` - Simulation step number
409    /// - `sequence` - Ordering of this transaction in the queue
410    ///
411    fn call_from_transaction(
412        evm: &mut Evm<'_, (), D>,
413        last_events: &mut Vec<Event>,
414        transaction: Transaction,
415        step: usize,
416        sequence: usize,
417    ) {
418        debug!(
419            "Calling {:?} of {}",
420            transaction.function_selector, transaction.transact_to
421        );
422        let function_selector = transaction.function_selector;
423        let check_call = transaction.checked;
424        let tx = utils::init_call_transaction(
425            transaction.callee,
426            transaction.transact_to,
427            transaction.args,
428            transaction.value,
429        );
430        let execution_result = evm.execute(tx);
431        let result = utils::result_to_output_with_events(
432            step,
433            sequence,
434            function_selector,
435            transaction.callee,
436            execution_result,
437            check_call,
438        );
439        last_events.push(result);
440    }
441
442    /// Process a queue of [Transaction]
443    ///
444    /// # Arguments
445    /// - `transactions` - Vector of transactions
446    /// - `step` - Step number of the simulation
447    ///
448    pub fn process_transactions<R: Rng>(
449        &mut self,
450        transactions: Vec<Transaction>,
451        rng: &mut R,
452        step: usize,
453    ) {
454        let transactions = self.validator.order_transactions(rng, transactions);
455
456        let mut evm = self.evm();
457        let mut events = Vec::<Event>::new();
458
459        for (i, call) in transactions.into_iter().enumerate() {
460            Self::call_from_transaction(&mut evm, &mut events, call, step, i);
461        }
462        self.evm_state = Some(evm.into_context_with_handler_cfg());
463        self.last_events.extend(events);
464    }
465
466    /// Store events from the last block
467    ///
468    /// Move events generated in the last block
469    /// into the historical storage.
470    ///
471    pub fn clear_events(&mut self) {
472        self.event_history.append(&mut self.last_events);
473    }
474}
475
476#[cfg(test)]
477mod tests {
478
479    use super::*;
480    use crate::utils;
481    use alloy_primitives::{Address, Signed, Uint};
482    use alloy_sol_types::{sol, SolValue};
483    use rand::SeedableRng;
484    use rand_xoshiro::Xoroshiro128StarStar;
485    use rstest::*;
486
487    sol!(
488        TestContract,
489        r#"[
490            {
491                "inputs": [
492                    {
493                        "internalType": "int256",
494                        "name": "x",
495                        "type": "int256"
496                    }
497                ],
498                "stateMutability": "nonpayable",
499                "type": "constructor"
500            },
501            {
502                "inputs": [],
503                "name": "getValue",
504                "outputs": [
505                    {
506                        "internalType": "int256",
507                        "name": "",
508                        "type": "int256"
509                    }
510                ],
511                "stateMutability": "view",
512                "type": "function"
513            },
514            {
515                "inputs": [
516                    {
517                        "internalType": "int256",
518                        "name": "x",
519                        "type": "int256"
520                    }
521                ],
522                "name": "setValue",
523                "outputs": [],
524                "stateMutability": "nonpayable",
525                "type": "function"
526            }
527        ]"#
528    );
529
530    #[fixture]
531    fn deployment() -> (Env<LocalDB, RandomValidator>, Address, Address) {
532        let mut network =
533            Env::<LocalDB, RandomValidator>::init(U256::ZERO, U256::ZERO, RandomValidator {});
534
535        let constructor_args = <i128>::abi_encode(&101);
536        let bytecode_hex = "608060405234801561001057600080fd5b50\
537        6040516102063803806102068339818101604052810190610032919061007a\
538        565b80600081905550506100a7565b600080fd5b6000819050919050565b61\
539        005781610044565b811461006257600080fd5b50565b600081519050610074\
540        8161004e565b92915050565b6000602082840312156100905761008f61003f\
541        565b5b600061009e84828501610065565b91505092915050565b6101508061\
542        00b66000396000f3fe608060405234801561001057600080fd5b5060043610\
543        6100365760003560e01c8063209652551461003b5780635093dc7d14610059\
544        575b600080fd5b610043610075565b60405161005091906100a1565b604051\
545        80910390f35b610073600480360381019061006e91906100ed565b61007e56\
546        5b005b60008054905090565b8060008190555050565b600081905091905056\
547        5b61009b81610088565b82525050565b60006020820190506100b660008301\
548        84610092565b92915050565b600080fd5b6100ca81610088565b81146100d5\
549        57600080fd5b50565b6000813590506100e7816100c1565b92915050565b60\
550        0060208284031215610103576101026100bc565b5b60006101118482850161\
551        00d8565b9150509291505056fea2646970667358221220d99fa7a11a5739cf\
552        9f1c4e30ebbb603943f8e1e44a3b4c0c10c3ea53799a236d64736f6c634300\
553        080a0033";
554
555        let user_address = Address::from(Uint::from(999));
556        network.insert_account(user_address, Eth::to_weth(100));
557
558        let mut bytecode: Vec<u8> = utils::data_bytes_from_hex(bytecode_hex);
559        bytecode.extend(constructor_args);
560        let contract_address = network.deploy_contract(user_address, "test", bytecode);
561
562        (network, contract_address, user_address)
563    }
564
565    #[rstest]
566    fn direct_execute_and_call(deployment: (Env<LocalDB, RandomValidator>, Address, Address)) {
567        let (mut network, contract_address, user_address) = deployment;
568
569        let (v, _) = network
570            .direct_call(
571                user_address,
572                contract_address,
573                TestContract::getValueCall {},
574                U256::ZERO,
575            )
576            .unwrap();
577
578        assert_eq!(v._0.as_i64(), 101i64);
579
580        let _ = network
581            .direct_execute(
582                user_address,
583                contract_address,
584                TestContract::setValueCall { x: Signed::ONE },
585                U256::ZERO,
586            )
587            .unwrap();
588
589        let (v, _) = network
590            .direct_call(
591                user_address,
592                contract_address,
593                TestContract::getValueCall {},
594                U256::ZERO,
595            )
596            .unwrap();
597
598        assert_eq!(v._0.as_i64(), 1i64);
599    }
600
601    #[rstest]
602    fn processing_calls(deployment: (Env<LocalDB, RandomValidator>, Address, Address)) {
603        let (mut network, contract_address, user_address) = deployment;
604
605        let calls = vec![
606            Transaction::basic(
607                user_address,
608                contract_address,
609                TestContract::setValueCall {
610                    x: Signed::try_from_be_slice(&303u128.to_be_bytes()).unwrap(),
611                },
612                true,
613            ),
614            Transaction::basic(
615                user_address,
616                contract_address,
617                TestContract::setValueCall {
618                    x: Signed::try_from_be_slice(&303u128.to_be_bytes()).unwrap(),
619                },
620                true,
621            ),
622        ];
623
624        let mut rng = Xoroshiro128StarStar::seed_from_u64(101);
625
626        network.process_transactions(calls, &mut rng, 1);
627
628        let (v, _) = network
629            .direct_call(
630                user_address,
631                contract_address,
632                TestContract::getValueCall {},
633                U256::ZERO,
634            )
635            .unwrap();
636
637        assert_eq!(v._0.as_i64(), 303i64);
638    }
639}