1use 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
25mod execute;
27
28mod prove;
30
31pub use execute::ExecuteRequest;
32pub(crate) use prove::BaseProveRequest;
33pub use prove::{ProveRequest, SP1ProvingKey};
34
35use crate::{SP1Proof, SP1ProofWithPublicValues};
36
37pub trait Prover: Clone + Send + Sync {
39 type ProvingKey: ProvingKey;
41
42 type Error: fmt::Debug + fmt::Display;
44
45 type ProveRequest<'a>: ProveRequest<'a, Self>
47 where
48 Self: 'a;
49
50 fn inner(&self) -> &SP1NodeCore;
52
53 fn version(&self) -> &str {
55 SP1_CIRCUIT_VERSION
56 }
57
58 fn setup(&self, elf: Elf) -> impl SendFutureResult<Self::ProvingKey, Self::Error>;
60
61 fn prove<'a>(&'a self, pk: &'a Self::ProvingKey, stdin: SP1Stdin) -> Self::ProveRequest<'a>;
63
64 fn execute(&self, elf: Elf, stdin: SP1Stdin) -> ExecuteRequest<'_, Self> {
66 ExecuteRequest::new(self, elf, stdin)
67 }
68
69 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
82pub trait ProvingKey: Clone + Send + Sync {
84 fn verifying_key(&self) -> &SP1VerifyingKey;
86
87 fn elf(&self) -> &Elf;
89}
90
91pub 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
98pub 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#[derive(Error, Debug)]
107pub enum SP1VerificationError {
108 #[error("Invalid public values")]
110 InvalidPublicValues,
111 #[error("Version mismatch: {0}")]
113 VersionMismatch(String),
114 #[error("Core machine verification error: {0}")]
116 Core(anyhow::Error),
117 #[error("Recursion verification error: {0}")]
119 Recursion(anyhow::Error),
120 #[error("Plonk verification error: {0}")]
122 Plonk(anyhow::Error),
123 #[error("Groth16 verification error: {0}")]
125 Groth16(anyhow::Error),
126 #[error("Unexpected error: {0:?}")]
128 Other(anyhow::Error),
129 #[error("Unexpected exit code: {0}")]
131 UnexpectedExitCode(u32),
132}
133
134pub(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 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 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 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 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 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}