diff --git a/arithmetic/src/virtual_polynomial.rs b/arithmetic/src/virtual_polynomial.rs index df7ef13..50ab6db 100644 --- a/arithmetic/src/virtual_polynomial.rs +++ b/arithmetic/src/virtual_polynomial.rs @@ -52,7 +52,7 @@ pub struct VirtualPolynomial { raw_pointers_lookup_table: HashMap<*const DenseMultilinearExtension, usize>, } -#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, CanonicalSerialize)] /// Auxiliary information about the multilinear polynomial pub struct VPAuxInfo { /// max number of multiplicands in each product diff --git a/hyperplonk/src/structs.rs b/hyperplonk/src/structs.rs index e5e4509..3f6e3f5 100644 --- a/hyperplonk/src/structs.rs +++ b/hyperplonk/src/structs.rs @@ -165,7 +165,7 @@ pub struct HyperPlonkVerifyingKey, Vec)>, } diff --git a/pcs/src/structs.rs b/pcs/src/structs.rs index 915c52d..bd0dfd9 100644 --- a/pcs/src/structs.rs +++ b/pcs/src/structs.rs @@ -1,7 +1,7 @@ use ark_ec::PairingEngine; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, Default, PartialEq)] +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, Default, PartialEq, Eq)] /// A commitment is an Affine point. pub struct Commitment { /// the actual commitment is an affine point. diff --git a/poly-iop/Cargo.toml b/poly-iop/Cargo.toml index 3388cde..e299ff2 100644 --- a/poly-iop/Cargo.toml +++ b/poly-iop/Cargo.toml @@ -6,12 +6,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +pcs = { path = "../pcs" } ark-ff = { version = "^0.3.0", default-features = false } ark-std = { version = "^0.3.0", default-features = false } ark-poly = { version = "^0.3.0", default-features = false } ark-serialize = { version = "^0.3.0", default-features = false } ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] } +ark-ec = { version = "^0.3.0", default-features = false } rand_chacha = { version = "0.3.0", default-features = false } displaydoc = { version = "0.2.3", default-features = false } @@ -37,7 +39,8 @@ parallel = [ "arithmetic/parallel", "ark-std/parallel", "ark-ff/parallel", - "ark-poly/parallel" + "ark-poly/parallel", + "pcs/parallel", ] print-trace = [ "arithmetic/print-trace", diff --git a/poly-iop/src/errors.rs b/poly-iop/src/errors.rs index bc71eec..916d4cc 100644 --- a/poly-iop/src/errors.rs +++ b/poly-iop/src/errors.rs @@ -3,6 +3,7 @@ use arithmetic::ArithErrors; use ark_std::string::String; use displaydoc::Display; +use pcs::prelude::PCSErrors; use transcript::TranscriptErrors; /// A `enum` specifying the possible failure modes of the PolyIOP. @@ -26,6 +27,8 @@ pub enum PolyIOPErrors { TranscriptErrors(TranscriptErrors), /// Arithmetic Error: {0} ArithmeticErrors(ArithErrors), + /// PCS error {0} + PCSErrors(PCSErrors), } impl From for PolyIOPErrors { @@ -45,3 +48,9 @@ impl From for PolyIOPErrors { Self::ArithmeticErrors(e) } } + +impl From for PolyIOPErrors { + fn from(e: PCSErrors) -> Self { + Self::PCSErrors(e) + } +} diff --git a/poly-iop/src/prod_check/mod.rs b/poly-iop/src/prod_check/mod.rs index 9c1d0d6..9fab5ba 100644 --- a/poly-iop/src/prod_check/mod.rs +++ b/poly-iop/src/prod_check/mod.rs @@ -1,37 +1,49 @@ -//! Main module for the Permutation Check protocol +//! Main module for the Product Check protocol -use crate::{errors::PolyIOPErrors, ZeroCheck}; -use arithmetic::VirtualPolynomial; -use ark_ff::PrimeField; +use crate::{ + errors::PolyIOPErrors, + prod_check::util::{compute_product_poly, prove_zero_check}, + PolyIOP, ZeroCheck, +}; +use arithmetic::VPAuxInfo; +use ark_ec::PairingEngine; +use ark_ff::{One, PrimeField, Zero}; use ark_poly::DenseMultilinearExtension; +use ark_std::{end_timer, start_timer}; +use pcs::prelude::PolynomialCommitmentScheme; +use std::{marker::PhantomData, rc::Rc}; use transcript::IOPTranscript; -/// A ProductCheck is derived from ZeroCheck. -/// -/// A ProductCheck IOP takes the following steps: +mod util; + +/// A product-check proves that two n-variate multilinear polynomials `f(x), +/// g(x)` satisfy: +/// \prod_{x \in {0,1}^n} f(x) = \prod_{x \in {0,1}^n} g(x) /// -/// Inputs: -/// - f(x) +/// A ProductCheck is derived from ZeroCheck. /// /// Prover steps: -/// 1. `compute_product_poly` to build `prod(x0, ..., x_n)` from virtual -/// polynomial f -/// 2. push commitments of `f(x)`, `prod(x)` to the transcript -/// (done by the snark caller) -/// 3. `generate_challenge` from current transcript (generate alpha) -/// 4. `prove` to generate the zerocheck proof for the virtual polynomial -/// prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x)) +/// 1. build `prod(x0, ..., x_n)` from f and g, +/// such that `prod(0, x1, ..., xn)` equals `f/g` over domain {0,1}^n +/// 2. push commitments of `prod(x)` to the transcript, +/// and `generate_challenge` from current transcript (generate alpha) +/// 3. generate the zerocheck proof for the virtual polynomial +/// prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x) * g(x)) /// /// Verifier steps: -/// 1. Extract commitments of `f(x)`, `prod(x)` from the proof, push them to the -/// transcript (done by the snark caller) +/// 1. Extract commitments of `prod(x)` from the proof, push +/// them to the transcript /// 2. `generate_challenge` from current transcript (generate alpha) /// 3. `verify` to verify the zerocheck proof and generate the subclaim for /// polynomial evaluations -pub trait ProductCheck: ZeroCheck { +pub trait ProductCheck: ZeroCheck +where + E: PairingEngine, + PCS: PolynomialCommitmentScheme, +{ type ProductCheckSubClaim; - type ProductCheckChallenge; type ProductProof; + type Polynomial; /// Initialize the system with a transcript /// @@ -41,58 +53,44 @@ pub trait ProductCheck: ZeroCheck { /// ProductCheck prover/verifier. fn init_transcript() -> Self::Transcript; - /// Generate random challenge `alpha` from a transcript. - fn generate_challenge( - transcript: &mut Self::Transcript, - ) -> Result; - - /// Compute the product polynomial `prod(x)` where - /// - /// - `prod(0,x) := prod(0, x1, …, xn)` is the MLE over the - /// evaluations of `f(x)` on the boolean hypercube {0,1}^n - /// - /// - `prod(1,x)` is a MLE over the evaluations of `prod(x, 0) * prod(x, 1)` - /// on the boolean hypercube {0,1}^n - /// - /// The caller needs to check num_vars matches in f - /// Cost: linear in N. - fn compute_product_poly( - fx: &VirtualPolynomial, - ) -> Result, PolyIOPErrors>; - - /// Initialize the prover to argue that for a virtual polynomial f(x), - /// it holds that `s = \prod_{x \in {0,1}^n} f(x)` + /// Generate a proof for product check, showing that witness multilinear + /// polynomials f(x), g(x) satisfy `\prod_{x \in {0,1}^n} f(x) = + /// \prod_{x \in {0,1}^n} g(x)` /// /// Inputs: - /// - fx: the virtual polynomial - /// - prod_x: the product polynomial - /// - transcript: a transcript that is used to generate the challenges alpha - /// - claimed_product: the claimed product value + /// - fx: the numerator multilinear polynomial + /// - gx: the denominator multilinear polynomial + /// - transcript: the IOP transcript + /// - pk: PCS committing key + /// + /// Outputs + /// - the product check proof + /// - the product polynomial (used for testing) /// /// Cost: O(N) fn prove( - fx: &VirtualPolynomial, - prod_x: &DenseMultilinearExtension, - transcript: &mut IOPTranscript, - claimed_product: F, - ) -> Result; - - /// Verify that for a witness virtual polynomial f(x), - /// it holds that `s = \prod_{x \in {0,1}^n} f(x)` + fx: &Self::Polynomial, + gx: &Self::Polynomial, + transcript: &mut IOPTranscript, + pk: &PCS::ProverParam, + ) -> Result<(Self::ProductProof, Self::Polynomial), PolyIOPErrors>; + + /// Verify that for witness multilinear polynomials f(x), g(x) + /// it holds that `\prod_{x \in {0,1}^n} f(x) = \prod_{x \in {0,1}^n} g(x)` fn verify( proof: &Self::ProductProof, - aux_info: &Self::VPAuxInfo, + num_vars: usize, transcript: &mut Self::Transcript, - claimed_product: F, ) -> Result; } /// A product check subclaim consists of /// - A zero check IOP subclaim for -/// `Q(x) = prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x)` -/// is 0, consists of the following: +/// `Q(x) = prod(1, x) - prod(x, 0) * prod(x, 1) + challenge * (f(x) - prod(0, +/// x) * g(x))` is 0, consists of the following: /// - the SubClaim from the SumCheck /// - the initial challenge r which is used to build eq(x, r) in ZeroCheck +/// - The challenge `challenge` /// - A final query for `prod(1, ..., 1, 0) = claimed_product`. // Note that this final query is in fact a constant that // is independent from the proof. So we should avoid @@ -102,16 +100,200 @@ pub struct ProductCheckSubClaim> { // the SubClaim from the ZeroCheck zero_check_sub_claim: ZC::ZeroCheckSubClaim, // final query which consists of - // - the vector `(1, ..., 1, 0)` - // - the evaluation `claimed_product` + // - the vector `(1, ..., 1, 0)` (needs to be reversed because Arkwork's MLE uses big-endian + // format for points) + // The expected final query evaluation is 1 final_query: (Vec, F), + challenge: F, } -/// The random challenges in a product check protocol -#[allow(dead_code)] -pub struct ProductCheckChallenge { - alpha: F, +/// A product check proof consists of +/// - a zerocheck proof +/// - a product polynomial commitment +#[derive(Clone, Debug, Default, PartialEq)] +pub struct ProductProof, ZC: ZeroCheck> +{ + zero_check_proof: ZC::ZeroCheckProof, + prod_x_comm: PCS::Commitment, +} + +impl ProductCheck for PolyIOP +where + E: PairingEngine, + PCS: PolynomialCommitmentScheme>>, +{ + type ProductCheckSubClaim = ProductCheckSubClaim; + type ProductProof = ProductProof; + type Polynomial = Rc>; + + fn init_transcript() -> Self::Transcript { + IOPTranscript::::new(b"Initializing ProductCheck transcript") + } + + fn prove( + fx: &Self::Polynomial, + gx: &Self::Polynomial, + transcript: &mut IOPTranscript, + pk: &PCS::ProverParam, + ) -> Result<(Self::ProductProof, Self::Polynomial), PolyIOPErrors> { + let start = start_timer!(|| "prod_check prove"); + + if fx.num_vars != gx.num_vars { + return Err(PolyIOPErrors::InvalidParameters( + "fx and gx have different number of variables".to_string(), + )); + } + + // compute the product polynomial + let prod_x = compute_product_poly(fx, gx)?; + + // generate challenge + let prod_x_comm = PCS::commit(pk, &Rc::new(prod_x.clone()))?; + transcript.append_serializable_element(b"prod(x)", &prod_x_comm)?; + let alpha = transcript.get_and_append_challenge(b"alpha")?; + + // build the zero-check proof + let (zero_check_proof, _) = prove_zero_check(fx, gx, &prod_x, &alpha, transcript)?; + + end_timer!(start); + + Ok(( + ProductProof { + zero_check_proof, + prod_x_comm, + }, + Rc::new(prod_x.clone()), + )) + } + + fn verify( + proof: &Self::ProductProof, + num_vars: usize, + transcript: &mut Self::Transcript, + ) -> Result { + let start = start_timer!(|| "prod_check verify"); + + // update transcript and generate challenge + transcript.append_serializable_element(b"prod(x)", &proof.prod_x_comm)?; + let alpha = transcript.get_and_append_challenge(b"alpha")?; + + // invoke the zero check on the iop_proof + // the virtual poly info for Q(x) + let aux_info = VPAuxInfo { + max_degree: 2, + num_variables: num_vars, + phantom: PhantomData::default(), + }; + let zero_check_sub_claim = + >::verify(&proof.zero_check_proof, &aux_info, transcript)?; + + // the final query is on prod_x, hence has length `num_vars` + 1 + let mut final_query = vec![E::Fr::one(); aux_info.num_variables + 1]; + // the point has to be reversed because Arkworks uses big-endian. + final_query[0] = E::Fr::zero(); + let final_eval = E::Fr::one(); + + end_timer!(start); + + Ok(ProductCheckSubClaim { + zero_check_sub_claim, + final_query: (final_query, final_eval), + challenge: alpha, + }) + } } #[cfg(test)] -mod test {} +mod test { + use super::ProductCheck; + use crate::{errors::PolyIOPErrors, PolyIOP}; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_ec::PairingEngine; + use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; + use ark_std::test_rng; + use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme}; + use std::rc::Rc; + + // f and g are guaranteed to have the same product + fn test_product_check_helper( + f: &DenseMultilinearExtension, + g: &DenseMultilinearExtension, + pk: &PCS::ProverParam, + ) -> Result<(), PolyIOPErrors> + where + E: PairingEngine, + PCS: PolynomialCommitmentScheme>>, + { + let mut transcript = as ProductCheck>::init_transcript(); + transcript.append_message(b"testing", b"initializing transcript for testing")?; + + let (proof, prod_x) = as ProductCheck>::prove( + &Rc::new(f.clone()), + &Rc::new(g.clone()), + &mut transcript, + pk, + )?; + + let mut transcript = as ProductCheck>::init_transcript(); + transcript.append_message(b"testing", b"initializing transcript for testing")?; + + let subclaim = + as ProductCheck>::verify(&proof, f.num_vars, &mut transcript)?; + assert_eq!( + prod_x.evaluate(&subclaim.final_query.0).unwrap(), + subclaim.final_query.1, + "different product" + ); + + // bad path + let mut transcript = as ProductCheck>::init_transcript(); + transcript.append_message(b"testing", b"initializing transcript for testing")?; + + let h = f + g; + let (bad_proof, prod_x_bad) = as ProductCheck>::prove( + &Rc::new(f.clone()), + &Rc::new(h.clone()), + &mut transcript, + pk, + )?; + + let mut transcript = as ProductCheck>::init_transcript(); + transcript.append_message(b"testing", b"initializing transcript for testing")?; + let bad_subclaim = as ProductCheck>::verify( + &bad_proof, + f.num_vars, + &mut transcript, + )?; + assert_ne!( + prod_x_bad.evaluate(&bad_subclaim.final_query.0).unwrap(), + bad_subclaim.final_query.1, + "can't detect wrong proof" + ); + + Ok(()) + } + + fn test_product_check(nv: usize) -> Result<(), PolyIOPErrors> { + let mut rng = test_rng(); + + let f: DenseMultilinearExtension = DenseMultilinearExtension::rand(nv, &mut rng); + let mut g = f.clone(); + g.evaluations.reverse(); + + let srs = KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, nv + 1)?; + let (pk, _) = KZGMultilinearPCS::::trim(&srs, nv + 1, Some(nv + 1))?; + + test_product_check_helper::>(&f, &g, &pk)?; + + Ok(()) + } + + #[test] + fn test_trivial_polynomial() -> Result<(), PolyIOPErrors> { + test_product_check(1) + } + #[test] + fn test_normal_polynomial() -> Result<(), PolyIOPErrors> { + test_product_check(10) + } +} diff --git a/poly-iop/src/prod_check/util.rs b/poly-iop/src/prod_check/util.rs new file mode 100644 index 0000000..79ca129 --- /dev/null +++ b/poly-iop/src/prod_check/util.rs @@ -0,0 +1,192 @@ +//! This module implements useful functions for the product check protocol. + +use crate::{errors::PolyIOPErrors, structs::IOPProof, utils::get_index, PolyIOP, ZeroCheck}; +use arithmetic::VirtualPolynomial; +use ark_ff::PrimeField; +use ark_poly::DenseMultilinearExtension; +use ark_std::{end_timer, start_timer}; +use std::rc::Rc; +use transcript::IOPTranscript; + +/// Compute the product polynomial `prod(x)` where +/// +/// - `prod(0,x) := prod(0, x1, …, xn)` is the MLE over the +/// evaluations of `f(x)/g(x)` on the boolean hypercube {0,1}^n +/// +/// - `prod(1,x)` is a MLE over the evaluations of `prod(x, 0) * prod(x, 1)` +/// on the boolean hypercube {0,1}^n +/// +/// The caller needs to check num_vars matches in f and g +/// Cost: linear in N. +pub(super) fn compute_product_poly( + fx: &DenseMultilinearExtension, + gx: &DenseMultilinearExtension, +) -> Result, PolyIOPErrors> { + let start = start_timer!(|| "compute evaluations of prod polynomial"); + let num_vars = fx.num_vars; + + // =================================== + // prod(0, x) + // =================================== + let prod_0x_eval = compute_prod_0(fx, gx)?; + + // =================================== + // prod(1, x) + // =================================== + // + // `prod(1, x)` can be computed via recursing the following formula for 2^n-1 + // times + // + // `prod(1, x_1, ..., x_n) := + // prod(x_1, x_2, ..., x_n, 0) * prod(x_1, x_2, ..., x_n, 1)` + // + // At any given step, the right hand side of the equation + // is available via either eval_0x or the current view of eval_1x + let mut prod_1x_eval = vec![]; + for x in 0..(1 << num_vars) - 1 { + // sign will decide if the evaluation should be looked up from eval_0x or + // eval_1x; x_zero_index is the index for the evaluation (x_2, ..., x_n, + // 0); x_one_index is the index for the evaluation (x_2, ..., x_n, 1); + let (x_zero_index, x_one_index, sign) = get_index(x, num_vars); + if !sign { + prod_1x_eval.push(prod_0x_eval[x_zero_index] * prod_0x_eval[x_one_index]); + } else { + // sanity check: if we are trying to look up from the eval_1x table, + // then the target index must already exist + if x_zero_index >= prod_1x_eval.len() || x_one_index >= prod_1x_eval.len() { + return Err(PolyIOPErrors::ShouldNotArrive); + } + prod_1x_eval.push(prod_1x_eval[x_zero_index] * prod_1x_eval[x_one_index]); + } + } + + // prod(1, 1, ..., 1) := 0 + prod_1x_eval.push(F::zero()); + + // =================================== + // prod(x) + // =================================== + // prod(x)'s evaluation is indeed `e := [eval_0x[..], eval_1x[..]].concat()` + let eval = [prod_0x_eval.as_slice(), prod_1x_eval.as_slice()].concat(); + + let prod_x = DenseMultilinearExtension::from_evaluations_vec(num_vars + 1, eval); + + end_timer!(start); + Ok(prod_x) +} + +/// generate the zerocheck proof for the virtual polynomial +/// prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x) * g(x)) +/// +/// Returns proof and Q(x) for testing purpose. +/// +/// Cost: O(N) +pub(super) fn prove_zero_check( + fx: &DenseMultilinearExtension, + gx: &DenseMultilinearExtension, + prod_x: &DenseMultilinearExtension, + alpha: &F, + transcript: &mut IOPTranscript, +) -> Result<(IOPProof, VirtualPolynomial), PolyIOPErrors> { + let start = start_timer!(|| "zerocheck in product check"); + + let prod_partial_evals = build_prod_partial_eval(prod_x)?; + let prod_0x = Rc::new(prod_partial_evals[0].clone()); + let prod_1x = Rc::new(prod_partial_evals[1].clone()); + let prod_x0 = Rc::new(prod_partial_evals[2].clone()); + let prod_x1 = Rc::new(prod_partial_evals[3].clone()); + let fx = Rc::new(fx.clone()); + let gx = Rc::new(gx.clone()); + + // compute g(x) * prod(0, x) * alpha + let mut q_x = VirtualPolynomial::new_from_mle(gx, F::one()); + q_x.mul_by_mle(prod_0x, *alpha)?; + + // g(x) * prod(0, x) * alpha + // - f(x) * alpha + q_x.add_mle_list([fx], -*alpha)?; + + // Q(x) := prod(1,x) - prod(x, 0) * prod(x, 1) + // + alpha * ( + // g(x) * prod(0, x) + // - f(x)) + q_x.add_mle_list([prod_x0, prod_x1], -F::one())?; + q_x.add_mle_list([prod_1x], F::one())?; + + let iop_proof = as ZeroCheck>::prove(&q_x, transcript)?; + + end_timer!(start); + Ok((iop_proof, q_x)) +} + +/// Helper function of the IOP. +/// +/// Input: +/// - prod(x) +/// +/// Output: the following 4 polynomials +/// - prod(0, x) +/// - prod(1, x) +/// - prod(x, 0) +/// - prod(x, 1) +fn build_prod_partial_eval( + prod_x: &DenseMultilinearExtension, +) -> Result<[DenseMultilinearExtension; 4], PolyIOPErrors> { + let start = start_timer!(|| "build partial prod polynomial"); + + let prod_x_eval = &prod_x.evaluations; + let num_vars = prod_x.num_vars - 1; + + // prod(0, x) + let prod_0_x = + DenseMultilinearExtension::from_evaluations_slice(num_vars, &prod_x_eval[0..1 << num_vars]); + // prod(1, x) + let prod_1_x = DenseMultilinearExtension::from_evaluations_slice( + num_vars, + &prod_x_eval[1 << num_vars..1 << (num_vars + 1)], + ); + + // =================================== + // prod(x, 0) and prod(x, 1) + // =================================== + // + // now we compute eval_x0 and eval_x1 + // eval_0x will be the odd coefficients of eval + // and eval_1x will be the even coefficients of eval + let mut eval_x0 = vec![]; + let mut eval_x1 = vec![]; + for (x, &prod_x) in prod_x_eval.iter().enumerate() { + if x & 1 == 0 { + eval_x0.push(prod_x); + } else { + eval_x1.push(prod_x); + } + } + let prod_x_0 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x0); + let prod_x_1 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x1); + + end_timer!(start); + + Ok([prod_0_x, prod_1_x, prod_x_0, prod_x_1]) +} + +/// Returns the evaluations of +/// - `prod(0,x) := prod(0, x1, …, xn)` which is the MLE over the +/// evaluations of f(x)/g(x) on the boolean hypercube {0,1}^n: +/// +/// The caller needs to check num_vars matches in f/g +/// Cost: linear in N. +fn compute_prod_0( + fx: &DenseMultilinearExtension, + gx: &DenseMultilinearExtension, +) -> Result, PolyIOPErrors> { + let start = start_timer!(|| "compute prod(0,x)"); + + let mut prod_0x_evals = vec![]; + for (&fi, &gi) in fx.iter().zip(gx.iter()) { + prod_0x_evals.push(fi / gi); + } + + end_timer!(start); + Ok(prod_0x_evals) +} diff --git a/poly-iop/src/structs.rs b/poly-iop/src/structs.rs index db336a5..741e177 100644 --- a/poly-iop/src/structs.rs +++ b/poly-iop/src/structs.rs @@ -16,7 +16,7 @@ pub struct IOPProof { /// A message from the prover to the verifier at a given round /// is a list of evaluations. -#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, CanonicalSerialize)] pub struct IOPProverMessage { pub(crate) evaluations: Vec, } diff --git a/poly-iop/src/sum_check/mod.rs b/poly-iop/src/sum_check/mod.rs index 1c43838..b132b12 100644 --- a/poly-iop/src/sum_check/mod.rs +++ b/poly-iop/src/sum_check/mod.rs @@ -113,7 +113,7 @@ pub trait SumCheckVerifier { /// A SumCheckSubClaim is a claim generated by the verifier at the end of /// verification when it is convinced. -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SumCheckSubClaim { /// the multi-dimensional point that this multilinear extension is evaluated /// to