fuel_vm/interpreter/executors/
instruction.rs

1use crate::{
2    constraints::reg_key::ProgramRegistersSegment,
3    error::{
4        InterpreterError,
5        IoResult,
6        RuntimeError,
7    },
8    interpreter::{
9        alu,
10        flow::{
11            JumpArgs,
12            JumpMode,
13        },
14        EcalHandler,
15        ExecutableTransaction,
16        Interpreter,
17        Memory,
18    },
19    state::ExecuteState,
20    storage::InterpreterStorage,
21};
22
23use fuel_asm::{
24    wideint,
25    Instruction,
26    PanicInstruction,
27    PanicReason,
28    RawInstruction,
29    RegId,
30};
31use fuel_types::Word;
32
33use core::ops::Div;
34
35impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
36where
37    M: Memory,
38    S: InterpreterStorage,
39    Tx: ExecutableTransaction,
40    Ecal: EcalHandler,
41{
42    /// Execute the current instruction located in `$m[$pc]`.
43    pub fn execute(&mut self) -> Result<ExecuteState, InterpreterError<S::DataError>> {
44        let raw_instruction = self.fetch_instruction()?;
45        self.instruction(raw_instruction)
46    }
47
48    /// Reads the current instruction located in `$m[$pc]`,
49    /// performing memory boundary checks.
50    fn fetch_instruction(
51        &self,
52    ) -> Result<RawInstruction, InterpreterError<S::DataError>> {
53        let pc = self.registers[RegId::PC];
54        let instruction = RawInstruction::from_be_bytes(
55            self.memory().read_bytes(pc).map_err(|reason| {
56                InterpreterError::PanicInstruction(PanicInstruction::error(
57                    reason,
58                    0, // The value is meaningless since fetch was out-of-bounds
59                ))
60            })?,
61        );
62        if pc < self.registers[RegId::IS] || pc >= self.registers[RegId::SSP] {
63            return Err(InterpreterError::PanicInstruction(PanicInstruction::error(
64                PanicReason::MemoryNotExecutable,
65                instruction,
66            )))
67        }
68        Ok(instruction)
69    }
70
71    /// Execute a provided instruction
72    pub fn instruction<R: Into<RawInstruction> + Copy>(
73        &mut self,
74        raw: R,
75    ) -> Result<ExecuteState, InterpreterError<S::DataError>> {
76        if self.debugger.is_active() {
77            let debug = self.eval_debugger_state();
78            if !debug.should_continue() {
79                return Ok(debug.into())
80            }
81        }
82
83        self.instruction_inner(raw.into())
84            .map_err(|e| InterpreterError::from_runtime(e, raw.into()))
85    }
86
87    fn instruction_inner(
88        &mut self,
89        raw: RawInstruction,
90    ) -> IoResult<ExecuteState, S::DataError> {
91        let instruction = Instruction::try_from(raw)
92            .map_err(|_| RuntimeError::from(PanicReason::InvalidInstruction))?;
93
94        // TODO additional branch that might be optimized after
95        // https://github.com/FuelLabs/fuel-asm/issues/68
96        if self.is_predicate() && !instruction.opcode().is_predicate_allowed() {
97            return Err(PanicReason::ContractInstructionNotAllowed.into())
98        }
99
100        // Short-hand for retrieving the value from the register with the given ID.
101        // We use a macro to "close over" `self.registers` without taking ownership of it.
102        macro_rules! r {
103            ($id:expr) => {
104                self.registers[$id]
105            };
106        }
107
108        match instruction {
109            Instruction::ADD(add) => {
110                self.gas_charge(self.gas_costs().add())?;
111                let (a, b, c) = add.unpack();
112                self.alu_capture_overflow(
113                    a.into(),
114                    u128::overflowing_add,
115                    r!(b).into(),
116                    r!(c).into(),
117                )?;
118            }
119
120            Instruction::ADDI(addi) => {
121                self.gas_charge(self.gas_costs().addi())?;
122                let (a, b, imm) = addi.unpack();
123                self.alu_capture_overflow(
124                    a.into(),
125                    u128::overflowing_add,
126                    r!(b).into(),
127                    imm.into(),
128                )?;
129            }
130
131            Instruction::AND(and) => {
132                self.gas_charge(self.gas_costs().and())?;
133                let (a, b, c) = and.unpack();
134                self.alu_set(a.into(), r!(b) & r!(c))?;
135            }
136
137            Instruction::ANDI(andi) => {
138                self.gas_charge(self.gas_costs().andi())?;
139                let (a, b, imm) = andi.unpack();
140                self.alu_set(a.into(), r!(b) & Word::from(imm))?;
141            }
142
143            Instruction::DIV(div) => {
144                self.gas_charge(self.gas_costs().div())?;
145                let (a, b, c) = div.unpack();
146                let c = r!(c);
147                self.alu_error(a.into(), Word::div, r!(b), c, c == 0)?;
148            }
149
150            Instruction::DIVI(divi) => {
151                self.gas_charge(self.gas_costs().divi())?;
152                let (a, b, imm) = divi.unpack();
153                let imm = Word::from(imm);
154                self.alu_error(a.into(), Word::div, r!(b), imm, imm == 0)?;
155            }
156
157            Instruction::EQ(eq) => {
158                self.gas_charge(self.gas_costs().eq_())?;
159                let (a, b, c) = eq.unpack();
160                self.alu_set(a.into(), (r!(b) == r!(c)) as Word)?;
161            }
162
163            Instruction::EXP(exp) => {
164                self.gas_charge(self.gas_costs().exp())?;
165                let (a, b, c) = exp.unpack();
166                self.alu_boolean_overflow(a.into(), alu::exp, r!(b), r!(c))?;
167            }
168
169            Instruction::EXPI(expi) => {
170                self.gas_charge(self.gas_costs().expi())?;
171                let (a, b, imm) = expi.unpack();
172                let expo = u32::from(imm);
173                self.alu_boolean_overflow(a.into(), Word::overflowing_pow, r!(b), expo)?;
174            }
175
176            Instruction::GT(gt) => {
177                self.gas_charge(self.gas_costs().gt())?;
178                let (a, b, c) = gt.unpack();
179                self.alu_set(a.into(), (r!(b) > r!(c)) as Word)?;
180            }
181
182            Instruction::LT(lt) => {
183                self.gas_charge(self.gas_costs().lt())?;
184                let (a, b, c) = lt.unpack();
185                self.alu_set(a.into(), (r!(b) < r!(c)) as Word)?;
186            }
187
188            Instruction::WDCM(wdcm) => {
189                self.gas_charge(self.gas_costs().wdcm())?;
190                let (a, b, c, imm) = wdcm.unpack();
191                let args = wideint::CompareArgs::from_imm(imm)
192                    .ok_or(PanicReason::InvalidImmediateValue)?;
193                self.alu_wideint_cmp_u128(a.into(), r!(b), r!(c), args)?;
194            }
195
196            Instruction::WQCM(wqcm) => {
197                self.gas_charge(self.gas_costs().wqcm())?;
198                let (a, b, c, imm) = wqcm.unpack();
199                let args = wideint::CompareArgs::from_imm(imm)
200                    .ok_or(PanicReason::InvalidImmediateValue)?;
201                self.alu_wideint_cmp_u256(a.into(), r!(b), r!(c), args)?;
202            }
203
204            Instruction::WDOP(wdop) => {
205                self.gas_charge(self.gas_costs().wdop())?;
206                let (a, b, c, imm) = wdop.unpack();
207                let args = wideint::MathArgs::from_imm(imm)
208                    .ok_or(PanicReason::InvalidImmediateValue)?;
209                self.alu_wideint_op_u128(r!(a), r!(b), r!(c), args)?;
210            }
211
212            Instruction::WQOP(wqop) => {
213                self.gas_charge(self.gas_costs().wqop())?;
214                let (a, b, c, imm) = wqop.unpack();
215                let args = wideint::MathArgs::from_imm(imm)
216                    .ok_or(PanicReason::InvalidImmediateValue)?;
217                self.alu_wideint_op_u256(r!(a), r!(b), r!(c), args)?;
218            }
219
220            Instruction::WDML(wdml) => {
221                self.gas_charge(self.gas_costs().wdml())?;
222                let (a, b, c, imm) = wdml.unpack();
223                let args = wideint::MulArgs::from_imm(imm)
224                    .ok_or(PanicReason::InvalidImmediateValue)?;
225                self.alu_wideint_mul_u128(r!(a), r!(b), r!(c), args)?;
226            }
227
228            Instruction::WQML(wqml) => {
229                self.gas_charge(self.gas_costs().wqml())?;
230                let (a, b, c, imm) = wqml.unpack();
231                let args = wideint::MulArgs::from_imm(imm)
232                    .ok_or(PanicReason::InvalidImmediateValue)?;
233                self.alu_wideint_mul_u256(r!(a), r!(b), r!(c), args)?;
234            }
235
236            Instruction::WDDV(wddv) => {
237                self.gas_charge(self.gas_costs().wddv())?;
238                let (a, b, c, imm) = wddv.unpack();
239                let args = wideint::DivArgs::from_imm(imm)
240                    .ok_or(PanicReason::InvalidImmediateValue)?;
241                self.alu_wideint_div_u128(r!(a), r!(b), r!(c), args)?;
242            }
243
244            Instruction::WQDV(wqdv) => {
245                self.gas_charge(self.gas_costs().wqdv())?;
246                let (a, b, c, imm) = wqdv.unpack();
247                let args = wideint::DivArgs::from_imm(imm)
248                    .ok_or(PanicReason::InvalidImmediateValue)?;
249                self.alu_wideint_div_u256(r!(a), r!(b), r!(c), args)?;
250            }
251
252            Instruction::WDMD(wdmd) => {
253                self.gas_charge(self.gas_costs().wdmd())?;
254                let (a, b, c, d) = wdmd.unpack();
255                self.alu_wideint_muldiv_u128(r!(a), r!(b), r!(c), r!(d))?;
256            }
257
258            Instruction::WQMD(wqmd) => {
259                self.gas_charge(self.gas_costs().wqmd())?;
260                let (a, b, c, d) = wqmd.unpack();
261                self.alu_wideint_muldiv_u256(r!(a), r!(b), r!(c), r!(d))?;
262            }
263
264            Instruction::WDAM(wdam) => {
265                self.gas_charge(self.gas_costs().wdam())?;
266                let (a, b, c, d) = wdam.unpack();
267                self.alu_wideint_addmod_u128(r!(a), r!(b), r!(c), r!(d))?;
268            }
269            Instruction::WQAM(wqam) => {
270                self.gas_charge(self.gas_costs().wqam())?;
271                let (a, b, c, d) = wqam.unpack();
272                self.alu_wideint_addmod_u256(r!(a), r!(b), r!(c), r!(d))?;
273            }
274            Instruction::WDMM(wdmm) => {
275                self.gas_charge(self.gas_costs().wdmm())?;
276                let (a, b, c, d) = wdmm.unpack();
277                self.alu_wideint_mulmod_u128(r!(a), r!(b), r!(c), r!(d))?;
278            }
279            Instruction::WQMM(wqmm) => {
280                self.gas_charge(self.gas_costs().wqmm())?;
281                let (a, b, c, d) = wqmm.unpack();
282                self.alu_wideint_mulmod_u256(r!(a), r!(b), r!(c), r!(d))?;
283            }
284
285            Instruction::MLOG(mlog) => {
286                self.gas_charge(self.gas_costs().mlog())?;
287                let (a, b, c) = mlog.unpack();
288                let (lhs, rhs) = (r!(b), r!(c));
289                self.alu_error(
290                    a.into(),
291                    |l, r| {
292                        l.checked_ilog(r)
293                            .expect("checked_ilog returned None for valid values")
294                            as Word
295                    },
296                    lhs,
297                    rhs,
298                    lhs == 0 || rhs <= 1,
299                )?;
300            }
301
302            Instruction::MOD(mod_) => {
303                self.gas_charge(self.gas_costs().mod_op())?;
304                let (a, b, c) = mod_.unpack();
305                let rhs = r!(c);
306                self.alu_error(a.into(), Word::wrapping_rem, r!(b), rhs, rhs == 0)?;
307            }
308
309            Instruction::MODI(modi) => {
310                self.gas_charge(self.gas_costs().modi())?;
311                let (a, b, imm) = modi.unpack();
312                let rhs = Word::from(imm);
313                self.alu_error(a.into(), Word::wrapping_rem, r!(b), rhs, rhs == 0)?;
314            }
315
316            Instruction::MOVE(move_) => {
317                self.gas_charge(self.gas_costs().move_op())?;
318                let (a, b) = move_.unpack();
319                self.alu_set(a.into(), r!(b))?;
320            }
321
322            Instruction::MOVI(movi) => {
323                self.gas_charge(self.gas_costs().movi())?;
324                let (a, imm) = movi.unpack();
325                self.alu_set(a.into(), Word::from(imm))?;
326            }
327
328            Instruction::MROO(mroo) => {
329                self.gas_charge(self.gas_costs().mroo())?;
330                let (a, b, c) = mroo.unpack();
331                let (lhs, rhs) = (r!(b), r!(c));
332                self.alu_error(
333                    a.into(),
334                    |l, r| {
335                        checked_nth_root(l, r)
336                            .expect("checked_nth_root returned None for valid values")
337                            as Word
338                    },
339                    lhs,
340                    rhs,
341                    rhs == 0,
342                )?;
343            }
344
345            Instruction::MUL(mul) => {
346                self.gas_charge(self.gas_costs().mul())?;
347                let (a, b, c) = mul.unpack();
348                self.alu_capture_overflow(
349                    a.into(),
350                    u128::overflowing_mul,
351                    r!(b).into(),
352                    r!(c).into(),
353                )?;
354            }
355
356            Instruction::MULI(muli) => {
357                self.gas_charge(self.gas_costs().muli())?;
358                let (a, b, imm) = muli.unpack();
359                self.alu_capture_overflow(
360                    a.into(),
361                    u128::overflowing_mul,
362                    r!(b).into(),
363                    imm.into(),
364                )?;
365            }
366
367            Instruction::MLDV(mldv) => {
368                self.gas_charge(self.gas_costs().mldv())?;
369                let (a, b, c, d) = mldv.unpack();
370                self.alu_muldiv(a.into(), r!(b), r!(c), r!(d))?;
371            }
372
373            Instruction::NOOP(_noop) => {
374                self.gas_charge(self.gas_costs().noop())?;
375                self.alu_clear()?;
376            }
377
378            Instruction::NOT(not) => {
379                self.gas_charge(self.gas_costs().not())?;
380                let (a, b) = not.unpack();
381                self.alu_set(a.into(), !r!(b))?;
382            }
383
384            Instruction::OR(or) => {
385                self.gas_charge(self.gas_costs().or())?;
386                let (a, b, c) = or.unpack();
387                self.alu_set(a.into(), r!(b) | r!(c))?;
388            }
389
390            Instruction::ORI(ori) => {
391                self.gas_charge(self.gas_costs().ori())?;
392                let (a, b, imm) = ori.unpack();
393                self.alu_set(a.into(), r!(b) | Word::from(imm))?;
394            }
395
396            Instruction::SLL(sll) => {
397                self.gas_charge(self.gas_costs().sll())?;
398                let (a, b, c) = sll.unpack();
399
400                self.alu_set(
401                    a.into(),
402                    if let Ok(c) = r!(c).try_into() {
403                        Word::checked_shl(r!(b), c).unwrap_or_default()
404                    } else {
405                        0
406                    },
407                )?;
408            }
409
410            Instruction::SLLI(slli) => {
411                self.gas_charge(self.gas_costs().slli())?;
412                let (a, b, imm) = slli.unpack();
413                let rhs = u32::from(imm);
414                self.alu_set(a.into(), r!(b).checked_shl(rhs).unwrap_or_default())?;
415            }
416
417            Instruction::SRL(srl) => {
418                self.gas_charge(self.gas_costs().srl())?;
419                let (a, b, c) = srl.unpack();
420                self.alu_set(
421                    a.into(),
422                    if let Ok(c) = r!(c).try_into() {
423                        Word::checked_shr(r!(b), c).unwrap_or_default()
424                    } else {
425                        0
426                    },
427                )?;
428            }
429
430            Instruction::SRLI(srli) => {
431                self.gas_charge(self.gas_costs().srli())?;
432                let (a, b, imm) = srli.unpack();
433                let rhs = u32::from(imm);
434                self.alu_set(a.into(), r!(b).checked_shr(rhs).unwrap_or_default())?;
435            }
436
437            Instruction::SUB(sub) => {
438                self.gas_charge(self.gas_costs().sub())?;
439                let (a, b, c) = sub.unpack();
440                self.alu_capture_overflow(
441                    a.into(),
442                    u128::overflowing_sub,
443                    r!(b).into(),
444                    r!(c).into(),
445                )?;
446            }
447
448            Instruction::SUBI(subi) => {
449                self.gas_charge(self.gas_costs().subi())?;
450                let (a, b, imm) = subi.unpack();
451                self.alu_capture_overflow(
452                    a.into(),
453                    u128::overflowing_sub,
454                    r!(b).into(),
455                    imm.into(),
456                )?;
457            }
458
459            Instruction::XOR(xor) => {
460                self.gas_charge(self.gas_costs().xor())?;
461                let (a, b, c) = xor.unpack();
462                self.alu_set(a.into(), r!(b) ^ r!(c))?;
463            }
464
465            Instruction::XORI(xori) => {
466                self.gas_charge(self.gas_costs().xori())?;
467                let (a, b, imm) = xori.unpack();
468                self.alu_set(a.into(), r!(b) ^ Word::from(imm))?;
469            }
470
471            Instruction::JI(ji) => {
472                self.gas_charge(self.gas_costs().ji())?;
473                let imm = ji.unpack();
474                self.jump(JumpArgs::new(JumpMode::Absolute).to_address(imm.into()))?;
475            }
476
477            Instruction::JNEI(jnei) => {
478                self.gas_charge(self.gas_costs().jnei())?;
479                let (a, b, imm) = jnei.unpack();
480                self.jump(
481                    JumpArgs::new(JumpMode::Absolute)
482                        .with_condition(r!(a) != r!(b))
483                        .to_address(imm.into()),
484                )?;
485            }
486
487            Instruction::JNZI(jnzi) => {
488                self.gas_charge(self.gas_costs().jnzi())?;
489                let (a, imm) = jnzi.unpack();
490                self.jump(
491                    JumpArgs::new(JumpMode::Absolute)
492                        .with_condition(r!(a) != 0)
493                        .to_address(imm.into()),
494                )?;
495            }
496
497            Instruction::JMP(jmp) => {
498                self.gas_charge(self.gas_costs().jmp())?;
499                let a = jmp.unpack();
500                self.jump(JumpArgs::new(JumpMode::Absolute).to_address(r!(a)))?;
501            }
502
503            Instruction::JNE(jne) => {
504                self.gas_charge(self.gas_costs().jne())?;
505                let (a, b, c) = jne.unpack();
506                self.jump(
507                    JumpArgs::new(JumpMode::Absolute)
508                        .with_condition(r!(a) != r!(b))
509                        .to_address(r!(c)),
510                )?;
511            }
512
513            Instruction::JMPF(jmpf) => {
514                self.gas_charge(self.gas_costs().jmpf())?;
515                let (a, offset) = jmpf.unpack();
516                self.jump(
517                    JumpArgs::new(JumpMode::RelativeForwards)
518                        .to_address(r!(a))
519                        .plus_fixed(offset.into()),
520                )?;
521            }
522
523            Instruction::JMPB(jmpb) => {
524                self.gas_charge(self.gas_costs().jmpb())?;
525                let (a, offset) = jmpb.unpack();
526                self.jump(
527                    JumpArgs::new(JumpMode::RelativeBackwards)
528                        .to_address(r!(a))
529                        .plus_fixed(offset.into()),
530                )?;
531            }
532
533            Instruction::JNZF(jnzf) => {
534                self.gas_charge(self.gas_costs().jnzf())?;
535                let (a, b, offset) = jnzf.unpack();
536                self.jump(
537                    JumpArgs::new(JumpMode::RelativeForwards)
538                        .with_condition(r!(a) != 0)
539                        .to_address(r!(b))
540                        .plus_fixed(offset.into()),
541                )?;
542            }
543
544            Instruction::JNZB(jnzb) => {
545                self.gas_charge(self.gas_costs().jnzb())?;
546                let (a, b, offset) = jnzb.unpack();
547                self.jump(
548                    JumpArgs::new(JumpMode::RelativeBackwards)
549                        .with_condition(r!(a) != 0)
550                        .to_address(r!(b))
551                        .plus_fixed(offset.into()),
552                )?;
553            }
554
555            Instruction::JNEF(jnef) => {
556                self.gas_charge(self.gas_costs().jnef())?;
557                let (a, b, c, offset) = jnef.unpack();
558                self.jump(
559                    JumpArgs::new(JumpMode::RelativeForwards)
560                        .with_condition(r!(a) != r!(b))
561                        .to_address(r!(c))
562                        .plus_fixed(offset.into()),
563                )?;
564            }
565
566            Instruction::JNEB(jneb) => {
567                self.gas_charge(self.gas_costs().jneb())?;
568                let (a, b, c, offset) = jneb.unpack();
569                self.jump(
570                    JumpArgs::new(JumpMode::RelativeBackwards)
571                        .with_condition(r!(a) != r!(b))
572                        .to_address(r!(c))
573                        .plus_fixed(offset.into()),
574                )?;
575            }
576
577            Instruction::RET(ret) => {
578                self.gas_charge(self.gas_costs().ret())?;
579                let a = ret.unpack();
580                let ra = r!(a);
581                self.ret(ra)?;
582                return Ok(ExecuteState::Return(ra))
583            }
584
585            Instruction::RETD(retd) => {
586                let (a, b) = retd.unpack();
587                let len = r!(b);
588                self.dependent_gas_charge(self.gas_costs().retd(), len)?;
589                return Ok(self.ret_data(r!(a), len).map(ExecuteState::ReturnData)?)
590            }
591
592            Instruction::RVRT(rvrt) => {
593                self.gas_charge(self.gas_costs().rvrt())?;
594                let a = rvrt.unpack();
595                let ra = r!(a);
596                self.revert(ra)?;
597                return Ok(ExecuteState::Revert(ra))
598            }
599
600            Instruction::SMO(smo) => {
601                let (a, b, c, d) = smo.unpack();
602                self.dependent_gas_charge(self.gas_costs().smo(), r!(c))?;
603                self.message_output(r!(a), r!(b), r!(c), r!(d))?;
604            }
605
606            Instruction::ALOC(aloc) => {
607                let a = aloc.unpack();
608                let number_of_bytes = r!(a);
609                self.dependent_gas_charge(self.gas_costs().aloc(), number_of_bytes)?;
610                self.malloc(number_of_bytes)?;
611            }
612
613            Instruction::CFEI(cfei) => {
614                let number_of_bytes = cfei.unpack().into();
615                self.dependent_gas_charge(self.gas_costs().cfei(), number_of_bytes)?;
616                self.stack_pointer_overflow(Word::overflowing_add, number_of_bytes)?;
617            }
618
619            Instruction::CFE(cfe) => {
620                let a = cfe.unpack();
621                let number_of_bytes = r!(a);
622                self.dependent_gas_charge(self.gas_costs().cfe(), number_of_bytes)?;
623                self.stack_pointer_overflow(Word::overflowing_add, number_of_bytes)?;
624            }
625
626            Instruction::CFSI(cfsi) => {
627                self.gas_charge(self.gas_costs().cfsi())?;
628                let imm = cfsi.unpack();
629                self.stack_pointer_overflow(Word::overflowing_sub, imm.into())?;
630            }
631
632            Instruction::CFS(cfs) => {
633                self.gas_charge(self.gas_costs().cfsi())?;
634                let a = cfs.unpack();
635                self.stack_pointer_overflow(Word::overflowing_sub, r!(a))?;
636            }
637
638            Instruction::PSHL(pshl) => {
639                self.gas_charge(self.gas_costs().pshl())?;
640                let bitmask = pshl.unpack();
641                self.push_selected_registers(ProgramRegistersSegment::Low, bitmask)?;
642            }
643
644            Instruction::PSHH(pshh) => {
645                self.gas_charge(self.gas_costs().pshh())?;
646                let bitmask = pshh.unpack();
647                self.push_selected_registers(ProgramRegistersSegment::High, bitmask)?;
648            }
649
650            Instruction::POPL(popl) => {
651                self.gas_charge(self.gas_costs().popl())?;
652                let bitmask = popl.unpack();
653                self.pop_selected_registers(ProgramRegistersSegment::Low, bitmask)?;
654            }
655
656            Instruction::POPH(poph) => {
657                self.gas_charge(self.gas_costs().poph())?;
658                let bitmask = poph.unpack();
659                self.pop_selected_registers(ProgramRegistersSegment::High, bitmask)?;
660            }
661
662            Instruction::LB(lb) => {
663                self.gas_charge(self.gas_costs().lb())?;
664                let (a, b, imm) = lb.unpack();
665                self.load_byte(a.into(), r!(b), imm.into())?;
666            }
667
668            Instruction::LW(lw) => {
669                self.gas_charge(self.gas_costs().lw())?;
670                let (a, b, imm) = lw.unpack();
671                self.load_word(a.into(), r!(b), imm)?;
672            }
673
674            Instruction::MCL(mcl) => {
675                let (a, b) = mcl.unpack();
676                let len = r!(b);
677                self.dependent_gas_charge(self.gas_costs().mcl(), len)?;
678                self.memclear(r!(a), len)?;
679            }
680
681            Instruction::MCLI(mcli) => {
682                let (a, imm) = mcli.unpack();
683                let len = Word::from(imm);
684                self.dependent_gas_charge(self.gas_costs().mcli(), len)?;
685                self.memclear(r!(a), len)?;
686            }
687
688            Instruction::MCP(mcp) => {
689                let (a, b, c) = mcp.unpack();
690                let len = r!(c);
691                self.dependent_gas_charge(self.gas_costs().mcp(), len)?;
692                self.memcopy(r!(a), r!(b), len)?;
693            }
694
695            Instruction::MCPI(mcpi) => {
696                let (a, b, imm) = mcpi.unpack();
697                let len = imm.into();
698                self.dependent_gas_charge(self.gas_costs().mcpi(), len)?;
699                self.memcopy(r!(a), r!(b), len)?;
700            }
701
702            Instruction::MEQ(meq) => {
703                let (a, b, c, d) = meq.unpack();
704                let len = r!(d);
705                self.dependent_gas_charge(self.gas_costs().meq(), len)?;
706                self.memeq(a.into(), r!(b), r!(c), len)?;
707            }
708
709            Instruction::SB(sb) => {
710                self.gas_charge(self.gas_costs().sb())?;
711                let (a, b, imm) = sb.unpack();
712                self.store_byte(r!(a), r!(b), imm.into())?;
713            }
714
715            Instruction::SW(sw) => {
716                self.gas_charge(self.gas_costs().sw())?;
717                let (a, b, imm) = sw.unpack();
718                self.store_word(r!(a), r!(b), imm)?;
719            }
720
721            Instruction::BAL(bal) => {
722                self.gas_charge(self.gas_costs().bal())?;
723                let (a, b, c) = bal.unpack();
724                self.contract_balance(a.into(), r!(b), r!(c))?;
725            }
726
727            Instruction::BHEI(bhei) => {
728                self.gas_charge(self.gas_costs().bhei())?;
729                let a = bhei.unpack();
730                self.block_height(a.into())?;
731            }
732
733            Instruction::BHSH(bhsh) => {
734                self.gas_charge(self.gas_costs().bhsh())?;
735                let (a, b) = bhsh.unpack();
736                self.block_hash(r!(a), r!(b))?;
737            }
738
739            Instruction::BURN(burn) => {
740                self.gas_charge(self.gas_costs().burn())?;
741                let (a, b) = burn.unpack();
742                self.burn(r!(a), r!(b))?;
743            }
744
745            Instruction::CALL(call) => {
746                // We charge for the gas inside of the `prepare_call` function.
747                let (a, b, c, d) = call.unpack();
748
749                // Enter call context
750                self.prepare_call(a, b, c, d)?;
751            }
752
753            Instruction::CB(cb) => {
754                self.gas_charge(self.gas_costs().cb())?;
755                let a = cb.unpack();
756                self.block_proposer(r!(a))?;
757            }
758
759            Instruction::CCP(ccp) => {
760                let (a, b, c, d) = ccp.unpack();
761                self.code_copy(r!(a), r!(b), r!(c), r!(d))?;
762            }
763
764            Instruction::CROO(croo) => {
765                let (a, b) = croo.unpack();
766                self.code_root(r!(a), r!(b))?;
767            }
768
769            Instruction::CSIZ(csiz) => {
770                // We charge for the gas inside of the `code_size` function.
771                let (a, b) = csiz.unpack();
772                self.code_size(a.into(), r!(b))?;
773            }
774
775            Instruction::LDC(ldc) => {
776                // We charge for the gas inside of the `load_contract_code` function.
777                let (a, b, c, mode) = ldc.unpack();
778                self.load_contract_code(r!(a), r!(b), r!(c), mode)?;
779            }
780
781            Instruction::LOG(log) => {
782                self.gas_charge(self.gas_costs().log())?;
783                let (a, b, c, d) = log.unpack();
784                self.log(r!(a), r!(b), r!(c), r!(d))?;
785            }
786
787            Instruction::LOGD(logd) => {
788                let (a, b, c, d) = logd.unpack();
789                self.dependent_gas_charge(self.gas_costs().logd(), r!(d))?;
790                self.log_data(r!(a), r!(b), r!(c), r!(d))?;
791            }
792
793            Instruction::MINT(mint) => {
794                self.gas_charge(self.gas_costs().mint())?;
795                let (a, b) = mint.unpack();
796                self.mint(r!(a), r!(b))?;
797            }
798
799            Instruction::SCWQ(scwq) => {
800                let (a, b, c) = scwq.unpack();
801                self.dependent_gas_charge(self.gas_costs().scwq(), r!(c))?;
802                self.state_clear_qword(r!(a), b.into(), r!(c))?;
803            }
804
805            Instruction::SRW(srw) => {
806                self.gas_charge(self.gas_costs().srw())?;
807                let (a, b, c) = srw.unpack();
808                self.state_read_word(a.into(), b.into(), r!(c))?;
809            }
810
811            Instruction::SRWQ(srwq) => {
812                let (a, b, c, d) = srwq.unpack();
813                self.dependent_gas_charge(self.gas_costs().srwq(), r!(d))?;
814                self.state_read_qword(r!(a), b.into(), r!(c), r!(d))?;
815            }
816
817            Instruction::SWW(sww) => {
818                self.gas_charge(self.gas_costs().sww())?;
819                let (a, b, c) = sww.unpack();
820                self.state_write_word(r!(a), b.into(), r!(c))?;
821            }
822
823            Instruction::SWWQ(swwq) => {
824                let (a, b, c, d) = swwq.unpack();
825                self.dependent_gas_charge(self.gas_costs().swwq(), r!(d))?;
826                self.state_write_qword(r!(a), b.into(), r!(c), r!(d))?;
827            }
828
829            Instruction::TIME(time) => {
830                self.gas_charge(self.gas_costs().time())?;
831                let (a, b) = time.unpack();
832                self.timestamp(a.into(), r!(b))?;
833            }
834
835            Instruction::ECK1(eck1) => {
836                self.gas_charge(self.gas_costs().eck1())?;
837                let (a, b, c) = eck1.unpack();
838                self.secp256k1_recover(r!(a), r!(b), r!(c))?;
839            }
840
841            Instruction::ECR1(ecr1) => {
842                self.gas_charge(self.gas_costs().ecr1())?;
843                let (a, b, c) = ecr1.unpack();
844                self.secp256r1_recover(r!(a), r!(b), r!(c))?;
845            }
846
847            Instruction::ED19(ed19) => {
848                let (a, b, c, len) = ed19.unpack();
849                let mut len = r!(len);
850
851                // Backwards compatibility with old contracts
852                if len == 0 {
853                    len = 32;
854                }
855
856                self.dependent_gas_charge(self.gas_costs().ed19(), len)?;
857                self.ed25519_verify(r!(a), r!(b), r!(c), len)?;
858            }
859
860            Instruction::K256(k256) => {
861                let (a, b, c) = k256.unpack();
862                let len = r!(c);
863                self.dependent_gas_charge(self.gas_costs().k256(), len)?;
864                self.keccak256(r!(a), r!(b), len)?;
865            }
866
867            Instruction::S256(s256) => {
868                let (a, b, c) = s256.unpack();
869                let len = r!(c);
870                self.dependent_gas_charge(self.gas_costs().s256(), len)?;
871                self.sha256(r!(a), r!(b), len)?;
872            }
873
874            Instruction::FLAG(flag) => {
875                self.gas_charge(self.gas_costs().flag())?;
876                let a = flag.unpack();
877                self.set_flag(r!(a))?;
878            }
879
880            Instruction::GM(gm) => {
881                self.gas_charge(self.gas_costs().gm())?;
882                let (a, imm) = gm.unpack();
883                self.metadata(a.into(), imm.into())?;
884            }
885
886            Instruction::GTF(gtf) => {
887                self.gas_charge(self.gas_costs().gtf())?;
888                let (a, b, imm) = gtf.unpack();
889                self.get_transaction_field(a.into(), r!(b), imm.into())?;
890            }
891
892            Instruction::TR(tr) => {
893                self.gas_charge(self.gas_costs().tr())?;
894                let (a, b, c) = tr.unpack();
895                self.transfer(r!(a), r!(b), r!(c))?;
896            }
897
898            Instruction::TRO(tro) => {
899                self.gas_charge(self.gas_costs().tro())?;
900                let (a, b, c, d) = tro.unpack();
901                self.transfer_output(r!(a), r!(b), r!(c), r!(d))?;
902            }
903
904            Instruction::ECAL(ecal) => {
905                let (a, b, c, d) = ecal.unpack();
906                self.external_call(a, b, c, d)?;
907            }
908
909            Instruction::BSIZ(bsiz) => {
910                // We charge for this inside the function.
911                let (a, b) = bsiz.unpack();
912                self.blob_size(a.into(), r!(b))?;
913            }
914
915            Instruction::BLDD(bldd) => {
916                // We charge for this inside the function.
917                let (a, b, c, d) = bldd.unpack();
918                self.blob_load_data(r!(a), r!(b), r!(c), r!(d))?;
919            }
920
921            Instruction::ECOP(ecop) => {
922                self.gas_charge(self.gas_costs().ecop().map_err(PanicReason::from)?)?;
923                let (a, b, c, d) = ecop.unpack();
924                self.ec_operation(r!(a), r!(b), r!(c), r!(d))?;
925            }
926
927            Instruction::EPAR(epar) => {
928                let (a, b, c, d) = epar.unpack();
929                let len = r!(c);
930                self.dependent_gas_charge(
931                    self.gas_costs().epar().map_err(PanicReason::from)?,
932                    len,
933                )?;
934                self.ec_pairing(a.into(), r!(b), len, r!(d))?;
935            }
936        }
937
938        Ok(ExecuteState::Proceed)
939    }
940}
941
942/// Computes nth root of target, rounding down to nearest integer.
943/// This function uses the floating point operation to get an approximate solution,
944/// but corrects the result using exponentation to check for inaccuracy.
945fn checked_nth_root(target: u64, nth_root: u64) -> Option<u64> {
946    if nth_root == 0 {
947        // Zeroth root is not defined
948        return None
949    }
950
951    if nth_root == 1 || target <= 1 {
952        // Corner cases
953        return Some(target)
954    }
955
956    if nth_root >= target || nth_root > 64 {
957        // For any root >= target, result always 1
958        // For any n>1, n**64 can never fit into u64
959        return Some(1)
960    }
961
962    let nth_root = u32::try_from(nth_root).expect("Never loses bits, checked above");
963
964    // Use floating point operation to get an approximation for the starting point.
965    // This is at most off by one in either direction.
966
967    #[cfg(feature = "std")]
968    let powf = f64::powf;
969    #[cfg(not(feature = "std"))]
970    let powf = libm::pow;
971
972    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
973    let guess = powf(target as f64, (nth_root as f64).recip()) as u64;
974
975    debug_assert!(guess != 0, "This should never occur for {{target, n}} > 1");
976
977    // Check if a value raised to nth_power is below the target value, handling overflow
978    // correctly
979    let is_nth_power_below_target = |v: u64| match v.checked_pow(nth_root) {
980        Some(pow) => target < pow,
981        None => true, // v**nth_root >= 2**64 and target < 2**64
982    };
983
984    // Compute guess**n to check if the guess is too large.
985    // Note that if guess == 1, then g1 == 1 as well, meaning that we will not return
986    // here.
987    if is_nth_power_below_target(guess) {
988        return Some(guess.saturating_sub(1))
989    }
990
991    // Check if the initial guess was correct
992    let guess_plus_one = guess.checked_add(1).expect(
993        "Guess cannot be u64::MAX, as we have taken a root > 2 of a value to get it",
994    );
995    if is_nth_power_below_target(guess_plus_one) {
996        return Some(guess)
997    }
998
999    // If not, then the value above must be the correct one.
1000    Some(guess_plus_one)
1001}
1002
1003#[cfg(test)]
1004mod tests;