sp1_sdk/
prover.rs

1//! # SP1 Prover Trait
2//!
3//! A trait that each prover variant must implement.
4
5use std::borrow::Borrow;
6
7use anyhow::Result;
8use itertools::Itertools;
9use p3_field::PrimeField32;
10use sp1_core_executor::{ExecutionReport, SP1Context};
11use sp1_core_machine::io::SP1Stdin;
12use sp1_primitives::io::SP1PublicValues;
13use sp1_prover::{
14    components::SP1ProverComponents, CoreSC, InnerSC, SP1CoreProofData, SP1Prover, SP1ProvingKey,
15    SP1VerifyingKey, SP1_CIRCUIT_VERSION,
16};
17use sp1_stark::{air::PublicValues, MachineVerificationError, Word};
18use thiserror::Error;
19
20use crate::{
21    install::try_install_circuit_artifacts, SP1Proof, SP1ProofMode, SP1ProofWithPublicValues,
22};
23
24/// A basic set of primitives that each prover variant must implement.
25pub trait Prover<C: SP1ProverComponents>: Send + Sync {
26    /// The inner [`SP1Prover`] struct used by the prover.
27    fn inner(&self) -> &SP1Prover<C>;
28
29    /// The version of the current SP1 circuit.
30    fn version(&self) -> &str {
31        SP1_CIRCUIT_VERSION
32    }
33
34    /// Generate the proving and verifying keys for the given program.
35    fn setup(&self, elf: &[u8]) -> (SP1ProvingKey, SP1VerifyingKey);
36
37    /// Executes the program on the given input.
38    fn execute(&self, elf: &[u8], stdin: &SP1Stdin) -> Result<(SP1PublicValues, ExecutionReport)> {
39        let (pv, _, report) = self.inner().execute(elf, stdin, SP1Context::default())?;
40        Ok((pv, report))
41    }
42
43    /// Proves the given program on the given input in the given proof mode.
44    fn prove(
45        &self,
46        pk: &SP1ProvingKey,
47        stdin: &SP1Stdin,
48        mode: SP1ProofMode,
49    ) -> Result<SP1ProofWithPublicValues>;
50
51    /// Verify that an SP1 proof is valid given its vkey and metadata.
52    /// For Plonk proofs, verifies that the public inputs of the `PlonkBn254` proof match
53    /// the hash of the VK and the committed public values of the `SP1ProofWithPublicValues`.
54    fn verify(
55        &self,
56        bundle: &SP1ProofWithPublicValues,
57        vkey: &SP1VerifyingKey,
58    ) -> Result<(), SP1VerificationError> {
59        verify_proof(self.inner(), self.version(), bundle, vkey)
60    }
61}
62
63/// An error that occurs when calling [`Prover::verify`].
64#[derive(Error, Debug)]
65pub enum SP1VerificationError {
66    /// An error that occurs when the public values are invalid.
67    #[error("Invalid public values")]
68    InvalidPublicValues,
69    /// An error that occurs when the SP1 version does not match the version of the circuit.
70    #[error("Version mismatch")]
71    VersionMismatch(String),
72    /// An error that occurs when the core machine verification fails.
73    #[error("Core machine verification error: {0}")]
74    Core(MachineVerificationError<CoreSC>),
75    /// An error that occurs when the recursion verification fails.
76    #[error("Recursion verification error: {0}")]
77    Recursion(MachineVerificationError<InnerSC>),
78    /// An error that occurs when the Plonk verification fails.
79    #[error("Plonk verification error: {0}")]
80    Plonk(anyhow::Error),
81    /// An error that occurs when the Groth16 verification fails.
82    #[error("Groth16 verification error: {0}")]
83    Groth16(anyhow::Error),
84    /// An error that occurs when the proof is invalid.
85    #[error("Unexpected error: {0:?}")]
86    Other(anyhow::Error),
87}
88
89/// In SP1, a proof's public values can either be hashed with SHA2 or Blake3. In SP1 V4, there is no
90/// metadata attached to the proof about which hasher function was used for public values hashing.
91/// Instead, when verifying the proof, the public values are hashed with SHA2 and Blake3, and
92/// if either matches the `expected_public_values_hash`, the verification is successful.
93///
94/// The security for this verification in SP1 V4 derives from the fact that both SHA2 and Blake3 are
95/// designed to be collision resistant. It is computationally infeasible to find an input i1 for
96/// SHA256 and an input i2 for Blake3 that the same hash value. Doing so would require breaking both
97/// algorithms simultaneously.
98pub(crate) fn verify_proof<C: SP1ProverComponents>(
99    prover: &SP1Prover<C>,
100    version: &str,
101    bundle: &SP1ProofWithPublicValues,
102    vkey: &SP1VerifyingKey,
103) -> Result<(), SP1VerificationError> {
104    // Check that the SP1 version matches the version of the currentcircuit.
105    if bundle.sp1_version != version {
106        return Err(SP1VerificationError::VersionMismatch(bundle.sp1_version.clone()));
107    }
108
109    match &bundle.proof {
110        SP1Proof::Core(proof) => {
111            let public_values: &PublicValues<Word<_>, _> =
112                proof.last().unwrap().public_values.as_slice().borrow();
113
114            // Get the committed value digest bytes.
115            let committed_value_digest_bytes = public_values
116                .committed_value_digest
117                .iter()
118                .flat_map(|w| w.0.iter().map(|x| x.as_canonical_u32() as u8))
119                .collect_vec();
120
121            // Make sure the committed value digest matches the public values hash.
122            // It is computationally infeasible to find two distinct inputs, one processed with
123            // SHA256 and the other with Blake3, that yield the same hash value.
124            if committed_value_digest_bytes != bundle.public_values.hash() &&
125                committed_value_digest_bytes != bundle.public_values.blake3_hash()
126            {
127                return Err(SP1VerificationError::InvalidPublicValues);
128            }
129
130            // Verify the core proof.
131            prover
132                .verify(&SP1CoreProofData(proof.clone()), vkey)
133                .map_err(SP1VerificationError::Core)
134        }
135        SP1Proof::Compressed(proof) => {
136            let public_values: &PublicValues<Word<_>, _> =
137                proof.proof.public_values.as_slice().borrow();
138
139            // Get the committed value digest bytes.
140            let committed_value_digest_bytes = public_values
141                .committed_value_digest
142                .iter()
143                .flat_map(|w| w.0.iter().map(|x| x.as_canonical_u32() as u8))
144                .collect_vec();
145
146            // Make sure the committed value digest matches the public values hash.
147            // It is computationally infeasible to find two distinct inputs, one processed with
148            // SHA256 and the other with Blake3, that yield the same hash value.
149            if committed_value_digest_bytes != bundle.public_values.hash() &&
150                committed_value_digest_bytes != bundle.public_values.blake3_hash()
151            {
152                return Err(SP1VerificationError::InvalidPublicValues);
153            }
154
155            prover.verify_compressed(proof, vkey).map_err(SP1VerificationError::Recursion)
156        }
157        SP1Proof::Plonk(proof) => prover
158            .verify_plonk_bn254(
159                proof,
160                vkey,
161                &bundle.public_values,
162                &if sp1_prover::build::sp1_dev_mode() {
163                    sp1_prover::build::plonk_bn254_artifacts_dev_dir()
164                } else {
165                    try_install_circuit_artifacts("plonk")
166                },
167            )
168            .map_err(SP1VerificationError::Plonk),
169        SP1Proof::Groth16(proof) => prover
170            .verify_groth16_bn254(
171                proof,
172                vkey,
173                &bundle.public_values,
174                &if sp1_prover::build::sp1_dev_mode() {
175                    sp1_prover::build::groth16_bn254_artifacts_dev_dir()
176                } else {
177                    try_install_circuit_artifacts("groth16")
178                },
179            )
180            .map_err(SP1VerificationError::Groth16),
181    }
182}