fuel-vm 0.58.2

FuelVM interpreter.
Documentation
#![allow(clippy::cast_possible_truncation)]
use alloc::{
    vec,
    vec::Vec,
};

use core::{
    convert::Infallible,
    ops::Range,
};

use crate::{
    context::Context,
    storage::{
        ContractsStateData,
        MemoryStorage,
    },
};
use test_case::test_case;

use super::*;

mod scwq;
mod srwq;
mod swwq;

fn mem(chains: &[&[u8]]) -> MemoryInstance {
    let mut vec: Vec<_> = chains.iter().flat_map(|i| i.iter().copied()).collect();
    vec.resize(MEM_SIZE, 0);
    vec.into()
}
const fn key(k: u8) -> [u8; 32] {
    [
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, k,
    ]
}

fn data(value: &[u8]) -> ContractsStateData {
    ContractsStateData::from(value)
}

impl OwnershipRegisters {
    pub fn test(stack: Range<u64>, heap: Range<u64>) -> Self {
        Self {
            sp: stack.end,
            ssp: stack.start,
            hp: heap.start,
            prev_hp: heap.end,
        }
    }
}

#[test_case(false, 0, None, 32 => Ok((0, 0)); "Nothing set")]
#[test_case(false, 0, 29, 32 => Ok((29, 1)); "29 set")]
#[test_case(false, 0, 0, 32 => Ok((0, 1)); "zero set")]
#[test_case(true, 0, None, 32 => Err(RuntimeError::Recoverable(PanicReason::ExpectedInternalContext)); "Can't read state from external context")]
#[test_case(false, 1, 29, 32 => Ok((0, 0)); "Wrong contract id")]
#[test_case(false, 0, 29, 33 => Ok((0, 0)); "Wrong key")]
#[test_case(true, 0, None, Word::MAX => Err(RuntimeError::Recoverable(PanicReason::MemoryOverflow)); "Overflowing key")]
#[test_case(true, 0, None, VM_MAX_RAM => Err(RuntimeError::Recoverable(PanicReason::MemoryOverflow)); "Overflowing key ram")]
fn test_state_read_word(
    external: bool,
    fp: Word,
    insert: impl Into<Option<Word>>,
    key: Word,
) -> Result<(Word, Word), RuntimeError<Infallible>> {
    let mut storage = MemoryStorage::default();
    let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
    memory[0..ContractId::LEN].copy_from_slice(&[3u8; ContractId::LEN][..]);
    memory[32..64].copy_from_slice(&[4u8; 32][..]);
    let is = 4;
    let mut cgas = 1000;
    let mut ggas = 1000;
    let mut pc = 4;
    let mut result = 0;
    let mut got_result = 0;
    let context = if external {
        Context::Script {
            block_height: Default::default(),
        }
    } else {
        Context::Call {
            block_height: Default::default(),
        }
    };

    if let Some(insert) = insert.into() {
        let fp = 0;
        let context = Context::Call {
            block_height: Default::default(),
        };
        let input = StateWriteWordCtx {
            storage: &mut storage,
            memory: &mut memory,
            context: &context,
            profiler: &mut Profiler::default(),
            new_storage_gas_per_byte: 1,
            current_contract: None,
            cgas: RegMut::new(&mut cgas),
            ggas: RegMut::new(&mut ggas),
            is: Reg::new(&is),
            fp: Reg::new(&fp),
            pc: RegMut::new(&mut pc),
        };
        state_write_word(input, 32, &mut 0, insert)?;
    }
    let mut pc = 4;

    let input = StateReadWordCtx {
        storage: &mut storage,
        memory: &mut memory,
        context: &context,
        fp: Reg::new(&fp),
        pc: RegMut::new(&mut pc),
    };
    state_read_word(input, &mut result, &mut got_result, key)?;

    assert_eq!(pc, 8);
    Ok((result, got_result))
}

#[test_case(false, 0, false, 32 => Ok(1); "Nothing set")]
#[test_case(false, 0, true, 32 => Ok(0); "Something set")]
#[test_case(true, 0, false, 32 => Err(RuntimeError::Recoverable(PanicReason::ExpectedInternalContext)); "Can't write state from external context")]
#[test_case(false, 1, false, 32 => Ok(1); "Wrong contract id")]
#[test_case(false, 0, false, 33 => Ok(1); "Wrong key")]
#[test_case(false, 1, true, 32 => Ok(1); "Wrong contract id with existing")]
#[test_case(false, 0, true, 33 => Ok(1); "Wrong key with existing")]
#[test_case(true, 0, false, Word::MAX => Err(RuntimeError::Recoverable(PanicReason::MemoryOverflow)); "Overflowing key")]
#[test_case(true, 0, false, VM_MAX_RAM => Err(RuntimeError::Recoverable(PanicReason::MemoryOverflow)); "Overflowing key ram")]
fn test_state_write_word(
    external: bool,
    fp: Word,
    insert: bool,
    key: Word,
) -> Result<Word, RuntimeError<Infallible>> {
    let mut storage = MemoryStorage::default();
    let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
    memory[0..ContractId::LEN].copy_from_slice(&[3u8; ContractId::LEN][..]);
    memory[32..64].copy_from_slice(&[4u8; 32][..]);
    let mut pc = 4;
    let mut result = 0;
    let context = if external {
        Context::Script {
            block_height: Default::default(),
        }
    } else {
        Context::Call {
            block_height: Default::default(),
        }
    };

    let is = 4;
    let mut cgas = 1000;
    let mut ggas = 1000;

    if insert {
        let fp = 0;
        let context = Context::Call {
            block_height: Default::default(),
        };
        let input = StateWriteWordCtx {
            storage: &mut storage,
            memory: &mut memory,
            context: &context,
            profiler: &mut Profiler::default(),
            new_storage_gas_per_byte: 1,
            current_contract: None,
            cgas: RegMut::new(&mut cgas),
            ggas: RegMut::new(&mut ggas),
            is: Reg::new(&is),
            fp: Reg::new(&fp),
            pc: RegMut::new(&mut pc),
        };
        state_write_word(input, 32, &mut 0, 20)?;
    }
    let mut pc = 4;

    let input = StateWriteWordCtx {
        storage: &mut storage,
        memory: &mut memory,
        context: &context,
        new_storage_gas_per_byte: 1,
        current_contract: None,
        profiler: &mut Profiler::default(),
        cgas: RegMut::new(&mut cgas),
        ggas: RegMut::new(&mut ggas),
        is: Reg::new(&is),
        fp: Reg::new(&fp),
        pc: RegMut::new(&mut pc),
    };
    state_write_word(input, key, &mut result, 30)?;

    assert_eq!(pc, 8);
    Ok(result)
}