revm_interpreter/instructions/
system.rs

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