Skip to main content

sp1_sdk/
prover.rs

1//! # SP1 Prover Trait
2//!
3//! A trait that each prover variant must implement.
4
5use crate::StatusCode;
6use anyhow::Result;
7use itertools::Itertools;
8use num_bigint::BigUint;
9use sp1_core_machine::io::SP1Stdin;
10use sp1_hypercube::PrimeField32;
11use sp1_hypercube::{air::PublicValues, PROOF_MAX_NUM_PVS};
12use sp1_primitives::types::Elf;
13use sp1_prover::{
14    verify::verify_public_values, worker::SP1NodeCore, SP1VerifyingKey, SP1_CIRCUIT_VERSION,
15};
16use sp1_recursion_executor::RecursionPublicValues;
17use std::{
18    borrow::Borrow,
19    fmt,
20    future::{Future, IntoFuture},
21    str::FromStr,
22};
23use thiserror::Error;
24
25/// The module that exposes the [`ExecuteRequest`] type.
26mod execute;
27
28/// The module that exposes the [`ProveRequest`] trait.
29mod prove;
30
31pub use execute::ExecuteRequest;
32pub(crate) use prove::BaseProveRequest;
33pub use prove::{ProveRequest, SP1ProvingKey};
34
35use crate::{SP1Proof, SP1ProofWithPublicValues};
36
37/// The entire user-facing functionality of a prover.
38pub trait Prover: Clone + Send + Sync {
39    /// The proving key used for this prover type.
40    type ProvingKey: ProvingKey;
41
42    /// The possible errors that can occur when proving.
43    type Error: fmt::Debug + fmt::Display;
44
45    /// The prove request builder.
46    type ProveRequest<'a>: ProveRequest<'a, Self>
47    where
48        Self: 'a;
49
50    /// The inner [`LocalProver`] struct used by the prover.
51    fn inner(&self) -> &SP1NodeCore;
52
53    /// The version of the current SP1 circuit.
54    fn version(&self) -> &str {
55        SP1_CIRCUIT_VERSION
56    }
57
58    /// Setup the prover with the given ELF.
59    fn setup(&self, elf: Elf) -> impl SendFutureResult<Self::ProvingKey, Self::Error>;
60
61    /// Prove the given program on the given input in the given proof mode.
62    fn prove<'a>(&'a self, pk: &'a Self::ProvingKey, stdin: SP1Stdin) -> Self::ProveRequest<'a>;
63
64    /// Execute the program on the given input.
65    fn execute(&self, elf: Elf, stdin: SP1Stdin) -> ExecuteRequest<'_, Self> {
66        ExecuteRequest::new(self, elf, stdin)
67    }
68
69    /// Verify the given proof.
70    ///
71    /// Note: If the status code is not set, the verification process will check for success.
72    fn verify(
73        &self,
74        proof: &SP1ProofWithPublicValues,
75        vkey: &SP1VerifyingKey,
76        status_code: Option<StatusCode>,
77    ) -> Result<(), SP1VerificationError> {
78        verify_proof(self.inner(), self.version(), proof, vkey, status_code)
79    }
80}
81
82/// A trait that represents a prover's proving key.
83pub trait ProvingKey: Clone + Send + Sync {
84    /// Get the verifying key corresponding to the proving key.
85    fn verifying_key(&self) -> &SP1VerifyingKey;
86
87    /// Get the ELF corresponding to the proving key.
88    fn elf(&self) -> &Elf;
89}
90
91/// A trait for [`Future`]s that are send and return a [`Result`].
92///
93/// This is just slightly better for the [`_Prover`] trait signature.
94pub trait SendFutureResult<T, E>: Future<Output = Result<T, E>> + Send {}
95
96impl<F, T, E> SendFutureResult<T, E> for F where F: Future<Output = Result<T, E>> + Send {}
97
98/// A trait for [`IntoFuture`]s that are send and return a [`Result`].
99///
100/// This is just slightly better for the [`_Prover`] trait signature.
101pub trait IntoSendFutureResult<T, E>: IntoFuture<Output = Result<T, E>> + Send {}
102
103impl<F, T, E> IntoSendFutureResult<T, E> for F where F: IntoFuture<Output = Result<T, E>> + Send {}
104
105/// An error that occurs when calling [`Prover::verify`].
106#[derive(Error, Debug)]
107pub enum SP1VerificationError {
108    /// An error that occurs when the public values are invalid.
109    #[error("Invalid public values")]
110    InvalidPublicValues,
111    /// An error that occurs when the SP1 version does not match the version of the circuit.
112    #[error("Version mismatch: {0}")]
113    VersionMismatch(String),
114    /// An error that occurs when the core machine verification fails.
115    #[error("Core machine verification error: {0}")]
116    Core(anyhow::Error),
117    /// An error that occurs when the recursion verification fails.
118    #[error("Recursion verification error: {0}")]
119    Recursion(anyhow::Error),
120    /// An error that occurs when the Plonk verification fails.
121    #[error("Plonk verification error: {0}")]
122    Plonk(anyhow::Error),
123    /// An error that occurs when the Groth16 verification fails.
124    #[error("Groth16 verification error: {0}")]
125    Groth16(anyhow::Error),
126    /// An error that occurs when the proof is invalid.
127    #[error("Unexpected error: {0:?}")]
128    Other(anyhow::Error),
129    /// An error that occurs when the exit code is unexpected.
130    #[error("Unexpected exit code: {0}")]
131    UnexpectedExitCode(u32),
132}
133
134/// In SP1, a proof's public values can either be hashed with SHA2 or Blake3. In SP1 V4, there is no
135/// metadata attached to the proof about which hasher function was used for public values hashing.
136/// Instead, when verifying the proof, the public values are hashed with SHA2 and Blake3, and
137/// if either matches the `expected_public_values_hash`, the verification is successful.
138///
139/// The security for this verification in SP1 V4 derives from the fact that both SHA2 and Blake3 are
140/// designed to be collision resistant. It is computationally infeasible to find an input i1 for
141/// SHA256 and an input i2 for Blake3 that the same hash value. Doing so would require breaking both
142/// algorithms simultaneously.
143pub(crate) fn verify_proof(
144    node: &SP1NodeCore,
145    version: &str,
146    bundle: &SP1ProofWithPublicValues,
147    vkey: &SP1VerifyingKey,
148    status_code: Option<StatusCode>,
149) -> Result<(), SP1VerificationError> {
150    let status_code = status_code.unwrap_or(StatusCode::SUCCESS);
151
152    // Check that the SP1 version matches the version of the current circuit.
153    if bundle.sp1_version != version {
154        return Err(SP1VerificationError::VersionMismatch(bundle.sp1_version.clone()));
155    }
156
157    match &bundle.proof {
158        SP1Proof::Core(proof) => {
159            if proof.is_empty() {
160                return Err(SP1VerificationError::Core(anyhow::anyhow!("Empty core proof")));
161            }
162
163            if proof.last().unwrap().public_values.len() != PROOF_MAX_NUM_PVS {
164                return Err(SP1VerificationError::InvalidPublicValues);
165            }
166
167            let public_values: &PublicValues<[_; 4], [_; 3], [_; 4], _> =
168                proof.last().unwrap().public_values.as_slice().borrow();
169
170            if !status_code.is_accepted_code(public_values.exit_code.as_canonical_u32()) {
171                return Err(SP1VerificationError::UnexpectedExitCode(
172                    public_values.exit_code.as_canonical_u32(),
173                ));
174            }
175
176            // Get the committed value digest bytes.
177            let committed_value_digest_bytes = public_values
178                .committed_value_digest
179                .iter()
180                .flat_map(|w| w.iter().map(|x| x.as_canonical_u32() as u8))
181                .collect_vec();
182
183            // Make sure the committed value digest matches the public values hash.
184            // It is computationally infeasible to find two distinct inputs, one processed with
185            // SHA256 and the other with Blake3, that yield the same hash value.
186            if committed_value_digest_bytes != bundle.public_values.hash()
187                && committed_value_digest_bytes != bundle.public_values.blake3_hash()
188            {
189                tracing::error!("committed value digest doesnt match");
190                return Err(SP1VerificationError::InvalidPublicValues);
191            }
192        }
193        SP1Proof::Compressed(proof) => {
194            if proof.proof.public_values.len() != PROOF_MAX_NUM_PVS {
195                return Err(SP1VerificationError::InvalidPublicValues);
196            }
197
198            let public_values: &RecursionPublicValues<_> =
199                proof.proof.public_values.as_slice().borrow();
200
201            if !status_code.is_accepted_code(public_values.exit_code.as_canonical_u32()) {
202                return Err(SP1VerificationError::UnexpectedExitCode(
203                    public_values.exit_code.as_canonical_u32(),
204                ));
205            }
206
207            // Get the committed value digest bytes.
208            let committed_value_digest_bytes = public_values
209                .committed_value_digest
210                .iter()
211                .flat_map(|w| w.iter().map(|x| x.as_canonical_u32() as u8))
212                .collect_vec();
213
214            // Make sure the committed value digest matches the public values hash.
215            // It is computationally infeasible to find two distinct inputs, one processed with
216            // SHA256 and the other with Blake3, that yield the same hash value.
217            if committed_value_digest_bytes != bundle.public_values.hash()
218                && committed_value_digest_bytes != bundle.public_values.blake3_hash()
219            {
220                return Err(SP1VerificationError::InvalidPublicValues);
221            }
222        }
223        SP1Proof::Plonk(proof) => {
224            let exit_code = BigUint::from_str(&proof.public_inputs[2])
225                .map_err(|e| SP1VerificationError::Plonk(anyhow::anyhow!(e)))?;
226
227            let exit_code_u32 =
228                u32::try_from(&exit_code).map_err(|_| SP1VerificationError::InvalidPublicValues)?;
229
230            if !status_code.is_accepted_code(exit_code_u32) {
231                return Err(SP1VerificationError::UnexpectedExitCode(exit_code_u32));
232            }
233
234            let public_values_hash = BigUint::from_str(&proof.public_inputs[1])
235                .map_err(|e| SP1VerificationError::Plonk(anyhow::anyhow!(e)))?;
236            verify_public_values(&bundle.public_values, public_values_hash)
237                .map_err(SP1VerificationError::Plonk)?;
238        }
239
240        SP1Proof::Groth16(proof) => {
241            let exit_code = BigUint::from_str(&proof.public_inputs[2])
242                .map_err(|e| SP1VerificationError::Plonk(anyhow::anyhow!(e)))?;
243
244            let exit_code_u32 =
245                u32::try_from(&exit_code).map_err(|_| SP1VerificationError::InvalidPublicValues)?;
246
247            if !status_code.is_accepted_code(exit_code_u32) {
248                return Err(SP1VerificationError::UnexpectedExitCode(exit_code_u32));
249            }
250
251            let public_values_hash = BigUint::from_str(&proof.public_inputs[1])
252                .map_err(|e| SP1VerificationError::Groth16(anyhow::anyhow!(e)))?;
253            verify_public_values(&bundle.public_values, public_values_hash)
254                .map_err(SP1VerificationError::Groth16)?;
255        }
256    }
257    node.verify(vkey, &bundle.proof).map_err(|e| match bundle.proof {
258        SP1Proof::Core(_) => SP1VerificationError::Core(e),
259        SP1Proof::Compressed(_) => SP1VerificationError::Recursion(e),
260        SP1Proof::Plonk(_) => SP1VerificationError::Plonk(e),
261        SP1Proof::Groth16(_) => SP1VerificationError::Groth16(e),
262    })
263}