diff --git a/triton-vm/src/fri.rs b/triton-vm/src/fri.rs index 68fab7a0..b842ad6c 100644 --- a/triton-vm/src/fri.rs +++ b/triton-vm/src/fri.rs @@ -1,6 +1,7 @@ use std::marker::PhantomData; use itertools::Itertools; +use num_traits::Zero; use rayon::iter::*; use twenty_first::math::traits::FiniteField; use twenty_first::math::traits::PrimitiveRootOfUnity; @@ -54,6 +55,7 @@ impl<'stream, H: AlgebraicHasher> FriProver<'stream, H> { self.commit_to_next_round()?; } self.send_last_codeword(); + self.send_last_polynomial(); Ok(()) } @@ -95,6 +97,15 @@ impl<'stream, H: AlgebraicHasher> FriProver<'stream, H> { self.proof_stream.enqueue(proof_item); } + fn send_last_polynomial(&mut self) { + let last_codeword = &self.rounds.last().unwrap().codeword; + let last_polynomial = ArithmeticDomain::of_length(last_codeword.len()) + .unwrap() + .interpolate(last_codeword); + let proof_item = ProofItem::FriPolynomial(last_polynomial.coefficients); + self.proof_stream.enqueue(proof_item); + } + fn query(&mut self) -> ProverResult<()> { self.sample_first_round_collinearity_check_indices(); @@ -194,6 +205,7 @@ struct FriVerifier<'stream, H: AlgebraicHasher> { rounds: Vec, first_round_domain: ArithmeticDomain, last_round_codeword: Vec, + last_round_polynomial: Polynomial, last_round_max_degree: usize, num_rounds: usize, num_collinearity_checks: usize, @@ -212,7 +224,8 @@ struct VerifierRound { impl<'stream, H: AlgebraicHasher> FriVerifier<'stream, H> { fn initialize(&mut self) -> VerifierResult<()> { self.initialize_verification_rounds()?; - self.receive_last_round_codeword() + self.receive_last_round_codeword()?; + self.receive_last_round_polynomial() } fn initialize_verification_rounds(&mut self) -> VerifierResult<()> { @@ -289,6 +302,12 @@ impl<'stream, H: AlgebraicHasher> FriVerifier<'stream, H> { Ok(()) } + fn receive_last_round_polynomial(&mut self) -> VerifierResult<()> { + let coefficients = self.proof_stream.dequeue()?.try_into_fri_polynomial()?; + self.last_round_polynomial = Polynomial::new(coefficients); + Ok(()) + } + fn compute_last_round_folded_partial_codeword(&mut self) -> VerifierResult<()> { self.sample_first_round_collinearity_check_indices(); self.receive_authentic_partially_revealed_codewords()?; @@ -504,25 +523,17 @@ impl<'stream, H: AlgebraicHasher> FriVerifier<'stream, H> { &mut self, ) -> VerifierResult<()> { let indeterminate = self.proof_stream.sample_scalars(1)[0]; - let last_round_polynomial = self.last_round_polynomial(); - let horner_evaluation = last_round_polynomial.evaluate(indeterminate); + let horner_evaluation = self.last_round_polynomial.evaluate(indeterminate); let barycentric_evaluation = barycentric_evaluate(&self.last_round_codeword, indeterminate); if horner_evaluation != barycentric_evaluation { return Err(LastRoundPolynomialEvaluationMismatch); } - if last_round_polynomial.degree() > self.last_round_max_degree.try_into().unwrap() { + if self.last_round_polynomial.degree() > self.last_round_max_degree.try_into().unwrap() { return Err(LastRoundPolynomialHasTooHighDegree); } Ok(()) } - fn last_round_polynomial(&self) -> Polynomial { - let domain = self.rounds.last().unwrap().domain; - domain - .with_offset(bfe!(1)) - .interpolate(&self.last_round_codeword) - } - fn first_round_partially_revealed_codeword(&self) -> Vec<(usize, XFieldElement)> { let partial_codeword_a = self.rounds[0].partial_codeword_a.clone(); let partial_codeword_b = self.rounds[0].partial_codeword_b.clone(); @@ -631,6 +642,7 @@ impl Fri { rounds: vec![], first_round_domain: self.domain, last_round_codeword: vec![], + last_round_polynomial: Polynomial::zero(), last_round_max_degree: self.last_round_max_degree(), num_rounds: self.num_rounds(), num_collinearity_checks: self.num_collinearity_checks, @@ -913,6 +925,7 @@ mod tests { (MerkleRoot(p), MerkleRoot(v)) => prop_assert_eq!(p, v), (FriResponse(p), FriResponse(v)) => prop_assert_eq!(p, v), (FriCodeword(p), FriCodeword(v)) => prop_assert_eq!(p, v), + (FriPolynomial(p), FriPolynomial(v)) => prop_assert_eq!(p, v), _ => panic!("Unknown items.\nProver: {prover_item:?}\nVerifier: {verifier_item:?}"), } } diff --git a/triton-vm/src/proof_item.rs b/triton-vm/src/proof_item.rs index 6af51f8c..0c0d8c06 100644 --- a/triton-vm/src/proof_item.rs +++ b/triton-vm/src/proof_item.rs @@ -110,6 +110,7 @@ proof_items!( Log2PaddedHeight(u32) => false, try_into_log2_padded_height, QuotientSegmentsElements(Vec) => false, try_into_quot_segments_elements, FriCodeword(Vec) => false, try_into_fri_codeword, + FriPolynomial(Vec) => false, try_into_fri_polynomial, FriResponse(FriResponse) => false, try_into_fri_response, ); @@ -189,6 +190,7 @@ pub(crate) mod tests { assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_log2_padded_height()); assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_quot_segments_elements()); assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_fri_codeword()); + assert!(let Err(UnexpectedItem{..}) = item.clone().try_into_fri_polynomial()); assert!(let Err(UnexpectedItem{..}) = item.try_into_fri_response()); }