1#![allow(missing_docs)]
5
6use std::{fmt::Debug, fs::File, path::Path};
7
8use anyhow::{Context, Result};
9use hashbrown::HashMap;
10use p3_baby_bear::BabyBear;
11use p3_field::{extension::BinomialExtensionField, AbstractField, PrimeField};
12use p3_fri::{FriProof, TwoAdicFriPcsProof};
13use serde::{Deserialize, Serialize};
14use sp1_primitives::io::SP1PublicValues;
15use sp1_prover::{Groth16Bn254Proof, HashableKey, PlonkBn254Proof, SP1ProvingKey};
16use sp1_stark::{
17 septic_digest::SepticDigest, SP1ReduceProof, ShardCommitment, ShardOpenedValues, ShardProof,
18 StarkVerifyingKey,
19};
20
21pub use sp1_stark::{SP1Proof, SP1ProofMode};
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct SP1ProofWithPublicValues {
27 pub proof: SP1Proof,
29 pub public_values: SP1PublicValues,
31 pub sp1_version: String,
34 pub tee_proof: Option<Vec<u8>>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ProofFromNetwork {
43 pub proof: SP1Proof,
44 pub public_values: SP1PublicValues,
45 pub sp1_version: String,
46}
47
48impl From<ProofFromNetwork> for SP1ProofWithPublicValues {
49 fn from(value: ProofFromNetwork) -> Self {
50 Self {
51 proof: value.proof,
52 public_values: value.public_values,
53 sp1_version: value.sp1_version,
54 tee_proof: None,
55 }
56 }
57}
58
59impl SP1ProofWithPublicValues {
60 pub(crate) const fn new(
64 proof: SP1Proof,
65 public_values: SP1PublicValues,
66 sp1_version: String,
67 ) -> Self {
68 Self { proof, public_values, sp1_version, tee_proof: None }
69 }
70
71 pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
73 bincode::serialize_into(
74 File::create(path.as_ref()).with_context(|| {
75 format!("failed to create file for saving proof: {}", path.as_ref().display())
76 })?,
77 self,
78 )
79 .map_err(Into::into)
80 }
81
82 pub fn load(path: impl AsRef<Path>) -> Result<Self> {
84 let maybe_this: Result<Self> =
86 bincode::deserialize_from(File::open(path.as_ref()).with_context(|| {
87 format!("failed to open file for loading proof: {}", path.as_ref().display())
88 })?)
89 .map_err(Into::into);
90
91 match maybe_this {
93 Ok(this) => Ok(this),
94 Err(e) => {
95 let maybe_proof_from_network: Result<ProofFromNetwork> =
98 bincode::deserialize_from(File::open(path.as_ref()).with_context(|| {
99 format!(
100 "failed to open file for loading proof: {}",
101 path.as_ref().display()
102 )
103 })?)
104 .map_err(Into::into);
105
106 if let Ok(proof_from_network) = maybe_proof_from_network {
107 Ok(proof_from_network.into())
109 } else {
110 Err(e)
112 }
113 }
114 }
115 }
116
117 #[must_use]
124 pub fn bytes(&self) -> Vec<u8> {
125 match &self.proof {
126 SP1Proof::Plonk(plonk_proof) => {
127 if plonk_proof.encoded_proof.is_empty() {
130 return Vec::new();
131 }
132
133 let proof_bytes =
134 hex::decode(&plonk_proof.encoded_proof).expect("Invalid Plonk proof");
135
136 if let Some(tee_proof) = &self.tee_proof {
137 return [
138 tee_proof.clone(),
139 plonk_proof.plonk_vkey_hash[..4].to_vec(),
140 proof_bytes,
141 ]
142 .concat();
143 }
144
145 [plonk_proof.plonk_vkey_hash[..4].to_vec(), proof_bytes].concat()
146 }
147 SP1Proof::Groth16(groth16_proof) => {
148 if groth16_proof.encoded_proof.is_empty() {
151 return Vec::new();
152 }
153
154 let proof_bytes =
155 hex::decode(&groth16_proof.encoded_proof).expect("Invalid Groth16 proof");
156
157 if let Some(tee_proof) = &self.tee_proof {
158 return [
159 tee_proof.clone(),
160 groth16_proof.groth16_vkey_hash[..4].to_vec(),
161 proof_bytes,
162 ]
163 .concat();
164 }
165
166 [groth16_proof.groth16_vkey_hash[..4].to_vec(), proof_bytes].concat()
167 }
168 proof => panic!(
169 "Proof type {proof} is not supported for onchain verification. \
170 Only Plonk and Groth16 proofs are verifiable onchain"
171 ),
172 }
173 }
174
175 #[must_use]
199 pub fn create_mock_proof(
200 pk: &SP1ProvingKey,
201 public_values: SP1PublicValues,
202 mode: SP1ProofMode,
203 sp1_version: &str,
204 ) -> Self {
205 let sp1_version = sp1_version.to_string();
206 match mode {
207 SP1ProofMode::Core => SP1ProofWithPublicValues {
208 proof: SP1Proof::Core(vec![]),
209 public_values,
210 sp1_version,
211
212 tee_proof: None,
213 },
214 SP1ProofMode::Compressed => {
215 let shard_proof = ShardProof {
216 commitment: ShardCommitment {
217 main_commit: [BabyBear::zero(); 8].into(),
218 permutation_commit: [BabyBear::zero(); 8].into(),
219 quotient_commit: [BabyBear::zero(); 8].into(),
220 },
221 opened_values: ShardOpenedValues { chips: vec![] },
222 opening_proof: TwoAdicFriPcsProof {
223 fri_proof: FriProof {
224 commit_phase_commits: vec![],
225 query_proofs: vec![],
226 final_poly: BinomialExtensionField::default(),
227 pow_witness: BabyBear::zero(),
228 },
229 query_openings: vec![],
230 },
231 chip_ordering: HashMap::new(),
232 public_values: vec![],
233 };
234
235 let reduce_vk = StarkVerifyingKey {
236 commit: [BabyBear::zero(); 8].into(),
237 pc_start: BabyBear::zero(),
238 chip_information: vec![],
239 chip_ordering: HashMap::new(),
240 initial_global_cumulative_sum: SepticDigest::zero(),
241 };
242
243 let proof = SP1Proof::Compressed(Box::new(SP1ReduceProof {
244 vk: reduce_vk,
245 proof: shard_proof,
246 }));
247
248 SP1ProofWithPublicValues { proof, public_values, sp1_version, tee_proof: None }
249 }
250 SP1ProofMode::Plonk => SP1ProofWithPublicValues {
251 proof: SP1Proof::Plonk(PlonkBn254Proof {
252 public_inputs: [
253 pk.vk.hash_bn254().as_canonical_biguint().to_string(),
254 public_values.hash_bn254().to_string(),
255 ],
256 encoded_proof: String::new(),
257 raw_proof: String::new(),
258 plonk_vkey_hash: [0; 32],
259 }),
260 public_values,
261 sp1_version,
262
263 tee_proof: None,
264 },
265 SP1ProofMode::Groth16 => SP1ProofWithPublicValues {
266 proof: SP1Proof::Groth16(Groth16Bn254Proof {
267 public_inputs: [
268 pk.vk.hash_bn254().as_canonical_biguint().to_string(),
269 public_values.hash_bn254().to_string(),
270 ],
271 encoded_proof: String::new(),
272 raw_proof: String::new(),
273 groth16_vkey_hash: [0; 32],
274 }),
275 public_values,
276 sp1_version,
277
278 tee_proof: None,
279 },
280 }
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 #![allow(clippy::print_stdout)]
287
288 use super::*;
289
290 #[test]
291 fn test_plonk_proof_bytes() {
292 let plonk_proof = SP1ProofWithPublicValues {
293 proof: SP1Proof::Plonk(PlonkBn254Proof {
294 encoded_proof: "ab".to_string(),
295 plonk_vkey_hash: [0; 32],
296 public_inputs: [String::new(), String::new()],
297 raw_proof: String::new(),
298 }),
299 public_values: SP1PublicValues::new(),
300 sp1_version: String::new(),
301 tee_proof: None,
302 };
303 let expected_bytes = [vec![0, 0, 0, 0], hex::decode("ab").unwrap()].concat();
304 assert_eq!(plonk_proof.bytes(), expected_bytes);
305 }
306
307 #[test]
308 fn test_groth16_proof_bytes() {
309 let groth16_proof = SP1ProofWithPublicValues {
310 proof: SP1Proof::Groth16(Groth16Bn254Proof {
311 encoded_proof: "ab".to_string(),
312 groth16_vkey_hash: [0; 32],
313 public_inputs: [String::new(), String::new()],
314 raw_proof: String::new(),
315 }),
316 public_values: SP1PublicValues::new(),
317 sp1_version: String::new(),
318 tee_proof: None,
319 };
320 let expected_bytes = [vec![0, 0, 0, 0], hex::decode("ab").unwrap()].concat();
321 assert_eq!(groth16_proof.bytes(), expected_bytes);
322 }
323
324 #[test]
325 fn test_mock_plonk_proof_bytes() {
326 let mock_plonk_proof = SP1ProofWithPublicValues {
327 proof: SP1Proof::Plonk(PlonkBn254Proof {
328 encoded_proof: String::new(),
329 plonk_vkey_hash: [0; 32],
330 public_inputs: [String::new(), String::new()],
331 raw_proof: String::new(),
332 }),
333 public_values: SP1PublicValues::new(),
334 sp1_version: String::new(),
335 tee_proof: None,
336 };
337 assert_eq!(mock_plonk_proof.bytes(), Vec::<u8>::new());
338 }
339
340 #[test]
341 fn test_mock_groth16_proof_bytes() {
342 let mock_groth16_proof = SP1ProofWithPublicValues {
343 proof: SP1Proof::Groth16(Groth16Bn254Proof {
344 encoded_proof: String::new(),
345 groth16_vkey_hash: [0; 32],
346 public_inputs: [String::new(), String::new()],
347 raw_proof: String::new(),
348 }),
349 public_values: SP1PublicValues::new(),
350 sp1_version: String::new(),
351 tee_proof: None,
352 };
353 assert_eq!(mock_groth16_proof.bytes(), Vec::<u8>::new());
354 }
355
356 #[test]
357 #[should_panic(
358 expected = "Proof type Core is not supported for onchain verification. Only Plonk and Groth16 proofs are verifiable onchain"
359 )]
360 fn test_core_proof_bytes_unimplemented() {
361 let core_proof = SP1ProofWithPublicValues {
362 proof: SP1Proof::Core(vec![]),
363 public_values: SP1PublicValues::new(),
364 sp1_version: String::new(),
365 tee_proof: None,
366 };
367 println!("{:?}", core_proof.bytes());
368 }
369
370 #[test]
371 fn test_deser_backwards_compat() {
372 let round_trip = SP1ProofWithPublicValues {
373 proof: SP1Proof::Core(vec![]),
374 public_values: SP1PublicValues::new(),
375 sp1_version: String::new(),
376 tee_proof: None,
377 };
378
379 let round_trip_bytes = bincode::serialize(&round_trip).unwrap();
380
381 bincode::deserialize::<SP1ProofWithPublicValues>(&round_trip_bytes).unwrap();
382
383 let _ = ProofFromNetwork {
384 proof: SP1Proof::Core(vec![]),
385 public_values: SP1PublicValues::new(),
386 sp1_version: String::new(),
387 };
388
389 let _ = bincode::deserialize::<ProofFromNetwork>(&round_trip_bytes).unwrap();
390 }
391
392 #[test]
393 fn test_round_trip_proof_save_load() {
394 use crate::Prover;
395
396 let prover = crate::CpuProver::new();
397 let (pk, _) = prover.setup(test_artifacts::FIBONACCI_BLAKE3_ELF);
398 let proof = prover.prove(&pk, &crate::SP1Stdin::new()).compressed().run().unwrap();
399
400 prover.verify(&proof, &pk.vk).unwrap();
402
403 let temp_dir = tempfile::tempdir().unwrap();
404 let path = temp_dir.path().join("proof.bin");
405 std::fs::File::create(&path).unwrap();
406 proof.save(&path).unwrap();
407
408 let proof_loaded = SP1ProofWithPublicValues::load(&path).unwrap();
409
410 prover.verify(&proof_loaded, &pk.vk).unwrap();
412 }
413}