Skip to main content

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        let input = &*input.as_bytes_memory(&context.interpreter.memory);
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        unsafe { ptr::copy_nonoverlapping(input.as_ptr().add(offset), word.as_mut_ptr(), count) };
120    }
121    *offset_ptr = word.into();
122}
123
124/// Implements the CALLDATASIZE instruction.
125///
126/// Pushes the size of input data onto the stack.
127pub fn calldatasize<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
128    push!(
129        context.interpreter,
130        U256::from(context.interpreter.input.input().len())
131    );
132}
133
134/// Implements the CALLVALUE instruction.
135///
136/// Pushes the value sent with the current call onto the stack.
137pub fn callvalue<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
138    push!(context.interpreter, context.interpreter.input.call_value());
139}
140
141/// Implements the CALLDATACOPY instruction.
142///
143/// Copies input data to memory.
144pub fn calldatacopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
145    context: InstructionContext<'_, H, WIRE>,
146) {
147    popn!([memory_offset, data_offset, len], context.interpreter);
148    let len = as_usize_or_fail!(context.interpreter, len);
149    let Some(memory_offset) = copy_cost_and_memory_resize(
150        context.interpreter,
151        context.host.gas_params(),
152        memory_offset,
153        len,
154    ) else {
155        return;
156    };
157
158    let data_offset = as_usize_saturated!(data_offset);
159    match context.interpreter.input.input() {
160        CallInput::Bytes(bytes) => {
161            context
162                .interpreter
163                .memory
164                .set_data(memory_offset, data_offset, len, bytes.as_ref());
165        }
166        CallInput::SharedBuffer(range) => {
167            context.interpreter.memory.set_data_from_global(
168                memory_offset,
169                data_offset,
170                len,
171                range.clone(),
172            );
173        }
174    }
175}
176
177/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
178pub fn returndatasize<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
179    check!(context.interpreter, BYZANTIUM);
180    push!(
181        context.interpreter,
182        U256::from(context.interpreter.return_data.buffer().len())
183    );
184}
185
186/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
187pub fn returndatacopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
188    context: InstructionContext<'_, H, WIRE>,
189) {
190    check!(context.interpreter, BYZANTIUM);
191    popn!([memory_offset, offset, len], context.interpreter);
192
193    let len = as_usize_or_fail!(context.interpreter, len);
194    let data_offset = as_usize_saturated!(offset);
195
196    // Old legacy behavior is to panic if data_end is out of scope of return buffer.
197    let data_end = data_offset.saturating_add(len);
198    if data_end > context.interpreter.return_data.buffer().len() {
199        context.interpreter.halt(InstructionResult::OutOfOffset);
200        return;
201    }
202
203    let Some(memory_offset) = copy_cost_and_memory_resize(
204        context.interpreter,
205        context.host.gas_params(),
206        memory_offset,
207        len,
208    ) else {
209        return;
210    };
211
212    // Note: This can't panic because we resized memory to fit.
213    context.interpreter.memory.set_data(
214        memory_offset,
215        data_offset,
216        len,
217        context.interpreter.return_data.buffer(),
218    );
219}
220
221/// Implements the GAS instruction.
222///
223/// Pushes the amount of remaining gas onto the stack.
224pub fn gas<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
225    push!(
226        context.interpreter,
227        U256::from(context.interpreter.gas.remaining())
228    );
229}
230
231/// Common logic for copying data from a source buffer to the EVM's memory.
232///
233/// Handles memory expansion and gas calculation for data copy operations.
234pub fn copy_cost_and_memory_resize(
235    interpreter: &mut Interpreter<impl InterpreterTypes>,
236    gas_params: &GasParams,
237    memory_offset: U256,
238    len: usize,
239) -> Option<usize> {
240    // Safe to cast usize to u64
241    gas!(interpreter, gas_params.copy_cost(len), None);
242    if len == 0 {
243        return None;
244    }
245    let memory_offset = as_usize_or_fail_ret!(interpreter, memory_offset, None);
246    resize_memory!(interpreter, gas_params, memory_offset, len, None);
247
248    Some(memory_offset)
249}