Skip to main content

revm_interpreter/interpreter_action/
call_inputs.rs

1use crate::interpreter_types::MemoryTr;
2use context_interface::{ContextTr, LocalContextTr};
3use core::ops::Range;
4use primitives::{Address, Bytes, B256, U256};
5use state::Bytecode;
6
7/// Input enum for a call.
8///
9/// As CallInput uses shared memory buffer it can get overridden if not used directly when call happens.
10#[derive(Clone, Debug, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub enum CallInput {
13    /// Bytes of the call data.
14    Bytes(Bytes),
15    /// The Range points to the SharedMemory buffer. Buffer can be found in [`context_interface::LocalContextTr::shared_memory_buffer_slice`] function.
16    /// And can be accessed with `evm.ctx().local().shared_memory_buffer()`
17    ///
18    /// # Warning
19    ///
20    /// Use it with caution, CallInput shared buffer can be overridden if context from child call is returned so
21    /// recommendation is to fetch buffer at first Inspector call and clone it from [`context_interface::LocalContextTr::shared_memory_buffer_slice`] function.
22    SharedBuffer(Range<usize>),
23}
24
25impl CallInput {
26    /// Returns the length of the call input.
27    #[inline]
28    pub fn len(&self) -> usize {
29        match self {
30            Self::Bytes(bytes) => bytes.len(),
31            Self::SharedBuffer(range) => range.len(),
32        }
33    }
34
35    /// Returns `true` if the call input is empty.
36    #[inline]
37    pub fn is_empty(&self) -> bool {
38        self.len() == 0
39    }
40
41    /// Returns the bytes of the call input from the given context.
42    #[inline]
43    pub fn as_bytes<'a, CTX: ContextTr>(
44        &'a self,
45        ctx: &'a CTX,
46    ) -> impl core::ops::Deref<Target = [u8]> + 'a {
47        match self {
48            Self::Bytes(bytes) => CallInputRef::Bytes(bytes.as_ref()),
49            Self::SharedBuffer(range) => {
50                CallInputRef::SharedBuffer(ctx.local().shared_memory_buffer_slice(range.clone()))
51            }
52        }
53    }
54
55    /// Returns the bytes of the call input from the given memory.
56    #[inline]
57    pub fn as_bytes_memory<'a, M: MemoryTr>(
58        &'a self,
59        memory: &'a M,
60    ) -> impl core::ops::Deref<Target = [u8]> + 'a {
61        match self {
62            Self::Bytes(bytes) => CallInputRef::Bytes(bytes.as_ref()),
63            Self::SharedBuffer(range) => {
64                CallInputRef::SharedBuffer(Some(memory.global_slice(range.clone())))
65            }
66        }
67    }
68
69    /// Returns the bytes of the call input.
70    ///
71    /// SharedMemory buffer can be shrunked or overwritten if the child call returns the
72    /// shared memory context to its parent, the range in `CallInput::SharedBuffer` can show unexpected data.
73    ///
74    /// # Allocation
75    ///
76    /// If this `CallInput` is a `SharedBuffer`, the slice will be copied
77    /// into a fresh `Bytes` buffer, which can pose a performance penalty.
78    pub fn bytes<CTX: ContextTr>(&self, ctx: &CTX) -> Bytes {
79        match self {
80            CallInput::Bytes(bytes) => bytes.clone(),
81            CallInput::SharedBuffer(range) => ctx
82                .local()
83                .shared_memory_buffer_slice(range.clone())
84                .map(|b| Bytes::from(b.to_vec()))
85                .unwrap_or_default(),
86        }
87    }
88}
89
90impl Default for CallInput {
91    #[inline]
92    fn default() -> Self {
93        CallInput::Bytes(Bytes::new())
94    }
95}
96
97enum CallInputRef<'a> {
98    Bytes(&'a [u8]),
99    SharedBuffer(Option<core::cell::Ref<'a, [u8]>>),
100}
101
102impl core::ops::Deref for CallInputRef<'_> {
103    type Target = [u8];
104
105    #[inline]
106    fn deref(&self) -> &Self::Target {
107        match self {
108            Self::Bytes(x) => x,
109            Self::SharedBuffer(x) => x.as_deref().unwrap_or_default(),
110        }
111    }
112}
113
114/// Inputs for a call.
115#[derive(Clone, Debug, PartialEq, Eq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub struct CallInputs {
118    /// The call data of the call.
119    pub input: CallInput,
120    /// The return memory offset where the output of the call is written.
121    pub return_memory_offset: Range<usize>,
122    /// The gas limit of the call.
123    pub gas_limit: u64,
124    /// The account address of bytecode that is going to be executed.
125    ///
126    /// Previously `context.code_address`.
127    pub bytecode_address: Address,
128    /// Known bytecode and its hash.
129    /// If None, bytecode will be loaded from the account at `bytecode_address`.
130    /// If Some((hash, bytecode)), the provided bytecode and hash will be used.
131    pub known_bytecode: Option<(B256, Bytecode)>,
132    /// Target address, this account storage is going to be modified.
133    ///
134    /// Previously `context.address`.
135    pub target_address: Address,
136    /// This caller is invoking the call.
137    ///
138    /// Previously `context.caller`.
139    pub caller: Address,
140    /// Call value.
141    ///
142    /// **Note**: This value may not necessarily be transferred from caller to callee, see [`CallValue`].
143    ///
144    /// Previously `transfer.value` or `context.apparent_value`.
145    pub value: CallValue,
146    /// The call scheme.
147    ///
148    /// Previously `context.scheme`.
149    pub scheme: CallScheme,
150    /// Whether the call is a static call, or is initiated inside a static call.
151    pub is_static: bool,
152}
153
154impl CallInputs {
155    /// Returns `true` if the call will transfer a non-zero value.
156    #[inline]
157    pub fn transfers_value(&self) -> bool {
158        self.value.transfer().is_some_and(|x| x > U256::ZERO)
159    }
160
161    /// Returns the transfer value.
162    ///
163    /// This is the value that is transferred from caller to callee, see [`CallValue`].
164    #[inline]
165    pub const fn transfer_value(&self) -> Option<U256> {
166        self.value.transfer()
167    }
168
169    /// Returns the **apparent** call value.
170    ///
171    /// This value is not actually transferred, see [`CallValue`].
172    #[inline]
173    pub const fn apparent_value(&self) -> Option<U256> {
174        self.value.apparent()
175    }
176
177    /// Returns the address of the transfer source account.
178    ///
179    /// This is only meaningful if `transfers_value` is `true`.
180    #[inline]
181    pub const fn transfer_from(&self) -> Address {
182        self.caller
183    }
184
185    /// Returns the address of the transfer target account.
186    ///
187    /// This is only meaningful if `transfers_value` is `true`.
188    #[inline]
189    pub const fn transfer_to(&self) -> Address {
190        self.target_address
191    }
192
193    /// Returns the call value, regardless of the transfer value type.
194    ///
195    /// **Note**: This value may not necessarily be transferred from caller to callee, see [`CallValue`].
196    #[inline]
197    pub const fn call_value(&self) -> U256 {
198        self.value.get()
199    }
200}
201
202/// Call scheme.
203#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
204#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
205pub enum CallScheme {
206    /// `CALL`.
207    Call,
208    /// `CALLCODE`
209    CallCode,
210    /// `DELEGATECALL`
211    DelegateCall,
212    /// `STATICCALL`
213    StaticCall,
214}
215
216impl CallScheme {
217    /// Returns true if it is `CALL`.
218    pub fn is_call(&self) -> bool {
219        matches!(self, Self::Call)
220    }
221
222    /// Returns true if it is `CALLCODE`.
223    pub fn is_call_code(&self) -> bool {
224        matches!(self, Self::CallCode)
225    }
226
227    /// Returns true if it is `DELEGATECALL`.
228    pub fn is_delegate_call(&self) -> bool {
229        matches!(self, Self::DelegateCall)
230    }
231
232    /// Returns true if it is `STATICCALL`.
233    pub fn is_static_call(&self) -> bool {
234        matches!(self, Self::StaticCall)
235    }
236}
237
238/// Call value.
239#[derive(Clone, Debug, PartialEq, Eq, Hash)]
240#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
241pub enum CallValue {
242    /// Concrete value, transferred from caller to callee at the end of the transaction.
243    Transfer(U256),
244    /// Apparent value, that is **not** actually transferred.
245    ///
246    /// Set when in a `DELEGATECALL` call type, and used by the `CALLVALUE` opcode.
247    Apparent(U256),
248}
249
250impl Default for CallValue {
251    #[inline]
252    fn default() -> Self {
253        CallValue::Transfer(U256::ZERO)
254    }
255}
256
257impl CallValue {
258    /// Returns the call value, regardless of the type.
259    #[inline]
260    pub const fn get(&self) -> U256 {
261        match *self {
262            Self::Transfer(value) | Self::Apparent(value) => value,
263        }
264    }
265
266    /// Returns the transferred value, if any.
267    #[inline]
268    pub const fn transfer(&self) -> Option<U256> {
269        match *self {
270            Self::Transfer(transfer) => Some(transfer),
271            Self::Apparent(_) => None,
272        }
273    }
274
275    /// Returns whether the call value will be transferred.
276    #[inline]
277    pub const fn is_transfer(&self) -> bool {
278        matches!(self, Self::Transfer(_))
279    }
280
281    /// Returns the apparent value, if any.
282    #[inline]
283    pub const fn apparent(&self) -> Option<U256> {
284        match *self {
285            Self::Transfer(_) => None,
286            Self::Apparent(apparent) => Some(apparent),
287        }
288    }
289
290    /// Returns whether the call value is apparent, and not actually transferred.
291    #[inline]
292    pub const fn is_apparent(&self) -> bool {
293        matches!(self, Self::Apparent(_))
294    }
295}