use crate::{
error::{
InterpreterError,
IoResult,
RuntimeError,
},
interpreter::{
EcalHandler,
ExecutableTransaction,
Interpreter,
Memory,
},
state::ExecuteState,
storage::InterpreterStorage,
verification::Verifier,
};
use fuel_asm::{
Opcode,
PanicInstruction,
PanicReason,
RawInstruction,
RegId,
};
impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
where
M: Memory,
S: InterpreterStorage,
Tx: ExecutableTransaction,
Ecal: EcalHandler,
V: Verifier,
{
pub fn execute<const PREDICATE: bool>(
&mut self,
) -> Result<ExecuteState, InterpreterError<S::DataError>> {
let raw_instruction = self.fetch_instruction()?;
self.instruction_per_inner::<PREDICATE>(raw_instruction)
}
fn fetch_instruction(&self) -> Result<[u8; 4], InterpreterError<S::DataError>> {
let pc = self.registers[RegId::PC];
let raw_instruction: [u8; 4] =
self.memory().read_bytes(pc).map_err(|reason| {
InterpreterError::PanicInstruction(PanicInstruction::error(
reason,
0, ))
})?;
if pc < self.registers[RegId::IS] || pc >= self.registers[RegId::SSP] {
return Err(InterpreterError::PanicInstruction(PanicInstruction::error(
PanicReason::MemoryNotExecutable,
RawInstruction::from_be_bytes(raw_instruction),
)))
}
Ok(raw_instruction)
}
pub fn instruction<R, const PREDICATE: bool>(
&mut self,
raw: R,
) -> Result<ExecuteState, InterpreterError<S::DataError>>
where
R: Into<RawInstruction> + Copy,
{
let raw = raw.into();
let raw = raw.to_be_bytes();
self.instruction_per_inner::<PREDICATE>(raw)
}
fn instruction_per_inner<const PREDICATE: bool>(
&mut self,
raw: [u8; 4],
) -> 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::<PREDICATE>(raw).map_err(|e| {
InterpreterError::from_runtime(e, RawInstruction::from_be_bytes(raw))
})
}
fn instruction_inner<const PREDICATE: bool>(
&mut self,
raw: [u8; 4],
) -> IoResult<ExecuteState, S::DataError> {
let opcode = Opcode::try_from(raw[0])
.map_err(|_| RuntimeError::from(PanicReason::InvalidInstruction))?;
if PREDICATE && !opcode.is_predicate_allowed() {
return Err(PanicReason::ContractInstructionNotAllowed.into())
}
execute_instruction(self, opcode, [raw[1], raw[2], raw[3]])
}
}
pub trait Execute<M, S, Tx, Ecal, V>
where
M: Memory,
S: InterpreterStorage,
Tx: ExecutableTransaction,
Ecal: EcalHandler,
V: Verifier,
{
fn execute(
self,
interpreter: &mut Interpreter<M, S, Tx, Ecal, V>,
) -> IoResult<ExecuteState, S::DataError>;
}
fn execute_instruction<M, S, Tx, Ecal, V>(
interpreter: &mut Interpreter<M, S, Tx, Ecal, V>,
opcode: Opcode,
raw_args: [u8; 3],
) -> IoResult<ExecuteState, S::DataError>
where
M: Memory,
S: InterpreterStorage,
Tx: ExecutableTransaction,
Ecal: EcalHandler,
V: Verifier,
{
macro_rules! execute_op {
($op:ident) => {
fuel_asm::op::$op::from_raw_args(raw_args)
.map_err(|_| RuntimeError::from(PanicReason::InvalidInstruction))?
.execute(interpreter)
};
}
match opcode {
Opcode::ADD => execute_op!(ADD),
Opcode::AND => execute_op!(AND),
Opcode::DIV => execute_op!(DIV),
Opcode::EQ => execute_op!(EQ),
Opcode::EXP => execute_op!(EXP),
Opcode::GT => execute_op!(GT),
Opcode::LT => execute_op!(LT),
Opcode::MLOG => execute_op!(MLOG),
Opcode::MROO => execute_op!(MROO),
Opcode::MOD => execute_op!(MOD),
Opcode::MOVE => execute_op!(MOVE),
Opcode::MUL => execute_op!(MUL),
Opcode::NOT => execute_op!(NOT),
Opcode::OR => execute_op!(OR),
Opcode::SLL => execute_op!(SLL),
Opcode::SRL => execute_op!(SRL),
Opcode::SUB => execute_op!(SUB),
Opcode::XOR => execute_op!(XOR),
Opcode::MLDV => execute_op!(MLDV),
Opcode::NIOP => execute_op!(NIOP),
Opcode::RET => execute_op!(RET),
Opcode::RETD => execute_op!(RETD),
Opcode::ALOC => execute_op!(ALOC),
Opcode::MCL => execute_op!(MCL),
Opcode::MCP => execute_op!(MCP),
Opcode::MEQ => execute_op!(MEQ),
Opcode::BHSH => execute_op!(BHSH),
Opcode::BHEI => execute_op!(BHEI),
Opcode::BURN => execute_op!(BURN),
Opcode::CALL => execute_op!(CALL),
Opcode::CCP => execute_op!(CCP),
Opcode::CROO => execute_op!(CROO),
Opcode::CSIZ => execute_op!(CSIZ),
Opcode::CB => execute_op!(CB),
Opcode::LDC => execute_op!(LDC),
Opcode::LOG => execute_op!(LOG),
Opcode::LOGD => execute_op!(LOGD),
Opcode::MINT => execute_op!(MINT),
Opcode::RVRT => execute_op!(RVRT),
Opcode::SCWQ => execute_op!(SCWQ),
Opcode::SRW => execute_op!(SRW),
Opcode::SRWQ => execute_op!(SRWQ),
Opcode::SWW => execute_op!(SWW),
Opcode::SWWQ => execute_op!(SWWQ),
Opcode::TR => execute_op!(TR),
Opcode::TRO => execute_op!(TRO),
Opcode::ECK1 => execute_op!(ECK1),
Opcode::ECR1 => execute_op!(ECR1),
Opcode::ED19 => execute_op!(ED19),
Opcode::K256 => execute_op!(K256),
Opcode::S256 => execute_op!(S256),
Opcode::TIME => execute_op!(TIME),
Opcode::NOOP => execute_op!(NOOP),
Opcode::FLAG => execute_op!(FLAG),
Opcode::BAL => execute_op!(BAL),
Opcode::JMP => execute_op!(JMP),
Opcode::JNE => execute_op!(JNE),
Opcode::SMO => execute_op!(SMO),
Opcode::ADDI => execute_op!(ADDI),
Opcode::ANDI => execute_op!(ANDI),
Opcode::DIVI => execute_op!(DIVI),
Opcode::EXPI => execute_op!(EXPI),
Opcode::MODI => execute_op!(MODI),
Opcode::MULI => execute_op!(MULI),
Opcode::ORI => execute_op!(ORI),
Opcode::SLLI => execute_op!(SLLI),
Opcode::SRLI => execute_op!(SRLI),
Opcode::SUBI => execute_op!(SUBI),
Opcode::XORI => execute_op!(XORI),
Opcode::JNEI => execute_op!(JNEI),
Opcode::LB => execute_op!(LB),
Opcode::LQW => execute_op!(LQW),
Opcode::LHW => execute_op!(LHW),
Opcode::LW => execute_op!(LW),
Opcode::SB => execute_op!(SB),
Opcode::SQW => execute_op!(SQW),
Opcode::SHW => execute_op!(SHW),
Opcode::SW => execute_op!(SW),
Opcode::MCPI => execute_op!(MCPI),
Opcode::GTF => execute_op!(GTF),
Opcode::MCLI => execute_op!(MCLI),
Opcode::GM => execute_op!(GM),
Opcode::MOVI => execute_op!(MOVI),
Opcode::JNZI => execute_op!(JNZI),
Opcode::JMPF => execute_op!(JMPF),
Opcode::JMPB => execute_op!(JMPB),
Opcode::JNZF => execute_op!(JNZF),
Opcode::JNZB => execute_op!(JNZB),
Opcode::JNEF => execute_op!(JNEF),
Opcode::JNEB => execute_op!(JNEB),
Opcode::JI => execute_op!(JI),
Opcode::CFEI => execute_op!(CFEI),
Opcode::CFSI => execute_op!(CFSI),
Opcode::CFE => execute_op!(CFE),
Opcode::CFS => execute_op!(CFS),
Opcode::PSHL => execute_op!(PSHL),
Opcode::PSHH => execute_op!(PSHH),
Opcode::POPL => execute_op!(POPL),
Opcode::POPH => execute_op!(POPH),
Opcode::JAL => execute_op!(JAL),
Opcode::WDCM => execute_op!(WDCM),
Opcode::WQCM => execute_op!(WQCM),
Opcode::WDOP => execute_op!(WDOP),
Opcode::WQOP => execute_op!(WQOP),
Opcode::WDML => execute_op!(WDML),
Opcode::WQML => execute_op!(WQML),
Opcode::WDDV => execute_op!(WDDV),
Opcode::WQDV => execute_op!(WQDV),
Opcode::WDMD => execute_op!(WDMD),
Opcode::WQMD => execute_op!(WQMD),
Opcode::WDAM => execute_op!(WDAM),
Opcode::WQAM => execute_op!(WQAM),
Opcode::WDMM => execute_op!(WDMM),
Opcode::WQMM => execute_op!(WQMM),
Opcode::ECAL => execute_op!(ECAL),
Opcode::BSIZ => execute_op!(BSIZ),
Opcode::BLDD => execute_op!(BLDD),
Opcode::ECOP => execute_op!(ECOP),
Opcode::EPAR => execute_op!(EPAR),
Opcode::SCLR => execute_op!(SCLR),
Opcode::SRDD => execute_op!(SRDD),
Opcode::SRDI => execute_op!(SRDI),
Opcode::SWRD => execute_op!(SWRD),
Opcode::SWRI => execute_op!(SWRI),
Opcode::SUPD => execute_op!(SUPD),
Opcode::SUPI => execute_op!(SUPI),
Opcode::SPLD => execute_op!(SPLD),
}
}
pub 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, clippy::cast_sign_loss)]
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.saturating_sub(1))
}
let guess_plus_one = guess.checked_add(1).expect(
"Guess cannot be u64::MAX, as we have taken a root > 2 of a value to get it",
);
if is_nth_power_below_target(guess_plus_one) {
return Some(guess)
}
Some(guess_plus_one)
}
#[cfg(test)]
mod tests;