revm_interpreter/instructions/
system.rs

1use crate::{
2    gas,
3    interpreter::Interpreter,
4    interpreter_types::{
5        InputsTr, InterpreterTypes, LegacyBytecode, LoopControl, MemoryTr, ReturnData, RuntimeFlag,
6        StackTr,
7    },
8    Host, InstructionResult,
9};
10use core::ptr;
11use primitives::{B256, KECCAK_EMPTY, U256};
12
13pub fn keccak256<WIRE: InterpreterTypes, H: Host + ?Sized>(
14    interpreter: &mut Interpreter<WIRE>,
15    _host: &mut H,
16) {
17    popn_top!([offset], top, interpreter);
18    let len = as_usize_or_fail!(interpreter, top);
19    gas_or_fail!(interpreter, gas::keccak256_cost(len));
20    let hash = if len == 0 {
21        KECCAK_EMPTY
22    } else {
23        let from = as_usize_or_fail!(interpreter, offset);
24        resize_memory!(interpreter, from, len);
25        primitives::keccak256(interpreter.memory.slice_len(from, len).as_ref())
26    };
27    *top = hash.into();
28}
29
30pub fn address<WIRE: InterpreterTypes, H: Host + ?Sized>(
31    interpreter: &mut Interpreter<WIRE>,
32    _host: &mut H,
33) {
34    gas!(interpreter, gas::BASE);
35    push!(
36        interpreter,
37        interpreter.input.target_address().into_word().into()
38    );
39}
40
41pub fn caller<WIRE: InterpreterTypes, H: Host + ?Sized>(
42    interpreter: &mut Interpreter<WIRE>,
43    _host: &mut H,
44) {
45    gas!(interpreter, gas::BASE);
46    push!(
47        interpreter,
48        interpreter.input.caller_address().into_word().into()
49    );
50}
51
52pub fn codesize<WIRE: InterpreterTypes, H: Host + ?Sized>(
53    interpreter: &mut Interpreter<WIRE>,
54    _host: &mut H,
55) {
56    gas!(interpreter, gas::BASE);
57    push!(interpreter, U256::from(interpreter.bytecode.bytecode_len()));
58}
59
60pub fn codecopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
61    interpreter: &mut Interpreter<WIRE>,
62    _host: &mut H,
63) {
64    popn!([memory_offset, code_offset, len], interpreter);
65    let len = as_usize_or_fail!(interpreter, len);
66    let Some(memory_offset) = memory_resize(interpreter, memory_offset, len) else {
67        return;
68    };
69    let code_offset = as_usize_saturated!(code_offset);
70
71    // Note: This can't panic because we resized memory to fit.
72    interpreter.memory.set_data(
73        memory_offset,
74        code_offset,
75        len,
76        interpreter.bytecode.bytecode_slice(),
77    );
78}
79
80pub fn calldataload<WIRE: InterpreterTypes, H: Host + ?Sized>(
81    interpreter: &mut Interpreter<WIRE>,
82    _host: &mut H,
83) {
84    gas!(interpreter, gas::VERYLOW);
85    //pop_top!(interpreter, offset_ptr);
86    popn_top!([], offset_ptr, interpreter);
87    let mut word = B256::ZERO;
88    let offset = as_usize_saturated!(offset_ptr);
89    let input = interpreter.input.input();
90    let input_len = input.len();
91    if offset < input_len {
92        let count = 32.min(input_len - offset);
93        // SAFETY: `count` is bounded by the calldata length.
94        // This is `word[..count].copy_from_slice(input[offset..offset + count])`, written using
95        // raw pointers as apparently the compiler cannot optimize the slice version, and using
96        // `get_unchecked` twice is uglier.
97        debug_assert!(count <= 32 && offset + count <= input_len);
98        unsafe { ptr::copy_nonoverlapping(input.as_ptr().add(offset), word.as_mut_ptr(), count) };
99    }
100    *offset_ptr = word.into();
101}
102
103pub fn calldatasize<WIRE: InterpreterTypes, H: Host + ?Sized>(
104    interpreter: &mut Interpreter<WIRE>,
105    _host: &mut H,
106) {
107    gas!(interpreter, gas::BASE);
108    push!(interpreter, U256::from(interpreter.input.input().len()));
109}
110
111pub fn callvalue<WIRE: InterpreterTypes, H: Host + ?Sized>(
112    interpreter: &mut Interpreter<WIRE>,
113    _host: &mut H,
114) {
115    gas!(interpreter, gas::BASE);
116    push!(interpreter, interpreter.input.call_value());
117}
118
119pub fn calldatacopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
120    interpreter: &mut Interpreter<WIRE>,
121    _host: &mut H,
122) {
123    popn!([memory_offset, data_offset, len], interpreter);
124    let len = as_usize_or_fail!(interpreter, len);
125    let Some(memory_offset) = memory_resize(interpreter, memory_offset, len) else {
126        return;
127    };
128
129    let data_offset = as_usize_saturated!(data_offset);
130    // Note: This can't panic because we resized memory to fit.
131    interpreter
132        .memory
133        .set_data(memory_offset, data_offset, len, interpreter.input.input());
134}
135
136/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
137pub fn returndatasize<WIRE: InterpreterTypes, H: Host + ?Sized>(
138    interpreter: &mut Interpreter<WIRE>,
139    _host: &mut H,
140) {
141    check!(interpreter, BYZANTIUM);
142    gas!(interpreter, gas::BASE);
143    push!(
144        interpreter,
145        U256::from(interpreter.return_data.buffer().len())
146    );
147}
148
149/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
150pub fn returndatacopy<WIRE: InterpreterTypes, H: Host + ?Sized>(
151    interpreter: &mut Interpreter<WIRE>,
152    _host: &mut H,
153) {
154    check!(interpreter, BYZANTIUM);
155    popn!([memory_offset, offset, len], interpreter);
156
157    let len = as_usize_or_fail!(interpreter, len);
158    let data_offset = as_usize_saturated!(offset);
159
160    // Old legacy behavior is to panic if data_end is out of scope of return buffer.
161    // This behavior is changed in EOF.
162    let data_end = data_offset.saturating_add(len);
163    if data_end > interpreter.return_data.buffer().len() && !interpreter.runtime_flag.is_eof() {
164        interpreter
165            .control
166            .set_instruction_result(InstructionResult::OutOfOffset);
167        return;
168    }
169
170    let Some(memory_offset) = memory_resize(interpreter, memory_offset, len) else {
171        return;
172    };
173
174    // Note: This can't panic because we resized memory to fit.
175    interpreter.memory.set_data(
176        memory_offset,
177        data_offset,
178        len,
179        interpreter.return_data.buffer(),
180    );
181}
182
183/// Part of EOF `<https://eips.ethereum.org/EIPS/eip-7069>`.
184pub fn returndataload<WIRE: InterpreterTypes, H: Host + ?Sized>(
185    interpreter: &mut Interpreter<WIRE>,
186    _host: &mut H,
187) {
188    require_eof!(interpreter);
189    gas!(interpreter, gas::VERYLOW);
190    popn_top!([], offset, interpreter);
191    let offset_usize = as_usize_saturated!(offset);
192
193    let mut output = [0u8; 32];
194    if let Some(available) = interpreter
195        .return_data
196        .buffer()
197        .len()
198        .checked_sub(offset_usize)
199    {
200        let copy_len = available.min(32);
201        output[..copy_len].copy_from_slice(
202            &interpreter.return_data.buffer()[offset_usize..offset_usize + copy_len],
203        );
204    }
205
206    *offset = B256::from(output).into();
207}
208
209pub fn gas<WIRE: InterpreterTypes, H: Host + ?Sized>(
210    interpreter: &mut Interpreter<WIRE>,
211    _host: &mut H,
212) {
213    gas!(interpreter, gas::BASE);
214    push!(
215        interpreter,
216        U256::from(interpreter.control.gas().remaining())
217    );
218}
219
220// common logic for copying data from a source buffer to the EVM's memory
221pub fn memory_resize(
222    interpreter: &mut Interpreter<impl InterpreterTypes>,
223    memory_offset: U256,
224    len: usize,
225) -> Option<usize> {
226    // Safe to cast usize to u64
227    gas_or_fail!(interpreter, gas::copy_cost_verylow(len), None);
228    if len == 0 {
229        return None;
230    }
231    let memory_offset = as_usize_or_fail_ret!(interpreter, memory_offset, None);
232    resize_memory!(interpreter, memory_offset, len, None);
233
234    Some(memory_offset)
235}
236
237// TODO : Tests
238/*
239#[cfg(test)]
240mod test {
241    use super::*;
242    use crate::{table::make_instruction_table, DummyHost, Gas, InstructionResult};
243    use bytecode::opcode::{RETURNDATACOPY, RETURNDATALOAD};
244    use bytecode::Bytecode;
245    use primitives::bytes;
246    use specification::hardfork::{PragueSpec, SpecId};
247    use context_interface::DefaultEthereumWiring;
248
249    #[test]
250    fn returndataload() {
251        let table = make_instruction_table::<Interpreter, DummyHost<DefaultEthereumWiring>>();
252        let mut host = DummyHost::default();
253
254        let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(
255            [
256                RETURNDATALOAD,
257                RETURNDATALOAD,
258                RETURNDATALOAD,
259                RETURNDATALOAD,
260            ]
261            .into(),
262        ));
263        interp.is_eof = true;
264        interp.spec_id = SpecId::PRAGUE;
265        interp.gas = Gas::new(10000);
266
267        interp.stack.push(U256::from(0)).unwrap();
268        interp.return_data_buffer =
269            bytes!("000000000000000400000000000000030000000000000002000000000000000100");
270        interp.step(&table, &mut host);
271        assert_eq!(
272            interp.stack.data(),
273            &vec![U256::from_limbs([0x01, 0x02, 0x03, 0x04])]
274        );
275
276        let _ = interp.stack.pop();
277        let _ = interp.stack.push(U256::from(1));
278
279        interp.step(&table, &mut host);
280        assert_eq!(interp.instruction_result, InstructionResult::Continue);
281        assert_eq!(
282            interp.stack.data(),
283            &vec![U256::from_limbs([0x0100, 0x0200, 0x0300, 0x0400])]
284        );
285
286        let _ = interp.stack.pop();
287        let _ = interp.stack.push(U256::from(32));
288        interp.step(&table, &mut host);
289        assert_eq!(interp.instruction_result, InstructionResult::Continue);
290        assert_eq!(
291            interp.stack.data(),
292            &vec![U256::from_limbs([0x00, 0x00, 0x00, 0x00])]
293        );
294
295        // Offset right at the boundary of the return data buffer size
296        let _ = interp.stack.pop();
297        let _ = interp
298            .stack
299            .push(U256::from(interp.return_data_buffer.len()));
300        interp.step(&table, &mut host);
301        assert_eq!(interp.instruction_result, InstructionResult::Continue);
302        assert_eq!(
303            interp.stack.data(),
304            &vec![U256::from_limbs([0x00, 0x00, 0x00, 0x00])]
305        );
306    }
307
308    #[test]
309    fn returndatacopy() {
310        let table = make_instruction_table::<Interpreter, _>();
311        let mut host = DummyHost::<DefaultEthereumWiring>::default();
312
313        let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(
314            [
315                RETURNDATACOPY,
316                RETURNDATACOPY,
317                RETURNDATACOPY,
318                RETURNDATACOPY,
319                RETURNDATACOPY,
320                RETURNDATACOPY,
321            ]
322            .into(),
323        ));
324        interp.is_eof = true;
325        interp.spec_id = SpecId::PRAGUE;
326        interp.gas = Gas::new(10000);
327
328        interp.return_data_buffer =
329            bytes!("000000000000000400000000000000030000000000000002000000000000000100");
330        interp.shared_memory.resize(256);
331
332        // Copying within bounds
333        interp.stack.push(U256::from(32)).unwrap();
334        interp.stack.push(U256::from(0)).unwrap();
335        interp.stack.push(U256::from(0)).unwrap();
336        interp.step(&table, &mut host);
337        assert_eq!(interp.instruction_result, InstructionResult::Continue);
338        assert_eq!(
339            interp.shared_memory.slice(0, 32),
340            &interp.return_data_buffer[0..32]
341        );
342
343        // Copying with partial out-of-bounds (should zero pad)
344        interp.stack.push(U256::from(64)).unwrap();
345        interp.stack.push(U256::from(16)).unwrap();
346        interp.stack.push(U256::from(64)).unwrap();
347        interp.step(&table, &mut host);
348        assert_eq!(interp.instruction_result, InstructionResult::Continue);
349        assert_eq!(
350            interp.shared_memory.slice(64, 16),
351            &interp.return_data_buffer[16..32]
352        );
353        assert_eq!(&interp.shared_memory.slice(80, 48), &[0u8; 48]);
354
355        // Completely out-of-bounds (should be all zeros)
356        interp.stack.push(U256::from(32)).unwrap();
357        interp.stack.push(U256::from(96)).unwrap();
358        interp.stack.push(U256::from(128)).unwrap();
359        interp.step(&table, &mut host);
360        assert_eq!(interp.instruction_result, InstructionResult::Continue);
361        assert_eq!(&interp.shared_memory.slice(128, 32), &[0u8; 32]);
362
363        // Large offset
364        interp.stack.push(U256::from(32)).unwrap();
365        interp.stack.push(U256::MAX).unwrap();
366        interp.stack.push(U256::from(0)).unwrap();
367        interp.step(&table, &mut host);
368        assert_eq!(interp.instruction_result, InstructionResult::Continue);
369        assert_eq!(&interp.shared_memory.slice(0, 32), &[0u8; 32]);
370
371        // Offset just before the boundary of the return data buffer size
372        interp.stack.push(U256::from(32)).unwrap();
373        interp
374            .stack
375            .push(U256::from(interp.return_data_buffer.len() - 32))
376            .unwrap();
377        interp.stack.push(U256::from(0)).unwrap();
378        interp.step(&table, &mut host);
379        assert_eq!(interp.instruction_result, InstructionResult::Continue);
380        assert_eq!(
381            interp.shared_memory.slice(0, 32),
382            &interp.return_data_buffer[interp.return_data_buffer.len() - 32..]
383        );
384
385        // Offset right at the boundary of the return data buffer size
386        interp.stack.push(U256::from(32)).unwrap();
387        interp
388            .stack
389            .push(U256::from(interp.return_data_buffer.len()))
390            .unwrap();
391        interp.stack.push(U256::from(0)).unwrap();
392        interp.step(&table, &mut host);
393        assert_eq!(interp.instruction_result, InstructionResult::Continue);
394        assert_eq!(&interp.shared_memory.slice(0, 32), &[0u8; 32]);
395    }
396}
397
398*/