Skip to content

Commit

Permalink
Merge branch 'dev' into aurelien/gro-154-multi-crate-elf
Browse files Browse the repository at this point in the history
  • Loading branch information
leruaa authored Nov 12, 2024
2 parents c94ecef + af1c35d commit eb67240
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 26 deletions.
2 changes: 2 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Note: this is only compatible with Linux runners.

name: Test setup
inputs:
pull_token:
Expand Down
2 changes: 1 addition & 1 deletion audits/rkm0959.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ this passes each core verification, and since the RecursionPublicValue of proof
- shard 1, 2's committed_value_digest = `0`
- shard 3, 4's committed_value_digest = `x`

this passes each core verification, as proof #2 thinks shard 3 is its "first" shard - so it actually thinks that the `committed_value_digest` didn't change. This means that the whole "no cpu chip means `commitTed_value_digest` equal" thing actually just passes. Then, in the compress verification, we'll just see the committed_value_digest go from `0` to `x`, which is also completely fine. However, the committed_value_digest will go `0, 0, x, x`, where the change occurs on a shard without cpu chip - which isn't supposed to happen.
this passes each core verification, as proof #2 thinks shard 3 is its "first" shard - so it actually thinks that the `committed_value_digest` didn't change. This means that the whole "no cpu chip means `committed_value_digest` equal" thing actually just passes. Then, in the compress verification, we'll just see the committed_value_digest go from `0` to `x`, which is also completely fine. However, the committed_value_digest will go `0, 0, x, x`, where the change occurs on a shard without cpu chip - which isn't supposed to happen.

While this is a slight incompatibility, the main invariant (if nonzero, public digest can only be one non-zero value) is preserved. Therefore, we did not fix this observation.

Expand Down
3 changes: 2 additions & 1 deletion book/generating-proofs/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ To make this more concrete, let's walk through a simple example of generating a
You can run the above script in the `script` directory with `RUST_LOG=info cargo run --release`. Note that running the above script will generate a proof locally.

<div class="warning">
WARNING: Local proving often is much slower than the prover network and for certain proof types (e.g. Groth16, PLONK) require a significant amount of RAM and will likely not work on a laptop.
WARNING: Local proving often is much slower than the prover network and for certain proof types (e.g. Groth16, PLONK) require a
significant amount of RAM. You might only be able to generate proofs for small inputs locally.
</div>

We recommend using the [prover network](./prover-network.md) to generate proofs. Read more about the [recommended workflow](./recommended-workflow.md) for developing with SP1.
4 changes: 3 additions & 1 deletion book/getting-started/hardware-requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ which can be parallelized with multiple cores.
Our prover requires keeping large matrices (i.e., traces) in memory to generate the proofs. Certain steps of the prover
have a minimum memory requirement, meaning that if you have less than this amount of memory, the process will OOM.

This effect is most noticeable when using the Groth16 or PLONK provers.
This effect is most noticeable when using the Groth16 or PLONK provers. If you're running the Groth16 or Plonk provers locally
on Mac or Windows using docker, you might need to increase the memory limit for
[docker desktop](https://docs.docker.com/desktop/settings-and-maintenance/settings/#resources).

### Disk

Expand Down
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
3 changes: 2 additions & 1 deletion book/verification/onchain/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ By default, the proofs generated by SP1 are not verifiable onchain, as they are

> WARNING: The Groth16 and PLONK provers are only guaranteed to work on official releases of SP1. To
> use Groth16 or PLONK proving & verification locally, ensure that you have Docker installed and have
> at least 128GB of RAM.
> at least 32GB of RAM. Note that you might need to increase the memory limit for
> [docker desktop](https://docs.docker.com/desktop/settings-and-maintenance/settings/#resources) if you're running on Mac.
### Example

Expand Down
4 changes: 3 additions & 1 deletion crates/recursion/gnark-ffi/src/ffi/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ fn call_docker(args: &[&str], mounts: &[(&str, &str)]) -> Result<()> {
}
cmd.arg(get_docker_image());
cmd.args(args);
if !cmd.status()?.success() {
let result = cmd.status()?;
if !result.success() {
log::error!("Failed to run `docker run`: {:?}", cmd);
log::error!("Execution result: {:?}", result);
return Err(anyhow!("docker command failed"));
}
Ok(())
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 eb67240

Please sign in to comment.