1use crate::{
2 instructions::InstructionProvider, EthFrame, ExecuteCommitEvm, ExecuteEvm, Handler,
3 MainnetHandler, PrecompileProvider,
4};
5use context::{
6 result::ResultAndState, ContextSetters, ContextTr, Evm, JournalTr, TransactionType, TxEnv,
7};
8use database_interface::DatabaseCommit;
9use interpreter::{interpreter::EthInterpreter, InterpreterResult};
10use primitives::{address, eip7825, Address, Bytes, TxKind};
11use state::EvmState;
12
13pub const SYSTEM_ADDRESS: Address = address!("0xfffffffffffffffffffffffffffffffffffffffe");
14
15pub trait SystemCallTx: Sized {
22 fn new_system_tx(system_contract_address: Address, data: Bytes) -> Self {
24 Self::new_system_tx_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
25 }
26
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 {
41 tx_type: TransactionType::Legacy as u8,
42 caller,
43 data,
44 kind: TxKind::Call(system_contract_address),
45 gas_limit: eip7825::TX_GAS_LIMIT_CAP,
46 ..Default::default()
47 }
48 }
49}
50
51pub trait SystemCallEvm: ExecuteEvm {
56 fn transact_system_call_with_caller(
63 &mut self,
64 caller: Address,
65 system_contract_address: Address,
66 data: Bytes,
67 ) -> Result<Self::ExecutionResult, Self::Error>;
68
69 fn transact_system_call(
71 &mut self,
72 system_contract_address: Address,
73 data: Bytes,
74 ) -> Result<Self::ExecutionResult, Self::Error> {
75 self.transact_system_call_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
76 }
77
78 fn transact_system_call_finalize(
82 &mut self,
83 system_contract_address: Address,
84 data: Bytes,
85 ) -> Result<ResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
86 self.transact_system_call_with_caller_finalize(
87 SYSTEM_ADDRESS,
88 system_contract_address,
89 data,
90 )
91 }
92
93 fn transact_system_call_with_caller_finalize(
95 &mut self,
96 caller: Address,
97 system_contract_address: Address,
98 data: Bytes,
99 ) -> Result<ResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
100 let result =
101 self.transact_system_call_with_caller(caller, system_contract_address, data)?;
102 let state = self.finalize();
103 Ok(ResultAndState::new(result, state))
104 }
105}
106
107pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
109 fn transact_system_call_commit(
111 &mut self,
112 system_contract_address: Address,
113 data: Bytes,
114 ) -> Result<Self::ExecutionResult, Self::Error> {
115 self.transact_system_call_with_caller_commit(SYSTEM_ADDRESS, system_contract_address, data)
116 }
117
118 fn transact_system_call_with_caller_commit(
120 &mut self,
121 caller: Address,
122 system_contract_address: Address,
123 data: Bytes,
124 ) -> Result<Self::ExecutionResult, Self::Error>;
125}
126
127impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm for Evm<CTX, INSP, INST, PRECOMPILES>
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 self.set_tx(CTX::Tx::new_system_tx_with_caller(
141 caller,
142 system_contract_address,
143 data,
144 ));
145 let mut handler = MainnetHandler::<_, _, EthFrame<_, _, _>>::default();
147 handler.run_system_call(self)
148 }
149}
150
151impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm for Evm<CTX, INSP, INST, PRECOMPILES>
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 .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 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}