Files
nx9-chronoseal-rs/shared/src/vm_extensions.rs
T

905 lines
31 KiB
Rust

use crate::{
constants::{
DEFAULT_MUTATION_ROUNDS, HASH_OPCODE_INSTRUCTION_COST, MAX_GENE_SIZE,
MAX_MUTATION_INSTRUCTION_BUDGET, MAX_MUTATION_PROGRAM_BYTES, SOFT_CAP_DURATION_MS,
},
gene::{
add_env_quantity, get_env_quantity, sub_env_quantity, validate_state, GeneError, GeneState,
},
};
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::time::Instant;
// Stack-machine mutation opcodes (v0.6.0).
//
// NOTE: stack effect notation:
// +1 => pushes one u32
// -1 => pops one u32
// 0 => net-zero (or no stack interaction)
//
// Security/performance notes:
// - All index operands are normalized with modulo to avoid panics.
// - Program size is bounded by MAX_MUTATION_PROGRAM_BYTES.
// - Environment arithmetic is saturating and deterministic.
// - Hashing uses fixed BLAKE3 commitment and fixed transcription algorithm.
pub const OP_GENE_LOAD: u8 = 0x23; // +1
pub const OP_GENE_STORE: u8 = 0x24; // -1
pub const OP_MUTATE_POINT: u8 = 0x25; // 0
pub const OP_INSERT: u8 = 0x26; // -1
pub const OP_DELETE: u8 = 0x27; // +1
pub const OP_TRANSCRIBE: u8 = 0x28; // +1
pub const OP_APPLY_MUTAGEN: u8 = 0x29; // -1
pub const OP_FINALIZE_GENE_HASH: u8 = 0x2A; // +1
pub const OP_CONSUME: u8 = 0x2B; // 0 (pop amount, push remaining)
pub const OP_PRODUCE: u8 = 0x2C; // 0 (pop amount, push resulting quantity)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MutationOrder {
pub step: u64,
pub program: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExecutionTrace {
pub final_ip: usize,
pub final_stack: Vec<u32>,
pub final_gene_commitment_hex: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MutationError {
ProgramTooLong { len: usize },
TruncatedInstruction { opcode: u8, ip: usize },
UnknownOpcode(u8),
EmptyGene,
StackUnderflow { opcode: u8, ip: usize },
GeneFull { current_len: usize },
Base64(base64::DecodeError),
Gene(GeneError),
}
impl std::fmt::Display for MutationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ProgramTooLong { len } => write!(f, "mutation program too long: {len} bytes"),
Self::TruncatedInstruction { opcode, ip } => {
write!(f, "truncated instruction {opcode:#04x} at ip={ip}")
}
Self::UnknownOpcode(opcode) => write!(f, "unknown mutation opcode: {opcode:#04x}"),
Self::EmptyGene => write!(f, "cannot mutate an empty gene"),
Self::StackUnderflow { opcode, ip } => {
write!(f, "stack underflow in opcode {opcode:#04x} at ip={ip}")
}
Self::GeneFull { current_len } => {
write!(f, "cannot insert; gene already at max size ({current_len})")
}
Self::Base64(err) => write!(f, "invalid base64 mutation order: {err}"),
Self::Gene(err) => write!(f, "{err}"),
}
}
}
impl std::error::Error for MutationError {}
impl From<GeneError> for MutationError {
fn from(value: GeneError) -> Self {
Self::Gene(value)
}
}
/// Encodes a `MutationOrder` into standard Base64 representation of its bytecode.
pub fn encode_order_b64(order: &MutationOrder) -> String {
base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &order.program)
}
/// Decodes a `MutationOrder` from its Base64 representation.
///
/// Validates that the decoded program size does not exceed the allowed maximum budget size.
///
/// # Arguments
/// * `step` - The step index associated with this mutation order.
/// * `b64` - The Base64 string containing the raw bytecode.
pub fn decode_order_b64(step: u64, b64: &str) -> Result<MutationOrder, MutationError> {
let program = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, b64)
.map_err(MutationError::Base64)?;
if program.len() > MAX_MUTATION_PROGRAM_BYTES {
return Err(MutationError::ProgramTooLong { len: program.len() });
}
Ok(MutationOrder { step, program })
}
/// Generates a randomized `MutationOrder` program for a given step and gene size.
///
/// Uses thread-local random number generator.
///
/// # Arguments
/// * `step` - The step index.
/// * `gene_size` - The length of the gene byte buffer.
pub fn generate_order(step: u64, gene_size: usize) -> MutationOrder {
let mut rng = rand::thread_rng();
generate_order_with_rng(&mut rng, step, gene_size)
}
/// Generates a randomized `MutationOrder` program using a specific custom RNG.
///
/// Builds a program containing between 20 and 36 mutation instructions (e.g. loads, point changes,
/// insertions, deletions, env modifications) and ensures a minimum number of finalize hash steps are included.
///
/// # Arguments
/// * `rng` - The random number generator.
/// * `step` - The step index.
/// * `gene_size` - The length of the gene byte buffer.
pub fn generate_order_with_rng<R: Rng + ?Sized>(
rng: &mut R,
step: u64,
gene_size: usize,
) -> MutationOrder {
let mut program = Vec::with_capacity(128);
let mut stack_depth: i32 = 0;
let mut estimated_gene_len = gene_size.clamp(1, MAX_GENE_SIZE);
let ops = rng.gen_range(20usize..=36usize);
let mut hash_ops_needed = rng.gen_range(2..=3);
for idx in 0..ops {
let remaining = ops - idx;
let op = if hash_ops_needed > 0 && remaining <= hash_ops_needed {
OP_FINALIZE_GENE_HASH
} else if stack_depth <= 0 {
rng.gen_range(0u8..3u8)
} else {
match rng.gen_range(0u8..12u8) {
0..=1 => OP_GENE_LOAD,
2..=3 => OP_TRANSCRIBE,
4 => OP_FINALIZE_GENE_HASH,
5 => OP_GENE_STORE,
6 => OP_MUTATE_POINT,
7 => OP_INSERT,
8 => OP_DELETE,
9 => OP_APPLY_MUTAGEN,
10 => OP_CONSUME,
_ => OP_PRODUCE,
}
};
match op {
OP_GENE_LOAD => {
program.push(OP_GENE_LOAD);
push_u16(&mut program, rng.r#gen::<u16>());
stack_depth += 1;
}
OP_TRANSCRIBE => {
program.push(OP_TRANSCRIBE);
push_u16(&mut program, rng.r#gen::<u16>());
program.push(rng.gen_range(1u8..=16u8));
stack_depth += 1;
}
OP_FINALIZE_GENE_HASH => {
program.push(OP_FINALIZE_GENE_HASH);
stack_depth += 1;
hash_ops_needed = hash_ops_needed.saturating_sub(1);
}
OP_GENE_STORE => {
if stack_depth > 0 {
program.push(OP_GENE_STORE);
push_u16(&mut program, rng.r#gen::<u16>());
stack_depth -= 1;
}
}
OP_MUTATE_POINT => {
program.push(OP_MUTATE_POINT);
push_u16(&mut program, rng.r#gen::<u16>());
program.push(rng.r#gen::<u8>());
}
OP_INSERT => {
if stack_depth > 0 && estimated_gene_len < MAX_GENE_SIZE {
program.push(OP_INSERT);
push_u16(&mut program, rng.r#gen::<u16>());
stack_depth -= 1;
estimated_gene_len += 1;
}
}
OP_DELETE => {
program.push(OP_DELETE);
push_u16(&mut program, rng.r#gen::<u16>());
stack_depth += 1;
if estimated_gene_len > 1 {
estimated_gene_len -= 1;
}
}
OP_APPLY_MUTAGEN => {
if stack_depth > 0 {
program.push(OP_APPLY_MUTAGEN);
push_u16(&mut program, rng.r#gen::<u16>());
push_u16(&mut program, rng.r#gen::<u16>());
stack_depth -= 1;
}
}
OP_CONSUME => {
if stack_depth > 0 {
program.push(OP_CONSUME);
push_u16(&mut program, rng.r#gen::<u16>());
}
}
OP_PRODUCE if stack_depth > 0 => {
program.push(OP_PRODUCE);
push_u16(&mut program, rng.r#gen::<u16>());
}
_ => {}
}
}
while hash_ops_needed > 0 && program.len() < MAX_MUTATION_PROGRAM_BYTES {
program.push(OP_FINALIZE_GENE_HASH);
hash_ops_needed -= 1;
}
MutationOrder { step, program }
}
/// Clones the `GeneState` and executes the mutation program for `DEFAULT_MUTATION_ROUNDS`.
pub fn apply_program_clone(state: &GeneState, program: &[u8]) -> Result<GeneState, MutationError> {
apply_program_clone_with_rounds(state, program, DEFAULT_MUTATION_ROUNDS)
}
/// Clones the `GeneState` and executes the mutation program for a specific number of rounds.
pub fn apply_program_clone_with_rounds(
state: &GeneState,
program: &[u8],
rounds: u8,
) -> Result<GeneState, MutationError> {
let mut next = state.clone();
execute_program_with_rounds(&mut next, program, rounds)?;
Ok(next)
}
/// Executes the mutation program on the mutable `GeneState` reference for a specific number of rounds.
pub fn apply_program_with_rounds(
state: &mut GeneState,
program: &[u8],
rounds: u8,
) -> Result<(), MutationError> {
let _ = execute_program_with_rounds(state, program, rounds)?;
Ok(())
}
/// Executes the mutation program on the mutable `GeneState` reference for `DEFAULT_MUTATION_ROUNDS`.
pub fn apply_program(state: &mut GeneState, program: &[u8]) -> Result<(), MutationError> {
let _ = execute_program_with_rounds(state, program, DEFAULT_MUTATION_ROUNDS)?;
Ok(())
}
/// Executes the mutation program on the mutable `GeneState` reference for multiple rounds.
///
/// Implements a soft instruction cost-budget cap check to prevent hostile/inefficient
/// programs from lagging the host server thread or client runtime.
///
/// # Arguments
/// * `state` - The mutable gene state buffer.
/// * `program` - The raw bytecode sequence.
/// * `rounds` - The requested number of execution rounds.
pub fn execute_program_with_rounds(
state: &mut GeneState,
program: &[u8],
rounds: u8,
) -> Result<ExecutionTrace, MutationError> {
if state.gene.is_empty() {
return Err(MutationError::EmptyGene);
}
validate_state(state)?;
if program.len() > MAX_MUTATION_PROGRAM_BYTES {
return Err(MutationError::ProgramTooLong { len: program.len() });
}
let program_cost = estimate_program_cost(program);
let max_rounds = std::cmp::max(1, MAX_MUTATION_INSTRUCTION_BUDGET / program_cost);
let actual_rounds = std::cmp::min(rounds as usize, max_rounds) as u8;
let start = Instant::now();
let mut trace = None;
for _round in 0..actual_rounds {
trace = Some(execute_program(state, program)?);
}
let elapsed = start.elapsed();
tracing::debug!(
rounds = actual_rounds,
requested_rounds = rounds,
elapsed_ms = elapsed.as_millis(),
program_len = program.len(),
"mutation execution"
);
if actual_rounds < rounds {
tracing::debug!(
requested_rounds = rounds,
executed_rounds = actual_rounds,
"mutation soft cap reduced mutation rounds to preserve host responsiveness"
);
}
if elapsed.as_millis() > SOFT_CAP_DURATION_MS {
tracing::debug!(
elapsed_ms = elapsed.as_millis(),
"mutation execution exceeded soft cap duration"
);
}
Ok(trace.unwrap_or_else(|| ExecutionTrace {
final_ip: 0,
final_stack: Vec::new(),
final_gene_commitment_hex: crate::gene::commitment_hex(state),
}))
}
fn estimate_program_cost(program: &[u8]) -> usize {
let mut ip = 0;
let mut cost = 0;
while ip < program.len() {
let opcode = program[ip];
ip += 1;
cost += if opcode == OP_FINALIZE_GENE_HASH {
HASH_OPCODE_INSTRUCTION_COST
} else {
1
};
ip += match opcode {
OP_GENE_LOAD | OP_GENE_STORE | OP_INSERT | OP_DELETE | OP_CONSUME | OP_PRODUCE => 2,
OP_MUTATE_POINT | OP_TRANSCRIBE => 3,
OP_APPLY_MUTAGEN => 4,
OP_FINALIZE_GENE_HASH => 0,
_ => 0,
}
}
cost.max(1)
}
/// Executes the VM mutation program on the mutable `GeneState` reference.
///
/// This interprets VM mutation opcodes to modify the gene byte array and environment records.
///
/// # Arguments
/// * `state` - The mutable gene state to mutate.
/// * `program` - The raw instruction bytecode slice.
pub fn execute_program(
state: &mut GeneState,
program: &[u8],
) -> Result<ExecutionTrace, MutationError> {
if state.gene.is_empty() {
return Err(MutationError::EmptyGene);
}
validate_state(state)?;
if program.len() > MAX_MUTATION_PROGRAM_BYTES {
return Err(MutationError::ProgramTooLong { len: program.len() });
}
let mut ip = 0usize;
let mut stack: Vec<u32> = Vec::with_capacity(16);
while ip < program.len() {
let opcode_ip = ip;
let opcode = take_u8(program, &mut ip, 0x00)?;
match opcode {
OP_GENE_LOAD => {
let idx = take_u16(program, &mut ip, opcode)?;
let normalized = normalize_index(idx as usize, state.gene.len());
stack.push(state.gene[normalized] as u32);
}
OP_GENE_STORE => {
let idx = take_u16(program, &mut ip, opcode)?;
let value = pop_stack(&mut stack, opcode, opcode_ip)? as u8;
let normalized = normalize_index(idx as usize, state.gene.len());
state.gene[normalized] = value;
}
OP_MUTATE_POINT => {
let idx = take_u16(program, &mut ip, opcode)?;
let delta = take_u8(program, &mut ip, opcode)? as i8;
let normalized = normalize_index(idx as usize, state.gene.len());
state.gene[normalized] = state.gene[normalized].wrapping_add(delta as u8);
}
OP_INSERT => {
let idx = take_u16(program, &mut ip, opcode)?;
let value = pop_stack(&mut stack, opcode, opcode_ip)? as u8;
if state.gene.len() >= MAX_GENE_SIZE {
return Err(MutationError::GeneFull {
current_len: state.gene.len(),
});
}
let insert_at = (idx as usize).min(state.gene.len());
state.gene.insert(insert_at, value);
}
OP_DELETE => {
let idx = take_u16(program, &mut ip, opcode)?;
let normalized = normalize_index(idx as usize, state.gene.len());
let removed = if state.gene.len() > 1 {
state.gene.remove(normalized)
} else {
let prev = state.gene[0];
state.gene[0] = 0;
prev
};
stack.push(removed as u32);
}
OP_TRANSCRIBE => {
let start = take_u16(program, &mut ip, opcode)?;
let span = take_u8(program, &mut ip, opcode)?;
let transcription = transcribe_window(&state.gene, start as usize, span);
stack.push(transcription);
}
OP_APPLY_MUTAGEN => {
let symbol = take_u16(program, &mut ip, opcode)?;
let idx = take_u16(program, &mut ip, opcode)?;
let stack_mask = pop_stack(&mut stack, opcode, opcode_ip)? as u8;
let quantity = get_env_quantity(state, symbol);
let mix = ((quantity as u8)
^ ((quantity >> 8) as u8)
^ ((quantity >> 16) as u8)
^ ((quantity >> 24) as u8))
^ ((symbol & 0x00ff) as u8)
^ ((symbol >> 8) as u8)
^ stack_mask;
let normalized = normalize_index(idx as usize, state.gene.len());
state.gene[normalized] ^= mix;
}
OP_FINALIZE_GENE_HASH => {
let commit = crate::gene::commitment(state);
let hash32 = u32::from_le_bytes([commit[0], commit[1], commit[2], commit[3]]);
stack.push(hash32);
}
OP_CONSUME => {
let symbol = take_u16(program, &mut ip, opcode)?;
let amount = pop_stack(&mut stack, opcode, opcode_ip)?;
let left = sub_env_quantity(state, symbol, amount)?;
stack.push(left);
}
OP_PRODUCE => {
let symbol = take_u16(program, &mut ip, opcode)?;
let amount = pop_stack(&mut stack, opcode, opcode_ip)?;
let next = add_env_quantity(state, symbol, amount)?;
stack.push(next);
}
_ => return Err(MutationError::UnknownOpcode(opcode)),
}
}
Ok(ExecutionTrace {
final_ip: ip,
final_stack: stack,
final_gene_commitment_hex: crate::gene::commitment_hex(state),
})
}
fn transcribe_window(gene: &[u8], start: usize, span: u8) -> u32 {
let count = usize::from(span.max(1));
let mut acc = 2_166_136_261u32; // FNV offset basis
for i in 0..count {
let idx = (start + i) % gene.len();
acc ^= gene[idx] as u32;
acc = acc.wrapping_mul(16_777_619); // FNV prime
}
acc
}
fn push_u16(buf: &mut Vec<u8>, value: u16) {
buf.extend_from_slice(&value.to_le_bytes());
}
fn take_u8(bytes: &[u8], ip: &mut usize, opcode: u8) -> Result<u8, MutationError> {
if *ip >= bytes.len() {
return Err(MutationError::TruncatedInstruction { opcode, ip: *ip });
}
let value = bytes[*ip];
*ip += 1;
Ok(value)
}
fn take_u16(bytes: &[u8], ip: &mut usize, opcode: u8) -> Result<u16, MutationError> {
if *ip + 2 > bytes.len() {
return Err(MutationError::TruncatedInstruction { opcode, ip: *ip });
}
let value = u16::from_le_bytes([bytes[*ip], bytes[*ip + 1]]);
*ip += 2;
Ok(value)
}
fn pop_stack(stack: &mut Vec<u32>, opcode: u8, ip: usize) -> Result<u32, MutationError> {
stack
.pop()
.ok_or(MutationError::StackUnderflow { opcode, ip })
}
fn normalize_index(idx: usize, len: usize) -> usize {
idx % len
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gene::{commitment, new_state, set_env_quantity};
use rand::{Rng, SeedableRng};
use std::time::Instant;
fn u16_bytes(v: u16) -> [u8; 2] {
v.to_le_bytes()
}
#[test]
fn test_opcode_gene_load() {
let mut state = new_state(4).unwrap();
state.gene = vec![10, 20, 30, 40];
let trace = execute_program(&mut state, &[OP_GENE_LOAD, 1, 0]).unwrap();
assert_eq!(trace.final_stack, vec![20]);
}
#[test]
fn test_opcode_gene_store() {
let mut state = new_state(4).unwrap();
state.gene = vec![1, 2, 3, 4];
let program = vec![
OP_GENE_LOAD,
0,
0, // stack: [1]
OP_GENE_STORE,
2,
0, // gene[2] <- 1
];
execute_program(&mut state, &program).unwrap();
assert_eq!(state.gene, vec![1, 2, 1, 4]);
}
#[test]
fn test_opcode_mutate_point() {
let mut state = new_state(4).unwrap();
state.gene[0] = 200;
let program = vec![OP_MUTATE_POINT, 0, 0, 100u8];
execute_program(&mut state, &program).unwrap();
assert_eq!(state.gene[0], 44);
}
#[test]
fn test_opcode_insert() {
let mut state = new_state(3).unwrap();
state.gene = vec![10, 20, 30];
let program = vec![
OP_GENE_LOAD,
1,
0, // stack: [20]
OP_INSERT,
0,
0, // insert 20 at position 0
];
execute_program(&mut state, &program).unwrap();
assert_eq!(state.gene, vec![20, 10, 20, 30]);
}
#[test]
fn test_opcode_delete() {
let mut state = new_state(4).unwrap();
state.gene = vec![9, 8, 7, 6];
let trace = execute_program(&mut state, &[OP_DELETE, 2, 0]).unwrap();
assert_eq!(state.gene, vec![9, 8, 6]);
assert_eq!(trace.final_stack, vec![7]);
}
#[test]
fn test_opcode_transcribe() {
let mut state = new_state(5).unwrap();
state.gene = vec![1, 2, 3, 4, 5];
let trace = execute_program(&mut state, &[OP_TRANSCRIBE, 1, 0, 3]).unwrap();
assert_eq!(trace.final_stack.len(), 1);
assert_ne!(trace.final_stack[0], 0);
}
#[test]
fn test_opcode_apply_mutagen() {
let mut state = new_state(4).unwrap();
set_env_quantity(&mut state, 7, 0x1234_5678).unwrap();
state.gene[1] = 0xAA;
let program = vec![
OP_GENE_LOAD,
0,
0, // stack mask source
OP_APPLY_MUTAGEN,
7,
0,
1,
0,
];
execute_program(&mut state, &program).unwrap();
assert_ne!(state.gene[1], 0xAA);
}
#[test]
fn test_opcode_finalize_gene_hash() {
let mut state = new_state(4).unwrap();
let trace = execute_program(&mut state, &[OP_FINALIZE_GENE_HASH]).unwrap();
assert_eq!(trace.final_stack.len(), 1);
}
#[test]
fn test_opcode_consume() {
let mut state = new_state(4).unwrap();
set_env_quantity(&mut state, 3, 100).unwrap();
let program = vec![
OP_GENE_LOAD,
0,
0, // stack = [0]
OP_MUTATE_POINT,
0,
0,
15, // gene[0]=15
OP_GENE_LOAD,
0,
0, // stack=[0,15]
OP_CONSUME,
3,
0, // consume 15
];
let trace = execute_program(&mut state, &program).unwrap();
assert_eq!(get_env_quantity(&state, 3), 85);
assert_eq!(trace.final_stack.last().copied().unwrap(), 85);
}
#[test]
fn test_opcode_produce() {
let mut state = new_state(4).unwrap();
set_env_quantity(&mut state, 9, 5).unwrap();
let program = vec![
OP_GENE_LOAD,
0,
0, // stack [0]
OP_MUTATE_POINT,
0,
0,
10, // gene[0]=10
OP_GENE_LOAD,
0,
0, // stack [0,10]
OP_PRODUCE,
9,
0, // +10
];
let trace = execute_program(&mut state, &program).unwrap();
assert_eq!(get_env_quantity(&state, 9), 15);
assert_eq!(trace.final_stack.last().copied().unwrap(), 15);
}
#[test]
fn test_zero_length_gene_is_rejected() {
let mut state = GeneState {
gene: vec![],
environment: vec![],
};
let err = execute_program(&mut state, &[OP_FINALIZE_GENE_HASH]).unwrap_err();
assert_eq!(err, MutationError::EmptyGene);
}
#[test]
fn test_insert_rejects_max_size_gene() {
let mut state = new_state(MAX_GENE_SIZE).unwrap();
let program = vec![
OP_GENE_LOAD,
0,
0, // push value
OP_INSERT,
0,
0,
];
let err = execute_program(&mut state, &program).unwrap_err();
assert!(matches!(err, MutationError::GeneFull { .. }));
}
#[test]
fn test_invalid_positions_wrap_deterministically() {
let mut state_a = new_state(5).unwrap();
let mut state_b = new_state(5).unwrap();
let max_u16 = u16::MAX;
let [a0, a1] = u16_bytes(max_u16);
let program = vec![OP_MUTATE_POINT, a0, a1, 1];
execute_program(&mut state_a, &program).unwrap();
let wrapped = (max_u16 as usize % 5) as u16;
let [w0, w1] = u16_bytes(wrapped);
let wrapped_program = vec![OP_MUTATE_POINT, w0, w1, 1];
execute_program(&mut state_b, &wrapped_program).unwrap();
assert_eq!(state_a, state_b);
}
#[test]
fn test_quantity_underflow_is_saturating() {
let mut state = new_state(4).unwrap();
set_env_quantity(&mut state, 1, 3).unwrap();
state.gene[0] = 8;
let program = vec![
OP_GENE_LOAD,
0,
0, // 8
OP_CONSUME,
1,
0, // consume 8 from qty 3 => 0
];
let trace = execute_program(&mut state, &program).unwrap();
assert_eq!(get_env_quantity(&state, 1), 0);
assert_eq!(trace.final_stack.last().copied().unwrap(), 0);
}
#[test]
fn test_rejects_unknown_opcode() {
let mut state = new_state(8).unwrap();
let err = execute_program(&mut state, &[0xFF]).unwrap_err();
assert_eq!(err, MutationError::UnknownOpcode(0xFF));
}
#[test]
fn test_rejects_truncated_instruction() {
let mut state = new_state(8).unwrap();
let err = execute_program(&mut state, &[OP_GENE_LOAD, 1]).unwrap_err();
assert!(matches!(err, MutationError::TruncatedInstruction { .. }));
}
#[test]
fn test_rejects_stack_underflow() {
let mut state = new_state(8).unwrap();
let err = execute_program(&mut state, &[OP_GENE_STORE, 0, 0]).unwrap_err();
assert!(matches!(err, MutationError::StackUnderflow { .. }));
}
#[test]
fn test_base64_order_roundtrip() {
let order = MutationOrder {
step: 17,
program: vec![OP_GENE_LOAD, 1, 0, OP_GENE_STORE, 2, 0],
};
let b64 = encode_order_b64(&order);
let decoded = decode_order_b64(order.step, &b64).unwrap();
assert_eq!(decoded, order);
}
#[test]
fn test_generate_order_is_deterministic_for_seeded_rng() {
let mut rng_a = rand::rngs::StdRng::seed_from_u64(99);
let mut rng_b = rand::rngs::StdRng::seed_from_u64(99);
let order_a = generate_order_with_rng(&mut rng_a, 5, 64);
let order_b = generate_order_with_rng(&mut rng_b, 5, 64);
assert_eq!(order_a, order_b);
}
#[test]
fn test_mutation_chain() {
let mut server_state = new_state(32).unwrap();
let mut client_state = new_state(32).unwrap();
let program = vec![
OP_GENE_LOAD,
0,
0,
OP_PRODUCE,
2,
0, // env[2]+=gene[0]
OP_GENE_LOAD,
1,
0,
OP_APPLY_MUTAGEN,
2,
0,
1,
0, // mutagen at idx1
OP_TRANSCRIBE,
0,
0,
8, // hash window
OP_GENE_STORE,
2,
0, // gene[2]=transcription_low_byte
OP_DELETE,
0,
0, // stack pushes removed
OP_INSERT,
3,
0, // insert removed at position 3
OP_FINALIZE_GENE_HASH,
];
let server_trace = execute_program(&mut server_state, &program).unwrap();
let client_trace = execute_program(&mut client_state, &program).unwrap();
assert_eq!(server_state, client_state);
assert_eq!(server_trace.final_stack, client_trace.final_stack);
assert_eq!(
server_trace.final_gene_commitment_hex,
client_trace.final_gene_commitment_hex
);
}
#[test]
fn test_server_client_parity_across_random_orders() {
let mut rng = rand::rngs::StdRng::seed_from_u64(7);
for step in 0..128u64 {
let order = generate_order_with_rng(&mut rng, step, 128);
let mut server_state = new_state(128).unwrap();
let mut client_state = new_state(128).unwrap();
let server_result = execute_program(&mut server_state, &order.program);
let client_result = execute_program(&mut client_state, &order.program);
assert_eq!(server_result.is_ok(), client_result.is_ok());
match (server_result, client_result) {
(Ok(server_trace), Ok(client_trace)) => {
assert_eq!(server_state, client_state);
assert_eq!(server_trace.final_stack, client_trace.final_stack);
assert_eq!(
commitment(&server_state),
commitment(&client_state),
"step {step}"
);
}
(Err(a), Err(b)) => assert_eq!(a.to_string(), b.to_string()),
_ => unreachable!(),
}
}
}
#[test]
fn test_fuzz_style_random_program_bytes_do_not_diverge() {
let mut rng = rand::rngs::StdRng::seed_from_u64(2026);
for _ in 0..256 {
let len = rng.gen_range(1usize..=MAX_MUTATION_PROGRAM_BYTES);
let mut program = vec![0u8; len];
for b in &mut program {
*b = rng.r#gen::<u8>();
}
let mut a = new_state(64).unwrap();
let mut b = new_state(64).unwrap();
let ra = execute_program(&mut a, &program);
let rb = execute_program(&mut b, &program);
assert_eq!(ra.is_ok(), rb.is_ok());
if ra.is_ok() {
assert_eq!(a, b);
}
}
}
#[test]
fn test_performance_smoke_mutation_execution() {
let mut rng = rand::rngs::StdRng::seed_from_u64(11);
let mut programs = Vec::new();
for step in 0..200u64 {
programs.push(generate_order_with_rng(&mut rng, step + 1, 512).program);
}
let start = Instant::now();
let mut state = new_state(512).unwrap();
for program in &programs {
let _ = execute_program(&mut state, program);
}
let elapsed = start.elapsed();
// Wide bound for CI variability; this is a regression guard, not a strict benchmark.
assert!(
elapsed.as_secs_f64() < 2.0,
"mutation execution too slow: {elapsed:?}"
);
}
#[test]
fn test_vm_instruction_budget_soft_cap() {
let state = new_state(8).unwrap();
// Construct a program with 130 OP_FINALIZE_GENE_HASH instructions.
// HASH has HASH_OPCODE_INSTRUCTION_COST = 16.
// Total cost will be 130 * 16 = 2080, which exceeds MAX_MUTATION_INSTRUCTION_BUDGET (2048).
let program = vec![OP_FINALIZE_GENE_HASH; 130];
let cost = estimate_program_cost(&program);
assert!(cost >= 2080);
// Assert that the max allowed rounds is calculated as 1 since cost > budget.
let expected_rounds = std::cmp::max(1, MAX_MUTATION_INSTRUCTION_BUDGET / cost);
assert_eq!(expected_rounds, 1);
// Execute the program with a requested 10 rounds.
// The runtime should execute it successfully without panic, while applying the round limitation.
let mut test_state = state.clone();
let trace = execute_program_with_rounds(&mut test_state, &program, 10).unwrap();
assert_eq!(trace.final_ip, program.len());
}
}