revm_handler/
system_call.rs

1use crate::{
2    instructions::InstructionProvider, EthFrame, ExecuteCommitEvm, ExecuteEvm, Handler,
3    MainnetHandler, PrecompileProvider,
4};
5use context::{ContextSetters, ContextTr, Evm, JournalOutput, JournalTr, TxEnv};
6use database_interface::DatabaseCommit;
7use interpreter::{interpreter::EthInterpreter, InterpreterResult};
8use primitives::{address, Address, Bytes, TxKind};
9
10pub const SYSTEM_ADDRESS: Address = address!("0xfffffffffffffffffffffffffffffffffffffffe");
11
12/// Creates the system transaction with default values and set data and tx call target to system contract address
13/// that is going to be called.
14///
15/// The caller is set to be [`SYSTEM_ADDRESS`].
16///
17/// It is used inside [`SystemCallEvm`] and [`SystemCallCommitEvm`] traits to prepare EVM for system call execution.
18pub trait SystemCallTx {
19    /// Creates new transaction for system call.
20    fn new_system_tx(data: Bytes, system_contract_address: Address) -> Self;
21}
22
23impl SystemCallTx for TxEnv {
24    fn new_system_tx(data: Bytes, system_contract_address: Address) -> Self {
25        TxEnv {
26            caller: SYSTEM_ADDRESS,
27            data,
28            kind: TxKind::Call(system_contract_address),
29            gas_limit: 30_000_000,
30            ..Default::default()
31        }
32    }
33}
34
35/// API for executing the system calls. System calls dont deduct the caller or reward the
36/// beneficiary. They are used before and after block execution to insert or obtain blockchain state.
37///
38/// It act similar to `transact` function and sets default Tx with data and system contract as a target.
39pub trait SystemCallEvm: ExecuteEvm {
40    /// System call is a special transaction call that is used to call a system contract.
41    ///
42    /// Transaction fields are reset and set in [`SystemCallTx`] and data and target are set to
43    /// given values.
44    ///
45    /// Block values are taken into account and will determent how system call will be executed.
46    fn transact_system_call(
47        &mut self,
48        data: Bytes,
49        system_contract_address: Address,
50    ) -> Self::Output;
51}
52
53/// Extension of the [`SystemCallEvm`] trait that adds a method that commits the state after execution.
54pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
55    /// Transact the system call and commit to the state.
56    fn transact_system_call_commit(
57        &mut self,
58        data: Bytes,
59        system_contract_address: Address,
60    ) -> Self::CommitOutput;
61}
62
63impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm for Evm<CTX, INSP, INST, PRECOMPILES>
64where
65    CTX: ContextTr<Journal: JournalTr<FinalOutput = JournalOutput>, Tx: SystemCallTx>
66        + ContextSetters,
67    INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
68    PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
69{
70    fn transact_system_call(
71        &mut self,
72        data: Bytes,
73        system_contract_address: Address,
74    ) -> Self::Output {
75        // set tx fields.
76        self.set_tx(CTX::Tx::new_system_tx(data, system_contract_address));
77        // create handler
78        let mut handler = MainnetHandler::<_, _, EthFrame<_, _, _>>::default();
79        handler.run_system_call(self)
80    }
81}
82
83impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm for Evm<CTX, INSP, INST, PRECOMPILES>
84where
85    CTX: ContextTr<
86            Journal: JournalTr<FinalOutput = JournalOutput>,
87            Db: DatabaseCommit,
88            Tx: SystemCallTx,
89        > + ContextSetters,
90    INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
91    PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
92{
93    fn transact_system_call_commit(
94        &mut self,
95        data: Bytes,
96        system_contract_address: Address,
97    ) -> Self::CommitOutput {
98        self.transact_system_call(data, system_contract_address)
99            .map(|r| {
100                self.db().commit(r.state);
101                r.result
102            })
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use crate::{MainBuilder, MainContext};
109
110    use super::*;
111    use context::{
112        result::{ExecutionResult, Output, SuccessReason},
113        Context,
114    };
115    use database::InMemoryDB;
116    use primitives::{b256, bytes, U256};
117    use state::{AccountInfo, Bytecode};
118
119    const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935");
120    static HISTORY_STORAGE_CODE: Bytes = bytes!("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500");
121
122    #[test]
123    fn test_system_call() {
124        let mut db = InMemoryDB::default();
125        db.insert_account_info(
126            HISTORY_STORAGE_ADDRESS,
127            AccountInfo::default().with_code(Bytecode::new_legacy(HISTORY_STORAGE_CODE.clone())),
128        );
129
130        let block_hash =
131            b256!("0x1111111111111111111111111111111111111111111111111111111111111111");
132
133        let mut my_evm = Context::mainnet()
134            .with_db(db)
135            // block with number 1 will set storage at slot 0.
136            .modify_block_chained(|b| b.number = 1)
137            .build_mainnet();
138        let res = my_evm
139            .transact_system_call(block_hash.0.into(), HISTORY_STORAGE_ADDRESS)
140            .unwrap();
141
142        let result = res.result;
143        let state = res.state;
144        assert_eq!(
145            result,
146            ExecutionResult::Success {
147                reason: SuccessReason::Stop,
148                gas_used: 22143,
149                gas_refunded: 0,
150                logs: vec![],
151                output: Output::Call(Bytes::default())
152            }
153        );
154        // only system contract is updated and present
155        assert_eq!(state.len(), 1);
156        assert_eq!(
157            state[&HISTORY_STORAGE_ADDRESS]
158                .storage
159                .get(&U256::from(0))
160                .map(|slot| slot.present_value)
161                .unwrap_or_default(),
162            U256::from_be_bytes(block_hash.0),
163            "State is not updated {state:?}"
164        );
165    }
166}