use std::collections::{BTreeMap, BTreeSet};
use aluvm::data::{ByteStr, Number};
use aluvm::reg::{Reg32, RegA, RegAFR, RegS};
use aluvm::Vm;
use crate::validation::OpInfo;
use crate::vm::{AluScript, EntryPoint};
use crate::OpFullType;
pub struct AluRuntime<'script> {
script: &'script AluScript,
}
impl<'script> AluRuntime<'script> {
pub fn new(script: &'script AluScript) -> Self { AluRuntime { script } }
pub fn run_validations(&self, info: &OpInfo) -> Result<(), String> {
let mut regs = RegSetup::default();
match info.ty {
OpFullType::Genesis => {
regs.nums
.insert((RegAFR::A(RegA::A16), Reg32::Reg1), (info.subschema as u8).into());
self.run(EntryPoint::ValidateGenesis, ®s, info)?;
}
OpFullType::StateTransition(ty) => {
self.run(EntryPoint::ValidateTransition(ty), ®s, info)?;
}
OpFullType::StateExtension(ty) => {
self.run(EntryPoint::ValidateExtension(ty), ®s, info)?;
}
}
for ty in info.global.keys() {
self.run(EntryPoint::ValidateGlobalState(*ty), ®s, info)?;
}
let used_state = info
.owned_state
.types()
.iter()
.chain(info.prev_state.keys())
.copied()
.collect::<BTreeSet<_>>();
for ty in used_state {
self.run(EntryPoint::ValidateGlobalState(ty), ®s, info)?;
}
Ok(())
}
fn run(&self, entry: EntryPoint, regs: &RegSetup, info: &OpInfo) -> Result<(), String> {
let mut vm = Vm::new();
for ((reg, idx), val) in ®s.nums {
vm.registers.set(*reg, *idx, *val);
}
for (reg, val) in ®s.data {
vm.registers.set_s(
*reg,
Some(
ByteStr::try_from(val.as_slice()).expect("state must be less than 2^16 bytes"),
),
);
}
match self.script.entry_points.get(&entry) {
Some(site) => match vm.call(self.script, *site, info) {
true => Ok(()),
false => Err(vm
.registers
.get_s(0)
.and_then(|bs| String::from_utf8(bs.to_vec()).ok())
.unwrap_or_else(|| s!("unspecified error"))),
},
None => Ok(()),
}
}
}
#[derive(Debug, Default)]
struct RegSetup {
pub nums: BTreeMap<(RegAFR, Reg32), Number>,
pub data: BTreeMap<RegS, Vec<u8>>,
}