use crate::consts::*;
use crate::contract::Contract;
use crate::crypto;
use crate::error::InterpreterError;
use crate::interpreter::{Interpreter, MemoryRange};
use crate::prelude::*;
use crate::state::{ExecuteState, ProgramState, StateTransitionRef};
use crate::storage::InterpreterStorage;
use fuel_asm::{InstructionResult, PanicReason};
use fuel_tx::{Input, Output, Receipt, Transaction};
use fuel_types::bytes::SerializableVec;
use fuel_types::Word;
impl<S> Interpreter<S>
where
S: InterpreterStorage,
{
pub(crate) fn run(&mut self) -> Result<ProgramState, InterpreterError> {
let mut state: ProgramState;
match &self.tx {
Transaction::Create {
salt, static_contracts, ..
} => {
if static_contracts
.iter()
.any(|id| !self.check_contract_exists(id).unwrap_or(false))
{
Err(InterpreterError::Panic(PanicReason::ContractNotFound))?
}
let contract = Contract::try_from(&self.tx)?;
let root = contract.root();
let id = contract.id(salt, &root);
if !&self
.tx
.outputs()
.iter()
.any(|output| matches!(output, Output::ContractCreated { contract_id } if contract_id == &id))
{
Err(InterpreterError::Panic(PanicReason::ContractNotInInputs))?;
}
self.storage
.storage_contract_insert(&id, &contract)
.map_err(InterpreterError::from_io)?;
self.storage
.storage_contract_root_insert(&id, salt, &root)
.map_err(InterpreterError::from_io)?;
let predicates: Vec<MemoryRange> = self
.tx
.inputs()
.iter()
.enumerate()
.filter_map(|(i, input)| match input {
Input::Coin { predicate, .. } if !predicate.is_empty() => self
.tx
.input_coin_predicate_offset(i)
.map(|ofs| (ofs as Word, predicate.len() as Word)),
_ => None,
})
.map(|(ofs, len)| (ofs + VM_TX_MEMORY as Word, len))
.map(|(ofs, len)| MemoryRange::new(ofs, len))
.collect();
state = ProgramState::Return(1);
for predicate in predicates {
state = self.verify_predicate(&predicate)?;
#[cfg(feature = "debug")]
if state.is_debug() {
return Ok(state);
}
}
}
Transaction::Script { .. } => {
let offset = (VM_TX_MEMORY + Transaction::script_offset()) as Word;
self.registers[REG_PC] = offset;
self.registers[REG_IS] = offset;
self.registers[REG_GGAS] = self.tx.gas_limit();
self.registers[REG_CGAS] = self.tx.gas_limit();
let program = self.run_program();
let gas_used = self.tx.gas_limit() - self.registers[REG_GGAS];
let (status, program) = match program {
Ok(s) => (InstructionResult::success(), s),
Err(e) => match e.instruction_result() {
Some(result) => {
self.append_panic_receipt(*result);
self.apply_revert();
(*result, ProgramState::Revert(0))
}
None => {
return Err(e);
}
},
};
let receipt = Receipt::script_result(status, gas_used);
self.receipts.push(receipt);
state = program;
}
}
#[cfg(feature = "debug")]
if state.is_debug() {
self.debugger_set_last_state(state.clone());
}
if self.tx.receipts_root().is_some() {
let receipts_root = if self.receipts().is_empty() {
EMPTY_RECEIPTS_MERKLE_ROOT.into()
} else {
crypto::ephemeral_merkle_root(self.receipts().iter().map(|r| r.clone().to_bytes()))
};
self.tx.set_receipts_root(receipts_root);
}
Ok(state)
}
pub(crate) fn run_call(&mut self) -> Result<ProgramState, RuntimeError> {
loop {
if self.registers[REG_PC] >= VM_MAX_RAM {
return Err(PanicReason::MemoryOverflow.into());
}
let state = self
.execute()
.map_err(|e| e.panic_reason().expect("Call routine should return only VM panic"))?;
match state {
ExecuteState::Return(r) => {
return Ok(ProgramState::Return(r));
}
ExecuteState::ReturnData(d) => {
return Ok(ProgramState::ReturnData(d));
}
ExecuteState::Revert(r) => {
return Ok(ProgramState::Revert(r));
}
ExecuteState::Proceed => (),
#[cfg(feature = "debug")]
ExecuteState::DebugEvent(d) => {
return Ok(ProgramState::RunProgram(d));
}
}
}
}
pub(crate) fn run_program(&mut self) -> Result<ProgramState, InterpreterError> {
loop {
if self.registers[REG_PC] >= VM_MAX_RAM {
return Err(InterpreterError::Panic(PanicReason::MemoryOverflow));
}
match self.execute()? {
ExecuteState::Return(r) => {
return Ok(ProgramState::Return(r));
}
ExecuteState::ReturnData(d) => {
return Ok(ProgramState::ReturnData(d));
}
ExecuteState::Revert(r) => {
return Ok(ProgramState::Revert(r));
}
ExecuteState::Proceed => (),
#[cfg(feature = "debug")]
ExecuteState::DebugEvent(d) => {
return Ok(ProgramState::RunProgram(d));
}
}
}
}
pub fn transact_owned(storage: S, tx: Transaction) -> Result<StateTransition, InterpreterError> {
Interpreter::with_storage(storage)
.transact(tx)
.map(|st| st.into_owned())
}
pub fn transact(&mut self, tx: Transaction) -> Result<StateTransitionRef<'_>, InterpreterError> {
let state_result = self.init(tx).and_then(|_| self.run());
#[cfg(feature = "profile-any")]
self.profiler.on_transaction(&state_result);
let state = state_result?;
let transition = StateTransitionRef::new(state, self.transaction(), self.receipts());
Ok(transition)
}
}