1use std::marker::PhantomData;
2
3use slop_algebra::{AbstractField, Field};
4use slop_bn254::{OUTER_CHALLENGER_RATE, OUTER_DIGEST_SIZE};
5use slop_challenger::DuplexChallenger;
6use slop_multilinear::Point;
7use slop_symmetric::CryptographicPermutation;
8use sp1_primitives::{SP1ExtensionField, SP1Field};
9use sp1_recursion_compiler::{
10 circuit::CircuitV2Builder,
11 ir::{DslIr, Var},
12 prelude::{Builder, Ext, Felt},
13};
14use sp1_recursion_executor::{HASH_RATE, NUM_BITS, PERMUTATION_WIDTH};
15
16use crate::CircuitConfig;
17
18pub const POSEIDON_2_BB_RATE: usize = 16;
20pub trait CanCopyChallenger<C: CircuitConfig> {
21 fn copy(&self, builder: &mut Builder<C>) -> Self;
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct SpongeChallengerShape {
26 pub input_buffer_len: usize,
27 pub output_buffer_len: usize,
28}
29
30pub trait CanObserveVariable<C: CircuitConfig, V> {
32 fn observe(&mut self, builder: &mut Builder<C>, value: V);
33
34 fn observe_slice(&mut self, builder: &mut Builder<C>, values: impl IntoIterator<Item = V>) {
35 for value in values {
36 self.observe(builder, value);
37 }
38 }
39}
40
41pub trait CanSampleVariable<C: CircuitConfig, V> {
42 fn sample(&mut self, builder: &mut Builder<C>) -> V;
43}
44
45pub trait FieldChallengerVariable<C: CircuitConfig, Bit>:
47 CanObserveVariable<C, Felt<SP1Field>>
48 + CanSampleVariable<C, Felt<SP1Field>>
49 + CanSampleBitsVariable<C, Bit>
50{
51 fn sample_ext(&mut self, builder: &mut Builder<C>) -> Ext<SP1Field, SP1ExtensionField>;
52
53 fn check_witness(&mut self, builder: &mut Builder<C>, nb_bits: usize, witness: Felt<SP1Field>);
54
55 fn duplexing(&mut self, builder: &mut Builder<C>);
56
57 fn sample_point(
58 &mut self,
59 builder: &mut Builder<C>,
60 dimension: u32,
61 ) -> Point<Ext<SP1Field, SP1ExtensionField>> {
62 (0..dimension).map(|_| self.sample_ext(builder)).collect()
63 }
64
65 fn observe_variable_length_slice(
66 &mut self,
67 builder: &mut Builder<C>,
68 values: &[Felt<SP1Field>],
69 ) {
70 let len = values.len();
71 let len_felt = builder.constant(SP1Field::from_canonical_usize(len));
72 self.observe(builder, len_felt);
73 self.observe_slice(builder, values.iter().cloned());
74 }
75
76 fn observe_ext_element(
77 &mut self,
78 builder: &mut Builder<C>,
79 element: Ext<SP1Field, SP1ExtensionField>,
80 ) where
81 C: CircuitConfig,
82 {
83 let felts = C::ext2felt(builder, element);
84 self.observe_slice(builder, felts);
85 }
86
87 fn observe_ext_element_slice(
88 &mut self,
89 builder: &mut Builder<C>,
90 elements: &[Ext<SP1Field, SP1ExtensionField>],
91 ) where
92 C: CircuitConfig,
93 {
94 for &element in elements {
95 self.observe_ext_element(builder, element);
96 }
97 }
98
99 fn observe_variable_length_extension_slice(
100 &mut self,
101 builder: &mut Builder<C>,
102 elements: &[Ext<SP1Field, SP1ExtensionField>],
103 ) where
104 C: CircuitConfig,
105 {
106 let len = elements.len();
107 let len_felt = builder.constant(SP1Field::from_canonical_usize(len));
108 self.observe(builder, len_felt);
109 self.observe_ext_element_slice(builder, elements);
110 }
111}
112
113pub trait CanSampleBitsVariable<C: CircuitConfig, V> {
114 fn sample_bits(&mut self, builder: &mut Builder<C>, nb_bits: usize) -> Vec<V>;
115}
116
117#[derive(Clone, Debug)]
119pub struct DuplexChallengerVariable<C: CircuitConfig> {
120 pub sponge_state: [Felt<SP1Field>; PERMUTATION_WIDTH],
121 pub input_buffer: Vec<Felt<SP1Field>>,
122 pub output_buffer: Vec<Felt<SP1Field>>,
123 pub marker: PhantomData<C>,
124}
125
126impl<C: CircuitConfig> DuplexChallengerVariable<C> {
127 pub fn new(builder: &mut Builder<C>) -> Self {
129 DuplexChallengerVariable::<C> {
130 sponge_state: core::array::from_fn(|_| builder.eval(SP1Field::zero())),
131 input_buffer: vec![],
132 output_buffer: vec![],
133 marker: PhantomData,
134 }
135 }
136
137 pub fn from_challenger<P: CryptographicPermutation<[SP1Field; PERMUTATION_WIDTH]>>(
139 builder: &mut Builder<C>,
140 challenger: &DuplexChallenger<SP1Field, P, PERMUTATION_WIDTH, HASH_RATE>,
141 ) -> Self {
142 let sponge_state = challenger.sponge_state.map(|x| builder.eval(x));
143 let input_buffer = challenger.input_buffer.iter().map(|x| builder.eval(*x)).collect();
144 let output_buffer = challenger.output_buffer.iter().map(|x| builder.eval(*x)).collect();
145 DuplexChallengerVariable::<C> {
146 sponge_state,
147 input_buffer,
148 output_buffer,
149 marker: PhantomData,
150 }
151 }
152
153 pub fn copy(&self, builder: &mut Builder<C>) -> Self {
155 let DuplexChallengerVariable { sponge_state, input_buffer, output_buffer, marker: _ } =
156 self;
157 let sponge_state = sponge_state.map(|x| builder.eval(x));
158 let mut copy_vec = |v: &Vec<Felt<SP1Field>>| v.iter().map(|x| builder.eval(*x)).collect();
159 DuplexChallengerVariable::<C> {
160 sponge_state,
161 input_buffer: copy_vec(input_buffer),
162 output_buffer: copy_vec(output_buffer),
163 marker: PhantomData,
164 }
165 }
166
167 fn observe(&mut self, builder: &mut Builder<C>, value: Felt<SP1Field>) {
168 self.output_buffer.clear();
169
170 self.input_buffer.push(value);
171
172 if self.input_buffer.len() == HASH_RATE {
173 self.duplexing(builder);
174 }
175 }
176
177 fn sample(&mut self, builder: &mut Builder<C>) -> Felt<SP1Field> {
178 if !self.input_buffer.is_empty() || self.output_buffer.is_empty() {
179 self.duplexing(builder);
180 }
181
182 self.output_buffer.pop().expect("output buffer should be non-empty")
183 }
184
185 fn sample_bits(&mut self, builder: &mut Builder<C>, nb_bits: usize) -> Vec<Felt<SP1Field>> {
186 assert!(nb_bits <= NUM_BITS);
187 let rand_f = self.sample(builder);
188 let mut rand_f_bits = builder.num2bits_v2_f(rand_f, NUM_BITS);
189 rand_f_bits.truncate(nb_bits);
190 rand_f_bits
191 }
192}
193
194impl<C: CircuitConfig> CanCopyChallenger<C> for DuplexChallengerVariable<C> {
195 fn copy(&self, builder: &mut Builder<C>) -> Self {
196 DuplexChallengerVariable::copy(self, builder)
197 }
198}
199
200impl<C: CircuitConfig> CanObserveVariable<C, Felt<SP1Field>> for DuplexChallengerVariable<C> {
201 fn observe(&mut self, builder: &mut Builder<C>, value: Felt<SP1Field>) {
202 DuplexChallengerVariable::observe(self, builder, value);
203 }
204}
205
206impl<C: CircuitConfig, const N: usize> CanObserveVariable<C, [Felt<SP1Field>; N]>
207 for DuplexChallengerVariable<C>
208{
209 fn observe(&mut self, builder: &mut Builder<C>, values: [Felt<SP1Field>; N]) {
210 for value in values {
211 self.observe(builder, value);
212 }
213 }
214}
215
216impl<C: CircuitConfig> CanSampleVariable<C, Felt<SP1Field>> for DuplexChallengerVariable<C> {
217 fn sample(&mut self, builder: &mut Builder<C>) -> Felt<SP1Field> {
218 DuplexChallengerVariable::sample(self, builder)
219 }
220}
221
222impl<C: CircuitConfig> CanSampleBitsVariable<C, Felt<SP1Field>> for DuplexChallengerVariable<C> {
223 fn sample_bits(&mut self, builder: &mut Builder<C>, nb_bits: usize) -> Vec<Felt<SP1Field>> {
224 DuplexChallengerVariable::sample_bits(self, builder, nb_bits)
225 }
226}
227
228impl<C: CircuitConfig> FieldChallengerVariable<C, Felt<SP1Field>> for DuplexChallengerVariable<C> {
229 fn sample_ext(&mut self, builder: &mut Builder<C>) -> Ext<SP1Field, SP1ExtensionField> {
230 let a = self.sample(builder);
231 let b = self.sample(builder);
232 let c = self.sample(builder);
233 let d = self.sample(builder);
234 builder.ext_from_base_slice(&[a, b, c, d])
235 }
236
237 fn check_witness(&mut self, builder: &mut Builder<C>, nb_bits: usize, witness: Felt<SP1Field>) {
238 self.observe(builder, witness);
239 let element_bits = self.sample_bits(builder, nb_bits);
240 for bit in element_bits {
241 builder.assert_felt_eq(bit, SP1Field::zero());
242 }
243 }
244
245 fn duplexing(&mut self, builder: &mut Builder<C>) {
246 assert!(self.input_buffer.len() <= HASH_RATE);
247
248 self.sponge_state[0..self.input_buffer.len()].copy_from_slice(self.input_buffer.as_slice());
249 self.input_buffer.clear();
250
251 self.sponge_state = C::poseidon2_permute_v2(builder, self.sponge_state);
252
253 self.output_buffer.clear();
254 self.output_buffer.extend_from_slice(&self.sponge_state[0..HASH_RATE]);
255 }
256}
257
258#[derive(Clone)]
259pub struct MultiField32ChallengerVariable<C: CircuitConfig> {
260 sponge_state: [Var<C::N>; 3],
261 input_buffer: Vec<Felt<SP1Field>>,
262 output_buffer: Vec<Felt<SP1Field>>,
263 output_var_buffer: Vec<Var<C::N>>,
264 num_duplex_elms: usize,
265 num_f_elms: usize,
266}
267
268impl<C: CircuitConfig> MultiField32ChallengerVariable<C> {
269 pub fn new(builder: &mut Builder<C>) -> Self {
270 MultiField32ChallengerVariable::<C> {
271 sponge_state: [
272 builder.eval(C::N::zero()),
273 builder.eval(C::N::zero()),
274 builder.eval(C::N::zero()),
275 ],
276 input_buffer: vec![],
277 output_buffer: vec![],
278 output_var_buffer: vec![],
279 num_duplex_elms: C::N::bits() / SP1Field::bits(),
280 num_f_elms: C::N::bits() / SP1Field::bits() / 2,
281 }
282 }
283
284 pub fn duplexing(&mut self, builder: &mut Builder<C>) {
285 assert!(self.input_buffer.len() <= self.num_duplex_elms * OUTER_CHALLENGER_RATE);
286
287 for (i, f_chunk) in self.input_buffer.chunks(self.num_duplex_elms).enumerate() {
288 self.sponge_state[i] = reduce_31(builder, f_chunk);
289 }
290 self.input_buffer.clear();
291
292 builder.push_op(DslIr::CircuitPoseidon2Permute(self.sponge_state));
293
294 self.output_buffer.clear();
295 self.output_var_buffer.clear();
296 for &pf_val in self.sponge_state[0..OUTER_CHALLENGER_RATE].iter() {
297 self.output_var_buffer.push(pf_val);
298 }
299 }
300
301 pub fn split_var(&mut self, builder: &mut Builder<C>) {
302 assert!(self.output_buffer.is_empty());
303 assert!(!self.output_var_buffer.is_empty());
304 let pf_val = self.output_var_buffer.pop().expect("output var buffer shouldn't be empty");
305 let f_vals = split_32(builder, pf_val, self.num_f_elms);
306 for f_val in f_vals {
307 self.output_buffer.push(f_val);
308 }
309 }
310
311 pub fn observe(&mut self, builder: &mut Builder<C>, value: Felt<SP1Field>) {
312 self.output_buffer.clear();
313 self.output_var_buffer.clear();
314
315 self.input_buffer.push(value);
316 if self.input_buffer.len() == self.num_duplex_elms * OUTER_CHALLENGER_RATE {
317 self.duplexing(builder);
318 }
319 }
320
321 pub fn observe_commitment(
322 &mut self,
323 builder: &mut Builder<C>,
324 value: [Var<C::N>; OUTER_DIGEST_SIZE],
325 ) {
326 for val in value {
327 let f_vals: Vec<Felt<SP1Field>> = split_32(builder, val, self.num_f_elms);
328 for f_val in f_vals {
329 self.observe(builder, f_val);
330 }
331 }
332 }
333
334 pub fn sample(&mut self, builder: &mut Builder<C>) -> Felt<SP1Field> {
335 if !self.input_buffer.is_empty()
336 || (self.output_buffer.is_empty() && self.output_var_buffer.is_empty())
337 {
338 self.duplexing(builder);
339 }
340
341 if self.output_buffer.is_empty() {
342 self.split_var(builder);
343 }
344 self.output_buffer.pop().expect("output buffer should be non-empty")
345 }
346
347 pub fn sample_ext(&mut self, builder: &mut Builder<C>) -> Ext<SP1Field, SP1ExtensionField> {
348 let a = self.sample(builder);
349 let b = self.sample(builder);
350 let c = self.sample(builder);
351 let d = self.sample(builder);
352 builder.felts2ext(&[a, b, c, d])
353 }
354
355 pub fn sample_bits(&mut self, builder: &mut Builder<C>, bits: usize) -> Vec<Var<C::N>> {
356 let rand_f = self.sample(builder);
357 builder.num2bits_f_circuit(rand_f)[0..bits].to_vec()
358 }
359
360 pub fn check_witness(
361 &mut self,
362 builder: &mut Builder<C>,
363 bits: usize,
364 witness: Felt<SP1Field>,
365 ) {
366 self.observe(builder, witness);
367 let element = self.sample_bits(builder, bits);
368 for bit in element {
369 builder.assert_var_eq(bit, C::N::from_canonical_usize(0));
370 }
371 }
372}
373
374impl<C: CircuitConfig> CanCopyChallenger<C> for MultiField32ChallengerVariable<C> {
375 fn copy(&self, builder: &mut Builder<C>) -> Self {
377 let MultiField32ChallengerVariable {
378 sponge_state,
379 input_buffer,
380 output_buffer,
381 output_var_buffer,
382 num_duplex_elms,
383 num_f_elms,
384 } = self;
385 let sponge_state = sponge_state.map(|x| builder.eval(x));
386 let mut copy_vec = |v: &Vec<Felt<SP1Field>>| v.iter().map(|x| builder.eval(*x)).collect();
387 MultiField32ChallengerVariable::<C> {
388 sponge_state,
389 num_duplex_elms: *num_duplex_elms,
390 num_f_elms: *num_f_elms,
391 input_buffer: copy_vec(input_buffer),
392 output_buffer: copy_vec(output_buffer),
393 output_var_buffer: output_var_buffer.iter().map(|x| builder.eval(*x)).collect(),
394 }
395 }
396}
397
398impl<C: CircuitConfig> CanObserveVariable<C, Felt<SP1Field>> for MultiField32ChallengerVariable<C> {
399 fn observe(&mut self, builder: &mut Builder<C>, value: Felt<SP1Field>) {
400 MultiField32ChallengerVariable::observe(self, builder, value);
401 }
402}
403
404impl<C: CircuitConfig> CanObserveVariable<C, [Var<C::N>; OUTER_DIGEST_SIZE]>
405 for MultiField32ChallengerVariable<C>
406{
407 fn observe(&mut self, builder: &mut Builder<C>, value: [Var<C::N>; OUTER_DIGEST_SIZE]) {
408 self.observe_commitment(builder, value)
409 }
410}
411
412impl<C: CircuitConfig> CanObserveVariable<C, Var<C::N>> for MultiField32ChallengerVariable<C> {
413 fn observe(&mut self, builder: &mut Builder<C>, value: Var<C::N>) {
414 self.observe_commitment(builder, [value])
415 }
416}
417
418impl<C: CircuitConfig> CanSampleVariable<C, Felt<SP1Field>> for MultiField32ChallengerVariable<C> {
419 fn sample(&mut self, builder: &mut Builder<C>) -> Felt<SP1Field> {
420 MultiField32ChallengerVariable::sample(self, builder)
421 }
422}
423
424impl<C: CircuitConfig> CanSampleBitsVariable<C, Var<C::N>> for MultiField32ChallengerVariable<C> {
425 fn sample_bits(&mut self, builder: &mut Builder<C>, bits: usize) -> Vec<Var<C::N>> {
426 MultiField32ChallengerVariable::sample_bits(self, builder, bits)
427 }
428}
429
430impl<C: CircuitConfig> FieldChallengerVariable<C, Var<C::N>> for MultiField32ChallengerVariable<C> {
431 fn sample_ext(&mut self, builder: &mut Builder<C>) -> Ext<SP1Field, SP1ExtensionField> {
432 MultiField32ChallengerVariable::sample_ext(self, builder)
433 }
434
435 fn check_witness(&mut self, builder: &mut Builder<C>, bits: usize, witness: Felt<SP1Field>) {
436 MultiField32ChallengerVariable::check_witness(self, builder, bits, witness);
437 }
438
439 fn duplexing(&mut self, builder: &mut Builder<C>) {
440 MultiField32ChallengerVariable::duplexing(self, builder);
441 }
442}
443
444pub fn reduce_31<C: CircuitConfig>(builder: &mut Builder<C>, vals: &[Felt<SP1Field>]) -> Var<C::N> {
445 let mut power = C::N::one();
446 let result: Var<C::N> = builder.eval(C::N::zero());
447 for val in vals.iter() {
448 let val = builder.felt2var_circuit(*val);
449 builder.assign(result, result + val * power);
450 power *= C::N::from_canonical_u64(1u64 << 31);
451 }
452 result
453}
454
455pub fn split_32<C: CircuitConfig>(
456 builder: &mut Builder<C>,
457 val: Var<C::N>,
458 n: usize,
459) -> Vec<Felt<SP1Field>> {
460 let bits = builder.num2bits_v_circuit(val, 256);
461 let mut results = Vec::new();
462 for i in 0..n {
463 let result: Felt<SP1Field> = builder.eval(SP1Field::zero());
464 for j in 0..64 {
465 let bit = bits[i * 64 + j];
466 let t = builder.eval(result + SP1Field::from_wrapped_u64(1 << j));
467 let z = builder.select_f(bit, t, result);
468 builder.assign(result, z);
469 }
470 results.push(result);
471 }
472 results
473}
474
475#[cfg(test)]
476pub(crate) mod tests {
477 #![allow(clippy::print_stdout)]
478
479 use std::{iter::zip, marker::PhantomData};
480
481 use crate::{
482 challenger::{CanCopyChallenger, MultiField32ChallengerVariable},
483 hash::{FieldHasherVariable, BN254_DIGEST_SIZE},
484 witness::OuterWitness,
485 };
486 use slop_algebra::AbstractField;
487
488 use slop_bn254::{outer_perm, Bn254Fr, OuterPerm};
489 use slop_challenger::{
490 CanObserve, CanSample, CanSampleBits, DuplexChallenger, FieldChallenger, IopCtx,
491 MultiField32Challenger,
492 };
493
494 use slop_symmetric::{CryptographicHasher, Hash, PseudoCompressionFunction};
495 use sp1_hypercube::inner_perm;
496 use sp1_primitives::{SP1Field, SP1GlobalContext, SP1OuterGlobalContext, SP1Perm};
497 use sp1_recursion_compiler::{
498 circuit::{AsmBuilder, AsmCompiler, AsmConfig},
499 config::OuterConfig,
500 constraints::ConstraintCompiler,
501 ir::{Builder, Config, Ext, ExtConst, Felt, Var},
502 };
503 use sp1_recursion_gnark_ffi::PlonkBn254Prover;
504 use sp1_recursion_machine::test::run_recursion_test_machines;
505
506 use crate::challenger::{DuplexChallengerVariable, FieldChallengerVariable};
507
508 type GC = SP1GlobalContext;
509 type C = OuterConfig;
510 type F = <GC as IopCtx>::F;
511 type EF = <GC as IopCtx>::EF;
512
513 #[tokio::test]
514 #[allow(clippy::uninlined_format_args)]
515 async fn test_compiler_challenger() {
516 let default_perm = inner_perm();
517 let mut challenger =
518 DuplexChallenger::<SP1Field, SP1Perm, 16, 8>::new(default_perm.clone());
519 challenger.observe(F::one());
520 challenger.observe(F::two());
521 challenger.observe(F::two());
522 challenger.observe(F::two());
523 let result: F = challenger.sample();
524 println!("expected result: {result}");
525 let result_ef: EF = challenger.sample_ext_element();
526 println!("expected result_ef: {result_ef}");
527
528 let mut builder = AsmBuilder::default();
529
530 let mut challenger = DuplexChallengerVariable::<AsmConfig> {
531 sponge_state: core::array::from_fn(|_| builder.eval(SP1Field::zero())),
532 input_buffer: vec![],
533 output_buffer: vec![],
534 marker: PhantomData,
535 };
536 let one: Felt<_> = builder.eval(F::one());
537 let two: Felt<_> = builder.eval(F::two());
538
539 challenger.observe(&mut builder, one);
540 challenger.observe(&mut builder, two);
541 challenger.observe(&mut builder, two);
542 challenger.observe(&mut builder, two);
543 let element = challenger.sample(&mut builder);
544 let element_ef = challenger.sample_ext(&mut builder);
545
546 let expected_result: Felt<_> = builder.eval(result);
547 let expected_result_ef: Ext<_, _> = builder.eval(result_ef.cons());
548 builder.print_f(element);
549 builder.assert_felt_eq(expected_result, element);
550 builder.print_e(element_ef);
551 builder.assert_ext_eq(expected_result_ef, element_ef);
552
553 let block = builder.into_root_block();
554 let mut compiler = AsmCompiler::default();
555 let program = compiler.compile_inner(block).validate().unwrap();
556
557 let witness_stream = Vec::new();
558 run_recursion_test_machines(program.clone(), witness_stream).await;
559 }
560
561 #[tokio::test]
562 #[allow(clippy::uninlined_format_args)]
563 async fn test_challenger_outer() {
564 type GC = SP1OuterGlobalContext;
565 type F = <GC as IopCtx>::F;
566 type EF = <GC as IopCtx>::EF;
567 type N = <C as Config>::N;
568
569 let default_perm = outer_perm();
570 let mut challenger =
571 MultiField32Challenger::<SP1Field, Bn254Fr, OuterPerm, 3, 2>::new(default_perm.clone())
572 .unwrap();
573 challenger.observe(F::one());
574 challenger.observe(F::two());
575 challenger.observe(F::two());
576 challenger.observe(F::two());
577 let commit = Hash::from([N::two()]);
578 challenger.observe(commit);
579 let result: F = challenger.sample();
580 println!("expected result: {result}");
581 let result_ef: EF = challenger.sample_ext_element();
582 println!("expected result_ef: {result_ef}");
583 let mut bits = challenger.sample_bits(30);
584 let mut bits_vec = vec![];
585 for _ in 0..30 {
586 bits_vec.push(bits % 2);
587 bits >>= 1;
588 }
589 println!("expected bits: {bits_vec:?}");
590
591 let mut builder = Builder::<C>::default();
592
593 let mut challenger = MultiField32ChallengerVariable::<C>::new(&mut builder);
595 let one: Felt<_> = builder.eval(F::one());
596 let two: Felt<_> = builder.eval(F::two());
597 let two_var: Var<_> = builder.eval(N::two());
598 challenger.observe(&mut builder, one);
600 challenger.observe(&mut builder, two);
601 challenger.observe(&mut builder, two);
602 challenger.observe(&mut builder, two);
603 challenger.observe_commitment(&mut builder, [two_var]);
604
605 challenger = challenger.copy(&mut builder);
607 let element = challenger.sample(&mut builder);
608 let element_ef = challenger.sample_ext(&mut builder);
609 let bits = challenger.sample_bits(&mut builder, 31);
610
611 let expected_result: Felt<_> = builder.eval(result);
612 let expected_result_ef: Ext<_, _> = builder.eval(result_ef.cons());
613 builder.print_f(element);
614 builder.assert_felt_eq(expected_result, element);
615 builder.print_e(element_ef);
616 builder.assert_ext_eq(expected_result_ef, element_ef);
617 for (expected_bit, bit) in zip(bits_vec.iter(), bits.iter()) {
618 let expected_bit: Var<_> = builder.eval(N::from_canonical_usize(*expected_bit));
619 builder.print_v(*bit);
620 builder.assert_var_eq(expected_bit, *bit);
621 }
622
623 let mut backend = ConstraintCompiler::<C>::default();
624 let constraints = backend.emit(builder.into_operations());
625 let witness = OuterWitness::default();
626 PlonkBn254Prover::test::<C>(constraints, witness);
627 }
628
629 #[test]
630 fn test_select_chain_digest() {
631 type N = <C as Config>::N;
632
633 let mut builder = Builder::<C>::default();
634
635 let one: Var<_> = builder.eval(N::one());
636 let two: Var<_> = builder.eval(N::two());
637
638 let to_swap = [[one], [two]];
639 let result = SP1OuterGlobalContext::select_chain_digest(&mut builder, one, to_swap);
640
641 builder.assert_var_eq(result[0][0], two);
642 builder.assert_var_eq(result[1][0], one);
643
644 let mut backend = ConstraintCompiler::<C>::default();
645 let constraints = backend.emit(builder.into_operations());
646 let witness = OuterWitness::default();
647 PlonkBn254Prover::test::<C>(constraints, witness);
648 }
649
650 #[test]
651 fn test_p2_hash() {
652 let (hasher, _) = SP1OuterGlobalContext::default_hasher_and_compressor();
653
654 let input: [SP1Field; 7] = [
655 SP1Field::from_canonical_u32(0),
656 SP1Field::from_canonical_u32(1),
657 SP1Field::from_canonical_u32(2),
658 SP1Field::from_canonical_u32(2),
659 SP1Field::from_canonical_u32(2),
660 SP1Field::from_canonical_u32(2),
661 SP1Field::from_canonical_u32(2),
662 ];
663 let output: [Bn254Fr; 1] = hasher.hash_iter(input);
664
665 let mut builder = Builder::<C>::default();
666 let a: Felt<_> = builder.eval(input[0]);
667 let b: Felt<_> = builder.eval(input[1]);
668 let c: Felt<_> = builder.eval(input[2]);
669 let d: Felt<_> = builder.eval(input[3]);
670 let e: Felt<_> = builder.eval(input[4]);
671 let f: Felt<_> = builder.eval(input[5]);
672 let g: Felt<_> = builder.eval(input[6]);
673 let result = SP1OuterGlobalContext::hash(&mut builder, &[a, b, c, d, e, f, g]);
674
675 builder.assert_var_eq(result[0], output[0]);
676
677 let mut backend = ConstraintCompiler::<C>::default();
678 let constraints = backend.emit(builder.into_operations());
679 PlonkBn254Prover::test::<C>(constraints.clone(), OuterWitness::default());
680 }
681
682 #[test]
683 fn test_p2_compress() {
684 type OuterDigestVariable = [Var<<C as Config>::N>; BN254_DIGEST_SIZE];
685 let (_, compressor) = SP1OuterGlobalContext::default_hasher_and_compressor();
686
687 let a: [Bn254Fr; 1] = [Bn254Fr::two()];
688 let b: [Bn254Fr; 1] = [Bn254Fr::two()];
689 let gt = compressor.compress([a, b]);
690
691 let mut builder = Builder::<C>::default();
692 let a: OuterDigestVariable = [builder.eval(a[0])];
693 let b: OuterDigestVariable = [builder.eval(b[0])];
694 let result = SP1OuterGlobalContext::compress(&mut builder, [a, b]);
695 builder.assert_var_eq(result[0], gt[0]);
696
697 let mut backend = ConstraintCompiler::<C>::default();
698 let constraints = backend.emit(builder.into_operations());
699 PlonkBn254Prover::test::<C>(constraints.clone(), OuterWitness::default());
700 }
701}