rtvm_interpreter/instructions/
control.rs1use 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 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 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 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(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 != U256::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 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 if interpreter.function_stack.return_stack_len() == 1024 {
93 interpreter.instruction_result = InstructionResult::EOFFunctionStackOverflow;
94 return;
95 }
96
97 interpreter
100 .function_stack
101 .push(interpreter.program_counter() + 2, idx);
102
103 interpreter.load_eof_code(idx, 0)
104}
105
106pub fn retf<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
107 require_eof!(interpreter);
108 gas!(interpreter, gas::RETF_GAS);
109
110 let Some(fframe) = interpreter.function_stack.pop() else {
111 panic!("Expected function frame")
112 };
113
114 interpreter.load_eof_code(fframe.idx, fframe.pc);
115}
116
117pub fn jumpf<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
118 require_eof!(interpreter);
119 gas!(interpreter, gas::LOW);
120
121 let idx = unsafe { read_u16(interpreter.instruction_pointer) } as usize;
122
123 interpreter.function_stack.set_current_code_idx(idx);
126 interpreter.load_eof_code(idx, 0)
127}
128
129pub fn pc<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
130 gas!(interpreter, gas::BASE);
131 push!(interpreter, U256::from(interpreter.program_counter() - 1));
133}
134
135#[inline]
136fn return_inner(interpreter: &mut Interpreter, instruction_result: InstructionResult) {
137 pop!(interpreter, offset, len);
140 let len = as_usize_or_fail!(interpreter, len);
141 let mut output = Bytes::default();
143 if len != 0 {
144 let offset = as_usize_or_fail!(interpreter, offset);
145 resize_memory!(interpreter, offset, len);
146
147 output = interpreter.shared_memory.slice(offset, len).to_vec().into()
148 }
149 interpreter.instruction_result = instruction_result;
150 interpreter.next_action = crate::InterpreterAction::Return {
151 result: InterpreterResult {
152 output,
153 gas: interpreter.gas,
154 result: instruction_result,
155 },
156 };
157}
158
159pub fn ret<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
160 return_inner(interpreter, InstructionResult::Return);
161}
162
163pub fn revert<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut H) {
165 check!(interpreter, BYZANTIUM);
166 return_inner(interpreter, InstructionResult::Revert);
167}
168
169pub fn stop<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
171 interpreter.instruction_result = InstructionResult::Stop;
172}
173
174pub fn invalid<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
176 interpreter.instruction_result = InstructionResult::InvalidFEOpcode;
177}
178
179pub fn unknown<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
181 interpreter.instruction_result = InstructionResult::OpcodeNotFound;
182}
183
184#[cfg(test)]
185mod test {
186 use rtvm_primitives::{bytes, Bytecode, Eof, PragueSpec};
187
188 use super::*;
189 use crate::{
190 opcode::{make_instruction_table, CALLF, JUMPF, NOP, RETF, RJUMP, RJUMPI, RJUMPV, STOP},
191 DummyHost, FunctionReturnFrame, Gas, Interpreter,
192 };
193
194 #[test]
195 fn rjump() {
196 let table = make_instruction_table::<_, PragueSpec>();
197 let mut host = DummyHost::default();
198 let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([
199 RJUMP, 0x00, 0x02, STOP, STOP,
200 ])));
201 interp.is_eof = true;
202 interp.gas = Gas::new(10000);
203
204 interp.step(&table, &mut host);
205 assert_eq!(interp.program_counter(), 5);
206 }
207
208 #[test]
209 fn rjumpi() {
210 let table = make_instruction_table::<_, PragueSpec>();
211 let mut host = DummyHost::default();
212 let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([
213 RJUMPI, 0x00, 0x03, RJUMPI, 0x00, 0x01, STOP, STOP,
214 ])));
215 interp.is_eof = true;
216 interp.stack.push(U256::from(1)).unwrap();
217 interp.stack.push(U256::from(0)).unwrap();
218 interp.gas = Gas::new(10000);
219
220 interp.step(&table, &mut host);
222 assert_eq!(interp.program_counter(), 3);
223 interp.step(&table, &mut host);
225 assert_eq!(interp.program_counter(), 7);
226 }
227
228 #[test]
229 fn rjumpv() {
230 let table = make_instruction_table::<_, PragueSpec>();
231 let mut host = DummyHost::default();
232 let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([
233 RJUMPV,
234 0x01, 0x00, 0x01,
237 0x00, 0x02,
239 NOP,
240 NOP,
241 NOP,
242 RJUMP,
243 0xFF,
244 (-12i8) as u8,
245 STOP,
246 ])));
247 interp.is_eof = true;
248 interp.gas = Gas::new(1000);
249
250 interp.stack.push(U256::from(10)).unwrap();
252 interp.step(&table, &mut host);
253 assert_eq!(interp.program_counter(), 6);
254
255 interp.step(&table, &mut host);
257 interp.step(&table, &mut host);
258 interp.step(&table, &mut host);
259 interp.step(&table, &mut host);
260 assert_eq!(interp.program_counter(), 0);
261
262 interp.stack.push(U256::from(0)).unwrap();
264 interp.step(&table, &mut host);
265 assert_eq!(interp.program_counter(), 7);
266
267 interp.step(&table, &mut host);
269 interp.step(&table, &mut host);
270 interp.step(&table, &mut host);
271 assert_eq!(interp.program_counter(), 0);
272
273 interp.stack.push(U256::from(1)).unwrap();
275 interp.step(&table, &mut host);
276 assert_eq!(interp.program_counter(), 8);
277 }
278
279 fn dummy_eof() -> Eof {
280 let bytes = bytes!("ef000101000402000100010400000000800000fe");
281 Eof::decode(bytes).unwrap()
282 }
283
284 #[test]
285 fn callf_retf_jumpf() {
286 let table = make_instruction_table::<_, PragueSpec>();
287 let mut host = DummyHost::default();
288 let mut eof = dummy_eof();
289
290 eof.body.code_section.clear();
291 eof.header.code_sizes.clear();
292
293 let bytes1 = Bytes::from([CALLF, 0x00, 0x01, JUMPF, 0x00, 0x01]);
294 eof.header.code_sizes.push(bytes1.len() as u16);
295 eof.body.code_section.push(bytes1.clone());
296 let bytes2 = Bytes::from([STOP, RETF]);
297 eof.header.code_sizes.push(bytes2.len() as u16);
298 eof.body.code_section.push(bytes2.clone());
299
300 let mut interp = Interpreter::new_bytecode(Bytecode::Eof(eof));
301 interp.gas = Gas::new(10000);
302
303 assert_eq!(interp.function_stack.current_code_idx, 0);
304 assert!(interp.function_stack.return_stack.is_empty());
305
306 interp.step(&table, &mut host);
308
309 assert_eq!(interp.function_stack.current_code_idx, 1);
310 assert_eq!(
311 interp.function_stack.return_stack[0],
312 FunctionReturnFrame::new(0, 3)
313 );
314 assert_eq!(interp.instruction_pointer, bytes2.as_ptr());
315
316 interp.step(&table, &mut host);
318 interp.step(&table, &mut host);
320
321 assert_eq!(interp.function_stack.current_code_idx, 0);
322 assert_eq!(interp.function_stack.return_stack, Vec::new());
323 assert_eq!(interp.program_counter(), 3);
324
325 interp.step(&table, &mut host);
327 assert_eq!(interp.function_stack.current_code_idx, 1);
328 assert_eq!(interp.function_stack.return_stack, Vec::new());
329 assert_eq!(interp.instruction_pointer, bytes2.as_ptr());
330 }
331}