Skip to content

Commit

Permalink
perf!: Include last FRI polynomial into proof
Browse files Browse the repository at this point in the history
BREAKING CHANGE

Now the prover sends the last polynomial in addition to the last
codeword in FRI. The verifier verifies that the polynomial is of
low degree directly (without iNTTs!) and checks that it matches
with the codeword using the barycentric evaluation function and
randomness sampled from the proof stream's sponge state.

No performance change was observed on my laptop using benchmark
`prove_verify_halt` (11ms in both cases) but the main selling
point comes from the smaller anticipated clock cycle count in the
recursive verifier.

Closes #156.
  • Loading branch information
aszepieniec committed Apr 22, 2024
1 parent c6d9e05 commit b05d5cb
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 13 deletions.
38 changes: 25 additions & 13 deletions triton-vm/src/fri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::marker::PhantomData;

use itertools::Itertools;
use num_traits::One;
use num_traits::Zero;
use rayon::iter::*;
use twenty_first::math::traits::FiniteField;
use twenty_first::math::traits::PrimitiveRootOfUnity;
Expand Down Expand Up @@ -55,6 +56,7 @@ impl<'stream, H: AlgebraicHasher> FriProver<'stream, H> {
self.commit_to_next_round()?;
}
self.send_last_codeword();
self.send_last_polynomial();
Ok(())
}

Expand Down Expand Up @@ -96,6 +98,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.clone();
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();

Expand Down Expand Up @@ -195,6 +206,7 @@ struct FriVerifier<'stream, H: AlgebraicHasher> {
rounds: Vec<VerifierRound>,
first_round_domain: ArithmeticDomain,
last_round_codeword: Vec<XFieldElement>,
last_round_polynomial: Polynomial<XFieldElement>,
last_round_max_degree: usize,
num_rounds: usize,
num_collinearity_checks: usize,
Expand All @@ -213,7 +225,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<()> {
Expand Down Expand Up @@ -290,6 +303,12 @@ impl<'stream, H: AlgebraicHasher> FriVerifier<'stream, H> {
Ok(())
}

fn receive_last_round_polynomial(&mut self) -> VerifierResult<()> {
self.last_round_polynomial =
Polynomial::new(self.proof_stream.dequeue()?.try_into_fri_polynomial()?);
Ok(())
}

fn compute_last_round_folded_partial_codeword(&mut self) -> VerifierResult<()> {
self.sample_first_round_collinearity_check_indices();
self.receive_authentic_partially_revealed_codewords()?;
Expand Down Expand Up @@ -505,27 +524,18 @@ impl<'stream, H: AlgebraicHasher> FriVerifier<'stream, H> {
&mut self,
) -> VerifierResult<()> {
let indeterminate = self.proof_stream.sample_scalars(1)[0];
let interpolant = self.last_round_polynomial();
let horner_evaluation = interpolant.evaluate(indeterminate);
let polynomial = self.last_round_polynomial.clone();
let horner_evaluation = polynomial.evaluate(indeterminate);
let barycentric_evaluation =
Fri::<Tip5>::barycentric_evaluate(&self.last_round_codeword, indeterminate);
if horner_evaluation != barycentric_evaluation
|| interpolant.degree() > self.last_round_max_degree.try_into().unwrap()
|| polynomial.degree() > self.last_round_max_degree.try_into().unwrap()
{
return Err(LastRoundPolynomialHasTooHighDegree);
}
println!("horner: {}", horner_evaluation);
println!("barycentric: {}", barycentric_evaluation);
Ok(())
}

fn last_round_polynomial(&self) -> Polynomial<XFieldElement> {
let domain = self.rounds.last().unwrap().domain;
domain
.with_offset(BFieldElement::one())
.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();
Expand Down Expand Up @@ -634,6 +644,7 @@ impl<H: AlgebraicHasher> Fri<H> {
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,
Expand Down Expand Up @@ -914,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:?}"),
}
}
Expand Down
2 changes: 2 additions & 0 deletions triton-vm/src/proof_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ proof_items!(
Log2PaddedHeight(u32) => false, try_into_log2_padded_height,
QuotientSegmentsElements(Vec<QuotientSegments>) => false, try_into_quot_segments_elements,
FriCodeword(Vec<XFieldElement>) => false, try_into_fri_codeword,
FriPolynomial(Vec<XFieldElement>) => false, try_into_fri_polynomial,
FriResponse(FriResponse) => false, try_into_fri_response,
);

Expand Down Expand Up @@ -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());
}

Expand Down

0 comments on commit b05d5cb

Please sign in to comment.