ethrex_levm/
execution_handlers.rs1use crate::{
2 constants::*,
3 errors::{ContextResult, ExceptionalHalt, InternalError, TxResult, VMError},
4 gas_cost::{CODE_DEPOSIT_COST, CODE_DEPOSIT_REGULAR_COST_PER_WORD},
5 utils::create_eth_transfer_log,
6 vm::VM,
7};
8
9use bytes::Bytes;
10use ethrex_common::types::{Code, Fork};
11
12impl<'a> VM<'a> {
13 pub fn handle_precompile_result(
14 precompile_result: Result<Bytes, VMError>,
15 gas_limit: u64,
16 gas_remaining: u64,
17 ) -> Result<ContextResult, VMError> {
18 match precompile_result {
19 Ok(output) => {
20 let gas_used = gas_limit
21 .checked_sub(gas_remaining)
22 .ok_or(InternalError::Underflow)?;
23 Ok(ContextResult {
24 result: TxResult::Success,
25 gas_used,
26 gas_spent: gas_used, output,
28 })
29 }
30 Err(error) => {
31 if error.should_propagate() {
32 return Err(error);
33 }
34
35 Ok(ContextResult {
36 result: TxResult::Revert(error),
37 gas_used: gas_limit,
38 gas_spent: gas_limit, output: Bytes::new(),
40 })
41 }
42 }
43 }
44
45 #[cold] pub fn handle_opcode_result(&mut self) -> Result<ContextResult, VMError> {
47 if self.is_create()? {
49 let validate_create = self.validate_contract_creation();
50
51 if let Err(error) = validate_create {
52 if error.should_propagate() {
53 return Err(error);
54 }
55
56 let callframe = &mut self.current_call_frame;
58 callframe.gas_remaining = 0;
59
60 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
61 let gas_used = callframe
62 .gas_limit
63 .checked_sub(callframe.gas_remaining as u64)
64 .ok_or(InternalError::Underflow)?;
65 return Ok(ContextResult {
66 result: TxResult::Revert(error),
67 gas_used,
68 gas_spent: gas_used, output: Bytes::new(),
70 });
71 }
72
73 let contract_address = self.current_call_frame.to;
75 let code = self.current_call_frame.output.clone();
76 self.update_account_bytecode(contract_address, Code::from_bytecode(code, self.crypto))?;
77 }
78
79 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
80 let gas_used = {
81 let callframe = &mut self.current_call_frame;
82 callframe
83 .gas_limit
84 .checked_sub(callframe.gas_remaining as u64)
85 .ok_or(InternalError::Underflow)?
86 };
87 Ok(ContextResult {
88 result: TxResult::Success,
89 gas_used,
90 gas_spent: gas_used, output: std::mem::take(&mut self.current_call_frame.output),
92 })
93 }
94
95 #[cold] pub fn handle_opcode_error(&mut self, error: VMError) -> Result<ContextResult, VMError> {
97 if error.should_propagate() {
98 return Err(error);
99 }
100
101 let callframe = &mut self.current_call_frame;
102
103 if !error.is_revert_opcode() {
105 callframe.gas_remaining = 0;
106 }
107
108 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
109 let gas_used = callframe
110 .gas_limit
111 .checked_sub(callframe.gas_remaining as u64)
112 .ok_or(InternalError::Underflow)?;
113 Ok(ContextResult {
114 result: TxResult::Revert(error),
115 gas_used,
116 gas_spent: gas_used, output: std::mem::take(&mut callframe.output),
118 })
119 }
120
121 pub fn handle_create_transaction(&mut self) -> Result<Option<ContextResult>, VMError> {
123 let new_contract_address = self.current_call_frame.to;
124
125 if let Some(recorder) = self.db.bal_recorder.as_mut() {
128 recorder.record_touched_address(new_contract_address);
129 }
130
131 let new_account = self.get_account_mut(new_contract_address)?;
132
133 if new_account.create_would_collide() {
134 self.current_call_frame.gas_remaining = 0;
140 return Ok(Some(ContextResult {
141 result: TxResult::Revert(ExceptionalHalt::AddressAlreadyOccupied.into()),
142 gas_used: self.env.gas_limit,
143 gas_spent: self.env.gas_limit, output: Bytes::new(),
145 }));
146 }
147
148 let value = self.current_call_frame.msg_value;
149 self.increase_account_balance(new_contract_address, value)?;
150
151 if self.env.config.fork >= Fork::Amsterdam && !value.is_zero() {
154 let log = create_eth_transfer_log(self.env.origin, new_contract_address, value);
155 self.substate.add_log(log);
156 }
157
158 self.increment_account_nonce(new_contract_address)?;
159
160 Ok(None)
161 }
162
163 fn validate_contract_creation(&mut self) -> Result<(), VMError> {
165 let fork = self.env.config.fork;
166 let code = &self.current_call_frame.output;
167
168 let code_length: u64 = code
169 .len()
170 .try_into()
171 .map_err(|_| InternalError::TypeConversion)?;
172
173 if code.first().is_some_and(|v| v == &EOF_PREFIX) {
175 return Err(ExceptionalHalt::InvalidContractPrefix.into());
176 }
177
178 if fork >= Fork::Amsterdam {
183 if code_length > AMSTERDAM_MAX_CODE_SIZE {
185 return Err(ExceptionalHalt::ContractOutputTooBig.into());
186 }
187
188 let words = code_length.div_ceil(32);
189 let regular = words
190 .checked_mul(CODE_DEPOSIT_REGULAR_COST_PER_WORD)
191 .ok_or(InternalError::Overflow)?;
192 let state = code_length
193 .checked_mul(self.cost_per_state_byte)
194 .ok_or(InternalError::Overflow)?;
195
196 self.current_call_frame.increase_consumed_gas(regular)?;
198 if state > 0 {
199 self.increase_state_gas(state)?;
200 }
201 } else {
202 if code_length > MAX_CODE_SIZE {
204 return Err(ExceptionalHalt::ContractOutputTooBig.into());
205 }
206 let regular = code_length
207 .checked_mul(CODE_DEPOSIT_COST)
208 .ok_or(InternalError::Overflow)?;
209 self.current_call_frame.increase_consumed_gas(regular)?;
210 }
211
212 Ok(())
213 }
214}