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_GAS_LIMIT: u64 = 30_000_000
47 + eip8037::SSTORE_SET_BYTES * eip8037::CPSB_GLAMSTERDAM * SYSTEM_MAX_SSTORES_PER_CALL;
48
49pub trait SystemCallTx: Sized {
56 fn new_system_tx(system_contract_address: Address, data: Bytes) -> Self {
58 Self::new_system_tx_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
59 }
60
61 fn new_system_tx_with_caller(
63 caller: Address,
64 system_contract_address: Address,
65 data: Bytes,
66 ) -> Self;
67}
68
69impl SystemCallTx for TxEnv {
70 fn new_system_tx_with_caller(
71 caller: Address,
72 system_contract_address: Address,
73 data: Bytes,
74 ) -> Self {
75 TxEnv::builder()
76 .caller(caller)
77 .data(data)
78 .kind(TxKind::Call(system_contract_address))
79 .gas_limit(SYSTEM_CALL_GAS_LIMIT)
80 .build()
81 .unwrap()
82 }
83}
84
85pub trait SystemCallEvm: ExecuteEvm {
95 fn system_call_one_with_caller(
102 &mut self,
103 caller: Address,
104 system_contract_address: Address,
105 data: Bytes,
106 ) -> Result<Self::ExecutionResult, Self::Error>;
107
108 fn system_call_one(
115 &mut self,
116 system_contract_address: Address,
117 data: Bytes,
118 ) -> Result<Self::ExecutionResult, Self::Error> {
119 self.system_call_one_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
120 }
121
122 fn system_call(
124 &mut self,
125 system_contract_address: Address,
126 data: Bytes,
127 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
128 self.system_call_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
129 }
130
131 fn system_call_with_caller(
133 &mut self,
134 caller: Address,
135 system_contract_address: Address,
136 data: Bytes,
137 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
138 let result = self.system_call_one_with_caller(caller, system_contract_address, data)?;
139 let state = self.finalize();
140 Ok(ExecResultAndState::new(result, state))
141 }
142
143 #[deprecated(since = "0.1.0", note = "Use `system_call_one_with_caller` instead")]
150 fn transact_system_call_with_caller(
151 &mut self,
152 caller: Address,
153 system_contract_address: Address,
154 data: Bytes,
155 ) -> Result<Self::ExecutionResult, Self::Error> {
156 self.system_call_one_with_caller(caller, system_contract_address, data)
157 }
158
159 #[deprecated(since = "0.1.0", note = "Use `system_call_one` instead")]
161 fn transact_system_call(
162 &mut self,
163 system_contract_address: Address,
164 data: Bytes,
165 ) -> Result<Self::ExecutionResult, Self::Error> {
166 self.system_call_one(system_contract_address, data)
167 }
168
169 #[deprecated(since = "0.1.0", note = "Use `system_call` instead")]
173 fn transact_system_call_finalize(
174 &mut self,
175 system_contract_address: Address,
176 data: Bytes,
177 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
178 self.system_call(system_contract_address, data)
179 }
180
181 #[deprecated(since = "0.1.0", note = "Use `system_call_with_caller` instead")]
183 fn transact_system_call_with_caller_finalize(
184 &mut self,
185 caller: Address,
186 system_contract_address: Address,
187 data: Bytes,
188 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
189 self.system_call_with_caller(caller, system_contract_address, data)
190 }
191}
192
193pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
195 fn system_call_commit(
197 &mut self,
198 system_contract_address: Address,
199 data: Bytes,
200 ) -> Result<Self::ExecutionResult, Self::Error> {
201 self.system_call_with_caller_commit(SYSTEM_ADDRESS, system_contract_address, data)
202 }
203
204 #[deprecated(since = "0.1.0", note = "Use `system_call_commit` instead")]
206 fn transact_system_call_commit(
207 &mut self,
208 system_contract_address: Address,
209 data: Bytes,
210 ) -> Result<Self::ExecutionResult, Self::Error> {
211 self.system_call_commit(system_contract_address, data)
212 }
213
214 fn system_call_with_caller_commit(
216 &mut self,
217 caller: Address,
218 system_contract_address: Address,
219 data: Bytes,
220 ) -> Result<Self::ExecutionResult, Self::Error>;
221
222 #[deprecated(since = "0.1.0", note = "Use `system_call_with_caller_commit` instead")]
224 fn transact_system_call_with_caller_commit(
225 &mut self,
226 caller: Address,
227 system_contract_address: Address,
228 data: Bytes,
229 ) -> Result<Self::ExecutionResult, Self::Error> {
230 self.system_call_with_caller_commit(caller, system_contract_address, data)
231 }
232}
233
234#[cfg(feature = "asyncdb")]
236pub trait SystemCallEvmAsync: SystemCallEvm {
237 fn system_call_one_with_caller_async(
239 &mut self,
240 caller: Address,
241 system_contract_address: Address,
242 data: Bytes,
243 ) -> impl core::future::Future<Output = AsyncResult<Self::ExecutionResult, Self::Error>> + Send + '_;
244
245 fn system_call_one_async(
247 &mut self,
248 system_contract_address: Address,
249 data: Bytes,
250 ) -> impl core::future::Future<Output = AsyncResult<Self::ExecutionResult, Self::Error>> + Send + '_
251 {
252 self.system_call_one_with_caller_async(SYSTEM_ADDRESS, system_contract_address, data)
253 }
254
255 fn system_call_with_caller_async(
257 &mut self,
258 caller: Address,
259 system_contract_address: Address,
260 data: Bytes,
261 ) -> impl core::future::Future<
262 Output = AsyncResult<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error>,
263 > + Send
264 + '_;
265
266 fn system_call_async(
268 &mut self,
269 system_contract_address: Address,
270 data: Bytes,
271 ) -> impl core::future::Future<
272 Output = AsyncResult<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error>,
273 > + Send
274 + '_ {
275 self.system_call_with_caller_async(SYSTEM_ADDRESS, system_contract_address, data)
276 }
277}
278
279impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm
280 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
281where
282 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
283 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
284 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
285{
286 fn system_call_one_with_caller(
287 &mut self,
288 caller: Address,
289 system_contract_address: Address,
290 data: Bytes,
291 ) -> Result<Self::ExecutionResult, Self::Error> {
292 self.set_tx(CTX::Tx::new_system_tx_with_caller(
294 caller,
295 system_contract_address,
296 data,
297 ));
298 MainnetHandler::default().run_system_call(self)
300 }
301}
302
303#[cfg(feature = "asyncdb")]
304impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvmAsync
305 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
306where
307 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
308 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
309 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
310 Self: ExecuteEvm,
311 <Self as ExecuteEvm>::ExecutionResult: Send,
312 <Self as ExecuteEvm>::State: Send,
313 <Self as ExecuteEvm>::Error: Send,
314{
315 #[inline]
316 fn system_call_one_with_caller_async(
317 &mut self,
318 caller: Address,
319 system_contract_address: Address,
320 data: Bytes,
321 ) -> impl core::future::Future<Output = AsyncResult<Self::ExecutionResult, Self::Error>> + Send + '_
322 {
323 let stack = NonNull::from(&mut self.async_stack);
324 unsafe {
327 on_fiber_result_with_stack(stack, move || {
328 self.system_call_one_with_caller(caller, system_contract_address, data)
329 })
330 }
331 }
332
333 #[inline]
334 fn system_call_with_caller_async(
335 &mut self,
336 caller: Address,
337 system_contract_address: Address,
338 data: Bytes,
339 ) -> impl core::future::Future<
340 Output = AsyncResult<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error>,
341 > + Send
342 + '_ {
343 let stack = NonNull::from(&mut self.async_stack);
344 unsafe {
347 on_fiber_result_with_stack(stack, move || {
348 self.system_call_with_caller(caller, system_contract_address, data)
349 })
350 }
351 }
352}
353
354impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm
355 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
356where
357 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Db: DatabaseCommit, Tx: SystemCallTx>
358 + ContextSetters,
359 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
360 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
361{
362 fn system_call_with_caller_commit(
363 &mut self,
364 caller: Address,
365 system_contract_address: Address,
366 data: Bytes,
367 ) -> Result<Self::ExecutionResult, Self::Error> {
368 self.system_call_with_caller(caller, system_contract_address, data)
369 .map(|output| {
370 self.db_mut().commit(output.state);
371 output.result
372 })
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use crate::{MainBuilder, MainContext};
379
380 use super::*;
381 use context::{
382 result::{ExecutionResult, Output, ResultGas, SuccessReason},
383 Context, Transaction,
384 };
385 use database::InMemoryDB;
386 use primitives::{b256, bytes, StorageKey, U256};
387 use state::{AccountInfo, Bytecode};
388
389 const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935");
390 static HISTORY_STORAGE_CODE: Bytes = bytes!("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500");
391
392 #[test]
393 fn test_system_call() {
394 let mut db = InMemoryDB::default();
395 db.insert_account_info(
396 HISTORY_STORAGE_ADDRESS,
397 AccountInfo::default().with_code(Bytecode::new_legacy(HISTORY_STORAGE_CODE.clone())),
398 );
399
400 let block_hash =
401 b256!("0x1111111111111111111111111111111111111111111111111111111111111111");
402
403 let mut evm = Context::mainnet()
404 .with_db(db)
405 .modify_block_chained(|b| b.number = U256::ONE)
407 .build_mainnet();
408 let output = evm
409 .system_call(HISTORY_STORAGE_ADDRESS, block_hash.0.into())
410 .unwrap();
411
412 assert_eq!(evm.ctx.tx().gas_limit(), SYSTEM_CALL_GAS_LIMIT);
414
415 assert_eq!(
416 output.result,
417 ExecutionResult::Success {
418 reason: SuccessReason::Stop,
419 gas: ResultGas::default().with_total_gas_spent(22143),
420 logs: vec![],
421 output: Output::Call(Bytes::default())
422 }
423 );
424 assert_eq!(output.state.len(), 1);
426 assert_eq!(
427 output.state[&HISTORY_STORAGE_ADDRESS]
428 .storage
429 .get(&StorageKey::from(0))
430 .map(|slot| slot.present_value)
431 .unwrap_or_default(),
432 U256::from_be_bytes(block_hash.0),
433 "State is not updated {:?}",
434 output.state
435 );
436 }
437}