use core::result::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Fault {
StackUnderflow,
StackOverflow,
GasExhausted,
AssertFailed,
GuardTrap,
CallStackOverflow,
CallStackUnderflow,
InvalidMemoryAccess,
}
const STACK_SIZE: usize = 256;
const MEMORY_SIZE: usize = 65536;
const CALL_STACK_SIZE: usize = 32;
pub struct FluxVM {
stack: [u8; STACK_SIZE],
sp: usize,
pc: usize,
gas: u32,
halted: bool,
yielded: bool,
memory: [u8; MEMORY_SIZE],
call_stack: [usize; CALL_STACK_SIZE],
csp: usize,
guard_reg: u8,
last_check_passed: bool,
}
impl FluxVM {
pub fn new(gas: u32) -> Self {
Self {
stack: [0u8; STACK_SIZE],
sp: 0,
pc: 0,
gas,
halted: false,
yielded: false,
memory: [0u8; MEMORY_SIZE],
call_stack: [0usize; CALL_STACK_SIZE],
csp: 0,
guard_reg: 0,
last_check_passed: true,
}
}
fn push(&mut self, value: u8) -> Result<(), Fault> {
if self.sp >= STACK_SIZE { return Err(Fault::StackOverflow); }
self.stack[self.sp] = value;
self.sp += 1;
Ok(())
}
fn pop(&mut self) -> Result<u8, Fault> {
if self.sp == 0 { return Err(Fault::StackUnderflow); }
self.sp -= 1;
Ok(self.stack[self.sp])
}
fn peek(&self) -> Result<u8, Fault> {
if self.sp == 0 { return Err(Fault::StackUnderflow); }
Ok(self.stack[self.sp - 1])
}
fn read_byte(&self, bytecode: &[u8]) -> Result<u8, Fault> {
bytecode.get(self.pc).copied().ok_or(Fault::InvalidMemoryAccess)
}
fn binop<F: FnOnce(u8, u8) -> u8>(&mut self, f: F) -> Result<(), Fault> {
let b = self.pop()?;
let a = self.pop()?;
self.push(f(a, b))
}
fn cmpop<F: FnOnce(u8, u8) -> bool>(&mut self, f: F) -> Result<(), Fault> {
let b = self.pop()?;
let a = self.pop()?;
self.push(if f(a, b) { 1 } else { 0 })
}
pub fn step(&mut self, bytecode: &[u8]) -> Result<bool, Fault> {
if self.halted { return Ok(true); }
if self.yielded { self.yielded = false; }
if self.gas == 0 { return Err(Fault::GasExhausted); }
if self.pc >= bytecode.len() { return Ok(true); }
let op = bytecode[self.pc];
self.pc += 1;
self.gas -= 1;
match op {
0x00 => { let v = self.read_byte(bytecode)?;
self.pc += 1;
self.push(v)?;
}
0x01 => { self.pop()?; } 0x02 => { let v = self.peek()?;
self.push(v)?;
}
0x03 => { let b = self.pop()?;
let a = self.pop()?;
self.push(b)?;
self.push(a)?;
}
0x04 => { let addr = self.read_byte(bytecode)? as usize;
self.pc += 1;
let v = *self.memory.get(addr).ok_or(Fault::InvalidMemoryAccess)?;
self.push(v)?;
}
0x05 => { let addr = self.read_byte(bytecode)? as usize;
self.pc += 1;
let v = self.pop()?;
*self.memory.get_mut(addr).ok_or(Fault::InvalidMemoryAccess)? = v;
}
0x06 => self.binop(|a, b| a.wrapping_add(b))?, 0x07 => self.binop(|a, b| a.wrapping_sub(b))?, 0x08 => self.binop(|a, b| a.wrapping_mul(b))?,
0x09 => self.binop(|a, b| a & b)?, 0x0A => self.binop(|a, b| a | b)?, 0x0B => self.binop(|a, b| a ^ b)?, 0x0C => { let a = self.pop()?; self.push(!a)?; } 0x0D => { let a = self.pop()?; self.push(a << 1)?; } 0x0E => { let a = self.pop()?; self.push(a >> 1)?; }
0x0F => self.cmpop(|a, b| a == b)?, 0x10 => self.cmpop(|a, b| a != b)?, 0x11 => self.cmpop(|a, b| a < b)?, 0x12 => self.cmpop(|a, b| a > b)?, 0x13 => self.cmpop(|a, b| a <= b)?, 0x14 => self.cmpop(|a, b| a >= b)?,
0x15 => { let addr = self.read_byte(bytecode)? as usize;
self.pc = addr;
}
0x16 => { let addr = self.read_byte(bytecode)? as usize;
self.pc += 1; let v = self.pop()?;
if v == 0 { self.pc = addr; }
}
0x17 => { let addr = self.read_byte(bytecode)? as usize;
self.pc += 1;
let v = self.pop()?;
if v != 0 { self.pc = addr; }
}
0x18 => { let addr = self.read_byte(bytecode)? as usize;
self.pc += 1;
if self.csp >= CALL_STACK_SIZE { return Err(Fault::CallStackOverflow); }
self.call_stack[self.csp] = self.pc;
self.csp += 1;
self.pc = addr;
}
0x19 => { if self.csp == 0 { return Err(Fault::CallStackUnderflow); }
self.csp -= 1;
self.pc = self.call_stack[self.csp];
}
0x1A => { self.halted = true; } 0x1B => { let v = self.pop()?;
self.last_check_passed = v != 0;
if v == 0 { return Err(Fault::AssertFailed); }
}
0x1C => { let mask = self.read_byte(bytecode)?;
self.pc += 1;
let v = self.pop()?;
let result = v & mask;
self.last_check_passed = result != 0;
self.push(result)?;
}
0x1D => { let lo = self.read_byte(bytecode)?;
self.pc += 1;
let hi = self.read_byte(bytecode)?;
self.pc += 1;
let v = self.pop()?;
let in_range = v >= lo && v <= hi;
self.last_check_passed = in_range;
self.push(if in_range { 1 } else { 0 })?;
}
0x1E => { self.push(self.guard_reg)?;
}
0x1F => { let _b3 = self.pop()?;
let _b2 = self.pop()?;
let _b1 = self.pop()?;
let _b0 = self.pop()?;
self.last_check_passed = true;
self.push(1)?;
}
0x20 => { return Err(Fault::GuardTrap); }
0x21 => { let mut acc: u8 = 0;
for i in 0..self.sp {
acc ^= self.stack[i];
}
self.push(acc)?;
}
0x22 => { let hi = self.read_byte(bytecode)?;
let lo = self.read_byte(bytecode)?;
self.pc += 2;
self.push(hi)?;
self.push(lo)?;
}
0x23 => { let b = self.pop()?;
let a = self.pop()?;
let xnor = !(a ^ b);
let count = xnor.count_ones() as u8;
self.push(count)?;
}
0x24 => self.cmpop(|a, b| a >= b)?, 0x25 => { let b = self.pop()?;
let a = self.pop()?;
self.push(if a < b { 1 } else { 0 })?;
}
0x26 => { let addr = self.read_byte(bytecode)? as usize;
self.pc += 1;
if !self.last_check_passed { self.pc = addr; }
}
0x27 => {} 0x28 => { self.sp = 0; } 0x29 => { self.yielded = true; }
_ => {} }
Ok(self.halted)
}
pub fn execute(&mut self, bytecode: &[u8], max_steps: usize) -> Result<(), Vec<Fault>> {
for _ in 0..max_steps {
match self.step(bytecode) {
Ok(true) => return Ok(()),
Ok(false) => continue,
Err(f) => return Err(vec![f]),
}
}
Ok(())
}
pub fn is_halted(&self) -> bool { self.halted }
pub fn is_yielded(&self) -> bool { self.yielded }
pub fn stack_top(&self) -> Option<u8> {
if self.sp > 0 { Some(self.stack[self.sp - 1]) } else { None }
}
pub fn stack_len(&self) -> usize { self.sp }
pub fn gas_remaining(&self) -> u32 { self.gas }
pub fn pc(&self) -> usize { self.pc }
pub fn get_memory(&self, addr: usize) -> Option<u8> { self.memory.get(addr).copied() }
pub fn set_memory(&mut self, addr: usize, val: u8) { if addr < MEMORY_SIZE { self.memory[addr] = val; } }
pub fn set_guard(&mut self, val: u8) { self.guard_reg = val; }
pub fn last_check_passed(&self) -> bool { self.last_check_passed }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_push_add_halt() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 3, 0x00, 4, 0x06, 0x1A], 100).unwrap();
assert!(vm.halted);
assert_eq!(vm.stack[0], 7);
}
#[test]
fn test_assert_fail() {
let mut vm = FluxVM::new(100);
let result = vm.execute(&[0x00, 0, 0x1B], 100);
assert!(result.is_err());
}
#[test]
fn test_guard_trap() {
let mut vm = FluxVM::new(100);
let result = vm.execute(&[0x20], 100);
assert!(matches!(result, Err(f) if f[0] == Fault::GuardTrap));
}
#[test]
fn test_gas_exhaustion() {
let mut vm = FluxVM::new(2);
let result = vm.execute(&[0x00, 1, 0x00, 2, 0x00, 3], 100);
assert!(matches!(result, Err(f) if f[0] == Fault::GasExhausted));
}
#[test]
fn test_jump_control_flow() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0, 0x16, 7, 0x00, 99, 0x1A, 0x00, 42, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 42);
}
#[test]
fn test_dup() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 7, 0x02, 0x1A], 100).unwrap();
assert_eq!(vm.stack_len(), 2);
assert_eq!(vm.stack_top(), Some(7));
}
#[test]
fn test_swap() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 3, 0x00, 5, 0x03, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(3)); }
#[test]
fn test_memory_load_store() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 42, 0x05, 100, 0x04, 100, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(42));
}
#[test]
fn test_shl_shr() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 1, 0x0D, 0x0D, 0x0E, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(2));
}
#[test]
fn test_lte_gte() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 5, 0x00, 5, 0x13, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(1));
}
#[test]
fn test_jnz() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 1, 0x17, 7, 0x00, 99, 0x1A, 0x00, 42, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(42)); }
#[test]
fn test_call_ret() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x18, 5, 0x1A, 0x00, 42, 0x19, 0x1A], 100).unwrap();
assert!(vm.is_halted());
}
#[test]
fn test_check_domain() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0x42, 0x1C, 0x0F, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(0x02));
assert!(vm.last_check_passed());
}
#[test]
fn test_bitmask_range() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 50, 0x1D, 0, 100, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(1));
assert!(vm.last_check_passed());
}
#[test]
fn test_bitmask_range_fail() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 200, 0x1D, 0, 100, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(0));
assert!(!vm.last_check_passed());
}
#[test]
fn test_xnor_popcount() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0xFF, 0x00, 0xFF, 0x23, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(8));
}
#[test]
fn test_carry_lt() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 3, 0x00, 5, 0x25, 0x1A], 100).unwrap();
assert_eq!(vm.stack_top(), Some(1));
}
#[test]
fn test_jfail() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 50, 0x1D, 0, 100, 0x26, 10, 0x00, 77, 0x1A, 0x20], 100).unwrap();
assert_eq!(vm.stack_top(), Some(77)); }
#[test]
fn test_flush() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 1, 0x00, 2, 0x00, 3, 0x28, 0x00, 42, 0x1A], 100).unwrap();
assert_eq!(vm.stack_len(), 1);
assert_eq!(vm.stack_top(), Some(42));
}
#[test]
fn test_yield() {
let mut vm = FluxVM::new(100);
vm.step(&[0x29]).unwrap(); assert!(vm.is_yielded());
assert!(!vm.is_halted());
}
#[test]
fn test_load_guard() {
let mut vm = FluxVM::new(100);
vm.set_guard(0xAB);
vm.execute(&[0x1E, 0x1A], 100).unwrap(); assert_eq!(vm.stack_top(), Some(0xAB));
}
#[test]
fn cert_identity() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 42, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 42);
}
#[test]
fn cert_add() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 3, 0x00, 4, 0x06, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 7);
}
#[test]
fn cert_mul() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 6, 0x00, 7, 0x08, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 42);
}
#[test]
fn cert_sub() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 10, 0x00, 3, 0x07, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 7);
}
#[test]
fn cert_and_mask() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0xFF, 0x00, 0x0F, 0x09, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 0x0F);
}
#[test]
fn cert_or() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0xF0, 0x00, 0x0F, 0x0A, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 0xFF);
}
#[test]
fn cert_xor() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0xAA, 0x00, 0x55, 0x0B, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 0xFF);
}
#[test]
fn cert_not() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0x00, 0x0C, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 0xFF);
}
#[test]
fn cert_eq() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 7, 0x00, 7, 0x0F, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 1);
}
#[test]
fn cert_neq() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 3, 0x00, 5, 0x10, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 1);
}
#[test]
fn cert_lt() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 3, 0x00, 5, 0x11, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 1);
}
#[test]
fn cert_gt() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 5, 0x00, 3, 0x12, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 1);
}
#[test]
fn cert_jz_skip() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0, 0x16, 7, 0x00, 99, 0x1A, 0x00, 42, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 42);
}
#[test]
fn cert_assert_pass() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 1, 0x1B, 0x00, 77, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 77);
}
#[test]
fn cert_nops() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x27, 0x27, 0x00, 13, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 13);
}
#[test]
fn test_bitwise_and_mask() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0xFF, 0x00, 0x0F, 0x09, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 0x0F);
}
#[test]
fn test_domain_check_pass() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0x42, 0x00, 0x0F, 0x09, 0x00, 0, 0x0F, 0x0C, 0x1B, 0x1A], 100).unwrap();
assert!(vm.is_halted());
}
#[test]
fn test_xor_swap() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 0xAA, 0x00, 0x55, 0x0B, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 0xFF);
}
#[test]
fn test_comparison_gt() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 10, 0x00, 5, 0x12, 0x1B, 0x1A], 100).unwrap();
assert!(vm.is_halted());
}
#[test]
fn test_nested_if_else() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 5, 0x00, 3, 0x12, 0x16, 10, 0x00, 42, 0x1A, 0x00, 99, 0x1A], 100).unwrap();
assert_eq!(vm.stack[0], 42);
}
#[test]
fn test_sub_and_assert() {
let mut vm = FluxVM::new(100);
vm.execute(&[0x00, 10, 0x00, 3, 0x07, 0x00, 7, 0x0F, 0x1B, 0x1A], 100).unwrap();
assert!(vm.is_halted());
}
}