revm_interpreter/instructions/contract/
call_helpers.rs1use crate::{
2 interpreter::Interpreter,
3 interpreter_types::{InterpreterTypes, MemoryTr, RuntimeFlag, StackTr},
4 InstructionContext,
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 InterpreterTypes>,
18 gas_params: &GasParams,
19) -> Option<(Range<usize>, Range<usize>)> {
20 popn!([in_offset, in_len, out_offset, out_len], interpreter, None);
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 Some((in_range, ret_range))
31}
32
33#[inline]
36pub fn resize_memory(
37 interpreter: &mut Interpreter<impl InterpreterTypes>,
38 gas_params: &GasParams,
39 offset: U256,
40 len: U256,
41) -> Option<Range<usize>> {
42 let len = as_usize_or_fail_ret!(interpreter, len, None);
43 let offset = if len != 0 {
44 let offset = as_usize_or_fail_ret!(interpreter, offset, None);
45 resize_memory!(interpreter, gas_params, offset, len, None);
46 offset
47 } else {
48 usize::MAX };
50 Some(offset..offset + len)
51}
52
53#[inline(never)]
55pub fn load_acc_and_calc_gas<H: Host + ?Sized>(
56 context: &mut InstructionContext<'_, H, impl InterpreterTypes>,
57 to: Address,
58 transfers_value: bool,
59 create_empty_account: bool,
60 stack_gas_limit: u64,
61) -> Option<(u64, Bytecode, B256)> {
62 if transfers_value {
64 gas!(
65 context.interpreter,
66 context.host.gas_params().transfer_value_cost(),
67 None
68 );
69 }
70
71 let (gas, bytecode, code_hash) =
73 load_account_delegated_handle_error(context, to, transfers_value, create_empty_account)?;
74 let interpreter = &mut context.interpreter;
75
76 gas!(interpreter, gas, None);
78
79 let interpreter = &mut context.interpreter;
80 let host = &mut context.host;
81
82 let mut gas_limit = if interpreter.runtime_flag.spec_id().is_enabled_in(TANGERINE) {
84 let reduced_gas_limit = host
86 .gas_params()
87 .call_stipend_reduction(interpreter.gas.remaining());
88 min(reduced_gas_limit, stack_gas_limit)
89 } else {
90 stack_gas_limit
91 };
92 gas!(interpreter, gas_limit, None);
93
94 if transfers_value {
96 gas_limit = gas_limit.saturating_add(host.gas_params().call_stipend());
97 }
98
99 Some((gas_limit, bytecode, code_hash))
100}
101
102#[inline]
104pub fn load_account_delegated_handle_error<H: Host + ?Sized>(
105 context: &mut InstructionContext<'_, H, impl InterpreterTypes>,
106 to: Address,
107 transfers_value: bool,
108 create_empty_account: bool,
109) -> Option<(u64, Bytecode, B256)> {
110 let remaining_gas = context.interpreter.gas.remaining();
112 match load_account_delegated(
113 context.host,
114 context.interpreter.runtime_flag.spec_id(),
115 remaining_gas,
116 to,
117 transfers_value,
118 create_empty_account,
119 ) {
120 Ok(out) => return Some(out),
121 Err(LoadError::ColdLoadSkipped) => {
122 context.interpreter.halt_oog();
123 }
124 Err(LoadError::DBError) => {
125 context.interpreter.halt_fatal();
126 }
127 }
128 None
129}
130
131#[inline]
135pub fn load_account_delegated<H: Host + ?Sized>(
136 host: &mut H,
137 spec: SpecId,
138 remaining_gas: u64,
139 address: Address,
140 transfers_value: bool,
141 create_empty_account: bool,
142) -> Result<(u64, Bytecode, B256), LoadError> {
143 let mut cost = 0;
144 let is_berlin = spec.is_enabled_in(SpecId::BERLIN);
145 let is_spurious_dragon = spec.is_enabled_in(SpecId::SPURIOUS_DRAGON);
146
147 let additional_cold_cost = host.gas_params().cold_account_additional_cost();
148 let warm_storage_read_cost = host.gas_params().warm_storage_read_cost();
149
150 let skip_cold_load = is_berlin && remaining_gas < additional_cold_cost;
151 let account = host.load_account_info_skip_cold_load(address, true, skip_cold_load)?;
152 if is_berlin && account.is_cold {
153 cost += additional_cold_cost;
154 }
155 let mut bytecode = account.code.clone().unwrap_or_default();
156 let mut code_hash = account.code_hash();
157 if create_empty_account && account.is_empty {
159 cost += host
160 .gas_params()
161 .new_account_cost(is_spurious_dragon, transfers_value);
162 return Ok((cost, bytecode, code_hash));
163 }
164
165 if let Some(Bytecode::Eip7702(code)) = &account.code {
167 cost += warm_storage_read_cost;
169 if cost > remaining_gas {
170 return Err(LoadError::ColdLoadSkipped);
171 }
172 let address = code.address();
173
174 let skip_cold_load = remaining_gas < cost + additional_cold_cost;
176 let delegate_account =
177 host.load_account_info_skip_cold_load(address, true, skip_cold_load)?;
178
179 if delegate_account.is_cold {
180 cost += additional_cold_cost;
181 }
182 bytecode = delegate_account.code.clone().unwrap_or_default();
183 code_hash = delegate_account.code_hash();
184 }
185
186 Ok((cost, bytecode, code_hash))
187}