Skip to content

Commit

Permalink
feat(verifier): verify_bytes api GRO-304 (#1784)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuwen01 authored Nov 12, 2024
1 parent 47eac33 commit af1c35d
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 21 deletions.
41 changes: 40 additions & 1 deletion book/verification/off-chain-verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

You can verify SP1 Groth16 and Plonk proofs in `no_std` environments with [`sp1-verifier`](https://docs.rs/sp1-verifier/latest/sp1_verifier/).

`sp1-verifier` is also patched to verify Groth16 and Plonk proofs within the SP1 ZKVM, using
`sp1-verifier` is also patched to verify Groth16 and Plonk proofs within the SP1 zkVM, using
[bn254](https://blog.succinct.xyz/succinctshipsprecompiles/) precompiles. For an example of this, see
the [Groth16 Example](https://github.com/succinctlabs/sp1/tree/main/examples/groth16/).

Expand Down Expand Up @@ -43,6 +43,45 @@ Here, the proof, public inputs, and vkey hash are read from stdin. See the follo

> Note that the SP1 SDK itself is *not* `no_std` compatible.
### Advanced: `verify_gnark_proof`

`sp1-verifier` also exposes [`Groth16Verifier::verify_gnark_proof`](https://docs.rs/sp1-verifier/latest/sp1_verifier/struct.Groth16Verifier.html#method.verify_gnark_proof) and [`PlonkVerifier::verify_gnark_proof`](https://docs.rs/sp1-verifier/latest/sp1_verifier/struct.PlonkVerifier.html#method.verify_gnark_proof),
which verifies any Groth16 or Plonk proof from Gnark. This is especially useful for verifying custom Groth16 and Plonk proofs
efficiently in the SP1 zkVM.

The following snippet demonstrates how you might serialize a Gnark proof in a way that `sp1-verifier` can use.

```go
// Write the verifier key.
vkFile, err := os.Create("vk.bin")
if err != nil {
panic(err)
}
defer vkFile.Close()

// Here, `vk` is a `groth16_bn254.VerifyingKey` or `plonk_bn254.VerifyingKey`.
_, err = vk.WriteTo(vkFile)
if err != nil {
panic(err)
}

// Write the proof.
proofFile, err := os.Create("proof.bin")
if err != nil {
panic(err)
}
defer proofFile.Close()

// Here, `proof` is a `groth16_bn254.Proof` or `plonk_bn254.Proof`.
_, err = proof.WriteTo(proofFile)
if err != nil {
panic(err)
}
```

Public values are serialized as big-endian `Fr` values. The default Gnark serialization will work
out of the box.

## Wasm Verification

The [`example-sp1-wasm-verifier`](https://github.com/succinctlabs/example-sp1-wasm-verifier) demonstrates how to
Expand Down
2 changes: 1 addition & 1 deletion crates/verifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ to be generated using the [SP1 SDK](../sdk).
## Features

Groth16 and Plonk proof verification are supported in `no-std` environments. Verification in the
SP1 ZKVM context is patched, in order to make use of the
SP1 zkVM context is patched, in order to make use of the
[bn254 precompiles](https://blog.succinct.xyz/succinctshipsprecompiles/).

### Pre-generated verification keys
Expand Down
2 changes: 1 addition & 1 deletion crates/verifier/src/groth16/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use super::error::Groth16Error;
/// Load the Groth16 proof from the given byte slice.
///
/// The byte slice is represented as 2 uncompressed g1 points, and one uncompressed g2 point,
/// as outputted from gnark.
/// as outputted from Gnark.
pub(crate) fn load_groth16_proof_from_bytes(buffer: &[u8]) -> Result<Groth16Proof, Groth16Error> {
let ar = uncompressed_bytes_to_g1_point(&buffer[..64])?;
let bs = uncompressed_bytes_to_g2_point(&buffer[64..192])?;
Expand Down
51 changes: 44 additions & 7 deletions crates/verifier/src/groth16/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@ mod converter;
pub mod error;
mod verify;

use bn::Fr;
pub(crate) use converter::{load_groth16_proof_from_bytes, load_groth16_verifying_key_from_bytes};
use sha2::{Digest, Sha256};
pub(crate) use verify::*;

use error::Groth16Error;

use crate::{bn254_public_values, decode_sp1_vkey_hash, error::Error};
use crate::{decode_sp1_vkey_hash, error::Error, hash_public_inputs};

use alloc::vec::Vec;
use sha2::{Digest, Sha256};

/// A verifier for Groth16 zero-knowledge proofs.
#[derive(Debug)]
pub struct Groth16Verifier;
impl Groth16Verifier {
/// Verifies a Groth16 proof.
/// Verifies an SP1 Groth16 proof, as generated by the SP1 SDK.
///
/// # Arguments
///
Expand Down Expand Up @@ -57,11 +60,45 @@ 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..])?;
let groth16_vk = load_groth16_verifying_key_from_bytes(groth16_vk)?;
Self::verify_gnark_proof(
&proof[4..],
&[sp1_vkey_hash, hash_public_inputs(sp1_public_inputs)],
groth16_vk,
)
}

/// Verifies a Gnark Groth16 proof using raw byte inputs.
///
/// WARNING: if you're verifying an SP1 proof, you should use [`verify`] instead.
/// This is a lower-level verification method that works directly with raw bytes rather than
/// the SP1 SDK's data structures.
///
/// # Arguments
///
/// * `proof` - The raw Groth16 proof bytes (without the 4-byte vkey hash prefix)
/// * `public_inputs` - The public inputs to the circuit
/// * `groth16_vk` - The Groth16 verifying key bytes
///
/// # Returns
///
/// A [`Result`] containing unit `()` if the proof is valid,
/// or a [`Groth16Error`] if verification fails.
///
/// # Note
///
/// This method expects the raw proof bytes without the 4-byte vkey hash prefix that
/// [`verify`] checks. If you have a complete proof with the prefix, use [`verify`] instead.
pub fn verify_gnark_proof(
proof: &[u8],
public_inputs: &[[u8; 32]],
groth16_vk: &[u8],
) -> Result<(), Groth16Error> {
let proof = load_groth16_proof_from_bytes(proof).unwrap();
let groth16_vk = load_groth16_verifying_key_from_bytes(groth16_vk).unwrap();

verify_groth16_raw(&groth16_vk, &proof, &public_inputs)
let public_inputs =
public_inputs.iter().map(|input| Fr::from_slice(input).unwrap()).collect::<Vec<_>>();
verify_groth16_algebraic(&groth16_vk, &proof, &public_inputs)
}
}
4 changes: 2 additions & 2 deletions crates/verifier/src/groth16/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ fn prepare_inputs(vk: Groth16VerifyingKey, public_inputs: &[Fr]) -> Result<G1, G
.into())
}

/// Verify the Groth16 proof
/// Verify the Groth16 proof using algebraic inputs.
///
/// First, prepare the public inputs by folding them with the verification key.
/// Then, verify the proof by checking the pairing equation.
pub(crate) fn verify_groth16_raw(
pub(crate) fn verify_groth16_algebraic(
vk: &Groth16VerifyingKey,
proof: &Groth16Proof,
public_inputs: &[Fr],
Expand Down
2 changes: 1 addition & 1 deletion crates/verifier/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! This crate provides verifiers for SP1 Groth16 and Plonk BN254 proofs in a no-std environment.
//! It is patched for efficient verification within the SP1 ZKVM context.
//! It is patched for efficient verification within the SP1 zkVM context.

#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
Expand Down
45 changes: 39 additions & 6 deletions crates/verifier/src/plonk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,21 @@ pub(crate) mod error;

pub(crate) use converter::{load_plonk_proof_from_bytes, load_plonk_verifying_key_from_bytes};
pub(crate) use proof::PlonkProof;
pub(crate) use verify::verify_plonk_raw;
pub(crate) use verify::verify_plonk_algebraic;

use alloc::vec::Vec;
use bn::Fr;
use error::PlonkError;
use sha2::{Digest, Sha256};

use crate::{bn254_public_values, decode_sp1_vkey_hash, error::Error};
use crate::{decode_sp1_vkey_hash, error::Error, hash_public_inputs};
/// A verifier for Plonk zero-knowledge proofs.
#[derive(Debug)]
pub struct PlonkVerifier;

impl PlonkVerifier {
/// Verifies an SP1 PLONK proof, as generated by the SP1 SDK.
///
/// # Arguments
///
/// * `proof` - The proof bytes.
Expand Down Expand Up @@ -66,11 +70,40 @@ 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 plonk_vk = load_plonk_verifying_key_from_bytes(plonk_vk)?;
let proof = load_plonk_proof_from_bytes(&proof[4..], plonk_vk.qcp.len())?;
Self::verify_gnark_proof(
&proof[4..],
&[sp1_vkey_hash, hash_public_inputs(sp1_public_inputs)],
plonk_vk,
)
}

/// Verifies a Gnark PLONK proof using raw byte inputs.
///
/// WARNING: if you're verifying an SP1 proof, you should use [`verify`] instead.
/// This is a lower-level verification method that works directly with raw bytes rather than
/// the SP1 SDK's data structures.
///
/// # Arguments
///
/// * `proof` - The raw PLONK proof bytes (without the 4-byte vkey hash prefix)
/// * `public_inputs` - The public inputs to the circuit
/// * `plonk_vk` - The PLONK verifying key bytes
///
/// # Returns
///
/// A [`Result`] containing unit `()` if the proof is valid,
/// or a [`PlonkError`] if verification fails.
pub fn verify_gnark_proof(
proof: &[u8],
public_inputs: &[[u8; 32]],
plonk_vk: &[u8],
) -> Result<(), PlonkError> {
let plonk_vk = load_plonk_verifying_key_from_bytes(plonk_vk).unwrap();
let proof = load_plonk_proof_from_bytes(proof, plonk_vk.qcp.len()).unwrap();

verify_plonk_raw(&plonk_vk, &proof, &public_inputs)
let public_inputs =
public_inputs.iter().map(|input| Fr::from_slice(input).unwrap()).collect::<Vec<_>>();
verify_plonk_algebraic(&plonk_vk, &proof, &public_inputs)
}
}
4 changes: 2 additions & 2 deletions crates/verifier/src/plonk/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub(crate) struct PlonkVerifyingKey {
pub(crate) commitment_constraint_indexes: Vec<usize>,
}

/// Verifies a PLONK proof
/// Verifies a PLONK proof using algebraic inputs.
///
/// # Arguments
///
Expand All @@ -44,7 +44,7 @@ pub(crate) struct PlonkVerifyingKey {
/// # Returns
///
/// * `Result<bool, PlonkError>` - Returns true if the proof is valid, or an error if verification fails
pub(crate) fn verify_plonk_raw(
pub(crate) fn verify_plonk_algebraic(
vk: &PlonkVerifyingKey,
proof: &PlonkProof,
public_inputs: &[Fr],
Expand Down

0 comments on commit af1c35d

Please sign in to comment.