revm_interpreter/instructions/contract/
call_helpers.rs1use crate::{
2 gas::params::GasParams,
3 interpreter::Interpreter,
4 interpreter_types::{InterpreterTypes, MemoryTr, RuntimeFlag, StackTr},
5 InstructionContext,
6};
7use context_interface::{host::LoadError, Host};
8use core::{cmp::min, ops::Range};
9use primitives::{
10 hardfork::SpecId::{self, *},
11 Address, B256, U256,
12};
13use state::Bytecode;
14
15#[inline]
17pub fn get_memory_input_and_out_ranges(
18 interpreter: &mut Interpreter<impl InterpreterTypes>,
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, 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, 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 offset: U256,
39 len: U256,
40) -> Option<Range<usize>> {
41 let len = as_usize_or_fail_ret!(interpreter, len, None);
42 let offset = if len != 0 {
43 let offset = as_usize_or_fail_ret!(interpreter, offset, None);
44 resize_memory!(interpreter, offset, len, None);
45 offset
46 } else {
47 usize::MAX };
49 Some(offset..offset + len)
50}
51
52#[inline(never)]
54pub fn load_acc_and_calc_gas<H: Host + ?Sized>(
55 context: &mut InstructionContext<'_, H, impl InterpreterTypes>,
56 to: Address,
57 transfers_value: bool,
58 create_empty_account: bool,
59 stack_gas_limit: u64,
60) -> Option<(u64, Bytecode, B256)> {
61 if transfers_value {
63 gas!(
64 context.interpreter,
65 context.interpreter.gas_params.transfer_value_cost(),
66 None
67 );
68 }
69
70 let (gas, bytecode, code_hash) =
72 load_account_delegated_handle_error(context, to, transfers_value, create_empty_account)?;
73 let interpreter = &mut context.interpreter;
74
75 gas!(interpreter, gas, None);
77
78 let interpreter = &mut context.interpreter;
79
80 let mut gas_limit = if interpreter.runtime_flag.spec_id().is_enabled_in(TANGERINE) {
82 let reduced_gas_limit = interpreter
84 .gas_params
85 .call_stipend_reduction(interpreter.gas.remaining());
86 min(reduced_gas_limit, stack_gas_limit)
87 } else {
88 stack_gas_limit
89 };
90 gas!(interpreter, gas_limit, None);
91
92 if transfers_value {
94 gas_limit = gas_limit.saturating_add(interpreter.gas_params.call_stipend());
95 }
96
97 Some((gas_limit, bytecode, code_hash))
98}
99
100#[inline]
102pub fn load_account_delegated_handle_error<H: Host + ?Sized>(
103 context: &mut InstructionContext<'_, H, impl InterpreterTypes>,
104 to: Address,
105 transfers_value: bool,
106 create_empty_account: bool,
107) -> Option<(u64, Bytecode, B256)> {
108 let remaining_gas = context.interpreter.gas.remaining();
110 let gas_table = &context.interpreter.gas_params;
111 match load_account_delegated(
112 context.host,
113 gas_table,
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 gas_table: &GasParams,
138 spec: SpecId,
139 remaining_gas: u64,
140 address: Address,
141 transfers_value: bool,
142 create_empty_account: bool,
143) -> Result<(u64, Bytecode, B256), LoadError> {
144 let mut cost = 0;
145 let is_berlin = spec.is_enabled_in(SpecId::BERLIN);
146 let is_spurious_dragon = spec.is_enabled_in(SpecId::SPURIOUS_DRAGON);
147
148 let additional_cold_cost = gas_table.cold_account_additional_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 += gas_table.new_account_cost(is_spurious_dragon, transfers_value);
160 return Ok((cost, bytecode, code_hash));
161 }
162
163 if let Some(Bytecode::Eip7702(code)) = &account.code {
165 cost += gas_table.warm_storage_read_cost();
167 if cost > remaining_gas {
168 return Err(LoadError::ColdLoadSkipped);
169 }
170 let address = code.address();
171
172 let skip_cold_load = remaining_gas < cost + additional_cold_cost;
174 let delegate_account =
175 host.load_account_info_skip_cold_load(address, true, skip_cold_load)?;
176
177 if delegate_account.is_cold {
178 cost += additional_cold_cost;
179 }
180 bytecode = delegate_account.code.clone().unwrap_or_default();
181 code_hash = delegate_account.code_hash();
182 }
183
184 Ok((cost, bytecode, code_hash))
185}