1use crate::{
2 frame::EthFrame, instructions::InstructionProvider, ExecuteCommitEvm, ExecuteEvm, Handler,
3 MainnetHandler, PrecompileProvider,
4};
5use context::{
6 result::ExecResultAndState, 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");
15
16pub trait SystemCallTx: Sized {
23 fn new_system_tx(system_contract_address: Address, data: Bytes) -> Self {
25 Self::new_system_tx_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
26 }
27
28 fn new_system_tx_with_caller(
30 caller: Address,
31 system_contract_address: Address,
32 data: Bytes,
33 ) -> Self;
34}
35
36impl SystemCallTx for TxEnv {
37 fn new_system_tx_with_caller(
38 caller: Address,
39 system_contract_address: Address,
40 data: Bytes,
41 ) -> Self {
42 TxEnv {
43 tx_type: TransactionType::Legacy as u8,
44 caller,
45 data,
46 kind: TxKind::Call(system_contract_address),
47 gas_limit: eip7825::TX_GAS_LIMIT_CAP,
48 ..Default::default()
49 }
50 }
51}
52
53pub trait SystemCallEvm: ExecuteEvm {
58 fn transact_system_call_with_caller(
65 &mut self,
66 caller: Address,
67 system_contract_address: Address,
68 data: Bytes,
69 ) -> Result<Self::ExecutionResult, Self::Error>;
70
71 fn transact_system_call(
73 &mut self,
74 system_contract_address: Address,
75 data: Bytes,
76 ) -> Result<Self::ExecutionResult, Self::Error> {
77 self.transact_system_call_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
78 }
79
80 fn transact_system_call_finalize(
84 &mut self,
85 system_contract_address: Address,
86 data: Bytes,
87 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
88 self.transact_system_call_with_caller_finalize(
89 SYSTEM_ADDRESS,
90 system_contract_address,
91 data,
92 )
93 }
94
95 fn transact_system_call_with_caller_finalize(
97 &mut self,
98 caller: Address,
99 system_contract_address: Address,
100 data: Bytes,
101 ) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
102 let result =
103 self.transact_system_call_with_caller(caller, system_contract_address, data)?;
104 let state = self.finalize();
105 Ok(ExecResultAndState::new(result, state))
106 }
107}
108
109pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
111 fn transact_system_call_commit(
113 &mut self,
114 system_contract_address: Address,
115 data: Bytes,
116 ) -> Result<Self::ExecutionResult, Self::Error> {
117 self.transact_system_call_with_caller_commit(SYSTEM_ADDRESS, system_contract_address, data)
118 }
119
120 fn transact_system_call_with_caller_commit(
122 &mut self,
123 caller: Address,
124 system_contract_address: Address,
125 data: Bytes,
126 ) -> Result<Self::ExecutionResult, Self::Error>;
127}
128
129impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm
130 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
131where
132 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
133 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
134 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
135{
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.set_tx(CTX::Tx::new_system_tx_with_caller(
144 caller,
145 system_contract_address,
146 data,
147 ));
148 MainnetHandler::default().run_system_call(self)
150 }
151}
152
153impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm
154 for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
155where
156 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Db: DatabaseCommit, Tx: SystemCallTx>
157 + ContextSetters,
158 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
159 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
160{
161 fn transact_system_call_with_caller_commit(
162 &mut self,
163 caller: Address,
164 system_contract_address: Address,
165 data: Bytes,
166 ) -> Result<Self::ExecutionResult, Self::Error> {
167 self.transact_system_call_with_caller_finalize(caller, system_contract_address, data)
168 .map(|output| {
169 self.db_mut().commit(output.state);
170 output.result
171 })
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use crate::{MainBuilder, MainContext};
178
179 use super::*;
180 use context::{
181 result::{ExecutionResult, Output, SuccessReason},
182 Context,
183 };
184 use database::InMemoryDB;
185 use primitives::{b256, bytes, StorageKey, U256};
186 use state::{AccountInfo, Bytecode};
187
188 const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935");
189 static HISTORY_STORAGE_CODE: Bytes = bytes!("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500");
190
191 #[test]
192 fn test_system_call() {
193 let mut db = InMemoryDB::default();
194 db.insert_account_info(
195 HISTORY_STORAGE_ADDRESS,
196 AccountInfo::default().with_code(Bytecode::new_legacy(HISTORY_STORAGE_CODE.clone())),
197 );
198
199 let block_hash =
200 b256!("0x1111111111111111111111111111111111111111111111111111111111111111");
201
202 let mut my_evm = Context::mainnet()
203 .with_db(db)
204 .modify_block_chained(|b| b.number = U256::ONE)
206 .build_mainnet();
207 let output = my_evm
208 .transact_system_call_finalize(HISTORY_STORAGE_ADDRESS, block_hash.0.into())
209 .unwrap();
210
211 assert_eq!(
212 output.result,
213 ExecutionResult::Success {
214 reason: SuccessReason::Stop,
215 gas_used: 22143,
216 gas_refunded: 0,
217 logs: vec![],
218 output: Output::Call(Bytes::default())
219 }
220 );
221 assert_eq!(output.state.len(), 1);
223 assert_eq!(
224 output.state[&HISTORY_STORAGE_ADDRESS]
225 .storage
226 .get(&StorageKey::from(0))
227 .map(|slot| slot.present_value)
228 .unwrap_or_default(),
229 U256::from_be_bytes(block_hash.0),
230 "State is not updated {:?}",
231 output.state
232 );
233 }
234}