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}