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