diff --git a/crates/sdk/src/proof.rs b/crates/sdk/src/proof.rs index 86bd723e50..222e69f135 100644 --- a/crates/sdk/src/proof.rs +++ b/crates/sdk/src/proof.rs @@ -53,27 +53,9 @@ impl SP1ProofWithPublicValues { } } - /// Returns the *raw* proof as bytes, prepended with the first 4 bytes of the vkey hash. - /// - /// This is the format expected by the `sp1-verifier` crate. The extra 4 bytes are used to - /// ensure that the proof will eventually be verified by the correct vkey. - pub fn raw_with_checksum(&self) -> Vec { - match &self.proof { - SP1Proof::Plonk(plonk) => { - let proof_bytes = hex::decode(&plonk.raw_proof).expect("Invalid Plonk proof"); - [plonk.plonk_vkey_hash[..4].to_vec(), proof_bytes].concat() - } - SP1Proof::Groth16(groth16) => { - let proof_bytes = hex::decode(&groth16.raw_proof).expect("Invalid Groth16 proof"); - [groth16.groth16_vkey_hash[..4].to_vec(), proof_bytes].concat() - } - _ => unimplemented!(), - } - } - /// For Plonk or Groth16 proofs, returns the proof in a byte encoding the onchain verifier /// accepts. The bytes consist of the first four bytes of Plonk vkey hash followed by the - /// *encoded* proof. + /// encoded proof, in a form optimized for onchain verification. pub fn bytes(&self) -> Vec { match &self.proof { SP1Proof::Plonk(plonk_proof) => { diff --git a/crates/verifier/Cargo.toml b/crates/verifier/Cargo.toml index 321fa3c905..0436c54f40 100644 --- a/crates/verifier/Cargo.toml +++ b/crates/verifier/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sp1-verifier" description = "Verifier for SP1 Groth16 and Plonk proofs." -readme = "../../README.md" +readme = "README.md" version = { workspace = true } edition = { workspace = true } license = { workspace = true } @@ -10,7 +10,7 @@ keywords = { workspace = true } categories = { workspace = true } [dependencies] -bn = { git = "https://github.com/sp1-patches/bn", tag = "substrate_bn-v0.6.0-patch-v2", package = "substrate-bn" } +bn = { git = "https://github.com/sp1-patches/bn", version = "0.6.0", tag = "substrate_bn-v0.6.0-patch-v2", package = "substrate-bn" } sha2 = { version = "0.10.8", default-features = false } thiserror-no-std = "2.0.2" hex = { version = "0.4.3", default-features = false, features = ["alloc"] } @@ -23,4 +23,4 @@ num-traits = "0.2.19" [features] default = ["std"] -std = [] +std = ["thiserror-no-std/std"] diff --git a/crates/verifier/src/error.rs b/crates/verifier/src/error.rs index 0facb98902..2d37bceac9 100644 --- a/crates/verifier/src/error.rs +++ b/crates/verifier/src/error.rs @@ -3,51 +3,19 @@ use thiserror_no_std::Error; #[derive(Error, Debug)] pub enum Error { - // Cryptographic Errors - #[error("BSB22 Commitment number mismatch")] - Bsb22CommitmentMismatch, - #[error("Challenge already computed")] - ChallengeAlreadyComputed, - #[error("Challenge not found")] - ChallengeNotFound, - #[error("Previous challenge not computed")] - PreviousChallengeNotComputed, - #[error("Pairing check failed")] - PairingCheckFailed, - #[error("Invalid point in subgroup check")] - InvalidPoint, - - // Arithmetic Errors - #[error("Beyond the modulus")] - BeyondTheModulus, - #[error("Ell too large")] - EllTooLarge, - #[error("Inverse not found")] - InverseNotFound, - #[error("Opening linear polynomial mismatch")] - OpeningPolyMismatch, - // Input Errors - #[error("DST too large")] - DSTTooLarge, - #[error("Invalid number of digests")] - InvalidNumberOfDigests, #[error("Invalid witness")] InvalidWitness, #[error("Invalid x length")] InvalidXLength, - #[error("Unexpected flag")] - UnexpectedFlag, #[error("Invalid data")] InvalidData, + #[error("Invalid point in subgroup check")] + InvalidPoint, // Conversion Errors #[error("Failed to get Fr from random bytes")] FailedToGetFrFromRandomBytes, - #[error("Failed to get x")] - FailedToGetX, - #[error("Failed to get y")] - FailedToGetY, // External Library Errors #[error("BN254 Field Error")] diff --git a/crates/verifier/src/groth16/error.rs b/crates/verifier/src/groth16/error.rs index bc88da868c..36952cb749 100644 --- a/crates/verifier/src/groth16/error.rs +++ b/crates/verifier/src/groth16/error.rs @@ -8,8 +8,6 @@ pub enum Groth16Error { ProcessVerifyingKeyFailed, #[error("Prepare inputs failed")] PrepareInputsFailed, - #[error("Unexpected identity")] - UnexpectedIdentity, #[error("General error")] GeneralError(#[from] crate::error::Error), #[error("Groth16 vkey hash mismatch")] diff --git a/crates/verifier/src/groth16/mod.rs b/crates/verifier/src/groth16/mod.rs index 123d71e4f6..c6cf98a23a 100644 --- a/crates/verifier/src/groth16/mod.rs +++ b/crates/verifier/src/groth16/mod.rs @@ -1,5 +1,5 @@ mod converter; -pub(crate) mod error; +pub mod error; mod verify; pub(crate) use converter::{load_groth16_proof_from_bytes, load_groth16_verifying_key_from_bytes}; @@ -8,7 +8,7 @@ pub(crate) use verify::*; use error::Groth16Error; -use crate::{bn254_public_values, decode_sp1_vkey_hash}; +use crate::{bn254_public_values, decode_sp1_vkey_hash, error::Error}; /// A verifier for Groth16 zero-knowledge proofs. #[derive(Debug)] @@ -30,21 +30,22 @@ impl Groth16Verifier { /// let sp1_vkey_hash = vk.bytes32(); /// ``` /// * `groth16_vk` - The Groth16 verifying key bytes. - /// Usually this will be the [`crate::GROTH16_VK_BYTES`] constant, which is the Groth16 + /// Usually this will be the [`static@crate::GROTH16_VK_BYTES`] constant, which is the Groth16 /// verifying key for the current SP1 version. /// /// # Returns /// - /// A [`Result`] containing a boolean indicating whether the proof is valid, - /// or a [`Groth16Error`] if verification fails. + /// A success [`Result`] if verification succeeds, or a [`Groth16Error`] if verification fails. pub fn verify( proof: &[u8], sp1_public_inputs: &[u8], sp1_vkey_hash: &str, groth16_vk: &[u8], - ) -> Result { + ) -> Result<(), Groth16Error> { // Hash the vk and get the first 4 bytes. - let groth16_vk_hash: [u8; 4] = Sha256::digest(groth16_vk)[..4].try_into().unwrap(); + let groth16_vk_hash: [u8; 4] = Sha256::digest(groth16_vk)[..4] + .try_into() + .map_err(|_| Groth16Error::GeneralError(Error::InvalidData))?; // Check to make sure that this proof was generated by the groth16 proving key corresponding to // the given groth16_vk. @@ -58,8 +59,8 @@ impl Groth16Verifier { let sp1_vkey_hash = decode_sp1_vkey_hash(sp1_vkey_hash)?; let public_inputs = bn254_public_values(&sp1_vkey_hash, sp1_public_inputs); - let proof = load_groth16_proof_from_bytes(&proof[4..]).unwrap(); - let groth16_vk = load_groth16_verifying_key_from_bytes(groth16_vk).unwrap(); + let proof = load_groth16_proof_from_bytes(&proof[4..])?; + let groth16_vk = load_groth16_verifying_key_from_bytes(groth16_vk)?; verify_groth16_raw(&groth16_vk, &proof, &public_inputs) } diff --git a/crates/verifier/src/groth16/verify.rs b/crates/verifier/src/groth16/verify.rs index 510815b971..686e62ff61 100644 --- a/crates/verifier/src/groth16/verify.rs +++ b/crates/verifier/src/groth16/verify.rs @@ -54,13 +54,18 @@ pub(crate) fn verify_groth16_raw( vk: &Groth16VerifyingKey, proof: &Groth16Proof, public_inputs: &[Fr], -) -> Result { +) -> Result<(), Groth16Error> { let prepared_inputs = prepare_inputs(vk.clone(), public_inputs)?; - Ok(pairing_batch(&[ + if pairing_batch(&[ (-Into::::into(proof.ar), proof.bs.into()), (prepared_inputs, vk.g2.gamma.into()), (proof.krs.into(), vk.g2.delta.into()), (vk.g1.alpha.into(), -Into::::into(vk.g2.beta)), - ]) == Gt::one()) + ]) == Gt::one() + { + Ok(()) + } else { + Err(Groth16Error::ProofVerificationFailed) + } } diff --git a/crates/verifier/src/lib.rs b/crates/verifier/src/lib.rs index 6abbd051e2..228d60624f 100644 --- a/crates/verifier/src/lib.rs +++ b/crates/verifier/src/lib.rs @@ -19,14 +19,17 @@ lazy_static! { mod constants; mod converter; mod error; -mod groth16; + mod utils; +pub use utils::*; +pub use groth16::error::Groth16Error; pub use groth16::Groth16Verifier; -pub use utils::*; +mod groth16; -mod plonk; +pub use plonk::error::PlonkError; pub use plonk::PlonkVerifier; +mod plonk; #[cfg(test)] mod tests; diff --git a/crates/verifier/src/plonk/converter.rs b/crates/verifier/src/plonk/converter.rs index 16bd387218..df94303d77 100644 --- a/crates/verifier/src/plonk/converter.rs +++ b/crates/verifier/src/plonk/converter.rs @@ -112,41 +112,46 @@ pub(crate) fn load_plonk_verifying_key_from_bytes( Ok(result) } -pub(crate) fn load_plonk_proof_from_bytes(buffer: &[u8]) -> Result { +/// See https://github.com/jtguibas/gnark/blob/26e3df73fc223292be8b7fc0b7451caa4059a649/backend/plonk/bn254/solidity.go +/// for how the proof is serialized. +pub(crate) fn load_plonk_proof_from_bytes( + buffer: &[u8], + num_bsb22_commitments: usize, +) -> Result { let lro0 = uncompressed_bytes_to_g1_point(&buffer[..64])?; let lro1 = uncompressed_bytes_to_g1_point(&buffer[64..128])?; let lro2 = uncompressed_bytes_to_g1_point(&buffer[128..192])?; - let z = uncompressed_bytes_to_g1_point(&buffer[192..256])?; - let h0 = uncompressed_bytes_to_g1_point(&buffer[256..320])?; - let h1 = uncompressed_bytes_to_g1_point(&buffer[320..384])?; - let h2 = uncompressed_bytes_to_g1_point(&buffer[384..448])?; - let batched_proof_h = uncompressed_bytes_to_g1_point(&buffer[448..512])?; - - let num_claimed_values = - u32::from_be_bytes([buffer[512], buffer[513], buffer[514], buffer[515]]) as usize; - - let mut claimed_values = Vec::new(); - let mut offset = 516; - for _ in 0..num_claimed_values { + let h0 = uncompressed_bytes_to_g1_point(&buffer[192..256])?; + let h1 = uncompressed_bytes_to_g1_point(&buffer[256..320])?; + let h2 = uncompressed_bytes_to_g1_point(&buffer[320..384])?; + + // Stores l_at_zeta, r_at_zeta, o_at_zeta, s 1_at_zeta, s2_at_zeta, bsb22_commitments + let mut claimed_values = Vec::with_capacity(5 + num_bsb22_commitments); + let mut offset = 384; + for _ in 1..6 { let value = Fr::from_slice(&buffer[offset..offset + 32]) .map_err(|e| PlonkError::GeneralError(Error::Field(e)))?; claimed_values.push(value); offset += 32; } - let z_shifted_opening_h = uncompressed_bytes_to_g1_point(&buffer[offset..offset + 64])?; + let z = uncompressed_bytes_to_g1_point(&buffer[offset..offset + 64])?; let z_shifted_opening_value = Fr::from_slice(&buffer[offset + 64..offset + 96]) .map_err(|e| PlonkError::GeneralError(Error::Field(e)))?; + offset += 96; - let num_bsb22_commitments = u32::from_be_bytes([ - buffer[offset + 96], - buffer[offset + 97], - buffer[offset + 98], - buffer[offset + 99], - ]) as usize; + let batched_proof_h = uncompressed_bytes_to_g1_point(&buffer[offset..offset + 64])?; + let z_shifted_opening_h = uncompressed_bytes_to_g1_point(&buffer[offset + 64..offset + 128])?; + offset += 128; + + for _ in 0..num_bsb22_commitments { + let commitment = Fr::from_slice(&buffer[offset..offset + 32]) + .map_err(|e| PlonkError::GeneralError(Error::Field(e)))?; + claimed_values.push(commitment); + offset += 32; + } - let mut bsb22_commitments = Vec::new(); - offset += 100; + let mut bsb22_commitments = Vec::with_capacity(num_bsb22_commitments); for _ in 0..num_bsb22_commitments { let commitment = uncompressed_bytes_to_g1_point(&buffer[offset..offset + 64])?; bsb22_commitments.push(commitment); diff --git a/crates/verifier/src/plonk/error.rs b/crates/verifier/src/plonk/error.rs index cc166638dd..1d33e503d6 100644 --- a/crates/verifier/src/plonk/error.rs +++ b/crates/verifier/src/plonk/error.rs @@ -14,34 +14,18 @@ pub enum PlonkError { DSTTooLarge, #[error("Ell too large")] EllTooLarge, - #[error("Failed to get Fr from random bytes")] - FailedToGetFrFromRandomBytes, - #[error("Failed to get x")] - FailedToGetX, - #[error("Failed to get y")] - FailedToGetY, #[error("Inverse not found")] InverseNotFound, #[error("Invalid number of digests")] InvalidNumberOfDigests, - #[error("Invalid point in subgroup check")] - InvalidPoint, #[error("Invalid witness")] InvalidWitness, - #[error("Invalid x length")] - InvalidXLength, - #[error("Opening linear polynomial mismatch")] - OpeningPolyMismatch, #[error("Pairing check failed")] PairingCheckFailed, #[error("Previous challenge not computed")] PreviousChallengeNotComputed, - #[error("Unexpected flag")] - UnexpectedFlag, #[error("Transcript error")] TranscriptError, - #[error("Hash to field initialization failed")] - HashToFieldInitializationFailed, #[error("Plonk vkey hash mismatch")] PlonkVkeyHashMismatch, #[error("General error")] diff --git a/crates/verifier/src/plonk/hash_to_field.rs b/crates/verifier/src/plonk/hash_to_field.rs index d473abc4fe..fb077019b1 100644 --- a/crates/verifier/src/plonk/hash_to_field.rs +++ b/crates/verifier/src/plonk/hash_to_field.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use core::hash::Hasher; use sha2::Digest; -use crate::error::Error; +use crate::PlonkError; pub(crate) struct WrappedHashToField { domain: Vec, @@ -12,18 +12,22 @@ pub(crate) struct WrappedHashToField { impl WrappedHashToField { // Creates a new instance with a domain separator - pub(crate) fn new(domain_separator: &[u8]) -> Result { + pub(crate) fn new(domain_separator: &[u8]) -> Result { Ok(Self { domain: domain_separator.to_vec(), to_hash: Vec::new() }) } // Hashes the bytes to a field element and returns the byte representation - pub(crate) fn sum(&self) -> Result, Error> { + pub(crate) fn sum(&self) -> Result, PlonkError> { let res = Self::hash(self.to_hash.clone(), self.domain.clone(), 1)?; Ok(res[0].clone()) } - pub(crate) fn hash(msg: Vec, dst: Vec, count: usize) -> Result>, Error> { + pub(crate) fn hash( + msg: Vec, + dst: Vec, + count: usize, + ) -> Result>, PlonkError> { let bytes = 32; let l = 16 + bytes; @@ -38,16 +42,16 @@ impl WrappedHashToField { Ok(res) } - fn expand_msg_xmd(msg: Vec, dst: Vec, len: usize) -> Result, Error> { + fn expand_msg_xmd(msg: Vec, dst: Vec, len: usize) -> Result, PlonkError> { let mut h = sha2::Sha256::new(); let ell = (len + 32 - 1) / 32; if ell > 255 { - Err(Error::EllTooLarge)?; + Err(PlonkError::EllTooLarge)?; } if dst.len() > 255 { - Err(Error::DSTTooLarge)?; + Err(PlonkError::DSTTooLarge)?; } let size_domain = dst.len(); diff --git a/crates/verifier/src/plonk/kzg.rs b/crates/verifier/src/plonk/kzg.rs index 549e048f9c..03098162ac 100644 --- a/crates/verifier/src/plonk/kzg.rs +++ b/crates/verifier/src/plonk/kzg.rs @@ -97,7 +97,7 @@ pub(crate) fn fold_proof( let nb_digests = digests.len(); if nb_digests != batch_opening_proof.claimed_values.len() { - return Err(Error::InvalidNumberOfDigests.into()); + return Err(PlonkError::InvalidNumberOfDigests); } let gamma = derive_gamma( @@ -141,15 +141,15 @@ pub(crate) fn batch_verify_multi_points( let nb_points = points.len(); if nb_digests != nb_proofs { - return Err(Error::InvalidNumberOfDigests.into()); + return Err(PlonkError::InvalidNumberOfDigests); } if nb_digests != nb_points { - return Err(Error::InvalidNumberOfDigests.into()); + return Err(PlonkError::InvalidNumberOfDigests); } if nb_digests == 1 { - todo!(); + unimplemented!(); } let mut random_numbers = Vec::with_capacity(nb_digests); @@ -186,7 +186,7 @@ pub(crate) fn batch_verify_multi_points( pairing_batch(&[(folded_digests.into(), vk.g2[0]), (folded_quotients.into(), vk.g2[1])]); if !pairing_result.is_one() { - return Err(Error::PairingCheckFailed.into()); + return Err(PlonkError::PairingCheckFailed); } Ok(()) diff --git a/crates/verifier/src/plonk/mod.rs b/crates/verifier/src/plonk/mod.rs index 4ee223b71e..0456704f3e 100644 --- a/crates/verifier/src/plonk/mod.rs +++ b/crates/verifier/src/plonk/mod.rs @@ -20,7 +20,7 @@ pub(crate) use verify::verify_plonk_raw; use error::PlonkError; use sha2::{Digest, Sha256}; -use crate::{bn254_public_values, decode_sp1_vkey_hash}; +use crate::{bn254_public_values, decode_sp1_vkey_hash, error::Error}; /// A verifier for Plonk zero-knowledge proofs. #[derive(Debug)] pub struct PlonkVerifier; @@ -40,20 +40,21 @@ impl PlonkVerifier { /// let sp1_vkey_hash = vk.bytes32(); /// ``` /// * `plonk_vk` - The Plonk verifying key bytes. - /// Usually this will be the [`crate::PLONK_VK_BYTES`] constant. + /// Usually this will be the [`static@crate::PLONK_VK_BYTES`] constant. /// /// # Returns /// - /// A `Result` containing a boolean indicating whether the proof is valid, - /// or a [`PlonkError`] if verification fails. + /// A success [`Result`] if verification succeeds, or a [`PlonkError`] if verification fails. pub fn verify( proof: &[u8], sp1_public_inputs: &[u8], sp1_vkey_hash: &str, plonk_vk: &[u8], - ) -> Result { + ) -> Result<(), PlonkError> { // Hash the vk and get the first 4 bytes. - let plonk_vk_hash: [u8; 4] = Sha256::digest(plonk_vk)[..4].try_into().unwrap(); + let plonk_vk_hash: [u8; 4] = Sha256::digest(plonk_vk)[..4] + .try_into() + .map_err(|_| PlonkError::GeneralError(Error::InvalidData))?; // Check to make sure that this proof was generated by the plonk proving key corresponding to // the given plonk vk. @@ -67,8 +68,8 @@ impl PlonkVerifier { let sp1_vkey_hash = decode_sp1_vkey_hash(sp1_vkey_hash)?; let public_inputs = bn254_public_values(&sp1_vkey_hash, sp1_public_inputs); - let proof = load_plonk_proof_from_bytes(&proof[4..]).unwrap(); - let plonk_vk = load_plonk_verifying_key_from_bytes(plonk_vk).unwrap(); + let plonk_vk = load_plonk_verifying_key_from_bytes(plonk_vk)?; + let proof = load_plonk_proof_from_bytes(&proof[4..], plonk_vk.qcp.len())?; verify_plonk_raw(&plonk_vk, &proof, &public_inputs) } diff --git a/crates/verifier/src/plonk/transcript.rs b/crates/verifier/src/plonk/transcript.rs index 5d1c2db8db..55dbd99dcb 100644 --- a/crates/verifier/src/plonk/transcript.rs +++ b/crates/verifier/src/plonk/transcript.rs @@ -1,7 +1,7 @@ use alloc::{collections::btree_map::BTreeMap, string::String, vec::Vec}; use sha2::{Digest, Sha256}; -use crate::error::Error; +use crate::PlonkError; /// A challenge in the transcript, derived with randomness from `bindings` and the previous /// challenge. @@ -24,7 +24,7 @@ pub(crate) struct Transcript { impl Transcript { /// Creates a new transcript. - pub(crate) fn new(challenges_id: Option>) -> Result { + pub(crate) fn new(challenges_id: Option>) -> Result { let h = Sha256::new(); if let Some(challenges_id) = challenges_id { @@ -48,10 +48,10 @@ impl Transcript { } /// Binds some data to a challenge. - pub(crate) fn bind(&mut self, id: &str, binding: &[u8]) -> Result<(), Error> { - let current_challenge = self.challenges.get_mut(id).ok_or(Error::ChallengeNotFound)?; + pub(crate) fn bind(&mut self, id: &str, binding: &[u8]) -> Result<(), PlonkError> { + let current_challenge = self.challenges.get_mut(id).ok_or(PlonkError::ChallengeNotFound)?; if current_challenge.is_computed { - return Err(Error::ChallengeAlreadyComputed); + return Err(PlonkError::ChallengeAlreadyComputed); } current_challenge.bindings.push(binding.to_vec()); @@ -63,8 +63,9 @@ impl Transcript { /// /// Challenges must be computed in order. The previous challenge is automatically fed into the /// challenge currently being computed. - pub(crate) fn compute_challenge(&mut self, challenge_id: &str) -> Result, Error> { - let challenge = self.challenges.get_mut(challenge_id).ok_or(Error::ChallengeNotFound)?; + pub(crate) fn compute_challenge(&mut self, challenge_id: &str) -> Result, PlonkError> { + let challenge = + self.challenges.get_mut(challenge_id).ok_or(PlonkError::ChallengeNotFound)?; if challenge.is_computed { return Ok(challenge.value.clone()); @@ -78,11 +79,11 @@ impl Transcript { if challenge.position != 0 { if let Some(previous_challenge) = &self.previous_challenge { if previous_challenge.position != challenge.position - 1 { - return Err(Error::PreviousChallengeNotComputed); + return Err(PlonkError::PreviousChallengeNotComputed); } self.h.update(&previous_challenge.value) } else { - return Err(Error::PreviousChallengeNotComputed); + return Err(PlonkError::PreviousChallengeNotComputed); } } diff --git a/crates/verifier/src/plonk/verify.rs b/crates/verifier/src/plonk/verify.rs index d00f35094b..4cfbcc884f 100644 --- a/crates/verifier/src/plonk/verify.rs +++ b/crates/verifier/src/plonk/verify.rs @@ -2,7 +2,10 @@ use alloc::{string::ToString, vec, vec::Vec}; use bn::{arith::U256, AffineG1, Fr}; use core::hash::Hasher; -use crate::{error::Error, plonk::transcript::Transcript}; +use crate::{ + error::Error, + plonk::{kzg::BatchOpeningProof, transcript::Transcript}, +}; use super::{ converter::g1_to_bytes, error::PlonkError, kzg, PlonkProof, ALPHA, BETA, GAMMA, U, ZETA, @@ -45,15 +48,15 @@ pub(crate) fn verify_plonk_raw( vk: &PlonkVerifyingKey, proof: &PlonkProof, public_inputs: &[Fr], -) -> Result { +) -> Result<(), PlonkError> { // Check if the number of BSB22 commitments matches the number of Qcp in the verifying key if proof.bsb22_commitments.len() != vk.qcp.len() { - return Err(PlonkError::GeneralError(Error::Bsb22CommitmentMismatch)); + return Err(PlonkError::Bsb22CommitmentMismatch); } // Check if the number of public inputs matches the number of public variables in the verifying key if public_inputs.len() != vk.nb_public_variables { - return Err(PlonkError::GeneralError(Error::InvalidWitness)); + return Err(PlonkError::InvalidWitness); } // Initialize the Fiat-Shamir transcript @@ -93,7 +96,7 @@ pub(crate) fn verify_plonk_raw( let zh_zeta = zeta_power_n - one; // Compute Lagrange polynomial at ζ: L₁(ζ) = (ζⁿ - 1) / (n * (ζ - 1)) - let mut lagrange_one = (zeta - one).inverse().ok_or(Error::InverseNotFound)?; + let mut lagrange_one = (zeta - one).inverse().ok_or(PlonkError::InverseNotFound)?; lagrange_one *= zh_zeta; lagrange_one *= vk.size_inv; @@ -138,7 +141,7 @@ pub(crate) fn verify_plonk_raw( let exponent = U256::from((vk.nb_public_variables + vk.commitment_constraint_indexes[i]) as u64); - let exponent = Fr::new(exponent).ok_or(Error::BeyondTheModulus)?; + let exponent = Fr::new(exponent).ok_or(PlonkError::BeyondTheModulus)?; let w_pow_i = vk.generator.pow(exponent); let mut den = zeta; den -= w_pow_i; @@ -153,11 +156,11 @@ pub(crate) fn verify_plonk_raw( } // Extract claimed values from the proof - let l = proof.batched_proof.claimed_values[1]; - let r = proof.batched_proof.claimed_values[2]; - let o = proof.batched_proof.claimed_values[3]; - let s1 = proof.batched_proof.claimed_values[4]; - let s2 = proof.batched_proof.claimed_values[5]; + let l = proof.batched_proof.claimed_values[0]; + let r = proof.batched_proof.claimed_values[1]; + let o = proof.batched_proof.claimed_values[2]; + let s1 = proof.batched_proof.claimed_values[3]; + let s2 = proof.batched_proof.claimed_values[4]; let zu = proof.z_shifted_opening.claimed_value; @@ -171,6 +174,7 @@ pub(crate) fn verify_plonk_raw( // Compute the constant term of the linearization polynomial: // -[PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ)] + let mut tmp = beta; tmp *= s1; tmp += gamma; @@ -196,13 +200,6 @@ pub(crate) fn verify_plonk_raw( const_lin = -const_lin; - // Check if the opening of the linearized polynomial is equal to -const_lin - let opening_lin_pol = proof.batched_proof.claimed_values[0]; - - if const_lin != opening_lin_pol { - return Err(Error::OpeningPolyMismatch.into()); - } - // Compute coefficients for the linearized polynomial // _s1 = α*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(ωζ) let mut _s1 = beta * s1 + l + gamma; @@ -253,7 +250,7 @@ pub(crate) fn verify_plonk_raw( points.push(proof.h[1]); points.push(proof.h[2]); - let qc = proof.batched_proof.claimed_values[6..].to_vec(); + let qc = proof.batched_proof.claimed_values[5..].to_vec(); let mut scalars = Vec::new(); scalars.extend_from_slice(&qc); @@ -283,11 +280,15 @@ pub(crate) fn verify_plonk_raw( digests_to_fold[4] = vk.s[0]; digests_to_fold[5] = vk.s[1]; + // Prepend the constant term of the linearization polynomial to the claimed values. + let claimed_values = [vec![const_lin], proof.batched_proof.claimed_values.clone()].concat(); + let batch_opening_proof = BatchOpeningProof { h: proof.batched_proof.h, claimed_values }; + // Fold the proof // Internally derives V, and binds it to the transcript to challenge U. let (folded_proof, folded_digest) = kzg::fold_proof( digests_to_fold, - &proof.batched_proof, + &batch_opening_proof, &zeta, Some(zu.into_u256().to_bytes_be().to_vec()), &mut fs, @@ -313,7 +314,7 @@ pub(crate) fn verify_plonk_raw( &vk.kzg, )?; - Ok(true) + Ok(()) } /// Binds all plonk public data to the transcript. diff --git a/crates/verifier/src/tests.rs b/crates/verifier/src/tests.rs index 7048c3c8e8..e99d71ebd9 100644 --- a/crates/verifier/src/tests.rs +++ b/crates/verifier/src/tests.rs @@ -16,13 +16,8 @@ fn test_verify_groth16() { // This vkey hash was derived by calling `vk.bytes32()` on the verifying key. let vkey_hash = "0x00e60860c07bfc6e4c480286c0ddbb879674eb47f84b4ef041cf858b17aa0ed1"; - let is_valid = - crate::Groth16Verifier::verify(&proof, &public_inputs, vkey_hash, &crate::GROTH16_VK_BYTES) - .expect("Groth16 proof is invalid"); - - if !is_valid { - panic!("Groth16 proof is invalid"); - } + crate::Groth16Verifier::verify(&proof, &public_inputs, vkey_hash, &crate::GROTH16_VK_BYTES) + .expect("Groth16 proof is invalid"); } #[test] @@ -33,19 +28,14 @@ fn test_verify_plonk() { // Load the saved proof and extract the proof and public inputs. let sp1_proof_with_public_values = SP1ProofWithPublicValues::load(proof_file).unwrap(); - let proof = sp1_proof_with_public_values.raw_with_checksum(); + let proof = sp1_proof_with_public_values.bytes(); let public_inputs = sp1_proof_with_public_values.public_values.to_vec(); // This vkey hash was derived by calling `vk.bytes32()` on the verifying key. let vkey_hash = "0x00e60860c07bfc6e4c480286c0ddbb879674eb47f84b4ef041cf858b17aa0ed1"; - let is_valid = - crate::PlonkVerifier::verify(&proof, &public_inputs, vkey_hash, &crate::PLONK_VK_BYTES) - .expect("Plonk proof is invalid"); - - if !is_valid { - panic!("Plonk proof is invalid"); - } + crate::PlonkVerifier::verify(&proof, &public_inputs, vkey_hash, &crate::PLONK_VK_BYTES) + .expect("Plonk proof is invalid"); } #[test] diff --git a/examples/groth16/program/src/main.rs b/examples/groth16/program/src/main.rs index f7dd8178f4..bc38ecdbc9 100644 --- a/examples/groth16/program/src/main.rs +++ b/examples/groth16/program/src/main.rs @@ -18,12 +18,9 @@ pub fn main() { println!("cycle-tracker-end: verify"); match result { - Ok(true) => { + Ok(()) => { println!("Proof is valid"); } - Ok(false) => { - println!("Proof is invalid"); - } Err(e) => { println!("Error verifying proof: {:?}", e); }