mod util;
use essential_asm::{Compute, Word};
use essential_vm::{
asm::{self, Op},
Gas, GasLimit, Vm,
};
use util::*;
type PreComputeGas = Gas;
type ComputeGas = Gas;
type PostComputeGas = Gas;
fn compute_ops(ops: &[Op]) -> (PreComputeGas, ComputeGas, PostComputeGas) {
let op_gas_cost = &|_: &Op| 1;
let compute_index = ops
.iter()
.position(|&op| op == Op::Compute(Compute::Compute))
.unwrap();
let compute_end_index = ops
.iter()
.position(|&op| op == Op::Compute(Compute::ComputeEnd))
.unwrap();
let pre_compute_gas = ops[..=compute_index].iter().map(op_gas_cost).sum::<Gas>();
let compute_gas = ops[compute_index + 1..=compute_end_index]
.iter()
.map(op_gas_cost)
.sum::<Gas>();
let post_compute_gas = ops[compute_end_index + 1..]
.iter()
.map(op_gas_cost)
.sum::<Gas>();
(pre_compute_gas, compute_gas, post_compute_gas)
}
#[test]
fn test_compute_memory() {
let mut vm = Vm::default();
let compute_breadth = 1000;
let ops = &[
asm::Stack::Push(41).into(),
asm::Stack::Push(1).into(),
asm::Memory::Alloc.into(),
asm::Memory::Store.into(),
asm::Stack::Push(compute_breadth).into(),
asm::Compute::Compute.into(),
asm::Stack::Push(1).into(), asm::Memory::Alloc.into(),
asm::Memory::Store.into(), asm::Compute::ComputeEnd.into(),
asm::Stack::Push(42).into(),
asm::Stack::Push(1).into(),
asm::Memory::Alloc.into(),
asm::Memory::Store.into(),
];
let op_gas_cost = &|_: &Op| 1;
let spent = vm
.exec_ops(
ops,
test_access().clone(),
&State::EMPTY,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
let (pre_compute_gas, compute_gas, post_compute_gas) = compute_ops(ops);
let expected_spent = pre_compute_gas + compute_breadth as u64 * compute_gas + post_compute_gas;
assert_eq!(vm.pc, ops.len());
assert_eq!(
&vm.memory[..],
vec![41]
.into_iter()
.chain(0..compute_breadth)
.chain(std::iter::once(42))
.collect::<Vec<Word>>()
);
assert!(vm.parent_memory.is_empty());
assert!(&vm.stack.is_empty());
assert_eq!(spent, expected_spent);
}
#[test]
fn test_compute_stack() {
let mut vm = Vm::default();
let compute_breadth = 1000;
let ops = &[
asm::Stack::Push(41).into(),
asm::Stack::Push(compute_breadth).into(),
asm::Compute::Compute.into(),
asm::Stack::Pop.into(), asm::Stack::Push(40).into(),
asm::Compute::ComputeEnd.into(),
asm::Stack::Push(42).into(),
];
let op_gas_cost = &|_: &Op| 1;
let spent = vm
.exec_ops(
ops,
test_access().clone(),
&State::EMPTY,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
let (pre_compute_gas, compute_gas, post_compute_gas) = compute_ops(ops);
let expected_spent = pre_compute_gas + compute_breadth as u64 * compute_gas + post_compute_gas;
assert_eq!(vm.pc, ops.len());
assert!(&vm.memory.is_empty());
assert!(vm.parent_memory.is_empty());
assert_eq!(&vm.stack[..], &[41, 42]);
assert_eq!(spent, expected_spent);
}
#[test]
fn test_compute_end() {
let mut vm = Vm::default();
let compute_breadth = 1000;
let ops = &[
asm::Stack::Push(compute_breadth).into(),
asm::Compute::Compute.into(),
asm::Stack::Push(1).into(), asm::Memory::Alloc.into(),
asm::Memory::Store.into(), ];
let op_gas_cost = &|_: &Op| 1;
let spent = vm
.exec_ops(
ops,
test_access().clone(),
&State::EMPTY,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
let pre_compute_gas = ops[..2].iter().map(op_gas_cost).sum::<Gas>();
let compute_gas = ops[2..].iter().map(op_gas_cost).sum::<Gas>();
let expected_spent = pre_compute_gas + compute_breadth as u64 * compute_gas;
assert_eq!(vm.pc, ops.len());
assert_eq!(&vm.memory[..], (0..compute_breadth).collect::<Vec<Word>>());
assert!(vm.parent_memory.is_empty());
assert!(&vm.stack.is_empty());
assert_eq!(spent, expected_spent);
}
#[test]
fn test_compute_halt() {
let mut vm = Vm::default();
let compute_breadth = 1000;
let ops = &[
asm::Stack::Push(compute_breadth).into(),
asm::Compute::Compute.into(),
asm::TotalControlFlow::Halt.into(),
asm::Stack::Push(42).into(),
];
let op_gas_cost = &|_: &Op| 1;
let spent = vm
.exec_ops(
ops,
test_access().clone(),
&State::EMPTY,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
let pre_compute_gas = ops[..2].iter().map(op_gas_cost).sum::<Gas>();
let halt_op = &ops[2];
let compute_gas = op_gas_cost(halt_op);
let parent_halt_gas = op_gas_cost(halt_op);
let expected_spent = pre_compute_gas + compute_breadth as u64 * compute_gas + parent_halt_gas;
assert_eq!(vm.pc, ops.len() - 2);
assert!(&vm.memory.is_empty());
assert!(vm.parent_memory.is_empty());
assert!(&vm.stack.is_empty());
assert_eq!(spent, expected_spent);
}