revm_handler/
system_call.rs

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