zk_scribble/mutation.rs
1use crate::target::Target;
2
3/// A trace tamper. `Clone + Debug` are
4/// required for proptest shrinking output.
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub enum Mutation {
7 /// XOR `mask` (truncated to column width)
8 /// into a single cell.
9 BitFlip {
10 target: Target,
11 col: usize,
12 row: usize,
13 mask: u128,
14 },
15
16 /// Inject a value the AIR packing
17 /// accepts but range-checks must reject.
18 OutOfBounds {
19 target: Target,
20 col: usize,
21 row: usize,
22 value: u128,
23 },
24
25 /// Toggle a `Bit` cell. Targets
26 /// must be `Bit`-typed columns.
27 FlipSelector {
28 target: Target,
29 col: usize,
30 row: usize,
31 },
32
33 /// Swap every column between two rows.
34 SwapRows {
35 target: Target,
36 row_a: usize,
37 row_b: usize,
38 },
39
40 /// Dispatch-swap primitive:
41 /// rearrange data while leaving the columns
42 /// outside `cols` (selectors, RAM bindings)
43 /// in place.
44 SwapColumns {
45 target: Target,
46 cols: Vec<usize>,
47 row_a: usize,
48 row_b: usize,
49 },
50
51 /// Char-2 duplication:
52 /// copy every column of `src_row` onto `dst_row`.
53 DuplicateRow {
54 target: Target,
55 src_row: usize,
56 dst_row: usize,
57 },
58 CopyColumns {
59 target: Target,
60 cols: Vec<usize>,
61 src_row: usize,
62 dst_row: usize,
63 },
64
65 /// Overwrite every row of `col` with the
66 /// same `value` (truncated to column width).
67 /// Catches "column should be non-trivial
68 /// somewhere" gaps the row-local AIR misses.
69 ColumnUniformWrite {
70 target: Target,
71 col: usize,
72 value: u128,
73 },
74
75 /// Zero every cell in the cross product
76 /// of `rows` and `cols`. Catches padding-
77 /// block forgeries and trace-tail filler.
78 RowSegmentZero {
79 target: Target,
80 rows: Vec<usize>,
81 cols: Vec<usize>,
82 },
83
84 /// Replace `col` with `base + i * step`
85 /// at row `i` (truncated to column width).
86 /// Catches CLK rewinds, address-sorted-
87 /// permutation forgeries, monotonic-counter
88 /// bypasses.
89 MonotonicReplace {
90 target: Target,
91 col: usize,
92 base: u128,
93 step: u128,
94 },
95
96 /// Apply mutations as one tamper.
97 /// Enables coordinated cross-trace
98 /// (chiplet + main) attacks the
99 /// per-table checks alone cannot catch.
100 Compound(Vec<Mutation>),
101}
102
103/// Layer-1 (proptest-discoverable) variants.
104/// `SwapColumns`, `CopyColumns`, and `Compound`
105/// are excluded, their search space defeats
106/// random discovery.
107#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
108pub enum MutationKind {
109 BitFlip,
110 OutOfBounds,
111 FlipSelector,
112 SwapRows,
113 DuplicateRow,
114 ColumnUniformWrite,
115 RowSegmentZero,
116 MonotonicReplace,
117}
118
119impl Mutation {
120 /// `Some` for Layer-1 variants;
121 /// `None` for `SwapColumns`, `CopyColumns`,
122 /// and `Compound` (Layer 2, hand-crafted only).
123 pub fn kind(&self) -> Option<MutationKind> {
124 match self {
125 Mutation::BitFlip { .. } => Some(MutationKind::BitFlip),
126 Mutation::OutOfBounds { .. } => Some(MutationKind::OutOfBounds),
127 Mutation::FlipSelector { .. } => Some(MutationKind::FlipSelector),
128 Mutation::SwapRows { .. } => Some(MutationKind::SwapRows),
129 Mutation::DuplicateRow { .. } => Some(MutationKind::DuplicateRow),
130 Mutation::ColumnUniformWrite { .. } => Some(MutationKind::ColumnUniformWrite),
131 Mutation::RowSegmentZero { .. } => Some(MutationKind::RowSegmentZero),
132 Mutation::MonotonicReplace { .. } => Some(MutationKind::MonotonicReplace),
133 Mutation::SwapColumns { .. } | Mutation::CopyColumns { .. } | Mutation::Compound(_) => {
134 None
135 }
136 }
137 }
138}