essential-state-read-vm 0.7.0

The Essential state reading VM
Documentation
mod util;

use essential_state_read_vm::{
    asm::{self, Op},
    types::{convert::word_4_from_u8_32, ContentAddress},
    GasLimit, Vm,
};
use util::*;

#[tokio::test]
async fn state_read_3_42s() {
    let access = *test_access();
    let state = State::new(vec![(
        access
            .solution
            .this_data()
            .predicate_to_solve
            .contract
            .clone(),
        vec![
            (vec![0, 0, 0, 0], vec![42]),
            (vec![0, 0, 0, 1], vec![42]),
            (vec![0, 0, 0, 2], vec![42]),
        ],
    )]);
    let mut vm = Vm::default();
    let num_words = 3;
    let ops = &[
        asm::Stack::Push(num_words).into(),
        asm::StateMemory::AllocSlots.into(),
        asm::Stack::Push(0).into(), // Key0
        asm::Stack::Push(0).into(), // Key1
        asm::Stack::Push(0).into(), // Key2
        asm::Stack::Push(0).into(), // Key3
        asm::Stack::Push(4).into(), // key length
        asm::Stack::Push(num_words).into(),
        asm::Stack::Push(0).into(), // slot index
        asm::StateRead::KeyRange,
        asm::TotalControlFlow::Halt.into(),
    ];
    vm.exec_ops(ops, access, &state, &|_: &Op| 1, GasLimit::UNLIMITED)
        .await
        .unwrap();
    assert_eq!(&vm.state_memory[..], &[vec![42], vec![42], vec![42]]);
    assert_eq!(vm.state_memory.len(), 3);
}

#[tokio::test]
async fn state_read_some_none_some() {
    let access = *test_access();
    let state = State::new(vec![(
        access
            .solution
            .this_data()
            .predicate_to_solve
            .contract
            .clone(),
        vec![(vec![0, 0, 0, 0], vec![42]), (vec![0, 0, 0, 2], vec![42])],
    )]);
    let mut vm = Vm::default();
    let num_words = 3;
    let ops = &[
        asm::Stack::Push(num_words).into(),
        asm::StateMemory::AllocSlots.into(),
        asm::Stack::Push(0).into(), // Key0
        asm::Stack::Push(0).into(), // Key1
        asm::Stack::Push(0).into(), // Key2
        asm::Stack::Push(0).into(), // Key3
        asm::Stack::Push(4).into(), // key length
        asm::Stack::Push(num_words).into(),
        asm::Stack::Push(0).into(), // slot index
        asm::StateRead::KeyRange,
        asm::TotalControlFlow::Halt.into(),
    ];
    vm.exec_ops(ops, access, &state, &|_: &Op| 1, GasLimit::UNLIMITED)
        .await
        .unwrap();
    assert_eq!(&vm.state_memory[..], &[vec![42], vec![], vec![42]]);
    assert_eq!(vm.state_memory.len(), 3);
}

#[tokio::test]
async fn state_read_ext() {
    let ext_contract_addr = ContentAddress([0x12; 32]);
    let state = State::new(vec![(
        ext_contract_addr.clone(),
        vec![
            (vec![1, 2, 3, 4], vec![40]),
            (vec![1, 2, 3, 5], vec![41]),
            (vec![1, 2, 3, 6], vec![42]),
        ],
    )]);
    let mut vm = Vm::default();
    let num_words = 3;
    let [addr0, addr1, addr2, addr3] = word_4_from_u8_32(ext_contract_addr.0);
    let ops = &[
        asm::Stack::Push(num_words).into(),
        asm::StateMemory::AllocSlots.into(),
        asm::Stack::Push(addr0).into(),
        asm::Stack::Push(addr1).into(),
        asm::Stack::Push(addr2).into(),
        asm::Stack::Push(addr3).into(),
        asm::Stack::Push(1).into(), // Key0
        asm::Stack::Push(2).into(), // Key1
        asm::Stack::Push(3).into(), // Key2
        asm::Stack::Push(4).into(), // Key3
        asm::Stack::Push(4).into(), // key length
        asm::Stack::Push(num_words).into(),
        asm::Stack::Push(0).into(), // slot index
        asm::StateRead::KeyRangeExtern,
        asm::TotalControlFlow::Halt.into(),
    ];
    vm.exec_ops(
        ops,
        *test_access(),
        &state,
        &|_: &Op| 1,
        GasLimit::UNLIMITED,
    )
    .await
    .unwrap();
    assert_eq!(&vm.state_memory[..], &[vec![40], vec![41], vec![42]]);
    assert_eq!(vm.state_memory.len(), 3);
}

#[tokio::test]
async fn state_read_ext_nones() {
    let ext_contract_addr = ContentAddress([0x12; 32]);
    let state = State::new(vec![(ext_contract_addr.clone(), vec![])]);
    let mut vm = Vm::default();
    let num_words = 3;
    let [addr0, addr1, addr2, addr3] = word_4_from_u8_32(ext_contract_addr.0);
    let ops = &[
        asm::Stack::Push(num_words).into(),
        asm::StateMemory::AllocSlots.into(),
        asm::Stack::Push(addr0).into(),
        asm::Stack::Push(addr1).into(),
        asm::Stack::Push(addr2).into(),
        asm::Stack::Push(addr3).into(),
        asm::Stack::Push(1).into(), // Key0
        asm::Stack::Push(2).into(), // Key1
        asm::Stack::Push(3).into(), // Key2
        asm::Stack::Push(4).into(), // Key3
        asm::Stack::Push(4).into(), // key length
        asm::Stack::Push(num_words).into(),
        asm::Stack::Push(0).into(), // slot index
        asm::StateRead::KeyRangeExtern,
        asm::TotalControlFlow::Halt.into(),
    ];
    vm.exec_ops(
        ops,
        *test_access(),
        &state,
        &|_: &Op| 1,
        GasLimit::UNLIMITED,
    )
    .await
    .unwrap();
    assert!(vm.state_memory.iter().all(Vec::is_empty));
    assert_eq!(vm.state_memory.len(), 3);
}

#[tokio::test]
async fn state_read_various_size_values() {
    let access = *test_access();
    let state = State::new(vec![(
        access
            .solution
            .this_data()
            .predicate_to_solve
            .contract
            .clone(),
        vec![
            (vec![0, 0, 0, 0], vec![0; 2]),
            (vec![0, 0, 0, 1], vec![1; 22]),
            (vec![0, 0, 0, 2], vec![2; 14]),
            (vec![0, 0, 0, 4], vec![4; 12]),
        ],
    )]);
    let mut vm = Vm::default();
    let num_values = 5;
    let ops = &[
        asm::Stack::Push(num_values).into(),
        asm::StateMemory::AllocSlots.into(),
        asm::Stack::Push(0).into(), // Key0
        asm::Stack::Push(0).into(), // Key1
        asm::Stack::Push(0).into(), // Key2
        asm::Stack::Push(0).into(), // Key3
        asm::Stack::Push(4).into(), // key length
        asm::Stack::Push(num_values).into(),
        asm::Stack::Push(0).into(), // slot index
        asm::StateRead::KeyRange,
        asm::TotalControlFlow::Halt.into(),
    ];
    vm.exec_ops(ops, access, &state, &|_: &Op| 1, GasLimit::UNLIMITED)
        .await
        .unwrap();
    assert_eq!(
        &vm.state_memory[..],
        &[vec![0; 2], vec![1; 22], vec![2; 14], vec![], vec![4; 12]]
    );
    assert_eq!(vm.state_memory.len(), 5);
}

#[tokio::test]
async fn state_read_various_key_sizes() {
    let access = *test_access();
    let state = State::new(vec![(
        access
            .solution
            .this_data()
            .predicate_to_solve
            .contract
            .clone(),
        vec![
            (vec![0], vec![0; 2]),
            (vec![0, 1], vec![7; 6]),
            (vec![1], vec![1; 22]),
            (vec![0, 0, 0, 0, 0, 2], vec![2; 14]),
            (vec![4; 1000], vec![4; 12]),
        ],
    )]);
    let mut vm = Vm::default();
    let ops = &[
        asm::Stack::Push(5).into(),
        asm::StateMemory::AllocSlots.into(),
        asm::Stack::Push(0).into(), // Key0
        asm::Stack::Push(1).into(), // key length
        asm::Stack::Push(3).into(), // num keys
        asm::Stack::Push(0).into(), // slot index
        asm::StateRead::KeyRange,
        asm::Stack::Push(0).into(),
        asm::Stack::Push(0).into(),
        asm::Stack::Push(0).into(),
        asm::Stack::Push(0).into(),
        asm::Stack::Push(0).into(),
        asm::Stack::Push(2).into(),
        asm::Stack::Push(6).into(), // key length
        asm::Stack::Push(2).into(), // num keys
        asm::Stack::Push(3).into(), // slot index
        asm::StateRead::KeyRange,
        asm::TotalControlFlow::Halt.into(),
    ];
    vm.exec_ops(ops, access, &state, &|_: &Op| 1, GasLimit::UNLIMITED)
        .await
        .unwrap();
    assert_eq!(
        &vm.state_memory[..],
        &[vec![0; 2], vec![1; 22], vec![], vec![2; 14], vec![]]
    );
    assert_eq!(vm.state_memory.len(), 5);
}

// TODO: Test slot index overflow
#[tokio::test]
async fn state_read_slot_index_overflow() {
    let access = *test_access();
    let state = State::new(vec![(
        access
            .solution
            .this_data()
            .predicate_to_solve
            .contract
            .clone(),
        vec![(vec![0], vec![0; 2])],
    )]);
    let mut vm = Vm::default();
    let ops = &[
        asm::Stack::Push(1).into(),
        asm::StateMemory::AllocSlots.into(),
        asm::Stack::Push(0).into(), // Key0
        asm::Stack::Push(1).into(), // key length
        asm::Stack::Push(1).into(), // num keys
        asm::Stack::Push(1).into(), // slot index
        asm::StateRead::KeyRange,
        asm::TotalControlFlow::Halt.into(),
    ];
    vm.exec_ops(ops, access, &state, &|_: &Op| 1, GasLimit::UNLIMITED)
        .await
        .unwrap_err();
}