sp1_verifier/plonk/
mod.rs

1pub(crate) const GAMMA: &str = "gamma";
2pub(crate) const BETA: &str = "beta";
3pub(crate) const ALPHA: &str = "alpha";
4pub(crate) const ZETA: &str = "zeta";
5pub(crate) const U: &str = "u";
6
7mod converter;
8mod hash_to_field;
9mod kzg;
10mod proof;
11mod transcript;
12mod verify;
13
14pub(crate) mod error;
15
16pub(crate) use converter::{load_plonk_proof_from_bytes, load_plonk_verifying_key_from_bytes};
17pub(crate) use proof::PlonkProof;
18pub(crate) use verify::verify_plonk_algebraic;
19
20use alloc::vec::Vec;
21use bn::Fr;
22use error::PlonkError;
23use sha2::{Digest, Sha256};
24
25use crate::{
26    blake3_hash, constants::VK_HASH_PREFIX_LENGTH, decode_sp1_vkey_hash, error::Error,
27    hash_public_inputs, hash_public_inputs_with_fn,
28};
29/// A verifier for Plonk zero-knowledge proofs.
30#[derive(Debug)]
31pub struct PlonkVerifier;
32
33impl PlonkVerifier {
34    /// Verifies an SP1 PLONK proof, as generated by the SP1 SDK.
35    ///
36    /// # Arguments
37    ///
38    /// * `proof` - The proof bytes.
39    /// * `public_inputs` - The SP1 public inputs.
40    /// * `sp1_vkey_hash` - The SP1 vkey hash. This is generated in the following manner:
41    ///
42    /// ```ignore
43    /// use sp1_sdk::ProverClient;
44    /// let client = ProverClient::new();
45    /// let (pk, vk) = client.setup(ELF);
46    /// let sp1_vkey_hash = vk.bytes32();
47    /// ```
48    /// * `plonk_vk` - The Plonk verifying key bytes. Usually this will be the
49    ///   [`static@crate::PLONK_VK_BYTES`] constant.
50    ///
51    /// # Returns
52    ///
53    /// A success [`Result`] if verification succeeds, or a [`PlonkError`] if verification fails.
54    pub fn verify(
55        proof: &[u8],
56        sp1_public_inputs: &[u8],
57        sp1_vkey_hash: &str,
58        plonk_vk: &[u8],
59    ) -> Result<(), PlonkError> {
60        if proof.len() < VK_HASH_PREFIX_LENGTH {
61            return Err(PlonkError::GeneralError(Error::InvalidData));
62        }
63
64        // Hash the vk and get the first 4 bytes.
65        let plonk_vk_hash: [u8; 4] = Sha256::digest(plonk_vk)[..VK_HASH_PREFIX_LENGTH]
66            .try_into()
67            .map_err(|_| PlonkError::GeneralError(Error::InvalidData))?;
68
69        // Check to make sure that this proof was generated by the plonk proving key corresponding
70        // to the given plonk vk.
71        //
72        // SP1 prepends the raw Plonk proof with the first 4 bytes of the plonk vkey to
73        // facilitate this check.
74        if plonk_vk_hash != proof[..VK_HASH_PREFIX_LENGTH] {
75            return Err(PlonkError::PlonkVkeyHashMismatch);
76        }
77
78        let sp1_vkey_hash = decode_sp1_vkey_hash(sp1_vkey_hash)?;
79
80        // First, check if the public values hashed with SHA2 match the expected public values.
81        // If not, try hashing with Blake3. If both fail, return an error. We perform the checks
82        // sequentially to avoid calculating both hashes unless necessary.
83        if Self::verify_gnark_proof(
84            &proof[VK_HASH_PREFIX_LENGTH..],
85            &[sp1_vkey_hash, hash_public_inputs(sp1_public_inputs)],
86            plonk_vk,
87        )
88        .is_ok()
89        {
90            return Ok(());
91        }
92
93        Self::verify_gnark_proof(
94            &proof[VK_HASH_PREFIX_LENGTH..],
95            &[sp1_vkey_hash, hash_public_inputs_with_fn(sp1_public_inputs, blake3_hash)],
96            plonk_vk,
97        )
98    }
99
100    /// Verifies a Gnark PLONK proof using raw byte inputs.
101    ///
102    /// WARNING: if you're verifying an SP1 proof, you should use [`verify`] instead.
103    /// This is a lower-level verification method that works directly with raw bytes rather than
104    /// the SP1 SDK's data structures.
105    ///
106    /// # Arguments
107    ///
108    /// * `proof` - The raw PLONK proof bytes (without the 4-byte vkey hash prefix)
109    /// * `public_inputs` - The public inputs to the circuit
110    /// * `plonk_vk` - The PLONK verifying key bytes
111    ///
112    /// # Returns
113    ///
114    /// A [`Result`] containing unit `()` if the proof is valid,
115    /// or a [`PlonkError`] if verification fails.
116    pub fn verify_gnark_proof(
117        proof: &[u8],
118        public_inputs: &[[u8; 32]],
119        plonk_vk: &[u8],
120    ) -> Result<(), PlonkError> {
121        let plonk_vk = load_plonk_verifying_key_from_bytes(plonk_vk).unwrap();
122        let proof = load_plonk_proof_from_bytes(proof, plonk_vk.qcp.len()).unwrap();
123
124        let public_inputs =
125            public_inputs.iter().map(|input| Fr::from_slice(input).unwrap()).collect::<Vec<_>>();
126        verify_plonk_algebraic(&plonk_vk, &proof, &public_inputs)
127    }
128}