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