1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use super::InputLayout;
use crate::EXT_DEGREE;
/// Logical inputs required by the ACE circuit.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InputKey {
/// Public input at the given index.
Public(usize),
/// Aux randomness α supplied as an input.
AuxRandAlpha,
/// Aux randomness β supplied as an input.
AuxRandBeta,
/// Multi-AIR β coefficient for the AIR at instance index `air` (caller order).
/// The verifier assigns β to the AIR at proof_order position 0 and 1 to the other.
/// Only present in layouts built with `num_airs >= 2`.
MultiAirBeta(usize),
/// Main trace value at (offset, index).
Main { offset: usize, index: usize },
/// Base-field coordinate for an aux trace column.
AuxCoord {
offset: usize,
index: usize,
coord: usize,
},
/// Aux bus boundary value at the given index.
AuxBusBoundary(usize),
/// Reserved stark-vars slot, kept zero. Provides word-alignment padding for the base
/// stark-vars block; not referenced by the production circuit.
Reserved,
/// Composition challenge used to fold constraints.
Alpha,
/// `zeta^N`, where `N` is the trace length.
ZPowN,
/// `zeta^(N / max_cycle_len)` for periodic columns.
ZK,
/// Precomputed first-row selector: `(z^N - 1) / (z - 1)`.
IsFirst,
/// Precomputed last-row selector: `(z^N - 1) / (z - g^{-1})`.
IsLast,
/// Precomputed transition selector: `z - g^{-1}`.
IsTransition,
/// Per-AIR lifted first-row selector at `z^{r_air}` (`r_air = n_max / n_air`) for the
/// AIR at instance index `air` (caller order). Equal to the canonical `IsFirst` when
/// the AIR is at log_max. Only present in layouts built with `num_airs >= 2`.
IsFirstAir(usize),
/// Per-AIR lifted last-row selector. Mirror of [`InputKey::IsFirstAir`].
IsLastAir(usize),
/// Per-AIR lifted transition selector. Mirror of [`InputKey::IsFirstAir`].
IsTransitionAir(usize),
/// First barycentric weight for quotient recomposition.
Weight0,
/// `f = h^N`, the chunk shift ratio between cosets.
F,
/// `s0 = offset^N`, the first chunk shift.
S0,
/// Base-field coordinate for a quotient chunk opening at `offset`
/// (0 = zeta, 1 = g * zeta).
QuotientChunkCoord {
offset: usize,
chunk: usize,
coord: usize,
},
}
/// Canonical InputKey → index mapping for a given layout.
#[derive(Debug, Clone, Copy)]
pub(crate) struct InputKeyMapper<'a> {
pub(super) layout: &'a InputLayout,
}
impl InputKeyMapper<'_> {
/// Return the input index for a key, if it exists in the layout.
pub(crate) fn index_of(self, key: InputKey) -> Option<usize> {
let layout = self.layout;
match key {
InputKey::Public(i) => layout.regions.public_values.index(i),
InputKey::AuxRandAlpha => Some(layout.aux_rand_alpha),
InputKey::AuxRandBeta => Some(layout.aux_rand_beta),
InputKey::MultiAirBeta(air) => {
layout.stark.multi_air.as_ref().and_then(|m| m.beta(air))
},
InputKey::Main { offset, index } => match offset {
0 => layout.regions.main_curr.index(index),
1 => layout.regions.main_next.index(index),
_ => None,
},
InputKey::AuxCoord { offset, index, coord } => {
if index >= layout.counts.aux_width || coord >= EXT_DEGREE {
return None;
}
let local = index * EXT_DEGREE + coord;
match offset {
0 => layout.regions.aux_curr.index(local),
1 => layout.regions.aux_next.index(local),
_ => None,
}
},
InputKey::AuxBusBoundary(i) => layout.regions.aux_bus_boundary.index(i),
// Extension-field stark vars.
InputKey::Alpha => Some(layout.stark.alpha),
InputKey::ZPowN => Some(layout.stark.z_pow_n),
InputKey::ZK => Some(layout.stark.z_k),
InputKey::IsFirst => Some(layout.stark.is_first),
InputKey::IsLast => Some(layout.stark.is_last),
InputKey::IsTransition => Some(layout.stark.is_transition),
InputKey::IsFirstAir(air) => {
layout.stark.multi_air.as_ref().and_then(|m| m.selector(air, 0))
},
InputKey::IsLastAir(air) => {
layout.stark.multi_air.as_ref().and_then(|m| m.selector(air, 1))
},
InputKey::IsTransitionAir(air) => {
layout.stark.multi_air.as_ref().and_then(|m| m.selector(air, 2))
},
InputKey::Reserved => Some(layout.stark.reserved),
// Base-field stark vars (stored as (val, 0) in the EF slot).
InputKey::Weight0 => Some(layout.stark.weight0),
InputKey::F => Some(layout.stark.f),
InputKey::S0 => Some(layout.stark.s0),
InputKey::QuotientChunkCoord { offset, chunk, coord } => {
if chunk >= layout.counts.num_quotient_chunks || coord >= EXT_DEGREE {
return None;
}
let idx = chunk * EXT_DEGREE + coord;
match offset {
0 => layout.regions.quotient_curr.index(idx),
1 => layout.regions.quotient_next.index(idx),
_ => None,
}
},
}
}
}