use std::borrow::Borrow; use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir}; use p3_baby_bear::{BabyBear, DiffusionMatrixBabyBear}; use p3_challenger::DuplexChallenger; use p3_commit::ExtensionMmcs; use p3_dft::Radix2DitParallel; use p3_field::extension::BinomialExtensionField; use p3_field::{AbstractField, Field, PrimeField64}; use p3_fri::{FriConfig, TwoAdicFriPcs}; use p3_matrix::dense::RowMajorMatrix; use p3_matrix::Matrix; use p3_merkle_tree::FieldMerkleTreeMmcs; use p3_poseidon2::{Poseidon2, Poseidon2ExternalMatrixGeneral}; use p3_symmetric::{PaddingFreeSponge, TruncatedPermutation}; use p3_uni_stark::{prove, verify, StarkConfig}; use p3_util::log2_ceil_usize; use rand::thread_rng; /// For testing the public values feature pub struct FibonacciAir {} impl BaseAir for FibonacciAir { fn width(&self) -> usize { NUM_FIBONACCI_COLS } } impl Air for FibonacciAir { fn eval(&self, builder: &mut AB) { let main = builder.main(); let pis = builder.public_values(); let a = pis[0]; let b = pis[1]; let x = pis[2]; let (local, next) = (main.row_slice(0), main.row_slice(1)); let local: &FibonacciRow = (*local).borrow(); let next: &FibonacciRow = (*next).borrow(); let mut when_first_row = builder.when_first_row(); when_first_row.assert_eq(local.left, a); when_first_row.assert_eq(local.right, b); let mut when_transition = builder.when_transition(); // a' <- b when_transition.assert_eq(local.right, next.left); // b' <- a + b when_transition.assert_eq(local.left + local.right, next.right); builder.when_last_row().assert_eq(local.right, x); } } pub fn generate_trace_rows(a: u64, b: u64, n: usize) -> RowMajorMatrix { assert!(n.is_power_of_two()); let mut trace = RowMajorMatrix::new(vec![F::zero(); n * NUM_FIBONACCI_COLS], NUM_FIBONACCI_COLS); let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::>() }; assert!(prefix.is_empty(), "Alignment should match"); assert!(suffix.is_empty(), "Alignment should match"); assert_eq!(rows.len(), n); rows[0] = FibonacciRow::new(F::from_canonical_u64(a), F::from_canonical_u64(b)); for i in 1..n { rows[i].left = rows[i - 1].right; rows[i].right = rows[i - 1].left + rows[i - 1].right; } trace } const NUM_FIBONACCI_COLS: usize = 2; pub struct FibonacciRow { pub left: F, pub right: F, } impl FibonacciRow { const fn new(left: F, right: F) -> FibonacciRow { FibonacciRow { left, right } } } impl Borrow> for [F] { fn borrow(&self) -> &FibonacciRow { debug_assert_eq!(self.len(), NUM_FIBONACCI_COLS); let (prefix, shorts, suffix) = unsafe { self.align_to::>() }; debug_assert!(prefix.is_empty(), "Alignment should match"); debug_assert!(suffix.is_empty(), "Alignment should match"); debug_assert_eq!(shorts.len(), 1); &shorts[0] } } type Val = BabyBear; type Perm = Poseidon2; type MyHash = PaddingFreeSponge; type MyCompress = TruncatedPermutation; type ValMmcs = FieldMerkleTreeMmcs<::Packing, ::Packing, MyHash, MyCompress, 8>; type Challenge = BinomialExtensionField; type ChallengeMmcs = ExtensionMmcs; type Challenger = DuplexChallenger; type Dft = Radix2DitParallel; type Pcs = TwoAdicFriPcs; type MyConfig = StarkConfig; #[test] fn test_public_value() { let perm = Perm::new_from_rng_128( Poseidon2ExternalMatrixGeneral, DiffusionMatrixBabyBear, &mut thread_rng(), ); let hash = MyHash::new(perm.clone()); let compress = MyCompress::new(perm.clone()); let val_mmcs = ValMmcs::new(hash, compress); let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); let dft = Dft {}; let trace = generate_trace_rows::(0, 1, 1 << 3); let fri_config = FriConfig { log_blowup: 2, num_queries: 28, proof_of_work_bits: 8, mmcs: challenge_mmcs, }; let pcs = Pcs::new(log2_ceil_usize(trace.height()), dft, val_mmcs, fri_config); let config = MyConfig::new(pcs); let mut challenger = Challenger::new(perm.clone()); let pis = vec![ BabyBear::from_canonical_u64(0), BabyBear::from_canonical_u64(1), BabyBear::from_canonical_u64(21), ]; let proof = prove(&config, &FibonacciAir {}, &mut challenger, trace, &pis); let mut challenger = Challenger::new(perm); verify(&config, &FibonacciAir {}, &mut challenger, &proof, &pis).expect("verification failed"); } #[cfg(debug_assertions)] #[test] #[should_panic(expected = "assertion `left == right` failed: constraints had nonzero value")] fn test_incorrect_public_value() { let perm = Perm::new_from_rng_128( Poseidon2ExternalMatrixGeneral, DiffusionMatrixBabyBear, &mut thread_rng(), ); let hash = MyHash::new(perm.clone()); let compress = MyCompress::new(perm.clone()); let val_mmcs = ValMmcs::new(hash, compress); let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone()); let dft = Dft {}; let fri_config = FriConfig { log_blowup: 2, num_queries: 28, proof_of_work_bits: 8, mmcs: challenge_mmcs, }; let trace = generate_trace_rows::(0, 1, 1 << 3); let pcs = Pcs::new(log2_ceil_usize(trace.height()), dft, val_mmcs, fri_config); let config = MyConfig::new(pcs); let mut challenger = Challenger::new(perm.clone()); let pis = vec![ BabyBear::from_canonical_u64(0), BabyBear::from_canonical_u64(1), BabyBear::from_canonical_u64(123_123), // incorrect result ]; prove(&config, &FibonacciAir {}, &mut challenger, trace, &pis); }