use crate::chain::ChainAssembler;
use crate::types::{Blake3Hash, 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),
]
}