use crate::{
error::{ComputeError, ExecError, MemoryError, OpError, OpResult},
Access, Gas, GasLimit, LazyCache, Memory, Op, OpAccess, OpGasCost, Repeat, Stack, StateReads,
Vm,
};
use rayon::prelude::*;
use std::sync::Arc;
pub const MAX_COMPUTE_DEPTH: usize = 1;
pub struct ComputeInputs<'a, S, OA, OG> {
pub pc: usize,
pub stack: &'a mut Stack,
pub memory: &'a mut Memory,
pub parent_memory: Vec<Arc<Memory>>,
pub halt: bool,
pub repeat: &'a Repeat,
pub cache: Arc<LazyCache>,
pub access: Access,
pub state_reads: &'a S,
pub op_access: OA,
pub op_gas_cost: &'a OG,
pub gas_limit: GasLimit,
}
pub fn compute<S, OA, OG>(
inputs: ComputeInputs<S, OA, OG>,
) -> OpResult<(usize, Gas, bool), S::Error>
where
S: StateReads,
OA: OpAccess<Op = Op>,
OA::Error: Into<OpError<S::Error>>,
OG: OpGasCost,
{
let ComputeInputs {
pc,
stack,
memory,
mut parent_memory,
halt,
repeat,
cache,
access,
state_reads,
op_access,
op_gas_cost,
gas_limit,
} = inputs;
let compute_breadth = stack
.pop()
.map_err(|e| OpError::Compute(ComputeError::Stack(e)))?;
if compute_breadth < 1 {
return Err(OpError::Compute(ComputeError::<S::Error>::InvalidBreadth(
compute_breadth,
)));
}
if parent_memory.len() < MAX_COMPUTE_DEPTH {
parent_memory.push(Arc::new(memory.to_owned()));
} else {
return Err(ComputeError::DepthReached(MAX_COMPUTE_DEPTH).into());
}
let results: Result<Vec<(Gas, usize, Memory, bool)>, _> = (0..compute_breadth)
.into_par_iter()
.map(|compute_index| {
let mut stack = stack.clone();
stack
.push(compute_index)
.map_err(|e| ExecError(pc, OpError::Compute(e.into())))?;
let mut vm = Vm {
pc: pc + 1,
stack,
memory: Memory::new(),
parent_memory: parent_memory.clone(),
repeat: repeat.clone(),
cache: cache.clone(),
..Default::default()
};
vm.exec(
access.clone(),
state_reads,
op_access.clone(),
op_gas_cost,
gas_limit,
)
.map(|gas| (gas, vm.pc, vm.memory, vm.halt))
})
.collect();
let oks = results.map_err(|e| OpError::Compute(ComputeError::Exec(Box::new(e))))?;
let (pc, total_gas, halt) = compute_effects(memory, pc, halt, oks)?;
parent_memory.pop();
Ok((pc, total_gas, halt))
}
fn compute_effects(
memory: &mut Memory,
mut pc: usize,
mut halt: bool,
compute_results: Vec<(Gas, usize, Memory, bool)>,
) -> Result<(usize, Gas, bool), MemoryError> {
let mut total_gas = 0;
let mut memory_to_alloc = 0;
compute_results
.iter()
.for_each(|(_, _, mem, _)| memory_to_alloc += mem.len().unwrap_or_default());
let mut memory_pointer = memory.len().expect("memory has to have length");
memory.alloc(memory_to_alloc)?;
compute_results.iter().for_each(|(gas, c_pc, mem, h)| {
pc = std::cmp::max(pc, *c_pc);
total_gas += gas;
memory.store_range(memory_pointer, mem).expect("for now");
memory_pointer += mem.len().unwrap();
halt |= h;
});
Ok((pc, total_gas, halt))
}