revm_interpreter/instructions/contract/
call_helpers.rs1use crate::{
2 gas::{
3 self, calc_call_static_gas, COLD_ACCOUNT_ACCESS_COST_ADDITIONAL, NEWACCOUNT,
4 WARM_STORAGE_READ_COST,
5 },
6 interpreter::Interpreter,
7 interpreter_types::{InterpreterTypes, MemoryTr, RuntimeFlag, StackTr},
8 InstructionContext,
9};
10use context_interface::{host::LoadError, Host};
11use core::{cmp::min, ops::Range};
12use primitives::{
13 hardfork::SpecId::{self, *},
14 Address, U256,
15};
16use state::Bytecode;
17
18#[inline]
20pub fn get_memory_input_and_out_ranges(
21 interpreter: &mut Interpreter<impl InterpreterTypes>,
22) -> Option<(Range<usize>, Range<usize>)> {
23 popn!([in_offset, in_len, out_offset, out_len], interpreter, None);
24
25 let mut in_range = resize_memory(interpreter, in_offset, in_len)?;
26
27 if !in_range.is_empty() {
28 let offset = interpreter.memory.local_memory_offset();
29 in_range = in_range.start.saturating_add(offset)..in_range.end.saturating_add(offset);
30 }
31
32 let ret_range = resize_memory(interpreter, out_offset, out_len)?;
33 Some((in_range, ret_range))
34}
35
36#[inline]
39pub fn resize_memory(
40 interpreter: &mut Interpreter<impl InterpreterTypes>,
41 offset: U256,
42 len: U256,
43) -> Option<Range<usize>> {
44 let len = as_usize_or_fail_ret!(interpreter, len, None);
45 let offset = if len != 0 {
46 let offset = as_usize_or_fail_ret!(interpreter, offset, None);
47 resize_memory!(interpreter, offset, len, None);
48 offset
49 } else {
50 usize::MAX };
52 Some(offset..offset + len)
53}
54
55#[inline]
57pub fn load_acc_and_calc_gas<H: Host + ?Sized>(
58 context: &mut InstructionContext<'_, H, impl InterpreterTypes>,
59 to: Address,
60 transfers_value: bool,
61 create_empty_account: bool,
62 stack_gas_limit: u64,
63) -> Option<u64> {
64 let spec = context.interpreter.runtime_flag.spec_id();
65 let static_gas = calc_call_static_gas(spec, transfers_value);
67 gas!(context.interpreter, static_gas, None);
68
69 let gas =
71 load_account_delegated_handle_error(context, to, transfers_value, create_empty_account)?;
72 let interpreter = &mut context.interpreter;
73
74 gas!(interpreter, gas, None);
76
77 let mut gas_limit = if interpreter.runtime_flag.spec_id().is_enabled_in(TANGERINE) {
79 min(interpreter.gas.remaining_63_of_64_parts(), stack_gas_limit)
81 } else {
82 stack_gas_limit
83 };
84
85 gas!(interpreter, gas_limit, None);
86 if transfers_value {
88 gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
89 }
90
91 Some(gas_limit)
92}
93
94#[inline]
96pub fn load_account_delegated_handle_error<H: Host + ?Sized>(
97 context: &mut InstructionContext<'_, H, impl InterpreterTypes>,
98 to: Address,
99 transfers_value: bool,
100 create_empty_account: bool,
101) -> Option<u64> {
102 let remaining_gas = context.interpreter.gas.remaining();
104 match load_account_delegated(
105 context.host,
106 context.interpreter.runtime_flag.spec_id(),
107 remaining_gas,
108 to,
109 transfers_value,
110 create_empty_account,
111 ) {
112 Ok(gas_cost) => return Some(gas_cost),
113 Err(LoadError::ColdLoadSkipped) => {
114 context.interpreter.halt_oog();
115 }
116 Err(LoadError::DBError) => {
117 context.interpreter.halt_fatal();
118 }
119 }
120 None
121}
122
123#[inline]
127pub fn load_account_delegated<H: Host + ?Sized>(
128 host: &mut H,
129 spec: SpecId,
130 remaining_gas: u64,
131 address: Address,
132 transfers_value: bool,
133 create_empty_account: bool,
134) -> Result<u64, LoadError> {
135 let mut cost = 0;
136 let is_berlin = spec.is_enabled_in(SpecId::BERLIN);
137 let is_spurioud_dragon = spec.is_enabled_in(SpecId::SPURIOUS_DRAGON);
138
139 let skip_cold_load = is_berlin && remaining_gas < COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
140 let account = host.load_account_info_skip_cold_load(address, true, skip_cold_load)?;
141 if is_berlin && account.is_cold {
142 cost += COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
143 }
144 if create_empty_account && account.is_empty {
146 cost += new_account_cost(is_spurioud_dragon, transfers_value);
147 return Ok(cost);
148 }
149
150 if let Some(Bytecode::Eip7702(code)) = &account.code {
152 cost += WARM_STORAGE_READ_COST;
154 if cost > remaining_gas {
155 return Err(LoadError::ColdLoadSkipped);
156 }
157 let address = code.address();
158
159 let skip_cold_load = remaining_gas < cost + COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
161 let delegate_account =
162 host.load_account_info_skip_cold_load(address, true, skip_cold_load)?;
163
164 if delegate_account.is_cold {
165 cost += COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
166 }
167 }
168
169 Ok(cost)
170}
171
172#[inline]
174pub fn new_account_cost(is_spurioud_dragon: bool, transfers_value: bool) -> u64 {
175 if !is_spurioud_dragon || transfers_value {
179 return NEWACCOUNT;
180 }
181 0
182}