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, Address, Bytes, TxKind};
30use state::EvmState;
31
32pub const SYSTEM_ADDRESS: Address = address!("0xfffffffffffffffffffffffffffffffffffffffe");
34
35pub trait SystemCallTx: Sized {
42 fn new_system_tx(system_contract_address: Address, data: Bytes) -> Self {
44 Self::new_system_tx_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
45 }
46
47 fn new_system_tx_with_caller(
49 caller: Address,
50 system_contract_address: Address,
51 data: Bytes,
52 ) -> Self;
53}
54
55impl SystemCallTx for TxEnv {
56 fn new_system_tx_with_caller(
57 caller: Address,
58 system_contract_address: Address,
59 data: Bytes,
60 ) -> Self {
61 TxEnv::builder()
62 .caller(caller)
63 .data(data)
64 .kind(TxKind::Call(system_contract_address))
65 .gas_limit(30_000_000)
66 .build()
67 .unwrap()
68 }
69}
70
71pub trait SystemCallEvm: ExecuteEvm {
81 fn system_call_one_with_caller(
88 &mut self,
89 caller: Address,
90 system_contract_address: Address,
91 data: Bytes,
92 ) -> Result<Self::ExecutionResult, Self::Error>;
93
94 fn system_call_one(
101 &mut self,
102 system_contract_address: Address,
103 data: Bytes,
104 ) -> Result<Self::ExecutionResult, Self::Error> {
105 self.system_call_one_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
106 }
107
108 fn system_call(
110 &mut self,
111 system_contract_address: Address,
112 data: Bytes,
113 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
114 self.system_call_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
115 }
116
117 fn system_call_with_caller(
119 &mut self,
120 caller: Address,
121 system_contract_address: Address,
122 data: Bytes,
123 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
124 let result = self.system_call_one_with_caller(caller, system_contract_address, data)?;
125 let state = self.finalize();
126 Ok(ExecResultAndState::new(result, state))
127 }
128
129 #[deprecated(since = "0.1.0", note = "Use `system_call_one_with_caller` instead")]
136 fn transact_system_call_with_caller(
137 &mut self,
138 caller: Address,
139 system_contract_address: Address,
140 data: Bytes,
141 ) -> Result<Self::ExecutionResult, Self::Error> {
142 self.system_call_one_with_caller(caller, system_contract_address, data)
143 }
144
145 #[deprecated(since = "0.1.0", note = "Use `system_call_one` instead")]
147 fn transact_system_call(
148 &mut self,
149 system_contract_address: Address,
150 data: Bytes,
151 ) -> Result<Self::ExecutionResult, Self::Error> {
152 self.system_call_one(system_contract_address, data)
153 }
154
155 #[deprecated(since = "0.1.0", note = "Use `system_call` instead")]
159 fn transact_system_call_finalize(
160 &mut self,
161 system_contract_address: Address,
162 data: Bytes,
163 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
164 self.system_call(system_contract_address, data)
165 }
166
167 #[deprecated(since = "0.1.0", note = "Use `system_call_with_caller` instead")]
169 fn transact_system_call_with_caller_finalize(
170 &mut self,
171 caller: Address,
172 system_contract_address: Address,
173 data: Bytes,
174 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
175 self.system_call_with_caller(caller, system_contract_address, data)
176 }
177}
178
179pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
181 fn system_call_commit(
183 &mut self,
184 system_contract_address: Address,
185 data: Bytes,
186 ) -> Result<Self::ExecutionResult, Self::Error> {
187 self.system_call_with_caller_commit(SYSTEM_ADDRESS, system_contract_address, data)
188 }
189
190 #[deprecated(since = "0.1.0", note = "Use `system_call_commit` instead")]
192 fn transact_system_call_commit(
193 &mut self,
194 system_contract_address: Address,
195 data: Bytes,
196 ) -> Result<Self::ExecutionResult, Self::Error> {
197 self.system_call_commit(system_contract_address, data)
198 }
199
200 fn system_call_with_caller_commit(
202 &mut self,
203 caller: Address,
204 system_contract_address: Address,
205 data: Bytes,
206 ) -> Result<Self::ExecutionResult, Self::Error>;
207
208 #[deprecated(since = "0.1.0", note = "Use `system_call_with_caller_commit` instead")]
210 fn transact_system_call_with_caller_commit(
211 &mut self,
212 caller: Address,
213 system_contract_address: Address,
214 data: Bytes,
215 ) -> Result<Self::ExecutionResult, Self::Error> {
216 self.system_call_with_caller_commit(caller, system_contract_address, data)
217 }
218}
219
220impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm
221 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
222where
223 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
224 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
225 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
226{
227 fn system_call_one_with_caller(
228 &mut self,
229 caller: Address,
230 system_contract_address: Address,
231 data: Bytes,
232 ) -> Result<Self::ExecutionResult, Self::Error> {
233 self.set_tx(CTX::Tx::new_system_tx_with_caller(
235 caller,
236 system_contract_address,
237 data,
238 ));
239 MainnetHandler::default().run_system_call(self)
241 }
242}
243
244impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm
245 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
246where
247 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Db: DatabaseCommit, Tx: SystemCallTx>
248 + ContextSetters,
249 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
250 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
251{
252 fn system_call_with_caller_commit(
253 &mut self,
254 caller: Address,
255 system_contract_address: Address,
256 data: Bytes,
257 ) -> Result<Self::ExecutionResult, Self::Error> {
258 self.system_call_with_caller(caller, system_contract_address, data)
259 .map(|output| {
260 self.db_mut().commit(output.state);
261 output.result
262 })
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use crate::{MainBuilder, MainContext};
269
270 use super::*;
271 use context::{
272 result::{ExecutionResult, Output, SuccessReason},
273 Context, Transaction,
274 };
275 use database::InMemoryDB;
276 use primitives::{b256, bytes, StorageKey, U256};
277 use state::{AccountInfo, Bytecode};
278
279 const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935");
280 static HISTORY_STORAGE_CODE: Bytes = bytes!("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500");
281
282 #[test]
283 fn test_system_call() {
284 let mut db = InMemoryDB::default();
285 db.insert_account_info(
286 HISTORY_STORAGE_ADDRESS,
287 AccountInfo::default().with_code(Bytecode::new_legacy(HISTORY_STORAGE_CODE.clone())),
288 );
289
290 let block_hash =
291 b256!("0x1111111111111111111111111111111111111111111111111111111111111111");
292
293 let mut evm = Context::mainnet()
294 .with_db(db)
295 .modify_block_chained(|b| b.number = U256::ONE)
297 .build_mainnet();
298 let output = evm
299 .system_call(HISTORY_STORAGE_ADDRESS, block_hash.0.into())
300 .unwrap();
301
302 assert_eq!(evm.ctx.tx().gas_limit(), 30_000_000);
304
305 assert_eq!(
306 output.result,
307 ExecutionResult::Success {
308 reason: SuccessReason::Stop,
309 gas_used: 22143,
310 gas_refunded: 0,
311 logs: vec![],
312 output: Output::Call(Bytes::default())
313 }
314 );
315 assert_eq!(output.state.len(), 1);
317 assert_eq!(
318 output.state[&HISTORY_STORAGE_ADDRESS]
319 .storage
320 .get(&StorageKey::from(0))
321 .map(|slot| slot.present_value)
322 .unwrap_or_default(),
323 U256::from_be_bytes(block_hash.0),
324 "State is not updated {:?}",
325 output.state
326 );
327 }
328}