openvm_stark_backend/transcript/
traits.rs1use std::ops::Deref;
2
3use p3_field::{BasedVectorSpace, PrimeCharacteristicRing, PrimeField, PrimeField64};
4use p3_maybe_rayon::prelude::*;
5use tracing::instrument;
6
7use crate::StarkProtocolConfig;
8
9pub trait FiatShamirTranscript<SC>: Clone + Send + Sync
12where
13 SC: StarkProtocolConfig,
14{
15 fn observe(&mut self, value: SC::F);
16 fn sample(&mut self) -> SC::F;
17
18 fn observe_commit(&mut self, digest: SC::Digest);
21
22 fn observe_ext(&mut self, value: SC::EF) {
23 for &base_val in value.as_basis_coefficients_slice() {
25 self.observe(base_val);
26 }
27 }
28
29 fn sample_ext(&mut self) -> SC::EF {
30 SC::EF::from_basis_coefficients_fn(|_| self.sample())
31 }
32
33 fn sample_bits(&mut self, bits: usize) -> u64 {
47 assert!(bits < (u32::BITS as usize));
48 assert!((1 << bits) < SC::F::ORDER_U64);
49 let rand_f: SC::F = self.sample();
50 let rand_u64 = rand_f.as_canonical_u64();
51 rand_u64 & ((1 << bits) - 1)
52 }
53
54 #[must_use]
63 fn check_witness(&mut self, bits: usize, witness: SC::F) -> bool {
64 if bits == 0 {
65 return true;
66 }
67 self.observe(witness);
68 self.sample_bits(bits) == 0
69 }
70
71 #[instrument(name = "grind_pow", skip_all)]
74 fn grind(&mut self, bits: usize) -> SC::F {
75 assert!(bits < (u32::BITS as usize));
76 assert!((1 << bits) < SC::F::ORDER_U64);
77 if bits == 0 {
79 return SC::F::ZERO;
80 }
81
82 let witness = (0..SC::F::ORDER_U64)
83 .into_par_iter()
84 .map(PrimeCharacteristicRing::from_u64)
85 .find_any(|witness| self.clone().check_witness(bits, *witness))
86 .expect("failed to find PoW witness");
87 assert!(self.check_witness(bits, witness));
88 witness
89 }
90}
91
92pub trait TranscriptHistory {
93 type F;
94 type State;
95
96 fn len(&self) -> usize;
97 fn into_log(self) -> TranscriptLog<Self::F, Self::State>;
98
99 fn is_empty(&self) -> bool {
100 self.len() == 0
101 }
102}
103
104#[derive(Clone, Debug)]
106pub struct TranscriptLog<F, State> {
107 values: Vec<F>,
109 is_sample: Vec<bool>,
111 perm_results: Vec<State>,
114}
115
116impl<F, State> Default for TranscriptLog<F, State> {
117 fn default() -> Self {
118 Self {
119 values: Vec::new(),
120 is_sample: Vec::new(),
121 perm_results: Vec::new(),
122 }
123 }
124}
125
126impl<F: Clone, State> TranscriptLog<F, State> {
127 pub fn new(values: Vec<F>, is_sample: Vec<bool>) -> Self {
128 debug_assert_eq!(values.len(), is_sample.len());
129 Self {
130 values,
131 is_sample,
132 perm_results: vec![],
133 }
134 }
135
136 pub fn values(&self) -> &[F] {
137 &self.values
138 }
139
140 pub fn values_mut(&mut self) -> &mut [F] {
141 &mut self.values
142 }
143
144 pub fn samples(&self) -> &[bool] {
145 &self.is_sample
146 }
147
148 pub fn samples_mut(&mut self) -> &mut [bool] {
149 &mut self.is_sample
150 }
151
152 pub fn push_observe(&mut self, value: F) {
153 self.values.push(value);
154 self.is_sample.push(false);
155 }
156
157 pub fn push_sample(&mut self, value: F) {
158 self.values.push(value);
159 self.is_sample.push(true);
160 }
161
162 pub fn push_perm_result(&mut self, state: State) {
163 self.perm_results.push(state);
164 }
165
166 pub fn extend_observe(&mut self, values: &[F]) {
167 self.values.extend_from_slice(values);
168 self.is_sample
169 .extend(core::iter::repeat_n(false, values.len()));
170 }
171
172 pub fn extend_sample(&mut self, values: &[F]) {
173 self.values.extend_from_slice(values);
174 self.is_sample
175 .extend(core::iter::repeat_n(true, values.len()));
176 }
177
178 pub fn extend_with_flags(&mut self, values: &[F], sample_flags: &[bool]) {
179 debug_assert_eq!(values.len(), sample_flags.len());
180 self.values.extend_from_slice(values);
181 self.is_sample.extend(sample_flags.iter().copied());
182 }
183
184 pub fn len(&self) -> usize {
185 self.values.len()
186 }
187
188 pub fn is_empty(&self) -> bool {
189 self.values.is_empty()
190 }
191
192 pub fn into_parts(self) -> (Vec<F>, Vec<bool>) {
193 (self.values, self.is_sample)
194 }
195
196 pub fn perm_results(&self) -> &Vec<State> {
197 &self.perm_results
198 }
199}
200
201impl<F, State> Deref for TranscriptLog<F, State> {
202 type Target = [F];
203
204 fn deref(&self) -> &Self::Target {
205 &self.values
206 }
207}
208
209#[derive(Clone, Debug)]
211pub struct ReadOnlyTranscript<'a, F, State> {
212 log: &'a TranscriptLog<F, State>,
213 position: usize,
214}
215
216impl<'a, F, State> ReadOnlyTranscript<'a, F, State> {
217 pub fn new(log: &'a TranscriptLog<F, State>, start_idx: usize) -> Self {
218 debug_assert!(start_idx <= log.len(), "start index out of bounds");
219 Self {
220 log,
221 position: start_idx,
222 }
223 }
224}
225
226impl<SC, F, const WIDTH: usize, const RATE: usize> FiatShamirTranscript<SC>
227 for ReadOnlyTranscript<'_, F, [F; WIDTH]>
228where
229 F: PrimeField,
230 SC: StarkProtocolConfig<F = F, Digest = [F; RATE]>,
231{
232 #[inline]
233 fn observe(&mut self, value: F) {
234 debug_assert!(
235 !self.log.samples()[self.position],
236 "expected observe at {}",
237 self.position
238 );
239 debug_assert_eq!(
240 self.log.values()[self.position],
241 value,
242 "value mismatch at {}",
243 self.position
244 );
245 self.position += 1;
246 }
247
248 #[inline]
249 fn sample(&mut self) -> F {
250 debug_assert!(
251 self.log.samples()[self.position],
252 "expected sample at {}",
253 self.position
254 );
255 let value = self.log.values()[self.position];
256 self.position += 1;
257 value
258 }
259
260 fn observe_commit(&mut self, digest: [F; RATE]) {
261 for x in digest {
262 FiatShamirTranscript::<SC>::observe(self, x);
263 }
264 }
265}
266
267impl<F, State> TranscriptHistory for ReadOnlyTranscript<'_, F, State>
268where
269 F: Clone,
270 State: Clone,
271{
272 type F = F;
273 type State = State;
274
275 fn len(&self) -> usize {
276 self.position
277 }
278
279 fn into_log(self) -> TranscriptLog<F, State> {
280 self.log.clone()
281 }
282}