use super::{
ExecutableTransaction,
Interpreter,
Memory,
MemoryInstance,
RuntimeBalances,
};
use crate::{
constraints::reg_key::*,
context::Context,
error::SimpleResult,
};
use fuel_asm::{
Flags,
Instruction,
PanicReason,
RegId,
};
use fuel_tx::{
Output,
field::Outputs,
};
use fuel_types::{
AssetId,
BlockHeight,
Bytes32,
ContractId,
Word,
canonical::Serialize,
};
use core::ops::Range;
#[cfg(test)]
mod message_tests;
#[cfg(test)]
mod tests;
impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
where
M: Memory,
Tx: ExecutableTransaction,
{
pub(crate) fn update_memory_output(&mut self, idx: usize) -> SimpleResult<()> {
let tx_offset = self.tx_offset();
update_memory_output(&self.tx, self.memory.as_mut(), tx_offset, idx)
}
}
pub(crate) fn set_variable_output<Tx: ExecutableTransaction>(
tx: &mut Tx,
memory: &mut MemoryInstance,
tx_offset: usize,
idx: usize,
variable: Output,
) -> SimpleResult<()> {
tx.replace_variable_output(idx, variable)?;
update_memory_output(tx, memory, tx_offset, idx)
}
fn absolute_output_offset<Tx: Outputs>(
tx: &Tx,
tx_offset: usize,
idx: usize,
) -> Option<usize> {
tx.outputs_offset_at(idx)
.map(|offset| tx_offset.saturating_add(offset))
}
pub(crate) fn absolute_output_mem_range<Tx: Outputs>(
tx: &Tx,
tx_offset: usize,
idx: usize,
) -> Option<Range<usize>> {
let offset = absolute_output_offset(tx, tx_offset, idx)?;
let size = tx.outputs().get(idx)?.size();
Some(offset..offset.saturating_add(size))
}
pub(crate) fn update_memory_output<Tx: ExecutableTransaction>(
tx: &Tx,
memory: &mut MemoryInstance,
tx_offset: usize,
idx: usize,
) -> SimpleResult<()> {
let range = absolute_output_mem_range(tx, tx_offset, idx)
.ok_or(PanicReason::OutputNotFound)?;
let mut mem = memory.write_noownerchecks(range.start, range.len())?;
let output = tx
.outputs()
.get(idx)
.expect("Invalid output index; checked above");
output
.encode(&mut mem)
.expect("Unable to write output into given memory range");
Ok(())
}
impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
where
M: Memory,
{
pub(crate) fn set_flag(&mut self, a: Word) -> SimpleResult<()> {
let (SystemRegisters { flag, pc, .. }, _) = split_registers(&mut self.registers);
set_flag(flag, pc, a)
}
pub const fn context(&self) -> &Context {
&self.context
}
pub(crate) fn internal_contract(&self) -> Result<ContractId, PanicReason> {
internal_contract(&self.context, self.registers.fp(), self.memory.as_ref())
}
pub(crate) fn get_block_height(&self) -> Result<BlockHeight, PanicReason> {
self.context()
.block_height()
.ok_or(PanicReason::TransactionValidity)
}
pub(crate) fn write_user_register(
&mut self,
reg: RegId,
val: Word,
) -> SimpleResult<()> {
if reg == RegId::ZERO {
return Ok(());
}
if reg < RegId::WRITABLE {
return Err(PanicReason::ReservedRegisterNotWritable.into());
}
self.registers[reg] = val;
Ok(())
}
pub(crate) fn write_user_register_legacy(
&mut self,
reg: RegId,
val: Word,
) -> SimpleResult<()> {
if reg < RegId::WRITABLE {
return Err(PanicReason::ReservedRegisterNotWritable.into());
}
self.registers[reg] = val;
Ok(())
}
}
pub(crate) fn clear_err(mut err: RegMut<ERR>) {
*err = 0;
}
pub(crate) fn set_err(mut err: RegMut<ERR>) {
*err = 1;
}
pub(crate) fn set_flag(
mut flag: RegMut<FLAG>,
pc: RegMut<PC>,
a: Word,
) -> SimpleResult<()> {
let Some(flags) = Flags::from_bits(a) else {
return Err(PanicReason::InvalidFlags.into())
};
*flag = flags.bits();
inc_pc(pc);
Ok(())
}
pub(crate) fn inc_pc(mut pc: RegMut<PC>) {
*pc = pc.saturating_add(Instruction::SIZE as Word);
}
pub(crate) fn tx_id(memory: &MemoryInstance) -> Bytes32 {
Bytes32::new(memory.read_bytes(0u64).expect("Bytes32::LEN < MEM_SIZE"))
}
pub(crate) fn base_asset_balance_sub(
base_asset_id: &AssetId,
balances: &mut RuntimeBalances,
memory: &mut MemoryInstance,
value: Word,
) -> SimpleResult<()> {
external_asset_id_balance_sub(balances, memory, base_asset_id, value)
}
pub(crate) fn external_asset_id_balance_sub(
balances: &mut RuntimeBalances,
memory: &mut MemoryInstance,
asset_id: &AssetId,
value: Word,
) -> SimpleResult<()> {
balances
.checked_balance_sub(memory, asset_id, value)
.ok_or(PanicReason::NotEnoughBalance)?;
Ok(())
}
pub(crate) fn current_contract(
context: &Context,
fp: Reg<FP>,
memory: &MemoryInstance,
) -> Result<Option<ContractId>, PanicReason> {
if context.is_internal() {
Ok(Some(ContractId::new(memory.read_bytes(*fp)?)))
} else {
Ok(None)
}
}
pub(crate) fn internal_contract(
context: &Context,
fp: Reg<FP>,
memory: &MemoryInstance,
) -> Result<ContractId, PanicReason> {
current_contract(context, fp, memory)?.ok_or(PanicReason::ExpectedInternalContext)
}
pub(crate) fn set_frame_pointer(
context: &mut Context,
mut register: RegMut<FP>,
fp: Word,
) {
context.update_from_frame_pointer(fp);
*register = fp;
}