use crate::chain::{ChainAssembler, FORMAT_VERSION};
use crate::types::{Blake3Hash, OperationEvent, Receipt};
use anyhow::{ensure, Result};
use clnrm_core::determinism::rng::create_seeded_rng;
use rand::RngCore;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MutationKind {
EventDrop,
EventReorder,
TypeChange,
PayloadFlip,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppliedMutation {
pub kind: MutationKind,
pub target_seq: u64,
pub mutated_receipt: Receipt,
}
pub trait MutationOperator: Send + Sync + 'static {
fn name(&self) -> &'static str;
fn kind(&self) -> MutationKind;
fn min_events(&self) -> usize;
fn apply(&self, receipt: &Receipt, seed: u64) -> Result<AppliedMutation>;
}
pub struct EventDropOperator;
impl MutationOperator for EventDropOperator {
fn name(&self) -> &'static str {
"EventDrop"
}
fn kind(&self) -> MutationKind {
MutationKind::EventDrop
}
fn min_events(&self) -> usize {
1
}
fn apply(&self, receipt: &Receipt, seed: u64) -> Result<AppliedMutation> {
let n = receipt.events.len();
ensure!(n >= self.min_events(), "Receipt has too few events for EventDrop");
let target_idx = (seed as usize) % n;
let target_seq = receipt.events[target_idx].seq;
let mut new_events = receipt.events.clone();
new_events.remove(target_idx);
for (i, event) in new_events.iter_mut().enumerate() {
event.seq = i as u64;
}
let mutated_receipt = ChainAssembler::from_events(new_events)?.finalize();
Ok(AppliedMutation {
kind: self.kind(),
target_seq,
mutated_receipt,
})
}
}
pub struct EventReorderOperator;
impl MutationOperator for EventReorderOperator {
fn name(&self) -> &'static str {
"EventReorder"
}
fn kind(&self) -> MutationKind {
MutationKind::EventReorder
}
fn min_events(&self) -> usize {
2
}
fn apply(&self, receipt: &Receipt, seed: u64) -> Result<AppliedMutation> {
let n = receipt.events.len();
ensure!(n >= self.min_events(), "Receipt has too few events for EventReorder");
let target_idx = (seed as usize) % (n - 1);
let target_seq = receipt.events[target_idx].seq;
let mut new_events = receipt.events.clone();
new_events.swap(target_idx, target_idx + 1);
new_events[target_idx].seq = target_idx as u64;
new_events[target_idx + 1].seq = (target_idx + 1) as u64;
let mutated_receipt = ChainAssembler::from_events(new_events)?.finalize();
Ok(AppliedMutation {
kind: self.kind(),
target_seq,
mutated_receipt,
})
}
}
pub struct TypeChangeOperator;
impl MutationOperator for TypeChangeOperator {
fn name(&self) -> &'static str {
"TypeChange"
}
fn kind(&self) -> MutationKind {
MutationKind::TypeChange
}
fn min_events(&self) -> usize {
1
}
fn apply(&self, receipt: &Receipt, seed: u64) -> Result<AppliedMutation> {
let n = receipt.events.len();
ensure!(n >= self.min_events(), "Receipt has too few events for TypeChange");
let target_idx = (seed as usize) % n;
let target_seq = receipt.events[target_idx].seq;
let mut new_events = receipt.events.clone();
new_events[target_idx].event_type = format!("mutated-type-{}", seed);
let mutated_receipt = ChainAssembler::from_events(new_events)?.finalize();
Ok(AppliedMutation {
kind: self.kind(),
target_seq,
mutated_receipt,
})
}
}
pub struct PayloadFlipOperator;
impl MutationOperator for PayloadFlipOperator {
fn name(&self) -> &'static str {
"PayloadFlip"
}
fn kind(&self) -> MutationKind {
MutationKind::PayloadFlip
}
fn min_events(&self) -> usize {
1
}
fn apply(&self, receipt: &Receipt, seed: u64) -> Result<AppliedMutation> {
let n = receipt.events.len();
ensure!(n >= self.min_events(), "Receipt has too few events for PayloadFlip");
let target_idx = (seed as usize) % n;
let target_seq = receipt.events[target_idx].seq;
let mut new_events = receipt.events.clone();
let mut rng = create_seeded_rng(seed);
let mut random_bytes = [0u8; 32];
rng.fill_bytes(&mut random_bytes);
let commitment_input = format!("mutated-payload-{}", seed);
new_events[target_idx].payload_commitment = Blake3Hash::from_bytes(commitment_input.as_bytes());
let mutated_receipt = ChainAssembler::from_events(new_events)?.finalize();
Ok(AppliedMutation {
kind: self.kind(),
target_seq,
mutated_receipt,
})
}
}
pub fn all_operators() -> Vec<Box<dyn MutationOperator>> {
vec![
Box::new(EventDropOperator),
Box::new(EventReorderOperator),
Box::new(TypeChangeOperator),
Box::new(PayloadFlipOperator),
]
}
fn main() -> Result<()> {
use crate::ocel;
println!("--- COMBINATORIAL MAXIMALISM: Feature 3.1 (Mutate Logic) ---");
let mut asm = ChainAssembler::new();
let mut counter = ocel::SeqCounter::new();
for i in 0..5 {
let event = ocel::build_event(
&format!("op.{}", i),
vec![ocel::object_ref(&format!("obj.{}", i), "artifact")],
format!("payload.{}", i).as_bytes(),
&mut counter,
)?;
asm.append(event)?;
}
let baseline = asm.finalize();
println!("Baseline receipt created with {} events.", baseline.events.len());
println!("Baseline chain_hash: {}", baseline.chain_hash);
let operators = all_operators();
let seed = 42u64;
for op in operators {
println!("\nApplying operator: {}", op.name());
let applied = op.apply(&baseline, seed)?;
println!(" Target seq: {}", applied.target_seq);
println!(" Mutated events: {}", applied.mutated_receipt.events.len());
println!(" Mutated chain_hash: {}", applied.mutated_receipt.chain_hash);
ensure!(
applied.mutated_receipt.chain_hash != baseline.chain_hash,
"Mutation failed to change the chain hash"
);
for (i, event) in applied.mutated_receipt.events.iter().enumerate() {
ensure!(
event.seq == i as u64,
"Sequence mismatch at index {}: expected {}, got {}",
i, i, event.seq
);
}
println!(" Status: VERIFIED (distinct and consistent)");
}
println!("\nAll maximalist mutation operators implemented and verified.");
Ok(())
}