// code forked from:
// https://github.com/EspressoSystems/hyperplonk/tree/main/subroutines/src/poly_iop/sum_check
//
// Copyright (c) 2023 Espresso Systems (espressosys.com)
// This file is part of the HyperPlonk library.
// You should have received a copy of the MIT License
// along with the HyperPlonk library. If not, see .
//! This module implements the sum check protocol.
use crate::utils::virtual_polynomial::{VPAuxInfo, VirtualPolynomial};
use ark_ff::PrimeField;
use ark_poly::DenseMultilinearExtension;
use ark_std::{end_timer, start_timer};
use std::{fmt::Debug, sync::Arc};
use espresso_subroutines::poly_iop::{prelude::PolyIOPErrors, PolyIOP};
use espresso_transcript::IOPTranscript;
use structs::{IOPProof, IOPProverState, IOPVerifierState};
mod prover;
pub mod structs;
pub mod verifier;
/// Trait for doing sum check protocols.
pub trait SumCheck {
type VirtualPolynomial;
type VPAuxInfo;
type MultilinearExtension;
type SumCheckProof: Clone + Debug + Default + PartialEq;
type Transcript;
type SumCheckSubClaim: Clone + Debug + Default + PartialEq;
/// Extract sum from the proof
fn extract_sum(proof: &Self::SumCheckProof) -> F;
/// Initialize the system with a transcript
///
/// This function is optional -- in the case where a SumCheck is
/// an building block for a more complex protocol, the transcript
/// may be initialized by this complex protocol, and passed to the
/// SumCheck prover/verifier.
fn init_transcript() -> Self::Transcript;
/// Generate proof of the sum of polynomial over {0,1}^`num_vars`
///
/// The polynomial is represented in the form of a VirtualPolynomial.
fn prove(
poly: &Self::VirtualPolynomial,
transcript: &mut Self::Transcript,
) -> Result;
/// Verify the claimed sum using the proof
fn verify(
sum: F,
proof: &Self::SumCheckProof,
aux_info: &Self::VPAuxInfo,
transcript: &mut Self::Transcript,
) -> Result;
}
/// Trait for sum check protocol prover side APIs.
pub trait SumCheckProver
where
Self: Sized,
{
type VirtualPolynomial;
type ProverMessage;
/// Initialize the prover state to argue for the sum of the input polynomial
/// over {0,1}^`num_vars`.
fn prover_init(polynomial: &Self::VirtualPolynomial) -> Result;
/// Receive message from verifier, generate prover message, and proceed to
/// next round.
///
/// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2).
fn prove_round_and_update_state(
&mut self,
challenge: &Option,
) -> Result;
}
/// Trait for sum check protocol verifier side APIs.
pub trait SumCheckVerifier {
type VPAuxInfo;
type ProverMessage;
type Challenge;
type Transcript;
type SumCheckSubClaim;
/// Initialize the verifier's state.
fn verifier_init(index_info: &Self::VPAuxInfo) -> Self;
/// Run verifier for the current round, given a prover message.
///
/// Note that `verify_round_and_update_state` only samples and stores
/// challenges; and update the verifier's state accordingly. The actual
/// verifications are deferred (in batch) to `check_and_generate_subclaim`
/// at the last step.
fn verify_round_and_update_state(
&mut self,
prover_msg: &Self::ProverMessage,
transcript: &mut Self::Transcript,
) -> Result;
/// This function verifies the deferred checks in the interactive version of
/// the protocol; and generate the subclaim. Returns an error if the
/// proof failed to verify.
///
/// If the asserted sum is correct, then the multilinear polynomial
/// evaluated at `subclaim.point` will be `subclaim.expected_evaluation`.
/// Otherwise, it is highly unlikely that those two will be equal.
/// Larger field size guarantees smaller soundness error.
fn check_and_generate_subclaim(
&self,
asserted_sum: &F,
) -> Result;
}
/// A SumCheckSubClaim is a claim generated by the verifier at the end of
/// verification when it is convinced.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SumCheckSubClaim {
/// the multi-dimensional point that this multilinear extension is evaluated
/// to
pub point: Vec,
/// the expected evaluation
pub expected_evaluation: F,
}
impl SumCheck for PolyIOP {
type SumCheckProof = IOPProof;
type VirtualPolynomial = VirtualPolynomial;
type VPAuxInfo = VPAuxInfo;
type MultilinearExtension = Arc>;
type SumCheckSubClaim = SumCheckSubClaim;
type Transcript = IOPTranscript;
fn extract_sum(proof: &Self::SumCheckProof) -> F {
let start = start_timer!(|| "extract sum");
let res = proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1];
end_timer!(start);
res
}
fn init_transcript() -> Self::Transcript {
let start = start_timer!(|| "init transcript");
let res = IOPTranscript::::new(b"Initializing SumCheck transcript");
end_timer!(start);
res
}
fn prove(
poly: &Self::VirtualPolynomial,
transcript: &mut Self::Transcript,
) -> Result {
let start = start_timer!(|| "sum check prove");
transcript.append_serializable_element(b"aux info", &poly.aux_info)?;
let mut prover_state = IOPProverState::prover_init(poly)?;
let mut challenge = None;
let mut prover_msgs = Vec::with_capacity(poly.aux_info.num_variables);
for _ in 0..poly.aux_info.num_variables {
let prover_msg =
IOPProverState::prove_round_and_update_state(&mut prover_state, &challenge)?;
transcript.append_serializable_element(b"prover msg", &prover_msg)?;
prover_msgs.push(prover_msg);
challenge = Some(transcript.get_and_append_challenge(b"Internal round")?);
}
// pushing the last challenge point to the state
if let Some(p) = challenge {
prover_state.challenges.push(p)
};
end_timer!(start);
Ok(IOPProof {
point: prover_state.challenges,
proofs: prover_msgs,
})
}
fn verify(
claimed_sum: F,
proof: &Self::SumCheckProof,
aux_info: &Self::VPAuxInfo,
transcript: &mut Self::Transcript,
) -> Result {
let start = start_timer!(|| "sum check verify");
transcript.append_serializable_element(b"aux info", aux_info)?;
let mut verifier_state = IOPVerifierState::verifier_init(aux_info);
for i in 0..aux_info.num_variables {
let prover_msg = proof.proofs.get(i).expect("proof is incomplete");
transcript.append_serializable_element(b"prover msg", prover_msg)?;
IOPVerifierState::verify_round_and_update_state(
&mut verifier_state,
prover_msg,
transcript,
)?;
}
let res = IOPVerifierState::check_and_generate_subclaim(&verifier_state, &claimed_sum);
end_timer!(start);
res
}
}