1use crate::{
23 frame::EthFrame, instructions::InstructionProvider, ExecuteCommitEvm, ExecuteEvm, Handler,
24 MainnetHandler, PrecompileProvider,
25};
26use context::{result::ExecResultAndState, ContextSetters, ContextTr, Evm, JournalTr, TxEnv};
27use database_interface::DatabaseCommit;
28use interpreter::{interpreter::EthInterpreter, InterpreterResult};
29use primitives::{address, eip8037, Address, Bytes, TxKind};
30use state::EvmState;
31
32pub const SYSTEM_ADDRESS: Address = address!("0xfffffffffffffffffffffffffffffffffffffffe");
34
35pub const SYSTEM_MAX_SSTORES_PER_CALL: u64 = 16;
37
38pub const SYSTEM_CALL_GAS_LIMIT: u64 = 30_000_000
43 + eip8037::SSTORE_SET_BYTES * eip8037::CPSB_GLAMSTERDAM * SYSTEM_MAX_SSTORES_PER_CALL;
44
45pub trait SystemCallTx: Sized {
52 fn new_system_tx(system_contract_address: Address, data: Bytes) -> Self {
54 Self::new_system_tx_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
55 }
56
57 fn new_system_tx_with_caller(
59 caller: Address,
60 system_contract_address: Address,
61 data: Bytes,
62 ) -> Self;
63}
64
65impl SystemCallTx for TxEnv {
66 fn new_system_tx_with_caller(
67 caller: Address,
68 system_contract_address: Address,
69 data: Bytes,
70 ) -> Self {
71 TxEnv::builder()
72 .caller(caller)
73 .data(data)
74 .kind(TxKind::Call(system_contract_address))
75 .gas_limit(SYSTEM_CALL_GAS_LIMIT)
76 .build()
77 .unwrap()
78 }
79}
80
81pub trait SystemCallEvm: ExecuteEvm {
91 fn system_call_one_with_caller(
98 &mut self,
99 caller: Address,
100 system_contract_address: Address,
101 data: Bytes,
102 ) -> Result<Self::ExecutionResult, Self::Error>;
103
104 fn system_call_one(
111 &mut self,
112 system_contract_address: Address,
113 data: Bytes,
114 ) -> Result<Self::ExecutionResult, Self::Error> {
115 self.system_call_one_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
116 }
117
118 fn system_call(
120 &mut self,
121 system_contract_address: Address,
122 data: Bytes,
123 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
124 self.system_call_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
125 }
126
127 fn system_call_with_caller(
129 &mut self,
130 caller: Address,
131 system_contract_address: Address,
132 data: Bytes,
133 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
134 let result = self.system_call_one_with_caller(caller, system_contract_address, data)?;
135 let state = self.finalize();
136 Ok(ExecResultAndState::new(result, state))
137 }
138
139 #[deprecated(since = "0.1.0", note = "Use `system_call_one_with_caller` instead")]
146 fn transact_system_call_with_caller(
147 &mut self,
148 caller: Address,
149 system_contract_address: Address,
150 data: Bytes,
151 ) -> Result<Self::ExecutionResult, Self::Error> {
152 self.system_call_one_with_caller(caller, system_contract_address, data)
153 }
154
155 #[deprecated(since = "0.1.0", note = "Use `system_call_one` instead")]
157 fn transact_system_call(
158 &mut self,
159 system_contract_address: Address,
160 data: Bytes,
161 ) -> Result<Self::ExecutionResult, Self::Error> {
162 self.system_call_one(system_contract_address, data)
163 }
164
165 #[deprecated(since = "0.1.0", note = "Use `system_call` instead")]
169 fn transact_system_call_finalize(
170 &mut self,
171 system_contract_address: Address,
172 data: Bytes,
173 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
174 self.system_call(system_contract_address, data)
175 }
176
177 #[deprecated(since = "0.1.0", note = "Use `system_call_with_caller` instead")]
179 fn transact_system_call_with_caller_finalize(
180 &mut self,
181 caller: Address,
182 system_contract_address: Address,
183 data: Bytes,
184 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
185 self.system_call_with_caller(caller, system_contract_address, data)
186 }
187}
188
189pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
191 fn system_call_commit(
193 &mut self,
194 system_contract_address: Address,
195 data: Bytes,
196 ) -> Result<Self::ExecutionResult, Self::Error> {
197 self.system_call_with_caller_commit(SYSTEM_ADDRESS, system_contract_address, data)
198 }
199
200 #[deprecated(since = "0.1.0", note = "Use `system_call_commit` instead")]
202 fn transact_system_call_commit(
203 &mut self,
204 system_contract_address: Address,
205 data: Bytes,
206 ) -> Result<Self::ExecutionResult, Self::Error> {
207 self.system_call_commit(system_contract_address, data)
208 }
209
210 fn system_call_with_caller_commit(
212 &mut self,
213 caller: Address,
214 system_contract_address: Address,
215 data: Bytes,
216 ) -> Result<Self::ExecutionResult, Self::Error>;
217
218 #[deprecated(since = "0.1.0", note = "Use `system_call_with_caller_commit` instead")]
220 fn transact_system_call_with_caller_commit(
221 &mut self,
222 caller: Address,
223 system_contract_address: Address,
224 data: Bytes,
225 ) -> Result<Self::ExecutionResult, Self::Error> {
226 self.system_call_with_caller_commit(caller, system_contract_address, data)
227 }
228}
229
230impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm
231 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
232where
233 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
234 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
235 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
236{
237 fn system_call_one_with_caller(
238 &mut self,
239 caller: Address,
240 system_contract_address: Address,
241 data: Bytes,
242 ) -> Result<Self::ExecutionResult, Self::Error> {
243 self.set_tx(CTX::Tx::new_system_tx_with_caller(
245 caller,
246 system_contract_address,
247 data,
248 ));
249 MainnetHandler::default().run_system_call(self)
251 }
252}
253
254impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm
255 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
256where
257 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Db: DatabaseCommit, Tx: SystemCallTx>
258 + ContextSetters,
259 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
260 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
261{
262 fn system_call_with_caller_commit(
263 &mut self,
264 caller: Address,
265 system_contract_address: Address,
266 data: Bytes,
267 ) -> Result<Self::ExecutionResult, Self::Error> {
268 self.system_call_with_caller(caller, system_contract_address, data)
269 .map(|output| {
270 self.db_mut().commit(output.state);
271 output.result
272 })
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use crate::{MainBuilder, MainContext};
279
280 use super::*;
281 use context::{
282 result::{ExecutionResult, Output, ResultGas, SuccessReason},
283 Context, Transaction,
284 };
285 use database::InMemoryDB;
286 use primitives::{b256, bytes, StorageKey, U256};
287 use state::{AccountInfo, Bytecode};
288
289 const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935");
290 static HISTORY_STORAGE_CODE: Bytes = bytes!("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500");
291
292 #[test]
293 fn test_system_call() {
294 let mut db = InMemoryDB::default();
295 db.insert_account_info(
296 HISTORY_STORAGE_ADDRESS,
297 AccountInfo::default().with_code(Bytecode::new_legacy(HISTORY_STORAGE_CODE.clone())),
298 );
299
300 let block_hash =
301 b256!("0x1111111111111111111111111111111111111111111111111111111111111111");
302
303 let mut evm = Context::mainnet()
304 .with_db(db)
305 .modify_block_chained(|b| b.number = U256::ONE)
307 .build_mainnet();
308 let output = evm
309 .system_call(HISTORY_STORAGE_ADDRESS, block_hash.0.into())
310 .unwrap();
311
312 assert_eq!(evm.ctx.tx().gas_limit(), SYSTEM_CALL_GAS_LIMIT);
314
315 assert_eq!(
316 output.result,
317 ExecutionResult::Success {
318 reason: SuccessReason::Stop,
319 gas: ResultGas::default().with_total_gas_spent(22143),
320 logs: vec![],
321 output: Output::Call(Bytes::default())
322 }
323 );
324 assert_eq!(output.state.len(), 1);
326 assert_eq!(
327 output.state[&HISTORY_STORAGE_ADDRESS]
328 .storage
329 .get(&StorageKey::from(0))
330 .map(|slot| slot.present_value)
331 .unwrap_or_default(),
332 U256::from_be_bytes(block_hash.0),
333 "State is not updated {:?}",
334 output.state
335 );
336 }
337}