use super::Interpreter;
use crate::consts::*;
use crate::error::RuntimeError;
use crate::storage::InterpreterStorage;
use fuel_asm::PanicReason;
use fuel_tx::{consts::CONTRACT_MAX_SIZE, Input};
use fuel_types::{bytes, Address, Bytes32, Bytes8, Color, ContractId, RegisterId, Word};
use std::mem;
const WORD_SIZE: usize = mem::size_of::<Word>();
impl<S> Interpreter<S>
where
S: InterpreterStorage,
{
pub(crate) fn coinbase(&self) -> Result<Address, RuntimeError> {
self.storage.coinbase().map_err(RuntimeError::from_io)
}
pub(crate) fn load_contract_code(&mut self, a: Word, b: Word, c: Word) -> Result<(), RuntimeError> {
let ssp = self.registers[REG_SSP];
let sp = self.registers[REG_SP];
let hp = self.registers[REG_HP];
let fp = self.registers[REG_FP];
let id_addr = a as usize; let start_in_contract = b as usize; let length_to_copy_unpadded = c;
if ssp + length_to_copy_unpadded > hp
|| (id_addr + ContractId::LEN) as u64 > VM_MAX_RAM
|| length_to_copy_unpadded > CONTRACT_MAX_SIZE.min(MEM_MAX_ACCESS_SIZE)
{
return Err(PanicReason::MemoryOverflow.into());
}
if ssp != sp {
return Err(PanicReason::ExpectedUnallocatedStack.into());
}
let contract_id = unsafe {
let bytes = core::slice::from_raw_parts(self.memory.as_ptr().add(id_addr), ContractId::LEN);
ContractId::as_ref_unchecked(bytes)
};
if !self.tx.input_contracts().any(|id| id == contract_id) {
return Err(PanicReason::ContractNotInInputs.into());
};
let cow_contract = self.contract(contract_id)?;
let contract = cow_contract.as_ref().as_ref();
let contract_len = contract.len();
let padded_len = bytes::padded_len_usize(length_to_copy_unpadded as usize);
let padding_len = padded_len - (length_to_copy_unpadded as usize);
let end_in_contract = (start_in_contract + padded_len).min(contract_len);
let copy_len = end_in_contract - start_in_contract;
unsafe {
let code = core::slice::from_raw_parts(contract.as_ptr().add(start_in_contract), copy_len);
self.push_stack(&code)?;
}
self.push_stack(&[0; core::mem::size_of::<Word>()][..padding_len])?;
self.registers[REG_SP] = ssp + (padded_len as u64);
let offset_in_frame = ContractId::LEN + Color::LEN + WORD_SIZE * VM_REGISTER_COUNT;
let start = (fp as usize) + offset_in_frame;
let old = Word::from_be_bytes(unsafe {
bytes::from_slice_unchecked::<WORD_SIZE>(&self.memory[start..start + WORD_SIZE])
});
let new = ((old as usize) + padded_len) as Word;
self.memory[start..start + WORD_SIZE].copy_from_slice(&new.to_be_bytes());
self.inc_pc()
}
pub(crate) fn burn(&mut self, a: Word) -> Result<(), RuntimeError> {
let (c, cx) = self.internal_contract_bounds()?;
let contract = unsafe { ContractId::as_ref_unchecked(&self.memory[c..cx]) };
let color = unsafe { Color::as_ref_unchecked(&self.memory[c..cx]) };
let balance = self.balance(contract, color)?;
let balance = balance.checked_sub(a).ok_or(PanicReason::NotEnoughBalance)?;
self.storage
.merkle_contract_color_balance_insert(contract, color, balance)
.map_err(RuntimeError::from_io)?;
self.inc_pc()
}
pub(crate) fn mint(&mut self, a: Word) -> Result<(), RuntimeError> {
let (c, cx) = self.internal_contract_bounds()?;
let contract = unsafe { ContractId::as_ref_unchecked(&self.memory[c..cx]) };
let color = unsafe { Color::as_ref_unchecked(&self.memory[c..cx]) };
let balance = self.balance(contract, color)?;
let balance = balance.checked_add(a).ok_or(PanicReason::ArithmeticOverflow)?;
self.storage
.merkle_contract_color_balance_insert(contract, color, balance)
.map_err(RuntimeError::from_io)?;
self.inc_pc()
}
pub(crate) fn code_copy(&mut self, a: Word, b: Word, c: Word, d: Word) -> Result<(), RuntimeError> {
if d > MEM_MAX_ACCESS_SIZE
|| a > VM_MAX_RAM - d
|| b > VM_MAX_RAM - ContractId::LEN as Word
|| c > VM_MAX_RAM - d
{
return Err(PanicReason::MemoryOverflow.into());
}
let (a, b, c, d) = (a as usize, b as usize, c as usize, d as usize);
let bx = b + ContractId::LEN;
let cd = c + d;
let contract = unsafe { ContractId::as_ref_unchecked(&self.memory[b..bx]) };
if !self
.tx
.inputs()
.iter()
.any(|input| matches!(input, Input::Contract { contract_id, .. } if contract_id == contract))
{
return Err(PanicReason::ContractNotInInputs.into());
}
let contract = self.contract(contract)?.into_owned();
if contract.as_ref().len() < d {
self.try_zeroize(a, d)?;
} else {
self.try_mem_write(a, &contract.as_ref()[c..cd])?;
}
self.inc_pc()
}
pub(crate) fn block_hash(&mut self, a: Word, b: Word) -> Result<(), RuntimeError> {
let hash = self.storage.block_hash(b as u32).map_err(|e| e.into())?;
self.try_mem_write(a as usize, hash.as_ref())?;
self.inc_pc()
}
pub(crate) fn block_proposer(&mut self, a: Word) -> Result<(), RuntimeError> {
self.coinbase()
.and_then(|data| self.try_mem_write(a as usize, data.as_ref()))?;
self.inc_pc()
}
pub(crate) fn code_root(&mut self, a: Word, b: Word) -> Result<(), RuntimeError> {
if a > VM_MAX_RAM - Bytes32::LEN as Word || b > VM_MAX_RAM - ContractId::LEN as Word {
return Err(PanicReason::MemoryOverflow.into());
}
let (a, b) = (a as usize, b as usize);
let contract_id = unsafe { ContractId::as_ref_unchecked(&self.memory[b..b + ContractId::LEN]) };
let (_, root) = self
.storage
.storage_contract_root(contract_id)
.transpose()
.ok_or(PanicReason::ContractNotFound)?
.map_err(RuntimeError::from_io)?
.into_owned();
self.try_mem_write(a, root.as_ref())?;
self.inc_pc()
}
pub(crate) fn code_size(&mut self, ra: RegisterId, b: Word) -> Result<(), RuntimeError> {
Self::is_register_writable(ra)?;
if b > VM_MAX_RAM - ContractId::LEN as Word {
return Err(PanicReason::MemoryOverflow.into());
}
let b = b as usize;
let contract_id = unsafe { ContractId::as_ref_unchecked(&self.memory[b..b + ContractId::LEN]) };
self.registers[ra] = self.contract(contract_id)?.as_ref().as_ref().len() as Word;
self.inc_pc()
}
pub(crate) fn state_read_word(&mut self, ra: RegisterId, b: Word) -> Result<(), RuntimeError> {
if b > VM_MAX_RAM - Bytes32::LEN as Word {
return Err(PanicReason::MemoryOverflow.into());
}
let b = b as usize;
let contract = self.internal_contract()?;
let key = unsafe { Bytes32::as_ref_unchecked(&self.memory[b..b + Bytes32::LEN]) };
self.registers[ra] = self
.storage
.merkle_contract_state(contract, key)
.map_err(RuntimeError::from_io)?
.map(|state| unsafe { Bytes8::from_slice_unchecked(state.as_ref().as_ref()).into() })
.map(Word::from_be_bytes)
.unwrap_or(0);
self.inc_pc()
}
pub(crate) fn state_read_qword(&mut self, a: Word, b: Word) -> Result<(), RuntimeError> {
if a > VM_MAX_RAM - Bytes32::LEN as Word || b > VM_MAX_RAM - Bytes32::LEN as Word {
return Err(PanicReason::MemoryOverflow.into());
}
let (a, b) = (a as usize, b as usize);
let contract = self.internal_contract()?;
let key = unsafe { Bytes32::as_ref_unchecked(&self.memory[b..b + Bytes32::LEN]) };
let state = self
.storage
.merkle_contract_state(contract, key)
.map_err(RuntimeError::from_io)?
.map(|s| s.into_owned())
.unwrap_or_default();
self.try_mem_write(a, state.as_ref())?;
self.inc_pc()
}
pub(crate) fn state_write_word(&mut self, a: Word, b: Word) -> Result<(), RuntimeError> {
if a > VM_MAX_RAM - Bytes32::LEN as Word {
return Err(PanicReason::MemoryOverflow.into());
}
let a = a as usize;
let (c, cx) = self.internal_contract_bounds()?;
let contract = unsafe { ContractId::as_ref_unchecked(&self.memory[c..cx]) };
let key = unsafe { Bytes32::as_ref_unchecked(&self.memory[a..a + Bytes32::LEN]) };
let mut value = Bytes32::default();
(&mut value[..WORD_SIZE]).copy_from_slice(&b.to_be_bytes());
self.storage
.merkle_contract_state_insert(contract, key, &value)
.map_err(RuntimeError::from_io)?;
self.inc_pc()
}
pub(crate) fn state_write_qword(&mut self, a: Word, b: Word) -> Result<(), RuntimeError> {
if a > VM_MAX_RAM - Bytes32::LEN as Word || b > VM_MAX_RAM - Bytes32::LEN as Word {
return Err(PanicReason::MemoryOverflow.into());
}
let (a, b) = (a as usize, b as usize);
let (c, cx) = self.internal_contract_bounds()?;
let contract = unsafe { ContractId::as_ref_unchecked(&self.memory[c..cx]) };
let key = unsafe { Bytes32::as_ref_unchecked(&self.memory[a..a + Bytes32::LEN]) };
let value = unsafe { Bytes32::as_ref_unchecked(&self.memory[b..b + Bytes32::LEN]) };
self.storage
.merkle_contract_state_insert(contract, key, value)
.map_err(RuntimeError::from_io)?;
self.inc_pc()
}
}