1use crate::{
23 frame::EthFrame, instructions::InstructionProvider, ExecuteCommitEvm, ExecuteEvm, Handler,
24 MainnetHandler, PrecompileProvider,
25};
26use context::{result::ExecResultAndState, ContextSetters, ContextTr, Evm, JournalTr, TxEnv};
27#[cfg(feature = "asyncdb")]
28use database_interface::async_db::{on_fiber_result_with_stack, AsyncResult};
29use database_interface::DatabaseCommit;
30use interpreter::{interpreter::EthInterpreter, InterpreterResult};
31use primitives::{address, eip8037, Address, Bytes, TxKind};
32use state::EvmState;
33#[cfg(feature = "asyncdb")]
34use std::ptr::NonNull;
35
36pub const SYSTEM_ADDRESS: Address = address!("0xfffffffffffffffffffffffffffffffffffffffe");
38
39pub const SYSTEM_MAX_SSTORES_PER_CALL: u64 = 16;
41
42pub const SYSTEM_CALL_REGULAR_GAS_LIMIT: u64 = 30_000_000;
47
48pub const SYSTEM_CALL_STATE_GAS_RESERVOIR: u64 =
51 eip8037::SSTORE_SET_BYTES * eip8037::CPSB_GLAMSTERDAM * SYSTEM_MAX_SSTORES_PER_CALL;
52
53pub const SYSTEM_CALL_GAS_LIMIT: u64 =
61 SYSTEM_CALL_REGULAR_GAS_LIMIT + SYSTEM_CALL_STATE_GAS_RESERVOIR;
62
63pub trait SystemCallTx: Sized {
70 fn new_system_tx(system_contract_address: Address, data: Bytes) -> Self {
72 Self::new_system_tx_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
73 }
74
75 fn new_system_tx_with_caller(
77 caller: Address,
78 system_contract_address: Address,
79 data: Bytes,
80 ) -> Self;
81}
82
83impl SystemCallTx for TxEnv {
84 fn new_system_tx_with_caller(
85 caller: Address,
86 system_contract_address: Address,
87 data: Bytes,
88 ) -> Self {
89 TxEnv::builder()
90 .caller(caller)
91 .data(data)
92 .kind(TxKind::Call(system_contract_address))
93 .gas_limit(SYSTEM_CALL_GAS_LIMIT)
94 .build()
95 .unwrap()
96 }
97}
98
99pub trait SystemCallEvm: ExecuteEvm {
109 fn system_call_one_with_caller(
116 &mut self,
117 caller: Address,
118 system_contract_address: Address,
119 data: Bytes,
120 ) -> Result<Self::ExecutionResult, Self::Error>;
121
122 fn system_call_one(
129 &mut self,
130 system_contract_address: Address,
131 data: Bytes,
132 ) -> Result<Self::ExecutionResult, Self::Error> {
133 self.system_call_one_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
134 }
135
136 fn system_call(
138 &mut self,
139 system_contract_address: Address,
140 data: Bytes,
141 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
142 self.system_call_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
143 }
144
145 fn system_call_with_caller(
147 &mut self,
148 caller: Address,
149 system_contract_address: Address,
150 data: Bytes,
151 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
152 let result = self.system_call_one_with_caller(caller, system_contract_address, data)?;
153 let state = self.finalize();
154 Ok(ExecResultAndState::new(result, state))
155 }
156
157 #[deprecated(since = "0.1.0", note = "Use `system_call_one_with_caller` instead")]
164 fn transact_system_call_with_caller(
165 &mut self,
166 caller: Address,
167 system_contract_address: Address,
168 data: Bytes,
169 ) -> Result<Self::ExecutionResult, Self::Error> {
170 self.system_call_one_with_caller(caller, system_contract_address, data)
171 }
172
173 #[deprecated(since = "0.1.0", note = "Use `system_call_one` instead")]
175 fn transact_system_call(
176 &mut self,
177 system_contract_address: Address,
178 data: Bytes,
179 ) -> Result<Self::ExecutionResult, Self::Error> {
180 self.system_call_one(system_contract_address, data)
181 }
182
183 #[deprecated(since = "0.1.0", note = "Use `system_call` instead")]
187 fn transact_system_call_finalize(
188 &mut self,
189 system_contract_address: Address,
190 data: Bytes,
191 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
192 self.system_call(system_contract_address, data)
193 }
194
195 #[deprecated(since = "0.1.0", note = "Use `system_call_with_caller` instead")]
197 fn transact_system_call_with_caller_finalize(
198 &mut self,
199 caller: Address,
200 system_contract_address: Address,
201 data: Bytes,
202 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
203 self.system_call_with_caller(caller, system_contract_address, data)
204 }
205}
206
207pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
209 fn system_call_commit(
211 &mut self,
212 system_contract_address: Address,
213 data: Bytes,
214 ) -> Result<Self::ExecutionResult, Self::Error> {
215 self.system_call_with_caller_commit(SYSTEM_ADDRESS, system_contract_address, data)
216 }
217
218 #[deprecated(since = "0.1.0", note = "Use `system_call_commit` instead")]
220 fn transact_system_call_commit(
221 &mut self,
222 system_contract_address: Address,
223 data: Bytes,
224 ) -> Result<Self::ExecutionResult, Self::Error> {
225 self.system_call_commit(system_contract_address, data)
226 }
227
228 fn system_call_with_caller_commit(
230 &mut self,
231 caller: Address,
232 system_contract_address: Address,
233 data: Bytes,
234 ) -> Result<Self::ExecutionResult, Self::Error>;
235
236 #[deprecated(since = "0.1.0", note = "Use `system_call_with_caller_commit` instead")]
238 fn transact_system_call_with_caller_commit(
239 &mut self,
240 caller: Address,
241 system_contract_address: Address,
242 data: Bytes,
243 ) -> Result<Self::ExecutionResult, Self::Error> {
244 self.system_call_with_caller_commit(caller, system_contract_address, data)
245 }
246}
247
248#[cfg(feature = "asyncdb")]
250pub trait SystemCallEvmAsync: SystemCallEvm {
251 fn system_call_one_with_caller_async(
253 &mut self,
254 caller: Address,
255 system_contract_address: Address,
256 data: Bytes,
257 ) -> impl core::future::Future<Output = AsyncResult<Self::ExecutionResult, Self::Error>> + Send + '_;
258
259 fn system_call_one_async(
261 &mut self,
262 system_contract_address: Address,
263 data: Bytes,
264 ) -> impl core::future::Future<Output = AsyncResult<Self::ExecutionResult, Self::Error>> + Send + '_
265 {
266 self.system_call_one_with_caller_async(SYSTEM_ADDRESS, system_contract_address, data)
267 }
268
269 fn system_call_with_caller_async(
271 &mut self,
272 caller: Address,
273 system_contract_address: Address,
274 data: Bytes,
275 ) -> impl core::future::Future<
276 Output = AsyncResult<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error>,
277 > + Send
278 + '_;
279
280 fn system_call_async(
282 &mut self,
283 system_contract_address: Address,
284 data: Bytes,
285 ) -> impl core::future::Future<
286 Output = AsyncResult<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error>,
287 > + Send
288 + '_ {
289 self.system_call_with_caller_async(SYSTEM_ADDRESS, system_contract_address, data)
290 }
291}
292
293impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm
294 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
295where
296 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
297 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
298 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
299{
300 fn system_call_one_with_caller(
301 &mut self,
302 caller: Address,
303 system_contract_address: Address,
304 data: Bytes,
305 ) -> Result<Self::ExecutionResult, Self::Error> {
306 self.set_tx(CTX::Tx::new_system_tx_with_caller(
308 caller,
309 system_contract_address,
310 data,
311 ));
312 MainnetHandler::default().run_system_call(self)
314 }
315}
316
317#[cfg(feature = "asyncdb")]
318impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvmAsync
319 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
320where
321 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
322 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
323 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
324 Self: ExecuteEvm,
325 <Self as ExecuteEvm>::ExecutionResult: Send,
326 <Self as ExecuteEvm>::State: Send,
327 <Self as ExecuteEvm>::Error: Send,
328{
329 #[inline]
330 fn system_call_one_with_caller_async(
331 &mut self,
332 caller: Address,
333 system_contract_address: Address,
334 data: Bytes,
335 ) -> impl core::future::Future<Output = AsyncResult<Self::ExecutionResult, Self::Error>> + Send + '_
336 {
337 let stack = NonNull::from(&mut self.async_stack);
338 unsafe {
341 on_fiber_result_with_stack(stack, move || {
342 self.system_call_one_with_caller(caller, system_contract_address, data)
343 })
344 }
345 }
346
347 #[inline]
348 fn system_call_with_caller_async(
349 &mut self,
350 caller: Address,
351 system_contract_address: Address,
352 data: Bytes,
353 ) -> impl core::future::Future<
354 Output = AsyncResult<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error>,
355 > + Send
356 + '_ {
357 let stack = NonNull::from(&mut self.async_stack);
358 unsafe {
361 on_fiber_result_with_stack(stack, move || {
362 self.system_call_with_caller(caller, system_contract_address, data)
363 })
364 }
365 }
366}
367
368impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm
369 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
370where
371 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Db: DatabaseCommit, Tx: SystemCallTx>
372 + ContextSetters,
373 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
374 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
375{
376 fn system_call_with_caller_commit(
377 &mut self,
378 caller: Address,
379 system_contract_address: Address,
380 data: Bytes,
381 ) -> Result<Self::ExecutionResult, Self::Error> {
382 self.system_call_with_caller(caller, system_contract_address, data)
383 .map(|output| {
384 self.db_mut().commit(output.state);
385 output.result
386 })
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use crate::{MainBuilder, MainContext};
393
394 use super::*;
395 use context::{
396 result::{ExecutionResult, Output, ResultGas, SuccessReason},
397 Context, Transaction,
398 };
399 use database::InMemoryDB;
400 use primitives::{b256, bytes, StorageKey, U256};
401 use state::{AccountInfo, Bytecode};
402
403 const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935");
404 static HISTORY_STORAGE_CODE: Bytes = bytes!("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500");
405
406 #[test]
407 fn test_system_call() {
408 let mut db = InMemoryDB::default();
409 db.insert_account_info(
410 HISTORY_STORAGE_ADDRESS,
411 AccountInfo::default().with_code(Bytecode::new_legacy(HISTORY_STORAGE_CODE.clone())),
412 );
413
414 let block_hash =
415 b256!("0x1111111111111111111111111111111111111111111111111111111111111111");
416
417 let mut evm = Context::mainnet()
418 .with_db(db)
419 .modify_block_chained(|b| b.number = U256::ONE)
421 .build_mainnet();
422 let output = evm
423 .system_call(HISTORY_STORAGE_ADDRESS, block_hash.0.into())
424 .unwrap();
425
426 assert_eq!(evm.ctx.tx().gas_limit(), SYSTEM_CALL_GAS_LIMIT);
428
429 assert_eq!(
430 output.result,
431 ExecutionResult::Success {
432 reason: SuccessReason::Stop,
433 gas: ResultGas::default().with_total_gas_spent(22143),
434 logs: vec![],
435 output: Output::Call(Bytes::default())
436 }
437 );
438 assert_eq!(output.state.len(), 1);
440 assert_eq!(
441 output.state[&HISTORY_STORAGE_ADDRESS]
442 .storage
443 .get(&StorageKey::from(0))
444 .map(|slot| slot.present_value)
445 .unwrap_or_default(),
446 U256::from_be_bytes(block_hash.0),
447 "State is not updated {:?}",
448 output.state
449 );
450 }
451
452 #[test]
458 fn test_system_call_eip8037_state_gas_reservoir() {
459 use primitives::{eip8037, hardfork::SpecId};
460
461 const SYSTEM_CONTRACT: Address = address!("0x000000000000000000000000000000000000c0de");
462 static CODE: Bytes = bytes!("0x5a5f5500");
464
465 let mut db = InMemoryDB::default();
466 db.insert_account_info(
467 SYSTEM_CONTRACT,
468 AccountInfo::default().with_code(Bytecode::new_legacy(CODE.clone())),
469 );
470
471 let mut evm = Context::mainnet()
472 .with_db(db)
473 .modify_cfg_chained(|cfg| cfg.set_spec_and_mainnet_gas_params(SpecId::AMSTERDAM))
474 .build_mainnet();
475 let output = evm.system_call(SYSTEM_CONTRACT, Bytes::default()).unwrap();
476 assert!(output.result.is_success(), "{:?}", output.result);
477
478 let gas_seen = output.state[&SYSTEM_CONTRACT]
479 .storage
480 .get(&StorageKey::from(0))
481 .map(|slot| slot.present_value)
482 .unwrap_or_default();
483 assert_eq!(gas_seen, U256::from(SYSTEM_CALL_REGULAR_GAS_LIMIT - 2));
485
486 let sstore_state_gas = eip8037::SSTORE_SET_BYTES * eip8037::CPSB_GLAMSTERDAM;
488 assert_eq!(output.result.gas().block_state_gas_used(), sstore_state_gas);
489 assert!(sstore_state_gas <= SYSTEM_CALL_STATE_GAS_RESERVOIR);
490 }
491}