Skip to main content

revm_interpreter/instructions/
system.rs

1use crate::{
2    interpreter::{resize_memory, Interpreter},
3    interpreter_types::{
4        InputsTr, InterpreterTypes as ITy, LegacyBytecode, MemoryTr, ReturnData, RuntimeFlag,
5        StackTr,
6    },
7    CallInput, InstructionExecResult as Result, InstructionResult,
8};
9use context_interface::{cfg::GasParams, Host};
10use core::ptr;
11use primitives::{B256, KECCAK_EMPTY, U256};
12
13use crate::InstructionContext as Ictx;
14
15/// Implements the KECCAK256 instruction.
16///
17/// Computes Keccak-256 hash of memory data.
18pub fn keccak256<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
19    popn_top!([offset], top, context.interpreter);
20    let len = as_usize_or_fail!(context.interpreter, top);
21    gas!(
22        context.interpreter,
23        context.host.gas_params().keccak256_cost(len)
24    );
25    let hash = if len == 0 {
26        KECCAK_EMPTY
27    } else {
28        let from = as_usize_or_fail!(context.interpreter, offset);
29        resize_memory(
30            &mut context.interpreter.gas,
31            &mut context.interpreter.memory,
32            context.host.gas_params(),
33            from,
34            len,
35        )?;
36        primitives::keccak256(context.interpreter.memory.slice_len(from, len).as_ref())
37    };
38    *top = hash.into();
39    Ok(())
40}
41
42/// Implements the ADDRESS instruction.
43///
44/// Pushes the current contract's address onto the stack.
45pub fn address<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
46    push!(
47        context.interpreter,
48        context
49            .interpreter
50            .input
51            .target_address()
52            .into_word()
53            .into()
54    );
55    Ok(())
56}
57
58/// Implements the CALLER instruction.
59///
60/// Pushes the caller's address onto the stack.
61pub fn caller<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
62    push!(
63        context.interpreter,
64        context
65            .interpreter
66            .input
67            .caller_address()
68            .into_word()
69            .into()
70    );
71    Ok(())
72}
73
74/// Implements the CODESIZE instruction.
75///
76/// Pushes the size of running contract's bytecode onto the stack.
77pub fn codesize<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
78    push!(
79        context.interpreter,
80        U256::from(context.interpreter.bytecode.bytecode_len())
81    );
82    Ok(())
83}
84
85/// Implements the CODECOPY instruction.
86///
87/// Copies running contract's bytecode to memory.
88pub fn codecopy<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
89    popn!([memory_offset, code_offset, len], context.interpreter);
90    let len = as_usize_or_fail!(context.interpreter, len);
91    let Some(memory_offset) = copy_cost_and_memory_resize(
92        context.interpreter,
93        context.host.gas_params(),
94        memory_offset,
95        len,
96    )?
97    else {
98        return Ok(());
99    };
100    let code_offset = as_usize_saturated!(code_offset);
101
102    // Note: This can't panic because we resized memory to fit.
103    context.interpreter.memory.set_data(
104        memory_offset,
105        code_offset,
106        len,
107        context.interpreter.bytecode.bytecode_slice(),
108    );
109    Ok(())
110}
111
112/// Implements the CALLDATALOAD instruction.
113///
114/// Loads 32 bytes of input data from the specified offset.
115pub fn calldataload<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
116    popn_top!([], offset_ptr, context.interpreter);
117    let mut word = B256::ZERO;
118    let offset = as_usize_saturated!(*offset_ptr);
119    let input = context.interpreter.input.input();
120    let input_len = input.len();
121    if offset < input_len {
122        let count = 32.min(input_len - offset);
123        let input = &*input.as_bytes_memory(&context.interpreter.memory);
124        // SAFETY: `count` is bounded by the calldata length.
125        // This is `word[..count].copy_from_slice(input[offset..offset + count])`, written using
126        // raw pointers as apparently the compiler cannot optimize the slice version, and using
127        // `get_unchecked` twice is uglier.
128        unsafe { ptr::copy_nonoverlapping(input.as_ptr().add(offset), word.as_mut_ptr(), count) };
129    }
130    *offset_ptr = word.into();
131    Ok(())
132}
133
134/// Implements the CALLDATASIZE instruction.
135///
136/// Pushes the size of input data onto the stack.
137pub fn calldatasize<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
138    push!(
139        context.interpreter,
140        U256::from(context.interpreter.input.input().len())
141    );
142    Ok(())
143}
144
145/// Implements the CALLVALUE instruction.
146///
147/// Pushes the value sent with the current call onto the stack.
148pub fn callvalue<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
149    push!(context.interpreter, context.interpreter.input.call_value());
150    Ok(())
151}
152
153/// Implements the CALLDATACOPY instruction.
154///
155/// Copies input data to memory.
156pub fn calldatacopy<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
157    popn!([memory_offset, data_offset, len], context.interpreter);
158    let len = as_usize_or_fail!(context.interpreter, len);
159    let Some(memory_offset) = copy_cost_and_memory_resize(
160        context.interpreter,
161        context.host.gas_params(),
162        memory_offset,
163        len,
164    )?
165    else {
166        return Ok(());
167    };
168
169    let data_offset = as_usize_saturated!(data_offset);
170    match context.interpreter.input.input() {
171        CallInput::Bytes(bytes) => {
172            context
173                .interpreter
174                .memory
175                .set_data(memory_offset, data_offset, len, bytes.as_ref());
176        }
177        CallInput::SharedBuffer(range) => {
178            context.interpreter.memory.set_data_from_global(
179                memory_offset,
180                data_offset,
181                len,
182                range.clone(),
183            );
184        }
185    }
186    Ok(())
187}
188
189/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
190pub fn returndatasize<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
191    check!(context.interpreter, BYZANTIUM);
192    push!(
193        context.interpreter,
194        U256::from(context.interpreter.return_data.buffer().len())
195    );
196    Ok(())
197}
198
199/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
200pub fn returndatacopy<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
201    check!(context.interpreter, BYZANTIUM);
202    popn!([memory_offset, offset, len], context.interpreter);
203
204    let len = as_usize_or_fail!(context.interpreter, len);
205    let data_offset = as_usize_saturated!(offset);
206
207    // Old legacy behavior is to panic if data_end is out of scope of return buffer.
208    let data_end = data_offset.saturating_add(len);
209    if data_end > context.interpreter.return_data.buffer().len() {
210        return Err(InstructionResult::OutOfOffset);
211    }
212
213    let Some(memory_offset) = copy_cost_and_memory_resize(
214        context.interpreter,
215        context.host.gas_params(),
216        memory_offset,
217        len,
218    )?
219    else {
220        return Ok(());
221    };
222
223    // Note: This can't panic because we resized memory to fit.
224    context.interpreter.memory.set_data(
225        memory_offset,
226        data_offset,
227        len,
228        context.interpreter.return_data.buffer(),
229    );
230    Ok(())
231}
232
233/// Implements the GAS instruction.
234///
235/// Pushes the amount of remaining gas onto the stack.
236/// Returns `gas_left` only (excluding the state gas reservoir) per EIP-8037.
237/// On mainnet (no state gas), this is equivalent to returning `remaining`.
238pub fn gas<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
239    let gas = &context.interpreter.gas;
240    push!(context.interpreter, U256::from(gas.remaining()));
241    Ok(())
242}
243
244/// Common logic for copying data from a source buffer to the EVM's memory.
245///
246/// Handles memory expansion and gas calculation for data copy operations.
247pub fn copy_cost_and_memory_resize(
248    interpreter: &mut Interpreter<impl ITy>,
249    gas_params: &GasParams,
250    memory_offset: U256,
251    len: usize,
252) -> Result<Option<usize>, InstructionResult> {
253    // Safe to cast usize to u64
254    gas!(interpreter, gas_params.copy_cost(len));
255    if len == 0 {
256        return Ok(None);
257    }
258    let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
259    interpreter.resize_memory(gas_params, memory_offset, len)?;
260
261    Ok(Some(memory_offset))
262}