revm_interpreter/instructions/
system.rs

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