revm_interpreter/instructions/
control.rs

1use super::utility::{read_i16, read_u16};
2use crate::{
3    gas,
4    primitives::{Bytes, Spec, U256},
5    Host, InstructionResult, Interpreter, InterpreterResult,
6};
7
8pub fn rjump<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
9    require_eof!(interpreter);
10    gas!(interpreter, gas::BASE);
11    let offset = unsafe { read_i16(interpreter.instruction_pointer) } as isize;
12    // In spec it is +3 but pointer is already incremented in
13    // `Interpreter::step` so for revm is +2.
14    interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(offset + 2) };
15}
16
17pub fn rjumpi<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
18    require_eof!(interpreter);
19    gas!(interpreter, gas::CONDITION_JUMP_GAS);
20    pop!(interpreter, condition);
21    // In spec it is +3 but pointer is already incremented in
22    // `Interpreter::step` so for revm is +2.
23    let mut offset = 2;
24    if !condition.is_zero() {
25        offset += unsafe { read_i16(interpreter.instruction_pointer) } as isize;
26    }
27
28    interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(offset) };
29}
30
31pub fn rjumpv<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
32    require_eof!(interpreter);
33    gas!(interpreter, gas::CONDITION_JUMP_GAS);
34    pop!(interpreter, case);
35    let case = as_isize_saturated!(case);
36
37    let max_index = unsafe { *interpreter.instruction_pointer } as isize;
38    // for number of items we are adding 1 to max_index, multiply by 2 as each offset is 2 bytes
39    // and add 1 for max_index itself. Note that revm already incremented the instruction pointer
40    let mut offset = (max_index + 1) * 2 + 1;
41
42    if case <= max_index {
43        offset += unsafe {
44            read_i16(
45                interpreter
46                    .instruction_pointer
47                    // offset for max_index that is one byte
48                    .offset(1 + case * 2),
49            )
50        } as isize;
51    }
52
53    interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(offset) };
54}
55
56pub fn jump<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
57    gas!(interpreter, gas::MID);
58    pop!(interpreter, target);
59    jump_inner(interpreter, target);
60}
61
62pub fn jumpi<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
63    gas!(interpreter, gas::HIGH);
64    pop!(interpreter, target, cond);
65    if !cond.is_zero() {
66        jump_inner(interpreter, target);
67    }
68}
69
70#[inline]
71fn jump_inner(interpreter: &mut Interpreter, target: U256) {
72    let target = as_usize_or_fail!(interpreter, target, InstructionResult::InvalidJump);
73    if !interpreter.contract.is_valid_jump(target) {
74        interpreter.instruction_result = InstructionResult::InvalidJump;
75        return;
76    }
77    // SAFETY: `is_valid_jump` ensures that `dest` is in bounds.
78    interpreter.instruction_pointer = unsafe { interpreter.bytecode.as_ptr().add(target) };
79}
80
81pub fn jumpdest_or_nop<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
82    gas!(interpreter, gas::JUMPDEST);
83}
84
85pub fn callf<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
86    require_eof!(interpreter);
87    gas!(interpreter, gas::LOW);
88
89    let idx = unsafe { read_u16(interpreter.instruction_pointer) } as usize;
90
91    if interpreter.function_stack.return_stack_len() >= 1024 {
92        interpreter.instruction_result = InstructionResult::EOFFunctionStackOverflow;
93        return;
94    }
95
96    // get target types
97    let Some(types) = interpreter.eof().unwrap().body.types_section.get(idx) else {
98        panic!("Invalid EOF in execution, expecting correct intermediate in callf")
99    };
100
101    // Check max stack height for target code section.
102    // safe to subtract as max_stack_height is always more than inputs.
103    if interpreter.stack.len() + (types.max_stack_size - types.inputs as u16) as usize > 1024 {
104        interpreter.instruction_result = InstructionResult::StackOverflow;
105        return;
106    }
107
108    // push current idx and PC to the callf stack.
109    // PC is incremented by 2 to point to the next instruction after callf.
110    interpreter
111        .function_stack
112        .push(interpreter.program_counter() + 2, idx);
113
114    interpreter.load_eof_code(idx, 0)
115}
116
117pub fn retf<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
118    require_eof!(interpreter);
119    gas!(interpreter, gas::RETF_GAS);
120
121    let Some(fframe) = interpreter.function_stack.pop() else {
122        panic!("Expected function frame")
123    };
124
125    interpreter.load_eof_code(fframe.idx, fframe.pc);
126}
127
128pub fn jumpf<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
129    require_eof!(interpreter);
130    gas!(interpreter, gas::LOW);
131
132    let idx = unsafe { read_u16(interpreter.instruction_pointer) } as usize;
133
134    // get target types
135    let Some(types) = interpreter.eof().unwrap().body.types_section.get(idx) else {
136        panic!("Invalid EOF in execution, expecting correct intermediate in jumpf")
137    };
138
139    // Check max stack height for target code section.
140    // safe to subtract as max_stack_height is always more than inputs.
141    if interpreter.stack.len() + (types.max_stack_size - types.inputs as u16) as usize > 1024 {
142        interpreter.instruction_result = InstructionResult::StackOverflow;
143        return;
144    }
145
146    interpreter.function_stack.set_current_code_idx(idx);
147    interpreter.load_eof_code(idx, 0)
148}
149
150pub fn pc<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
151    gas!(interpreter, gas::BASE);
152    // - 1 because we have already advanced the instruction pointer in `Interpreter::step`
153    push!(interpreter, U256::from(interpreter.program_counter() - 1));
154}
155
156#[inline]
157fn return_inner(interpreter: &mut Interpreter, instruction_result: InstructionResult) {
158    // zero gas cost
159    // gas!(interpreter, gas::ZERO);
160    pop!(interpreter, offset, len);
161    let len = as_usize_or_fail!(interpreter, len);
162    // important: offset must be ignored if len is zeros
163    let mut output = Bytes::default();
164    if len != 0 {
165        let offset = as_usize_or_fail!(interpreter, offset);
166        resize_memory!(interpreter, offset, len);
167
168        output = interpreter.shared_memory.slice(offset, len).to_vec().into()
169    }
170    interpreter.instruction_result = instruction_result;
171    interpreter.next_action = crate::InterpreterAction::Return {
172        result: InterpreterResult {
173            output,
174            gas: interpreter.gas,
175            result: instruction_result,
176        },
177    };
178}
179
180pub fn ret<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
181    return_inner(interpreter, InstructionResult::Return);
182}
183
184/// EIP-140: REVERT instruction
185pub fn revert<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut H) {
186    check!(interpreter, BYZANTIUM);
187    return_inner(interpreter, InstructionResult::Revert);
188}
189
190/// Stop opcode. This opcode halts the execution.
191pub fn stop<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
192    interpreter.instruction_result = InstructionResult::Stop;
193}
194
195/// Invalid opcode. This opcode halts the execution.
196pub fn invalid<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
197    interpreter.instruction_result = InstructionResult::InvalidFEOpcode;
198}
199
200/// Unknown opcode. This opcode halts the execution.
201pub fn unknown<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
202    interpreter.instruction_result = InstructionResult::OpcodeNotFound;
203}
204
205#[cfg(test)]
206mod test {
207    use std::sync::Arc;
208
209    use revm_primitives::{bytes, eof::TypesSection, Bytecode, Eof, PragueSpec};
210
211    use super::*;
212    use crate::{
213        opcode::{make_instruction_table, CALLF, JUMPF, NOP, RETF, RJUMP, RJUMPI, RJUMPV, STOP},
214        DummyHost, FunctionReturnFrame, Gas, Interpreter,
215    };
216
217    #[test]
218    fn rjump() {
219        let table = make_instruction_table::<_, PragueSpec>();
220        let mut host = DummyHost::default();
221        let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([
222            RJUMP, 0x00, 0x02, STOP, STOP,
223        ])));
224        interp.is_eof = true;
225        interp.gas = Gas::new(10000);
226
227        interp.step(&table, &mut host);
228        assert_eq!(interp.program_counter(), 5);
229    }
230
231    #[test]
232    fn rjumpi() {
233        let table = make_instruction_table::<_, PragueSpec>();
234        let mut host = DummyHost::default();
235        let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([
236            RJUMPI, 0x00, 0x03, RJUMPI, 0x00, 0x01, STOP, STOP,
237        ])));
238        interp.is_eof = true;
239        interp.stack.push(U256::from(1)).unwrap();
240        interp.stack.push(U256::from(0)).unwrap();
241        interp.gas = Gas::new(10000);
242
243        // dont jump
244        interp.step(&table, &mut host);
245        assert_eq!(interp.program_counter(), 3);
246        // jumps to last opcode
247        interp.step(&table, &mut host);
248        assert_eq!(interp.program_counter(), 7);
249    }
250
251    #[test]
252    fn rjumpv() {
253        let table = make_instruction_table::<_, PragueSpec>();
254        let mut host = DummyHost::default();
255        let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([
256            RJUMPV,
257            0x01, // max index, 0 and 1
258            0x00, // first x0001
259            0x01,
260            0x00, // second 0x002
261            0x02,
262            NOP,
263            NOP,
264            NOP,
265            RJUMP,
266            0xFF,
267            (-12i8) as u8,
268            STOP,
269        ])));
270        interp.is_eof = true;
271        interp.gas = Gas::new(1000);
272
273        // more then max_index
274        interp.stack.push(U256::from(10)).unwrap();
275        interp.step(&table, &mut host);
276        assert_eq!(interp.program_counter(), 6);
277
278        // cleanup
279        interp.step(&table, &mut host);
280        interp.step(&table, &mut host);
281        interp.step(&table, &mut host);
282        interp.step(&table, &mut host);
283        assert_eq!(interp.program_counter(), 0);
284
285        // jump to first index of vtable
286        interp.stack.push(U256::from(0)).unwrap();
287        interp.step(&table, &mut host);
288        assert_eq!(interp.program_counter(), 7);
289
290        // cleanup
291        interp.step(&table, &mut host);
292        interp.step(&table, &mut host);
293        interp.step(&table, &mut host);
294        assert_eq!(interp.program_counter(), 0);
295
296        // jump to second index of vtable
297        interp.stack.push(U256::from(1)).unwrap();
298        interp.step(&table, &mut host);
299        assert_eq!(interp.program_counter(), 8);
300    }
301
302    fn dummy_eof() -> Eof {
303        let bytes = bytes!("ef000101000402000100010400000000800000fe");
304        Eof::decode(bytes).unwrap()
305    }
306
307    fn eof_setup(bytes1: Bytes, bytes2: Bytes) -> Interpreter {
308        eof_setup_with_types(bytes1, bytes2, TypesSection::default())
309    }
310
311    /// Two code section and types section is for last code.
312    fn eof_setup_with_types(bytes1: Bytes, bytes2: Bytes, types: TypesSection) -> Interpreter {
313        let mut eof = dummy_eof();
314
315        eof.body.code_section.clear();
316        eof.body.types_section.clear();
317        eof.header.code_sizes.clear();
318
319        eof.header.code_sizes.push(bytes1.len() as u16);
320        eof.body.code_section.push(bytes1.clone());
321        eof.body.types_section.push(TypesSection::new(0, 0, 11));
322
323        eof.header.code_sizes.push(bytes2.len() as u16);
324        eof.body.code_section.push(bytes2.clone());
325        eof.body.types_section.push(types);
326
327        let mut interp = Interpreter::new_bytecode(Bytecode::Eof(Arc::new(eof)));
328        interp.gas = Gas::new(10000);
329        interp
330    }
331
332    #[test]
333    fn callf_retf_stop() {
334        let table = make_instruction_table::<_, PragueSpec>();
335        let mut host = DummyHost::default();
336
337        let bytes1 = Bytes::from([CALLF, 0x00, 0x01, STOP]);
338        let bytes2 = Bytes::from([RETF]);
339        let mut interp = eof_setup(bytes1, bytes2.clone());
340
341        // CALLF
342        interp.step(&table, &mut host);
343
344        assert_eq!(interp.function_stack.current_code_idx, 1);
345        assert_eq!(
346            interp.function_stack.return_stack[0],
347            FunctionReturnFrame::new(0, 3)
348        );
349        assert_eq!(interp.instruction_pointer, bytes2.as_ptr());
350
351        // RETF
352        interp.step(&table, &mut host);
353
354        assert_eq!(interp.function_stack.current_code_idx, 0);
355        assert_eq!(interp.function_stack.return_stack, Vec::new());
356        assert_eq!(interp.program_counter(), 3);
357
358        // STOP
359        interp.step(&table, &mut host);
360        assert_eq!(interp.instruction_result, InstructionResult::Stop);
361    }
362
363    #[test]
364    fn callf_stop() {
365        let table = make_instruction_table::<_, PragueSpec>();
366        let mut host = DummyHost::default();
367
368        let bytes1 = Bytes::from([CALLF, 0x00, 0x01]);
369        let bytes2 = Bytes::from([STOP]);
370        let mut interp = eof_setup(bytes1, bytes2.clone());
371
372        // CALLF
373        interp.step(&table, &mut host);
374
375        assert_eq!(interp.function_stack.current_code_idx, 1);
376        assert_eq!(
377            interp.function_stack.return_stack[0],
378            FunctionReturnFrame::new(0, 3)
379        );
380        assert_eq!(interp.instruction_pointer, bytes2.as_ptr());
381
382        // STOP
383        interp.step(&table, &mut host);
384        assert_eq!(interp.instruction_result, InstructionResult::Stop);
385    }
386
387    #[test]
388    fn callf_stack_overflow() {
389        let table = make_instruction_table::<_, PragueSpec>();
390        let mut host = DummyHost::default();
391
392        let bytes1 = Bytes::from([CALLF, 0x00, 0x01]);
393        let bytes2 = Bytes::from([STOP]);
394        let mut interp =
395            eof_setup_with_types(bytes1, bytes2.clone(), TypesSection::new(0, 0, 1025));
396
397        // CALLF
398        interp.step(&table, &mut host);
399
400        // stack overflow
401        assert_eq!(interp.instruction_result, InstructionResult::StackOverflow);
402    }
403
404    #[test]
405    fn jumpf_stop() {
406        let table = make_instruction_table::<_, PragueSpec>();
407        let mut host = DummyHost::default();
408
409        let bytes1 = Bytes::from([JUMPF, 0x00, 0x01]);
410        let bytes2 = Bytes::from([STOP]);
411        let mut interp = eof_setup(bytes1, bytes2.clone());
412
413        // JUMPF
414        interp.step(&table, &mut host);
415
416        assert_eq!(interp.function_stack.current_code_idx, 1);
417        assert!(interp.function_stack.return_stack.is_empty());
418        assert_eq!(interp.instruction_pointer, bytes2.as_ptr());
419
420        // STOP
421        interp.step(&table, &mut host);
422        assert_eq!(interp.instruction_result, InstructionResult::Stop);
423    }
424
425    #[test]
426    fn jumpf_stack_overflow() {
427        let table = make_instruction_table::<_, PragueSpec>();
428        let mut host = DummyHost::default();
429
430        let bytes1 = Bytes::from([JUMPF, 0x00, 0x01]);
431        let bytes2 = Bytes::from([STOP]);
432        let mut interp =
433            eof_setup_with_types(bytes1, bytes2.clone(), TypesSection::new(0, 0, 1025));
434
435        // JUMPF
436        interp.step(&table, &mut host);
437
438        // stack overflow
439        assert_eq!(interp.instruction_result, InstructionResult::StackOverflow);
440    }
441}