use crate::{
constraints::reg_key::ProgramRegistersSegment,
error::{
InterpreterError,
IoResult,
RuntimeError,
},
interpreter::{
alu,
flow::{
JumpArgs,
JumpMode,
},
EcalHandler,
ExecutableTransaction,
Interpreter,
},
state::ExecuteState,
storage::InterpreterStorage,
};
use fuel_asm::{
wideint,
Instruction,
PanicInstruction,
PanicReason,
RawInstruction,
RegId,
};
use fuel_types::Word;
use core::ops::Div;
impl<S, Tx, Ecal> Interpreter<S, Tx, Ecal>
where
S: InterpreterStorage,
Tx: ExecutableTransaction,
Ecal: EcalHandler,
{
pub fn execute(&mut self) -> Result<ExecuteState, InterpreterError<S::DataError>> {
let raw_instruction = self.fetch_instruction()?;
self.instruction(raw_instruction)
}
fn fetch_instruction_bytes(&self, addr: Word) -> Option<RawInstruction> {
let start: usize = addr.try_into().ok()?;
let end = start.checked_add(Instruction::SIZE)?;
let bytes = self.memory.get(start..end)?;
Some(RawInstruction::from_be_bytes(
bytes.try_into().expect("Slice len mismatch"),
))
}
fn fetch_instruction(
&self,
) -> Result<RawInstruction, InterpreterError<S::DataError>> {
let pc = self.registers[RegId::PC];
let instruction = self.fetch_instruction_bytes(pc).ok_or(
InterpreterError::PanicInstruction(PanicInstruction::error(
PanicReason::MemoryOverflow,
0, )),
)?;
if pc < self.registers[RegId::IS] || pc >= self.registers[RegId::SSP] {
return Err(InterpreterError::PanicInstruction(PanicInstruction::error(
PanicReason::MemoryNotExecutable,
instruction,
)))
}
Ok(instruction)
}
pub fn instruction<R: Into<RawInstruction> + Copy>(
&mut self,
raw: R,
) -> Result<ExecuteState, InterpreterError<S::DataError>> {
if self.debugger.is_active() {
let debug = self.eval_debugger_state();
if !debug.should_continue() {
return Ok(debug.into())
}
}
self.instruction_inner(raw.into())
.map_err(|e| InterpreterError::from_runtime(e, raw.into()))
}
fn instruction_inner(
&mut self,
raw: RawInstruction,
) -> IoResult<ExecuteState, S::DataError> {
let instruction = Instruction::try_from(raw)
.map_err(|_| RuntimeError::from(PanicReason::InvalidInstruction))?;
if self.is_predicate() && !instruction.opcode().is_predicate_allowed() {
return Err(PanicReason::ContractInstructionNotAllowed.into())
}
macro_rules! r {
($id:expr) => {
self.registers[$id]
};
}
match instruction {
Instruction::ADD(add) => {
self.gas_charge(self.gas_costs().add)?;
let (a, b, c) = add.unpack();
self.alu_capture_overflow(
a.into(),
u128::overflowing_add,
r!(b).into(),
r!(c).into(),
)?;
}
Instruction::ADDI(addi) => {
self.gas_charge(self.gas_costs().addi)?;
let (a, b, imm) = addi.unpack();
self.alu_capture_overflow(
a.into(),
u128::overflowing_add,
r!(b).into(),
imm.into(),
)?;
}
Instruction::AND(and) => {
self.gas_charge(self.gas_costs().and)?;
let (a, b, c) = and.unpack();
self.alu_set(a.into(), r!(b) & r!(c))?;
}
Instruction::ANDI(andi) => {
self.gas_charge(self.gas_costs().andi)?;
let (a, b, imm) = andi.unpack();
self.alu_set(a.into(), r!(b) & Word::from(imm))?;
}
Instruction::DIV(div) => {
self.gas_charge(self.gas_costs().div)?;
let (a, b, c) = div.unpack();
let c = r!(c);
self.alu_error(a.into(), Word::div, r!(b), c, c == 0)?;
}
Instruction::DIVI(divi) => {
self.gas_charge(self.gas_costs().divi)?;
let (a, b, imm) = divi.unpack();
let imm = Word::from(imm);
self.alu_error(a.into(), Word::div, r!(b), imm, imm == 0)?;
}
Instruction::EQ(eq) => {
self.gas_charge(self.gas_costs().eq)?;
let (a, b, c) = eq.unpack();
self.alu_set(a.into(), (r!(b) == r!(c)) as Word)?;
}
Instruction::EXP(exp) => {
self.gas_charge(self.gas_costs().exp)?;
let (a, b, c) = exp.unpack();
self.alu_boolean_overflow(a.into(), alu::exp, r!(b), r!(c))?;
}
Instruction::EXPI(expi) => {
self.gas_charge(self.gas_costs().expi)?;
let (a, b, imm) = expi.unpack();
let expo = u32::from(imm);
self.alu_boolean_overflow(a.into(), Word::overflowing_pow, r!(b), expo)?;
}
Instruction::GT(gt) => {
self.gas_charge(self.gas_costs().gt)?;
let (a, b, c) = gt.unpack();
self.alu_set(a.into(), (r!(b) > r!(c)) as Word)?;
}
Instruction::LT(lt) => {
self.gas_charge(self.gas_costs().lt)?;
let (a, b, c) = lt.unpack();
self.alu_set(a.into(), (r!(b) < r!(c)) as Word)?;
}
Instruction::WDCM(wdcm) => {
self.gas_charge(self.gas_costs().wdcm)?;
let (a, b, c, imm) = wdcm.unpack();
let args = wideint::CompareArgs::from_imm(imm)
.ok_or(PanicReason::InvalidImmediateValue)?;
self.alu_wideint_cmp_u128(a.into(), r!(b), r!(c), args)?;
}
Instruction::WQCM(wdcm) => {
self.gas_charge(self.gas_costs().wqcm)?;
let (a, b, c, imm) = wdcm.unpack();
let args = wideint::CompareArgs::from_imm(imm)
.ok_or(PanicReason::InvalidImmediateValue)?;
self.alu_wideint_cmp_u256(a.into(), r!(b), r!(c), args)?;
}
Instruction::WDOP(wdop) => {
self.gas_charge(self.gas_costs().wdcm)?;
let (a, b, c, imm) = wdop.unpack();
let args = wideint::MathArgs::from_imm(imm)
.ok_or(PanicReason::InvalidImmediateValue)?;
self.alu_wideint_op_u128(r!(a), r!(b), r!(c), args)?;
}
Instruction::WQOP(wqop) => {
self.gas_charge(self.gas_costs().wqcm)?;
let (a, b, c, imm) = wqop.unpack();
let args = wideint::MathArgs::from_imm(imm)
.ok_or(PanicReason::InvalidImmediateValue)?;
self.alu_wideint_op_u256(r!(a), r!(b), r!(c), args)?;
}
Instruction::WDML(wdml) => {
self.gas_charge(self.gas_costs().wdml)?;
let (a, b, c, imm) = wdml.unpack();
let args = wideint::MulArgs::from_imm(imm)
.ok_or(PanicReason::InvalidImmediateValue)?;
self.alu_wideint_mul_u128(r!(a), r!(b), r!(c), args)?;
}
Instruction::WQML(wqml) => {
self.gas_charge(self.gas_costs().wqml)?;
let (a, b, c, imm) = wqml.unpack();
let args = wideint::MulArgs::from_imm(imm)
.ok_or(PanicReason::InvalidImmediateValue)?;
self.alu_wideint_mul_u256(r!(a), r!(b), r!(c), args)?;
}
Instruction::WDDV(wddv) => {
self.gas_charge(self.gas_costs().wddv)?;
let (a, b, c, imm) = wddv.unpack();
let args = wideint::DivArgs::from_imm(imm)
.ok_or(PanicReason::InvalidImmediateValue)?;
self.alu_wideint_div_u128(r!(a), r!(b), r!(c), args)?;
}
Instruction::WQDV(wqdv) => {
self.gas_charge(self.gas_costs().wqdv)?;
let (a, b, c, imm) = wqdv.unpack();
let args = wideint::DivArgs::from_imm(imm)
.ok_or(PanicReason::InvalidImmediateValue)?;
self.alu_wideint_div_u256(r!(a), r!(b), r!(c), args)?;
}
Instruction::WDMD(wdmd) => {
self.gas_charge(self.gas_costs().wdmd)?;
let (a, b, c, d) = wdmd.unpack();
self.alu_wideint_muldiv_u128(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::WQMD(wqmd) => {
self.gas_charge(self.gas_costs().wqmd)?;
let (a, b, c, d) = wqmd.unpack();
self.alu_wideint_muldiv_u256(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::WDAM(wdam) => {
self.gas_charge(self.gas_costs().wdam)?;
let (a, b, c, d) = wdam.unpack();
self.alu_wideint_addmod_u128(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::WQAM(wqam) => {
self.gas_charge(self.gas_costs().wqam)?;
let (a, b, c, d) = wqam.unpack();
self.alu_wideint_addmod_u256(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::WDMM(wdmm) => {
self.gas_charge(self.gas_costs().wdmm)?;
let (a, b, c, d) = wdmm.unpack();
self.alu_wideint_mulmod_u128(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::WQMM(wqmm) => {
self.gas_charge(self.gas_costs().wqmm)?;
let (a, b, c, d) = wqmm.unpack();
self.alu_wideint_mulmod_u256(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::MLOG(mlog) => {
self.gas_charge(self.gas_costs().mlog)?;
let (a, b, c) = mlog.unpack();
let (lhs, rhs) = (r!(b), r!(c));
self.alu_error(
a.into(),
|l, r| {
l.checked_ilog(r)
.expect("checked_ilog returned None for valid values")
as Word
},
lhs,
rhs,
lhs == 0 || rhs <= 1,
)?;
}
Instruction::MOD(mod_) => {
self.gas_charge(self.gas_costs().mod_op)?;
let (a, b, c) = mod_.unpack();
let rhs = r!(c);
self.alu_error(a.into(), Word::wrapping_rem, r!(b), rhs, rhs == 0)?;
}
Instruction::MODI(modi) => {
self.gas_charge(self.gas_costs().modi)?;
let (a, b, imm) = modi.unpack();
let rhs = Word::from(imm);
self.alu_error(a.into(), Word::wrapping_rem, r!(b), rhs, rhs == 0)?;
}
Instruction::MOVE(move_) => {
self.gas_charge(self.gas_costs().move_op)?;
let (a, b) = move_.unpack();
self.alu_set(a.into(), r!(b))?;
}
Instruction::MOVI(movi) => {
self.gas_charge(self.gas_costs().movi)?;
let (a, imm) = movi.unpack();
self.alu_set(a.into(), Word::from(imm))?;
}
Instruction::MROO(mroo) => {
self.gas_charge(self.gas_costs().mroo)?;
let (a, b, c) = mroo.unpack();
let (lhs, rhs) = (r!(b), r!(c));
self.alu_error(
a.into(),
|l, r| {
checked_nth_root(l, r)
.expect("checked_nth_root returned None for valid values")
as Word
},
lhs,
rhs,
rhs == 0,
)?;
}
Instruction::MUL(mul) => {
self.gas_charge(self.gas_costs().mul)?;
let (a, b, c) = mul.unpack();
self.alu_capture_overflow(
a.into(),
u128::overflowing_mul,
r!(b).into(),
r!(c).into(),
)?;
}
Instruction::MULI(muli) => {
self.gas_charge(self.gas_costs().muli)?;
let (a, b, imm) = muli.unpack();
self.alu_capture_overflow(
a.into(),
u128::overflowing_mul,
r!(b).into(),
imm.into(),
)?;
}
Instruction::MLDV(mldv) => {
self.gas_charge(self.gas_costs().mldv)?;
let (a, b, c, d) = mldv.unpack();
self.alu_muldiv(a.into(), r!(b), r!(c), r!(d))?;
}
Instruction::NOOP(_noop) => {
self.gas_charge(self.gas_costs().noop)?;
self.alu_clear()?;
}
Instruction::NOT(not) => {
self.gas_charge(self.gas_costs().not)?;
let (a, b) = not.unpack();
self.alu_set(a.into(), !r!(b))?;
}
Instruction::OR(or) => {
self.gas_charge(self.gas_costs().or)?;
let (a, b, c) = or.unpack();
self.alu_set(a.into(), r!(b) | r!(c))?;
}
Instruction::ORI(ori) => {
self.gas_charge(self.gas_costs().ori)?;
let (a, b, imm) = ori.unpack();
self.alu_set(a.into(), r!(b) | Word::from(imm))?;
}
Instruction::SLL(sll) => {
self.gas_charge(self.gas_costs().sll)?;
let (a, b, c) = sll.unpack();
self.alu_set(
a.into(),
if let Ok(c) = r!(c).try_into() {
Word::checked_shl(r!(b), c).unwrap_or_default()
} else {
0
},
)?;
}
Instruction::SLLI(slli) => {
self.gas_charge(self.gas_costs().slli)?;
let (a, b, imm) = slli.unpack();
let rhs = u32::from(imm);
self.alu_set(a.into(), r!(b).checked_shl(rhs).unwrap_or_default())?;
}
Instruction::SRL(srl) => {
self.gas_charge(self.gas_costs().srl)?;
let (a, b, c) = srl.unpack();
self.alu_set(
a.into(),
if let Ok(c) = r!(c).try_into() {
Word::checked_shr(r!(b), c).unwrap_or_default()
} else {
0
},
)?;
}
Instruction::SRLI(srli) => {
self.gas_charge(self.gas_costs().srli)?;
let (a, b, imm) = srli.unpack();
let rhs = u32::from(imm);
self.alu_set(a.into(), r!(b).checked_shr(rhs).unwrap_or_default())?;
}
Instruction::SUB(sub) => {
self.gas_charge(self.gas_costs().sub)?;
let (a, b, c) = sub.unpack();
self.alu_capture_overflow(
a.into(),
u128::overflowing_sub,
r!(b).into(),
r!(c).into(),
)?;
}
Instruction::SUBI(subi) => {
self.gas_charge(self.gas_costs().subi)?;
let (a, b, imm) = subi.unpack();
self.alu_capture_overflow(
a.into(),
u128::overflowing_sub,
r!(b).into(),
imm.into(),
)?;
}
Instruction::XOR(xor) => {
self.gas_charge(self.gas_costs().xor)?;
let (a, b, c) = xor.unpack();
self.alu_set(a.into(), r!(b) ^ r!(c))?;
}
Instruction::XORI(xori) => {
self.gas_charge(self.gas_costs().xori)?;
let (a, b, imm) = xori.unpack();
self.alu_set(a.into(), r!(b) ^ Word::from(imm))?;
}
Instruction::JI(ji) => {
self.gas_charge(self.gas_costs().ji)?;
let imm = ji.unpack();
self.jump(JumpArgs::new(JumpMode::Absolute).to_address(imm.into()))?;
}
Instruction::JNEI(jnei) => {
self.gas_charge(self.gas_costs().jnei)?;
let (a, b, imm) = jnei.unpack();
self.jump(
JumpArgs::new(JumpMode::Absolute)
.with_condition(r!(a) != r!(b))
.to_address(imm.into()),
)?;
}
Instruction::JNZI(jnzi) => {
self.gas_charge(self.gas_costs().jnzi)?;
let (a, imm) = jnzi.unpack();
self.jump(
JumpArgs::new(JumpMode::Absolute)
.with_condition(r!(a) != 0)
.to_address(imm.into()),
)?;
}
Instruction::JMP(jmp) => {
self.gas_charge(self.gas_costs().jmp)?;
let a = jmp.unpack();
self.jump(JumpArgs::new(JumpMode::Absolute).to_address(r!(a)))?;
}
Instruction::JNE(jne) => {
self.gas_charge(self.gas_costs().jne)?;
let (a, b, c) = jne.unpack();
self.jump(
JumpArgs::new(JumpMode::Absolute)
.with_condition(r!(a) != r!(b))
.to_address(r!(c)),
)?;
}
Instruction::JMPF(jmpf) => {
self.gas_charge(self.gas_costs().jmpf)?;
let (a, offset) = jmpf.unpack();
self.jump(
JumpArgs::new(JumpMode::RelativeForwards)
.to_address(r!(a))
.plus_fixed(offset.into()),
)?;
}
Instruction::JMPB(jmpb) => {
self.gas_charge(self.gas_costs().jmpb)?;
let (a, offset) = jmpb.unpack();
self.jump(
JumpArgs::new(JumpMode::RelativeBackwards)
.to_address(r!(a))
.plus_fixed(offset.into()),
)?;
}
Instruction::JNZF(jnzf) => {
self.gas_charge(self.gas_costs().jnzf)?;
let (a, b, offset) = jnzf.unpack();
self.jump(
JumpArgs::new(JumpMode::RelativeForwards)
.with_condition(r!(a) != 0)
.to_address(r!(b))
.plus_fixed(offset.into()),
)?;
}
Instruction::JNZB(jnzb) => {
self.gas_charge(self.gas_costs().jnzb)?;
let (a, b, offset) = jnzb.unpack();
self.jump(
JumpArgs::new(JumpMode::RelativeBackwards)
.with_condition(r!(a) != 0)
.to_address(r!(b))
.plus_fixed(offset.into()),
)?;
}
Instruction::JNEF(jnef) => {
self.gas_charge(self.gas_costs().jnef)?;
let (a, b, c, offset) = jnef.unpack();
self.jump(
JumpArgs::new(JumpMode::RelativeForwards)
.with_condition(r!(a) != r!(b))
.to_address(r!(c))
.plus_fixed(offset.into()),
)?;
}
Instruction::JNEB(jneb) => {
self.gas_charge(self.gas_costs().jneb)?;
let (a, b, c, offset) = jneb.unpack();
self.jump(
JumpArgs::new(JumpMode::RelativeBackwards)
.with_condition(r!(a) != r!(b))
.to_address(r!(c))
.plus_fixed(offset.into()),
)?;
}
Instruction::RET(ret) => {
self.gas_charge(self.gas_costs().ret)?;
let a = ret.unpack();
let ra = r!(a);
self.ret(ra)?;
return Ok(ExecuteState::Return(ra))
}
Instruction::RETD(retd) => {
let (a, b) = retd.unpack();
let len = r!(b);
self.dependent_gas_charge(self.gas_costs().retd, len)?;
return Ok(self.ret_data(r!(a), len).map(ExecuteState::ReturnData)?)
}
Instruction::RVRT(rvrt) => {
self.gas_charge(self.gas_costs().rvrt)?;
let a = rvrt.unpack();
let ra = r!(a);
self.revert(ra)?;
return Ok(ExecuteState::Revert(ra))
}
Instruction::SMO(smo) => {
let (a, b, c, d) = smo.unpack();
self.dependent_gas_charge(self.gas_costs().smo, r!(c))?;
self.message_output(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::ALOC(aloc) => {
self.gas_charge(self.gas_costs().aloc)?;
let a = aloc.unpack();
self.malloc(r!(a))?;
}
Instruction::CFEI(cfei) => {
self.gas_charge(self.gas_costs().cfei)?;
let imm = cfei.unpack();
self.stack_pointer_overflow(Word::overflowing_add, imm.into())?;
}
Instruction::CFE(cfe) => {
self.gas_charge(self.gas_costs().cfei)?;
let a = cfe.unpack();
self.stack_pointer_overflow(Word::overflowing_add, r!(a))?;
}
Instruction::CFSI(cfsi) => {
self.gas_charge(self.gas_costs().cfsi)?;
let imm = cfsi.unpack();
self.stack_pointer_overflow(Word::overflowing_sub, imm.into())?;
}
Instruction::CFS(cfs) => {
self.gas_charge(self.gas_costs().cfsi)?;
let a = cfs.unpack();
self.stack_pointer_overflow(Word::overflowing_sub, r!(a))?;
}
Instruction::PSHL(pshl) => {
self.gas_charge(self.gas_costs().pshl)?;
let bitmask = pshl.unpack();
self.push_selected_registers(ProgramRegistersSegment::Low, bitmask)?;
}
Instruction::PSHH(pshh) => {
self.gas_charge(self.gas_costs().pshh)?;
let bitmask = pshh.unpack();
self.push_selected_registers(ProgramRegistersSegment::High, bitmask)?;
}
Instruction::POPL(popl) => {
self.gas_charge(self.gas_costs().popl)?;
let bitmask = popl.unpack();
self.pop_selected_registers(ProgramRegistersSegment::Low, bitmask)?;
}
Instruction::POPH(poph) => {
self.gas_charge(self.gas_costs().poph)?;
let bitmask = poph.unpack();
self.pop_selected_registers(ProgramRegistersSegment::High, bitmask)?;
}
Instruction::LB(lb) => {
self.gas_charge(self.gas_costs().lb)?;
let (a, b, imm) = lb.unpack();
self.load_byte(a.into(), r!(b), imm.into())?;
}
Instruction::LW(lw) => {
self.gas_charge(self.gas_costs().lw)?;
let (a, b, imm) = lw.unpack();
self.load_word(a.into(), r!(b), imm.into())?;
}
Instruction::MCL(mcl) => {
let (a, b) = mcl.unpack();
let len = r!(b);
self.dependent_gas_charge(self.gas_costs().mcl, len)?;
self.memclear(r!(a), len)?;
}
Instruction::MCLI(mcli) => {
let (a, imm) = mcli.unpack();
let len = Word::from(imm);
self.dependent_gas_charge(self.gas_costs().mcli, len)?;
self.memclear(r!(a), len)?;
}
Instruction::MCP(mcp) => {
let (a, b, c) = mcp.unpack();
let len = r!(c);
self.dependent_gas_charge(self.gas_costs().mcp, len)?;
self.memcopy(r!(a), r!(b), len)?;
}
Instruction::MCPI(mcpi) => {
let (a, b, imm) = mcpi.unpack();
let len = imm.into();
self.dependent_gas_charge(self.gas_costs().mcpi, len)?;
self.memcopy(r!(a), r!(b), len)?;
}
Instruction::MEQ(meq) => {
let (a, b, c, d) = meq.unpack();
let len = r!(d);
self.dependent_gas_charge(self.gas_costs().meq, len)?;
self.memeq(a.into(), r!(b), r!(c), len)?;
}
Instruction::SB(sb) => {
self.gas_charge(self.gas_costs().sb)?;
let (a, b, imm) = sb.unpack();
self.store_byte(r!(a), r!(b), imm.into())?;
}
Instruction::SW(sw) => {
self.gas_charge(self.gas_costs().sw)?;
let (a, b, imm) = sw.unpack();
self.store_word(r!(a), r!(b), imm.into())?;
}
Instruction::BAL(bal) => {
self.gas_charge(self.gas_costs().bal)?;
let (a, b, c) = bal.unpack();
self.contract_balance(a.into(), r!(b), r!(c))?;
}
Instruction::BHEI(bhei) => {
self.gas_charge(self.gas_costs().bhei)?;
let a = bhei.unpack();
self.block_height(a.into())?;
}
Instruction::BHSH(bhsh) => {
self.gas_charge(self.gas_costs().bhsh)?;
let (a, b) = bhsh.unpack();
self.block_hash(r!(a), r!(b))?;
}
Instruction::BURN(burn) => {
self.gas_charge(self.gas_costs().burn)?;
let (a, b) = burn.unpack();
self.burn(r!(a), r!(b))?;
}
Instruction::CALL(call) => {
let (a, b, c, d) = call.unpack();
self.prepare_call(a, b, c, d)?;
}
Instruction::CB(cb) => {
self.gas_charge(self.gas_costs().cb)?;
let a = cb.unpack();
self.block_proposer(r!(a))?;
}
Instruction::CCP(ccp) => {
let (a, b, c, d) = ccp.unpack();
self.code_copy(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::CROO(croo) => {
self.gas_charge(self.gas_costs().croo)?;
let (a, b) = croo.unpack();
self.code_root(r!(a), r!(b))?;
}
Instruction::CSIZ(csiz) => {
let (a, b) = csiz.unpack();
self.code_size(a.into(), r!(b))?;
}
Instruction::LDC(ldc) => {
let (a, b, c) = ldc.unpack();
self.load_contract_code(r!(a), r!(b), r!(c))?;
}
Instruction::LOG(log) => {
self.gas_charge(self.gas_costs().log)?;
let (a, b, c, d) = log.unpack();
self.log(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::LOGD(logd) => {
let (a, b, c, d) = logd.unpack();
self.dependent_gas_charge(self.gas_costs().logd, r!(d))?;
self.log_data(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::MINT(mint) => {
self.gas_charge(self.gas_costs().mint)?;
let (a, b) = mint.unpack();
self.mint(r!(a), r!(b))?;
}
Instruction::SCWQ(scwq) => {
let (a, b, c) = scwq.unpack();
self.dependent_gas_charge(self.gas_costs().scwq, r!(c))?;
self.state_clear_qword(r!(a), b.into(), r!(c))?;
}
Instruction::SRW(srw) => {
self.gas_charge(self.gas_costs().srw)?;
let (a, b, c) = srw.unpack();
self.state_read_word(a.into(), b.into(), r!(c))?;
}
Instruction::SRWQ(srwq) => {
let (a, b, c, d) = srwq.unpack();
self.dependent_gas_charge(self.gas_costs().srwq, r!(d))?;
self.state_read_qword(r!(a), b.into(), r!(c), r!(d))?;
}
Instruction::SWW(sww) => {
self.gas_charge(self.gas_costs().sww)?;
let (a, b, c) = sww.unpack();
self.state_write_word(r!(a), b.into(), r!(c))?;
}
Instruction::SWWQ(swwq) => {
let (a, b, c, d) = swwq.unpack();
self.dependent_gas_charge(self.gas_costs().swwq, r!(d))?;
self.state_write_qword(r!(a), b.into(), r!(c), r!(d))?;
}
Instruction::TIME(time) => {
self.gas_charge(self.gas_costs().time)?;
let (a, b) = time.unpack();
self.timestamp(a.into(), r!(b))?;
}
Instruction::ECK1(eck1) => {
self.gas_charge(self.gas_costs().eck1)?;
let (a, b, c) = eck1.unpack();
self.secp256k1_recover(r!(a), r!(b), r!(c))?;
}
Instruction::ECR1(ecr1) => {
self.gas_charge(self.gas_costs().ecr1)?;
let (a, b, c) = ecr1.unpack();
self.secp256r1_recover(r!(a), r!(b), r!(c))?;
}
Instruction::ED19(ed19) => {
self.gas_charge(self.gas_costs().ed19)?;
let (a, b, c) = ed19.unpack();
self.ed25519_verify(r!(a), r!(b), r!(c))?;
}
Instruction::K256(k256) => {
let (a, b, c) = k256.unpack();
let len = r!(c);
self.dependent_gas_charge(self.gas_costs().k256, len)?;
self.keccak256(r!(a), r!(b), len)?;
}
Instruction::S256(s256) => {
let (a, b, c) = s256.unpack();
let len = r!(c);
self.dependent_gas_charge(self.gas_costs().s256, len)?;
self.sha256(r!(a), r!(b), len)?;
}
Instruction::FLAG(flag) => {
self.gas_charge(self.gas_costs().flag)?;
let a = flag.unpack();
self.set_flag(r!(a))?;
}
Instruction::GM(gm) => {
self.gas_charge(self.gas_costs().gm)?;
let (a, imm) = gm.unpack();
self.metadata(a.into(), imm.into())?;
}
Instruction::GTF(gtf) => {
self.gas_charge(self.gas_costs().gtf)?;
let (a, b, imm) = gtf.unpack();
self.get_transaction_field(a.into(), r!(b), imm.into())?;
}
Instruction::TR(tr) => {
self.gas_charge(self.gas_costs().tr)?;
let (a, b, c) = tr.unpack();
self.transfer(r!(a), r!(b), r!(c))?;
}
Instruction::TRO(tro) => {
self.gas_charge(self.gas_costs().tro)?;
let (a, b, c, d) = tro.unpack();
self.transfer_output(r!(a), r!(b), r!(c), r!(d))?;
}
Instruction::ECAL(ecal) => {
let (a, b, c, d) = ecal.unpack();
self.external_call(a, b, c, d)?;
}
}
Ok(ExecuteState::Proceed)
}
}
fn checked_nth_root(target: u64, nth_root: u64) -> Option<u64> {
if nth_root == 0 {
return None
}
if nth_root == 1 || target <= 1 {
return Some(target)
}
if nth_root >= target || nth_root > 64 {
return Some(1)
}
let nth_root = u32::try_from(nth_root).expect("Never loses bits, checked above");
#[cfg(feature = "std")]
let powf = f64::powf;
#[cfg(not(feature = "std"))]
let powf = libm::pow;
#[allow(clippy::cast_possible_truncation)]
let guess = powf(target as f64, (nth_root as f64).recip()) as u64;
debug_assert!(guess != 0, "This should never occur for {{target, n}} > 1");
let is_nth_power_below_target = |v: u64| match v.checked_pow(nth_root) {
Some(pow) => target < pow,
None => true, };
if is_nth_power_below_target(guess) {
return Some(guess - 1)
}
if is_nth_power_below_target(guess + 1) {
return Some(guess)
}
Some(guess + 1)
}
#[cfg(test)]
mod tests;