openvm_static_verifier/
prover.rs

1use halo2_base::{
2    gates::{
3        circuit::{builder::BaseCircuitBuilder, BaseCircuitParams, CircuitBuilderStage},
4        flex_gate::MultiPhaseThreadBreakPoints,
5    },
6    halo2_proofs::{
7        dev::MockProver,
8        halo2curves::bn256::{Bn256, Fr, G1Affine},
9        plonk::{create_proof, verify_proof, ProvingKey, VerifyingKey},
10        poly::{
11            commitment::ParamsProver,
12            kzg::{
13                commitment::{KZGCommitmentScheme, ParamsKZG},
14                multiopen::{ProverSHPLONK, VerifierSHPLONK},
15                strategy::SingleStrategy,
16            },
17        },
18        transcript::{
19            Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer,
20        },
21    },
22};
23use openvm_stark_sdk::{
24    config::baby_bear_bn254_poseidon2::BabyBearBn254Poseidon2Config as RootConfig,
25    openvm_stark_backend::proof::Proof,
26};
27use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
28use serde::{Deserialize, Serialize};
29
30use crate::{circuit::StaticVerifierCircuit, config::StaticVerifierShape};
31
32/// KZG parameters for the Halo2 BN256 proving system.
33pub type Halo2Params = ParamsKZG<Bn256>;
34
35/// Serializable metadata that accompanies a Halo2 proving key.
36///
37/// Stores everything needed to reconstruct a prover builder (config params and
38/// break points) without the heavyweight [`ProvingKey`] itself.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct Halo2ProvingMetadata {
41    pub config_params: BaseCircuitParams,
42    pub break_points: MultiPhaseThreadBreakPoints,
43    pub num_pvs: Vec<usize>,
44}
45
46/// A proving key together with the metadata needed to reconstruct prover
47/// circuits.
48#[derive(Debug, Clone)]
49pub struct Halo2ProvingPinning {
50    pub pk: ProvingKey<G1Affine>,
51    pub metadata: Halo2ProvingMetadata,
52}
53
54/// Output of [`StaticVerifierCircuit::prove`].
55pub struct StaticVerifierProof {
56    pub proof_bytes: Vec<u8>,
57    pub public_inputs: Vec<Fr>,
58}
59
60impl StaticVerifierCircuit {
61    /// Create a [`BaseCircuitBuilder`] configured for the given `stage` and
62    /// `shape`.
63    pub fn builder(
64        stage: CircuitBuilderStage,
65        shape: &StaticVerifierShape,
66    ) -> BaseCircuitBuilder<Fr> {
67        BaseCircuitBuilder::from_stage(stage)
68            .use_k(shape.k)
69            .use_lookup_bits(shape.lookup_bits)
70            .use_instance_columns(shape.instance_columns)
71    }
72
73    /// Run the [`MockProver`] and panic if any constraint is unsatisfied.
74    ///
75    /// Uses full [`Self::populate`]. Continuations-shaped end-to-end coverage is in `openvm-sdk`
76    /// integration tests.
77    pub fn mock(&self, shape: &StaticVerifierShape, proof: &Proof<RootConfig>) {
78        let mut builder = Self::builder(CircuitBuilderStage::Mock, shape);
79        let public_inputs = self.populate(&mut builder, proof);
80
81        let _ = builder.calculate_params(Some(shape.minimum_rows));
82
83        let prover = MockProver::run(shape.k as u32, &builder, vec![public_inputs.to_vec()])
84            .expect("MockProver should initialize");
85        prover.assert_satisfied();
86    }
87
88    /// Generate a Halo2 proof using a previously computed [`Halo2ProvingPinning`].
89    ///
90    /// Uses full [`Self::populate`]. Continuations-shaped proving is covered in `openvm-sdk`
91    /// integration tests.
92    pub fn prove(
93        &self,
94        params: &Halo2Params,
95        pinning: &Halo2ProvingPinning,
96        shape: &StaticVerifierShape,
97        proof: &Proof<RootConfig>,
98    ) -> StaticVerifierProof {
99        let mut builder = BaseCircuitBuilder::prover(
100            pinning.metadata.config_params.clone(),
101            pinning.metadata.break_points.clone(),
102        );
103        builder = builder.use_instance_columns(shape.instance_columns);
104
105        let public_inputs = self.populate(&mut builder, proof);
106        let public_inputs = public_inputs.to_vec();
107
108        let rng = ChaCha20Rng::from_seed(Default::default());
109        let instances: &[&[Fr]] = &[&public_inputs];
110        let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);
111        create_proof::<
112            KZGCommitmentScheme<Bn256>,
113            ProverSHPLONK<'_, Bn256>,
114            Challenge255<_>,
115            _,
116            Blake2bWrite<Vec<u8>, G1Affine, _>,
117            _,
118        >(
119            params,
120            &pinning.pk,
121            &[builder],
122            &[instances],
123            rng,
124            &mut transcript,
125        )
126        .expect("Halo2 proof generation should succeed");
127
128        StaticVerifierProof {
129            proof_bytes: transcript.finalize(),
130            public_inputs,
131        }
132    }
133
134    /// Verify a Halo2 proof against a verifying key.
135    pub fn verify(
136        params: &Halo2Params,
137        vk: &VerifyingKey<G1Affine>,
138        proof: &StaticVerifierProof,
139    ) -> bool {
140        let verifier_params = params.verifier_params();
141        let strategy = SingleStrategy::new(params);
142        let mut transcript =
143            Blake2bRead::<_, _, Challenge255<_>>::init(proof.proof_bytes.as_slice());
144        // One entry per circuit; inner slice length must match `num_instance_columns` (here: 0).
145        let no_instance_cols: &[&[Fr]] = &[];
146        let ok = if proof.public_inputs.is_empty() {
147            verify_proof::<
148                KZGCommitmentScheme<Bn256>,
149                VerifierSHPLONK<'_, Bn256>,
150                Challenge255<G1Affine>,
151                Blake2bRead<&[u8], G1Affine, Challenge255<G1Affine>>,
152                SingleStrategy<'_, Bn256>,
153            >(
154                verifier_params,
155                vk,
156                strategy,
157                &[no_instance_cols],
158                &mut transcript,
159            )
160        } else {
161            let instances: &[&[Fr]] = &[&proof.public_inputs];
162            verify_proof::<
163                KZGCommitmentScheme<Bn256>,
164                VerifierSHPLONK<'_, Bn256>,
165                Challenge255<G1Affine>,
166                Blake2bRead<&[u8], G1Affine, Challenge255<G1Affine>>,
167                SingleStrategy<'_, Bn256>,
168            >(verifier_params, vk, strategy, &[instances], &mut transcript)
169        };
170        ok.is_ok()
171    }
172}
173
174impl Halo2ProvingPinning {
175    /// Return the proving key as raw Halo2 bytes.
176    pub fn pk_to_bytes(&self) -> Vec<u8> {
177        use halo2_base::halo2_proofs::SerdeFormat;
178        self.pk.to_bytes(SerdeFormat::RawBytes)
179    }
180}