1use crate::{
2 instructions::InstructionProvider, EthFrame, ExecuteCommitEvm, ExecuteEvm, Handler,
3 MainnetHandler, PrecompileProvider,
4};
5use context::{result::ResultAndState, ContextSetters, ContextTr, Evm, JournalTr, TxEnv};
6use database_interface::DatabaseCommit;
7use interpreter::{interpreter::EthInterpreter, InterpreterResult};
8use primitives::{address, Address, Bytes, TxKind};
9use state::EvmState;
10
11pub const SYSTEM_ADDRESS: Address = address!("0xfffffffffffffffffffffffffffffffffffffffe");
12
13pub trait SystemCallTx {
20 fn new_system_tx(data: Bytes, system_contract_address: Address) -> Self;
22}
23
24impl SystemCallTx for TxEnv {
25 fn new_system_tx(data: Bytes, system_contract_address: Address) -> Self {
26 TxEnv {
27 caller: SYSTEM_ADDRESS,
28 data,
29 kind: TxKind::Call(system_contract_address),
30 gas_limit: 30_000_000,
31 ..Default::default()
32 }
33 }
34}
35
36pub trait SystemCallEvm: ExecuteEvm {
41 fn transact_system_call(
48 &mut self,
49 system_contract_address: Address,
50 data: Bytes,
51 ) -> Result<Self::ExecutionResult, Self::Error>;
52
53 fn transact_system_call_finalize(
57 &mut self,
58 system_contract_address: Address,
59 data: Bytes,
60 ) -> Result<ResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
61 let result = self.transact_system_call(system_contract_address, data)?;
62 let state = self.finalize();
63 Ok(ResultAndState::new(result, state))
64 }
65}
66
67pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
69 fn transact_system_call_commit(
71 &mut self,
72 system_contract_address: Address,
73 data: Bytes,
74 ) -> Result<Self::ExecutionResult, Self::Error>;
75}
76
77impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm for Evm<CTX, INSP, INST, PRECOMPILES>
78where
79 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
80 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
81 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
82{
83 fn transact_system_call(
84 &mut self,
85 system_contract_address: Address,
86 data: Bytes,
87 ) -> Result<Self::ExecutionResult, Self::Error> {
88 self.set_tx(CTX::Tx::new_system_tx(data, system_contract_address));
90 let mut handler = MainnetHandler::<_, _, EthFrame<_, _, _>>::default();
92 handler.run_system_call(self)
93 }
94}
95
96impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm for Evm<CTX, INSP, INST, PRECOMPILES>
97where
98 CTX: ContextTr<Journal: JournalTr<State = EvmState>, Db: DatabaseCommit, Tx: SystemCallTx>
99 + ContextSetters,
100 INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
101 PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
102{
103 fn transact_system_call_commit(
104 &mut self,
105 system_contract_address: Address,
106 data: Bytes,
107 ) -> Result<Self::ExecutionResult, Self::Error> {
108 self.transact_system_call_finalize(system_contract_address, data)
109 .map(|output| {
110 self.db_mut().commit(output.state);
111 output.result
112 })
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use crate::{MainBuilder, MainContext};
119
120 use super::*;
121 use context::{
122 result::{ExecutionResult, Output, SuccessReason},
123 Context,
124 };
125 use database::InMemoryDB;
126 use primitives::{b256, bytes, StorageKey, U256};
127 use state::{AccountInfo, Bytecode};
128
129 const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935");
130 static HISTORY_STORAGE_CODE: Bytes = bytes!("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500");
131
132 #[test]
133 fn test_system_call() {
134 let mut db = InMemoryDB::default();
135 db.insert_account_info(
136 HISTORY_STORAGE_ADDRESS,
137 AccountInfo::default().with_code(Bytecode::new_legacy(HISTORY_STORAGE_CODE.clone())),
138 );
139
140 let block_hash =
141 b256!("0x1111111111111111111111111111111111111111111111111111111111111111");
142
143 let mut my_evm = Context::mainnet()
144 .with_db(db)
145 .modify_block_chained(|b| b.number = U256::ONE)
147 .build_mainnet();
148 let output = my_evm
149 .transact_system_call_finalize(HISTORY_STORAGE_ADDRESS, block_hash.0.into())
150 .unwrap();
151
152 assert_eq!(
153 output.result,
154 ExecutionResult::Success {
155 reason: SuccessReason::Stop,
156 gas_used: 22143,
157 gas_refunded: 0,
158 logs: vec![],
159 output: Output::Call(Bytes::default())
160 }
161 );
162 assert_eq!(output.state.len(), 1);
164 assert_eq!(
165 output.state[&HISTORY_STORAGE_ADDRESS]
166 .storage
167 .get(&StorageKey::from(0))
168 .map(|slot| slot.present_value)
169 .unwrap_or_default(),
170 U256::from_be_bytes(block_hash.0),
171 "State is not updated {:?}",
172 output.state
173 );
174 }
175}