rtvm_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_bytes(),
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    let len = as_usize_or_fail!(interpreter, len);
129    gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64));
130    let data_offset = as_usize_saturated!(offset);
131    let data_end = data_offset.saturating_add(len);
132    if data_end > interpreter.return_data_buffer.len() {
133        interpreter.instruction_result = InstructionResult::OutOfOffset;
134        return;
135    }
136    if len != 0 {
137        let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
138        resize_memory!(interpreter, memory_offset, len);
139        interpreter.shared_memory.set(
140            memory_offset,
141            &interpreter.return_data_buffer[data_offset..data_end],
142        );
143    }
144}
145
146/// Part of EOF `<https://eips.ethereum.org/EIPS/eip-7069>`.
147pub fn returndataload<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
148    require_eof!(interpreter);
149    gas!(interpreter, gas::VERYLOW);
150    pop_top!(interpreter, offset);
151    let offset_usize = as_usize_or_fail!(interpreter, offset);
152    if offset_usize.saturating_add(32) > interpreter.return_data_buffer.len() {
153        // TODO(EOF) proper error.
154        interpreter.instruction_result = InstructionResult::OutOfOffset;
155        return;
156    }
157    *offset =
158        B256::from_slice(&interpreter.return_data_buffer[offset_usize..offset_usize + 32]).into();
159}
160
161pub fn gas<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
162    gas!(interpreter, gas::BASE);
163    push!(interpreter, U256::from(interpreter.gas.remaining()));
164}
165
166#[cfg(test)]
167mod test {
168    use super::*;
169    use crate::{
170        opcode::{make_instruction_table, RETURNDATALOAD},
171        primitives::{bytes, Bytecode, PragueSpec},
172        DummyHost, Gas,
173    };
174
175    #[test]
176    fn returndataload() {
177        let table = make_instruction_table::<_, PragueSpec>();
178        let mut host = DummyHost::default();
179
180        let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(
181            [RETURNDATALOAD, RETURNDATALOAD, RETURNDATALOAD].into(),
182        ));
183        interp.is_eof = true;
184        interp.gas = Gas::new(10000);
185
186        interp.stack.push(U256::from(0)).unwrap();
187        interp.return_data_buffer =
188            bytes!("000000000000000400000000000000030000000000000002000000000000000100");
189        interp.step(&table, &mut host);
190        assert_eq!(
191            interp.stack.data(),
192            &vec![U256::from_limbs([0x01, 0x02, 0x03, 0x04])]
193        );
194
195        let _ = interp.stack.pop();
196        let _ = interp.stack.push(U256::from(1));
197
198        interp.step(&table, &mut host);
199        assert_eq!(interp.instruction_result, InstructionResult::Continue);
200        assert_eq!(
201            interp.stack.data(),
202            &vec![U256::from_limbs([0x0100, 0x0200, 0x0300, 0x0400])]
203        );
204
205        let _ = interp.stack.pop();
206        let _ = interp.stack.push(U256::from(2));
207        interp.step(&table, &mut host);
208        assert_eq!(interp.instruction_result, InstructionResult::OutOfOffset);
209    }
210}