revm_interpreter/instructions/contract/
call_helpers.rs1use crate::{
2 interpreter::Interpreter,
3 interpreter_types::{InterpreterTypes as ITy, MemoryTr, RuntimeFlag, StackTr},
4 InstructionContext as Ictx, InstructionResult,
5};
6use context_interface::{cfg::GasParams, host::LoadError, Host};
7use core::{cmp::min, ops::Range};
8use primitives::{
9 hardfork::SpecId::{self, *},
10 Address, B256, U256,
11};
12use state::Bytecode;
13
14#[inline]
16pub fn get_memory_input_and_out_ranges(
17 interpreter: &mut Interpreter<impl ITy>,
18 gas_params: &GasParams,
19) -> Result<(Range<usize>, Range<usize>), InstructionResult> {
20 popn!([in_offset, in_len, out_offset, out_len], interpreter);
21
22 let mut in_range = resize_memory(interpreter, gas_params, in_offset, in_len)?;
23
24 if !in_range.is_empty() {
25 let offset = interpreter.memory.local_memory_offset();
26 in_range = in_range.start.saturating_add(offset)..in_range.end.saturating_add(offset);
27 }
28
29 let ret_range = resize_memory(interpreter, gas_params, out_offset, out_len)?;
30 Ok((in_range, ret_range))
31}
32
33#[inline]
36pub fn resize_memory(
37 interpreter: &mut Interpreter<impl ITy>,
38 gas_params: &GasParams,
39 offset: U256,
40 len: U256,
41) -> Result<Range<usize>, InstructionResult> {
42 let len = as_usize_or_fail!(interpreter, len);
43 let offset = if len != 0 {
44 let offset = as_usize_or_fail!(interpreter, offset);
45 interpreter.resize_memory(gas_params, offset, len)?;
46 offset
47 } else {
48 usize::MAX };
50 Ok(offset..offset + len)
51}
52
53#[inline(never)]
61pub fn load_acc_and_calc_gas<H: Host + ?Sized>(
62 context: &mut Ictx<'_, H, impl ITy>,
63 to: Address,
64 transfers_value: bool,
65 create_empty_account: bool,
66 stack_gas_limit: u64,
67) -> Result<(u64, Bytecode, B256, bool), InstructionResult> {
68 if transfers_value {
70 gas!(
71 context.interpreter,
72 context.host.gas_params().transfer_value_cost()
73 );
74 }
75
76 let (gas, state_gas_cost, bytecode, code_hash) =
78 load_account_delegated_handle_error(context, to, transfers_value, create_empty_account)?;
79 let charged_new_account_state_gas = state_gas_cost > 0;
80 let interpreter = &mut context.interpreter;
81
82 gas!(interpreter, gas);
84
85 state_gas!(interpreter, state_gas_cost);
87
88 let interpreter = &mut context.interpreter;
89 let host = &mut context.host;
90
91 let mut gas_limit = if interpreter.runtime_flag.spec_id().is_enabled_in(TANGERINE) {
93 let reduced_gas_limit = host
95 .gas_params()
96 .call_stipend_reduction(interpreter.gas.remaining());
97 min(reduced_gas_limit, stack_gas_limit)
98 } else {
99 stack_gas_limit
100 };
101 gas!(interpreter, gas_limit);
102
103 if transfers_value {
105 gas_limit = gas_limit.saturating_add(host.gas_params().call_stipend());
106 }
107
108 Ok((
109 gas_limit,
110 bytecode,
111 code_hash,
112 charged_new_account_state_gas,
113 ))
114}
115
116#[inline]
120pub fn load_account_delegated_handle_error<H: Host + ?Sized>(
121 context: &mut Ictx<'_, H, impl ITy>,
122 to: Address,
123 transfers_value: bool,
124 create_empty_account: bool,
125) -> Result<(u64, u64, Bytecode, B256), InstructionResult> {
126 let remaining_gas = context.interpreter.gas.remaining();
128 Ok(load_account_delegated(
129 context.host,
130 context.interpreter.runtime_flag.spec_id(),
131 remaining_gas,
132 to,
133 transfers_value,
134 create_empty_account,
135 )?)
136}
137
138#[inline]
145pub fn load_account_delegated<H: Host + ?Sized>(
146 host: &mut H,
147 spec: SpecId,
148 remaining_gas: u64,
149 address: Address,
150 transfers_value: bool,
151 create_empty_account: bool,
152) -> Result<(u64, u64, Bytecode, B256), LoadError> {
153 let mut cost = 0;
154 let mut state_gas_cost = 0;
155 let is_berlin = spec.is_enabled_in(SpecId::BERLIN);
156 let is_spurious_dragon = spec.is_enabled_in(SpecId::SPURIOUS_DRAGON);
157
158 let additional_cold_cost = host.gas_params().cold_account_additional_cost();
159 let warm_storage_read_cost = host.gas_params().warm_storage_read_cost();
160
161 let skip_cold_load = is_berlin && remaining_gas < additional_cold_cost;
162 let account = host.load_account_info_skip_cold_load(address, true, skip_cold_load)?;
163 if is_berlin && account.is_cold {
164 cost += additional_cold_cost;
165 }
166 let mut bytecode = account.code.clone().unwrap_or_default();
167 let mut code_hash = account.code_hash();
168 if create_empty_account && account.is_empty {
170 cost += host
171 .gas_params()
172 .new_account_cost(is_spurious_dragon, transfers_value);
173 if host.is_amsterdam_eip8037_enabled() && transfers_value {
174 state_gas_cost += host.gas_params().new_account_state_gas();
175 }
176 return Ok((cost, state_gas_cost, bytecode, code_hash));
177 }
178
179 if let Some(address) = account.code.as_ref().and_then(Bytecode::eip7702_address) {
181 cost += warm_storage_read_cost;
183 if cost > remaining_gas {
184 return Err(LoadError::ColdLoadSkipped);
185 }
186
187 let skip_cold_load = remaining_gas < cost + additional_cold_cost;
189 let delegate_account =
190 host.load_account_info_skip_cold_load(address, true, skip_cold_load)?;
191
192 if delegate_account.is_cold {
193 cost += additional_cold_cost;
194 }
195 bytecode = delegate_account.code.clone().unwrap_or_default();
196 code_hash = delegate_account.code_hash();
197 }
198
199 Ok((cost, state_gas_cost, bytecode, code_hash))
200}