Skip to main content

sp1_verifier/compressed/
internal.rs

1//! Internal constants and types that determine the verifier configuration.
2
3use alloc::vec::Vec;
4use core::borrow::Borrow;
5
6use slop_algebra::{AbstractField, PrimeField32};
7use slop_symmetric::CryptographicHasher;
8use sp1_hypercube::{
9    verify_merkle_proof, HashableKey, InnerSC, MachineVerifier, MachineVerifierError,
10    SP1RecursionProof, ShardVerifier, DIGEST_SIZE, PROOF_MAX_NUM_PVS,
11};
12use sp1_primitives::{fri_params::recursion_fri_config, poseidon2_hasher, SP1Field};
13use sp1_recursion_executor::{RecursionPublicValues, NUM_PV_ELMS_TO_HASH};
14
15use super::CompressedError;
16use crate::{
17    blake3_hash,
18    compressed::{RECURSION_LOG_STACKING_HEIGHT, RECURSION_MAX_LOG_ROW_COUNT},
19};
20
21/// The finite field used for compress proofs.
22type GC = sp1_primitives::SP1GlobalContext;
23/// The stark configuration used for compress proofs.
24type C = sp1_hypercube::SP1PcsProofInner;
25
26/// Degree of Poseidon2 etc. in the compress machine.
27pub const COMPRESS_DEGREE: usize = 3;
28
29pub type CompressAir<SP1Field> = sp1_recursion_machine::RecursionAir<SP1Field, COMPRESS_DEGREE, 2>;
30
31// // The rest of the functions in this file have been copied from elsewhere with slight
32// modifications.
33
34/// A verifier for SP1 "compressed" proofs.
35pub struct SP1CompressedVerifier {
36    verifier: MachineVerifier<GC, InnerSC<CompressAir<SP1Field>>>,
37    vk_merkle_root: [SP1Field; DIGEST_SIZE],
38}
39
40impl Default for SP1CompressedVerifier {
41    fn default() -> Self {
42        let compress_log_stacking_height = RECURSION_LOG_STACKING_HEIGHT;
43        let compress_max_log_row_count = RECURSION_MAX_LOG_ROW_COUNT;
44
45        let machine = CompressAir::<SP1Field>::compress_machine();
46        let recursion_shard_verifier = ShardVerifier::from_basefold_parameters(
47            recursion_fri_config(),
48            compress_log_stacking_height,
49            compress_max_log_row_count,
50            machine.clone(),
51        );
52
53        let verifier = MachineVerifier::new(recursion_shard_verifier);
54        let vk_merkle_root = crate::VerifierRecursionVks::default().root();
55        Self { verifier, vk_merkle_root }
56    }
57}
58
59impl SP1CompressedVerifier {
60    pub fn new() -> Self {
61        Self::default()
62    }
63
64    /// Compute the digest of the public values.
65    pub fn recursion_public_values_digest(
66        &self,
67        public_values: &RecursionPublicValues<SP1Field>,
68    ) -> [SP1Field; 8] {
69        let hasher = poseidon2_hasher();
70        hasher.hash_slice(&public_values.as_array()[0..NUM_PV_ELMS_TO_HASH])
71    }
72
73    /// Assert that the digest of the public values is correct.
74    pub fn is_recursion_public_values_valid(
75        &self,
76        public_values: &RecursionPublicValues<SP1Field>,
77    ) -> bool {
78        let expected_digest = self.recursion_public_values_digest(public_values);
79        public_values.digest.iter().copied().eq(expected_digest)
80    }
81
82    /// Verify a compressed proof.
83    pub fn verify_compressed(
84        &self,
85        proof: &SP1RecursionProof<GC, C>,
86        vkey_hash: &[SP1Field; 8],
87    ) -> Result<(), CompressedError> {
88        let SP1RecursionProof { vk: compress_vk, proof, vk_merkle_proof } = proof;
89
90        let mut challenger = self.verifier.challenger();
91        compress_vk.observe_into(&mut challenger);
92
93        // Verify the shard proof.
94        self.verifier
95            .verify_shard(compress_vk, proof, &mut challenger)
96            .map_err(MachineVerifierError::InvalidShardProof)?;
97
98        // Check the public values length.
99        if proof.public_values.len() != PROOF_MAX_NUM_PVS {
100            return Err(MachineVerifierError::InvalidPublicValues("invalid public values length"))?;
101        }
102
103        // Validate the public values.
104        let public_values: &RecursionPublicValues<_> = proof.public_values.as_slice().borrow();
105
106        // The `digest` is the correct hash of the recursion public values.
107        if !self.is_recursion_public_values_valid(public_values) {
108            return Err(MachineVerifierError::InvalidPublicValues(
109                "recursion public values are invalid",
110            )
111            .into());
112        }
113
114        // Verify the merkle proof of inclusion in the tree.
115        verify_merkle_proof(vk_merkle_proof, compress_vk.hash_koalabear(), self.vk_merkle_root)
116            .map_err(CompressedError::InvalidVkey)?;
117
118        // Verify that the vk merkle tree root in the public values matches the expected root.
119        if public_values.vk_root != self.vk_merkle_root {
120            return Err(MachineVerifierError::InvalidPublicValues("vk merkle root mismatch"))?;
121        }
122
123        // `is_complete` should be 1. This ensures that the proof is fully reduced.
124        if public_values.is_complete != SP1Field::one() {
125            return Err(MachineVerifierError::InvalidPublicValues("is_complete is not 1").into());
126        }
127
128        // Verify that the proof is for the sp1 vkey we are expecting.
129        if public_values.sp1_vk_digest != *vkey_hash {
130            return Err(MachineVerifierError::InvalidPublicValues("sp1 vk hash mismatch").into());
131        }
132
133        Ok(())
134    }
135
136    /// Verify a compressed proof.
137    pub fn verify_compressed_with_public_values(
138        &self,
139        proof: &SP1RecursionProof<GC, C>,
140        sp1_public_inputs: &[u8],
141        vkey_hash: &[SP1Field; 8],
142    ) -> Result<(), CompressedError> {
143        // Verify the proof
144        self.verify_compressed(proof, vkey_hash)?;
145
146        // Verify the public values are corresponding to the digest of the public inputs in the
147        // proof
148
149        let SP1RecursionProof { proof, .. } = proof;
150
151        // Validate the public values.
152        let public_values: &RecursionPublicValues<_> = proof.public_values.as_slice().borrow();
153
154        // Validate the SP1 public values against the committed digest.
155        let committed_value_digest_bytes = public_values
156            .committed_value_digest
157            .iter()
158            .flat_map(|w| w.iter().map(|x| x.as_canonical_u32() as u8))
159            .collect::<Vec<_>>();
160
161        // For compressed proofs, the committed digest uses the full hash (no bit masking).
162        // hash_public_inputs zeroes the top 3 bits for Plonk/Groth16 field compatibility,
163        // but that doesn't apply here.
164        let sha256_digest = crate::sha256_hash(sp1_public_inputs);
165        let blake3_digest = blake3_hash(sp1_public_inputs);
166        if committed_value_digest_bytes.as_slice() != sha256_digest.as_slice()
167            && committed_value_digest_bytes.as_slice() != blake3_digest.as_slice()
168        {
169            return Err(CompressedError::PublicValuesMismatch);
170        }
171        Ok(())
172    }
173}