1use std::cmp::Reverse;
2
3use itertools::Itertools;
4use openvm_native_compiler::ir::{
5 unsafe_array_transmute, Array, ArrayLike, Builder, Config, Ext, Felt, MemVariable, Usize, Var,
6 DIGEST_SIZE,
7};
8use openvm_native_compiler_derive::iter_zip;
9use openvm_stark_backend::{
10 config::Com,
11 keygen::types::TraceWidth,
12 p3_commit::{BatchOpening, ExtensionMmcs},
13 p3_field::{
14 extension::BinomialExtensionField, BasedVectorSpace, Field, PrimeCharacteristicRing,
15 },
16 p3_util::log2_strict_usize,
17 proof::{AdjacentOpenedValues, AirProofData, Commitments, OpenedValues, OpeningProof, Proof},
18};
19use openvm_stark_sdk::{
20 config::baby_bear_poseidon2::BabyBearPoseidon2Config,
21 p3_baby_bear::{BabyBear, Poseidon2BabyBear},
22};
23use p3_fri::{CommitPhaseProofStep, FriProof, QueryProof};
24use p3_merkle_tree::MerkleTreeMmcs;
25use p3_symmetric::{PaddingFreeSponge, TruncatedPermutation};
26
27use crate::{
28 types::InnerConfig,
29 vars::{
30 AdjacentOpenedValuesVariable, AirProofDataVariable, CommitmentsVariable,
31 OpenedValuesVariable, OpeningProofVariable, StarkProofVariable, TraceWidthVariable,
32 },
33};
34
35pub type InnerVal = BabyBear;
36pub type InnerChallenge = BinomialExtensionField<InnerVal, 4>;
37pub type InnerPerm = Poseidon2BabyBear<16>;
38pub type InnerHash = PaddingFreeSponge<InnerPerm, 16, 8, 8>;
39pub type InnerDigest = [InnerVal; DIGEST_SIZE];
40pub type InnerCompress = TruncatedPermutation<InnerPerm, 2, 8, 16>;
41pub type InnerValMmcs = MerkleTreeMmcs<
42 <InnerVal as Field>::Packing,
43 <InnerVal as Field>::Packing,
44 InnerHash,
45 InnerCompress,
46 8,
47>;
48pub type InnerChallengeMmcs = ExtensionMmcs<InnerVal, InnerChallenge, InnerValMmcs>;
49pub type InnerInputProof = Vec<InnerBatchOpening>;
50pub type InnerQueryProof = QueryProof<InnerChallenge, InnerChallengeMmcs, InnerInputProof>;
51pub type InnerCommitPhaseStep = CommitPhaseProofStep<InnerChallenge, InnerChallengeMmcs>;
52pub type InnerFriProof = FriProof<InnerChallenge, InnerChallengeMmcs, InnerVal, InnerInputProof>;
53pub type InnerBatchOpening = BatchOpening<InnerVal, InnerValMmcs>;
54
55pub trait Hintable<C: Config> {
56 type HintVariable: MemVariable<C>;
57
58 fn read(builder: &mut Builder<C>) -> Self::HintVariable;
59
60 fn write(&self) -> Vec<Vec<C::N>>;
61
62 fn witness(variable: &Self::HintVariable, builder: &mut Builder<C>) {
63 let target = Self::read(builder);
64 builder.assign(variable, target);
65 }
66}
67
68impl<C: Config> Hintable<C> for usize {
69 type HintVariable = Var<C::N>;
70
71 fn read(builder: &mut Builder<C>) -> Self::HintVariable {
72 builder.hint_var()
73 }
74
75 fn write(&self) -> Vec<Vec<C::N>> {
76 vec![vec![PrimeCharacteristicRing::from_usize(*self)]]
77 }
78}
79
80impl Hintable<InnerConfig> for InnerVal {
82 type HintVariable = Felt<InnerVal>;
83
84 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
85 builder.hint_felt()
86 }
87
88 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
89 vec![vec![*self]]
90 }
91}
92
93impl Hintable<InnerConfig> for InnerChallenge {
95 type HintVariable = Ext<InnerVal, InnerChallenge>;
96
97 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
98 builder.hint_ext()
99 }
100
101 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
102 self.as_basis_coefficients_slice()
103 .iter()
104 .copied()
105 .map(|x| vec![x])
106 .collect()
107 }
108}
109
110pub trait VecAutoHintable {}
113
114impl VecAutoHintable for Vec<usize> {}
115
116impl VecAutoHintable for Vec<InnerVal> {}
117
118impl VecAutoHintable for Vec<Vec<InnerChallenge>> {}
119
120impl VecAutoHintable for AdjacentOpenedValues<InnerChallenge> {}
121
122impl VecAutoHintable for Vec<AdjacentOpenedValues<InnerChallenge>> {}
123
124impl VecAutoHintable for Vec<Vec<AdjacentOpenedValues<InnerChallenge>>> {}
125
126impl VecAutoHintable for AirProofData<InnerVal, InnerChallenge> {}
127impl VecAutoHintable for Proof<BabyBearPoseidon2Config> {}
128
129impl<C: Config, I: VecAutoHintable + Hintable<C>> Hintable<C> for Vec<I> {
130 type HintVariable = Array<C, I::HintVariable>;
131
132 fn read(builder: &mut Builder<C>) -> Self::HintVariable {
133 let len = builder.hint_var();
134 let arr = builder.dyn_array(len);
135 iter_zip!(builder, arr).for_each(|idx_vec, builder| {
136 let hint = I::read(builder);
137 let ptr = idx_vec[0];
138 builder.iter_ptr_set(&arr, ptr, hint);
139 });
140 arr
141 }
142
143 fn write(&self) -> Vec<Vec<<C as Config>::N>> {
144 let mut stream = Vec::new();
145
146 let len = C::N::from_usize(self.len());
147 stream.push(vec![len]);
148
149 self.iter().for_each(|i| {
150 let comm = I::write(i);
151 stream.extend(comm);
152 });
153
154 stream
155 }
156}
157
158impl Hintable<InnerConfig> for Vec<usize> {
159 type HintVariable = Array<InnerConfig, Var<InnerVal>>;
160
161 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
162 builder.hint_vars()
163 }
164
165 fn write(&self) -> Vec<Vec<InnerVal>> {
166 vec![self.iter().map(|x| InnerVal::from_usize(*x)).collect()]
167 }
168}
169
170impl<C: Config> Hintable<C> for Vec<u8> {
171 type HintVariable = Array<C, Var<C::N>>;
172
173 fn read(builder: &mut Builder<C>) -> Self::HintVariable {
174 builder.hint_vars()
175 }
176
177 fn write(&self) -> Vec<Vec<C::N>> {
178 vec![self
179 .iter()
180 .map(|x| PrimeCharacteristicRing::from_u8(*x))
181 .collect()]
182 }
183}
184
185impl Hintable<InnerConfig> for Vec<InnerVal> {
186 type HintVariable = Array<InnerConfig, Felt<InnerVal>>;
187
188 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
189 builder.hint_felts()
190 }
191
192 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
193 vec![self.clone()]
194 }
195}
196
197impl Hintable<InnerConfig> for Vec<InnerChallenge> {
198 type HintVariable = Array<InnerConfig, Ext<InnerVal, InnerChallenge>>;
199
200 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
201 builder.hint_exts()
202 }
203
204 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
205 vec![
206 vec![InnerVal::from_usize(self.len())],
207 self.iter()
208 .flat_map(|x| (*x).as_basis_coefficients_slice().to_vec())
209 .collect(),
210 ]
211 }
212}
213
214impl Hintable<InnerConfig> for Vec<Vec<InnerChallenge>> {
215 type HintVariable = Array<InnerConfig, Array<InnerConfig, Ext<InnerVal, InnerChallenge>>>;
216
217 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
218 let len = builder.hint_var();
219 let arr = builder.dyn_array(len);
220 iter_zip!(builder, arr).for_each(|idx_vec, builder| {
221 let hint = Vec::<InnerChallenge>::read(builder);
222 builder.iter_ptr_set(&arr, idx_vec[0], hint);
223 });
224 arr
225 }
226
227 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
228 let mut stream = Vec::new();
229
230 let len = InnerVal::from_usize(self.len());
231 stream.push(vec![len]);
232
233 self.iter().for_each(|arr| {
234 let comm = Vec::<InnerChallenge>::write(arr);
235 stream.extend(comm);
236 });
237
238 stream
239 }
240}
241
242impl Hintable<InnerConfig> for TraceWidth {
243 type HintVariable = TraceWidthVariable<InnerConfig>;
244
245 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
246 let preprocessed = unsafe_array_transmute(Vec::<usize>::read(builder));
247 let cached_mains = unsafe_array_transmute(Vec::<usize>::read(builder));
248 let common_main = Usize::Var(usize::read(builder));
249 let after_challenge = unsafe_array_transmute(Vec::<usize>::read(builder));
250
251 TraceWidthVariable {
252 preprocessed,
253 cached_mains,
254 common_main,
255 after_challenge,
256 }
257 }
258
259 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
260 let mut stream = Vec::new();
261
262 stream.extend(self.preprocessed.into_iter().collect::<Vec<_>>().write());
263 stream.extend(self.cached_mains.write());
264 stream.extend(<usize as Hintable<InnerConfig>>::write(&self.common_main));
265 stream.extend(self.after_challenge.write());
266
267 stream
268 }
269}
270
271impl Hintable<InnerConfig> for Proof<BabyBearPoseidon2Config> {
272 type HintVariable = StarkProofVariable<InnerConfig>;
273
274 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
275 let commitments = Commitments::<Com<BabyBearPoseidon2Config>>::read(builder);
276 let opening = OpeningProof::<BabyBearPoseidon2Config>::read(builder);
277 let per_air = Vec::<AirProofData<InnerVal, InnerChallenge>>::read(builder);
278 let raw_air_perm_by_height = Vec::<usize>::read(builder);
279 let air_perm_by_height = unsafe_array_transmute(raw_air_perm_by_height);
281 let log_up_pow_witness = builder.hint_felt();
282
283 StarkProofVariable {
284 commitments,
285 opening,
286 per_air,
287 air_perm_by_height,
288 log_up_pow_witness,
289 }
290 }
291
292 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
293 let mut stream = Vec::new();
294
295 stream.extend(self.commitments.write());
296 stream.extend(self.opening.write());
297 stream.extend(<Vec<AirProofData<_, _>> as Hintable<_>>::write(
298 &self.per_air,
299 ));
300 let air_perm_by_height: Vec<_> = (0..self.per_air.len())
301 .sorted_by_key(|i| Reverse(self.per_air[*i].degree))
302 .collect();
303 stream.extend(air_perm_by_height.write());
304 stream.extend(
305 self.rap_phase_seq_proof
306 .as_ref()
307 .map(|p| p.logup_pow_witness)
308 .unwrap_or_default()
309 .write(),
310 );
311
312 stream
313 }
314}
315
316impl Hintable<InnerConfig> for AirProofData<InnerVal, InnerChallenge> {
317 type HintVariable = AirProofDataVariable<InnerConfig>;
318 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
319 let air_id = Usize::Var(usize::read(builder));
320 let log_degree = Usize::Var(usize::read(builder));
321 let exposed_values_after_challenge = Vec::<Vec<InnerChallenge>>::read(builder);
322 let public_values = Vec::<InnerVal>::read(builder);
323 Self::HintVariable {
324 air_id,
325 log_degree,
326 exposed_values_after_challenge,
327 public_values,
328 }
329 }
330 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
331 let mut stream = Vec::new();
332
333 stream.extend(<usize as Hintable<InnerConfig>>::write(&self.air_id));
334 stream.extend(<usize as Hintable<InnerConfig>>::write(&log2_strict_usize(
335 self.degree,
336 )));
337 stream.extend(self.exposed_values_after_challenge.write());
338 stream.extend(self.public_values.write());
339
340 stream
341 }
342}
343
344impl Hintable<InnerConfig> for OpeningProof<BabyBearPoseidon2Config> {
345 type HintVariable = OpeningProofVariable<InnerConfig>;
346
347 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
348 builder.cycle_tracker_start("HintOpeningProof");
349 let proof = InnerFriProof::read(builder);
350 builder.cycle_tracker_end("HintOpeningProof");
351 builder.cycle_tracker_start("HintOpeningValues");
352 let values = OpenedValues::read(builder);
353 builder.cycle_tracker_end("HintOpeningValues");
354 let deep_pow_witness = builder.hint_felt();
355
356 OpeningProofVariable {
357 proof,
358 values,
359 deep_pow_witness,
360 }
361 }
362
363 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
364 let mut stream = Vec::new();
365
366 stream.extend(self.proof.write());
367 stream.extend(self.values.write());
368 stream.extend(self.deep_pow_witness.write());
369
370 stream
371 }
372}
373
374impl Hintable<InnerConfig> for OpenedValues<InnerChallenge> {
375 type HintVariable = OpenedValuesVariable<InnerConfig>;
376
377 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
378 let preprocessed = Vec::<AdjacentOpenedValues<InnerChallenge>>::read(builder);
379 let main = Vec::<Vec<AdjacentOpenedValues<InnerChallenge>>>::read(builder);
380 let quotient = Vec::<Vec<Vec<InnerChallenge>>>::read(builder);
381 let after_challenge = Vec::<Vec<AdjacentOpenedValues<InnerChallenge>>>::read(builder);
382
383 OpenedValuesVariable {
384 preprocessed,
385 main,
386 quotient,
387 after_challenge,
388 }
389 }
390
391 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
392 let mut stream = Vec::new();
393
394 stream.extend(self.preprocessed.write());
395 stream.extend(self.main.write());
396 stream.extend(self.quotient.write());
397 stream.extend(self.after_challenge.write());
398
399 stream
400 }
401}
402
403impl Hintable<InnerConfig> for AdjacentOpenedValues<InnerChallenge> {
404 type HintVariable = AdjacentOpenedValuesVariable<InnerConfig>;
405
406 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
407 let local = Vec::<InnerChallenge>::read(builder);
408 let next = Vec::<InnerChallenge>::read(builder);
409 AdjacentOpenedValuesVariable { local, next }
410 }
411
412 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
413 let mut stream = Vec::new();
414 stream.extend(self.local.write());
415 stream.extend(self.next.write());
416 stream
417 }
418}
419
420impl Hintable<InnerConfig> for Commitments<Com<BabyBearPoseidon2Config>> {
421 type HintVariable = CommitmentsVariable<InnerConfig>;
422
423 fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
424 let main_trace = Vec::<InnerDigest>::read(builder);
425 let after_challenge = Vec::<InnerDigest>::read(builder);
426 let quotient = InnerDigest::read(builder);
427
428 CommitmentsVariable {
429 main_trace,
430 after_challenge,
431 quotient,
432 }
433 }
434
435 fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
436 let mut stream = Vec::new();
437
438 stream.extend(Vec::<InnerDigest>::write(
439 &self.main_trace.iter().map(|&x| x.into()).collect(),
440 ));
441 stream.extend(Vec::<InnerDigest>::write(
442 &self.after_challenge.iter().map(|&x| x.into()).collect(),
443 ));
444 let h: InnerDigest = self.quotient.into();
445 stream.extend(h.write());
446
447 stream
448 }
449}
450
451#[cfg(test)]
452mod test {
453 use openvm_native_circuit::execute_program;
454 use openvm_native_compiler::{
455 asm::AsmBuilder,
456 ir::{Ext, Felt, Var},
457 };
458 use openvm_stark_backend::p3_field::PrimeCharacteristicRing;
459
460 use crate::hints::{Hintable, InnerChallenge, InnerVal};
461
462 #[test]
463 fn test_var_array() {
464 let x = vec![
465 InnerVal::from_usize(1),
466 InnerVal::from_usize(2),
467 InnerVal::from_usize(3),
468 ];
469 let stream = Vec::<InnerVal>::write(&x);
470 assert_eq!(stream, vec![x.clone()]);
471
472 let mut builder = AsmBuilder::<InnerVal, InnerChallenge>::default();
473 let arr = Vec::<InnerVal>::read(&mut builder);
474
475 let expected: Var<_> = builder.constant(InnerVal::from_usize(3));
476 builder.assert_var_eq(arr.len(), expected);
477
478 for (i, &val) in x.iter().enumerate() {
479 let actual = builder.get(&arr, i);
480 let expected: Felt<InnerVal> = builder.constant(val);
481 builder.assert_felt_eq(actual, expected);
482 }
483
484 builder.halt();
485
486 let program = builder.compile_isa();
487 execute_program(program, stream);
488 }
489
490 #[test]
491 fn test_ext_array() {
492 let x = vec![
493 InnerChallenge::from_usize(1),
494 InnerChallenge::from_usize(2),
495 InnerChallenge::from_usize(3),
496 ];
497 let stream = Vec::<InnerChallenge>::write(&x);
498 assert_eq!(
499 stream,
500 vec![
501 vec![InnerVal::from_usize(x.len())],
502 vec![
503 InnerVal::from_usize(1),
504 InnerVal::from_usize(0),
505 InnerVal::from_usize(0),
506 InnerVal::from_usize(0),
507 InnerVal::from_usize(2),
508 InnerVal::from_usize(0),
509 InnerVal::from_usize(0),
510 InnerVal::from_usize(0),
511 InnerVal::from_usize(3),
512 InnerVal::from_usize(0),
513 InnerVal::from_usize(0),
514 InnerVal::from_usize(0),
515 ],
516 ]
517 );
518
519 let mut builder = AsmBuilder::<InnerVal, InnerChallenge>::default();
520 let arr = Vec::<InnerChallenge>::read(&mut builder);
521
522 let expected: Var<_> = builder.constant(InnerVal::from_usize(3));
523 builder.assert_var_eq(arr.len(), expected);
524
525 for (i, &val) in x.iter().enumerate() {
526 let actual = builder.get(&arr, i);
527 let expected: Ext<InnerVal, InnerChallenge> = builder.constant(val);
528 builder.assert_ext_eq(actual, expected);
529 }
530
531 builder.halt();
532
533 let program = builder.compile_isa();
534 execute_program(program, stream);
535 }
536}