openvm_static_verifier/
keygen.rs

1use halo2_base::{
2    gates::circuit::CircuitBuilderStage,
3    halo2_proofs::plonk::{keygen_pk, keygen_vk},
4};
5use openvm_stark_sdk::{
6    config::baby_bear_bn254_poseidon2::BabyBearBn254Poseidon2Config as RootConfig,
7    openvm_stark_backend::proof::Proof,
8};
9#[cfg(feature = "evm-prove")]
10use serde::{Deserialize, Serialize};
11
12use crate::{
13    circuit::StaticVerifierCircuit,
14    config::StaticVerifierShape,
15    prover::{Halo2Params, Halo2ProvingMetadata, Halo2ProvingPinning, StaticVerifierProof},
16};
17
18impl StaticVerifierCircuit {
19    /// Run keygen to produce a [`Halo2ProvingPinning`].
20    ///
21    /// The `representative_proof` is used as a witness for keygen; any valid proof for this static
22    /// circuit shape will do.
23    pub fn keygen(
24        &self,
25        params: &Halo2Params,
26        shape: &StaticVerifierShape,
27        representative_proof: &Proof<RootConfig>,
28    ) -> Halo2ProvingPinning {
29        let mut builder = Self::builder(CircuitBuilderStage::Keygen, shape);
30        self.populate(&mut builder, representative_proof);
31
32        let config_params = builder.calculate_params(Some(shape.minimum_rows));
33
34        let vk = keygen_vk(params, &builder).expect("keygen_vk should succeed");
35        let pk = keygen_pk(params, vk, &builder).expect("keygen_pk should succeed");
36        let break_points = builder.break_points();
37
38        Halo2ProvingPinning {
39            pk,
40            metadata: Halo2ProvingMetadata {
41                config_params,
42                break_points,
43                num_pvs: builder
44                    .assigned_instances
45                    .iter()
46                    .map(|instances| instances.len())
47                    .collect(),
48            },
49        }
50    }
51}
52
53/// High-level proving key that owns a [`StaticVerifierCircuit`], [`Halo2ProvingPinning`], and
54/// [`StaticVerifierShape`].
55#[derive(Clone)]
56pub struct StaticVerifierProvingKey {
57    pub circuit: StaticVerifierCircuit,
58    pub pinning: Halo2ProvingPinning,
59    pub shape: StaticVerifierShape,
60}
61
62impl StaticVerifierProvingKey {
63    /// Run keygen and return a proving key that can be reused for multiple proofs.
64    pub fn keygen(
65        params: &Halo2Params,
66        shape: StaticVerifierShape,
67        circuit: StaticVerifierCircuit,
68        representative_proof: &Proof<RootConfig>,
69    ) -> Self {
70        let pinning = circuit.keygen(params, &shape, representative_proof);
71        Self {
72            circuit,
73            pinning,
74            shape,
75        }
76    }
77
78    /// Generate a proof using the stored pinning and shape.
79    pub fn prove(&self, params: &Halo2Params, proof: &Proof<RootConfig>) -> StaticVerifierProof {
80        self.circuit
81            .prove(params, &self.pinning, &self.shape, proof)
82    }
83
84    /// Verify a proof against this proving key's verifying key.
85    pub fn verify(&self, params: &Halo2Params, proof: &StaticVerifierProof) -> bool {
86        StaticVerifierCircuit::verify(params, self.pinning.pk.get_vk(), proof)
87    }
88}
89
90// --- EVM support (feature-gated) ---
91
92#[cfg(feature = "evm-prove")]
93use halo2_base::{
94    gates::circuit::builder::BaseCircuitBuilder, halo2_proofs::halo2curves::bn256::Fr,
95};
96#[cfg(feature = "evm-prove")]
97use snark_verifier_sdk::{
98    evm::{gen_evm_proof_shplonk, gen_evm_verifier_sol_code},
99    SHPLONK,
100};
101
102/// EVM-compatible proof consisting of instances and raw proof bytes.
103#[cfg(feature = "evm-prove")]
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct RawEvmProof {
106    pub instances: Vec<Fr>,
107    pub proof: Vec<u8>,
108}
109
110#[cfg(feature = "evm-prove")]
111impl StaticVerifierProvingKey {
112    /// Generate a Solidity verifier contract for this circuit.
113    pub fn generate_fallback_evm_verifier(&self, params: &Halo2Params) -> String {
114        gen_evm_verifier_sol_code::<BaseCircuitBuilder<Fr>, SHPLONK>(
115            params,
116            self.pinning.pk.get_vk(),
117            self.pinning.metadata.num_pvs.clone(),
118        )
119    }
120
121    /// Produce a [`Snark`](snark_verifier_sdk::Snark) for consumption by the wrapper circuit.
122    ///
123    /// Unlike [`prove_for_evm_unwrapped`](Self::prove_for_evm_unwrapped), this
124    /// returns a `Snark` (not a raw EVM proof), which should be fed into
125    /// [`Halo2WrapperProvingKey::prove_for_evm`](crate::wrapper::Halo2WrapperProvingKey::prove_for_evm).
126    pub fn prove_wrapped(
127        &self,
128        params: &Halo2Params,
129        proof: &Proof<RootConfig>,
130    ) -> snark_verifier_sdk::Snark {
131        let mut builder = BaseCircuitBuilder::prover(
132            self.pinning.metadata.config_params.clone(),
133            self.pinning.metadata.break_points.clone(),
134        )
135        .use_instance_columns(self.shape.instance_columns);
136
137        let _public_inputs = self.circuit.populate(&mut builder, proof);
138
139        snark_verifier_sdk::halo2::gen_snark_shplonk(
140            params,
141            &self.pinning.pk,
142            builder,
143            None::<&str>,
144        )
145    }
146
147    /// Generate a dummy snark for wrapper keygen.
148    pub fn generate_dummy_snark(
149        &self,
150        reader: &impl crate::wrapper::Halo2ParamsReader,
151    ) -> snark_verifier_sdk::Snark {
152        let k = self.pinning.metadata.config_params.k;
153        let params = reader.read_params(k);
154        snark_verifier_sdk::halo2::gen_dummy_snark_from_vk::<SHPLONK>(
155            &params,
156            self.pinning.pk.get_vk(),
157            self.pinning.metadata.num_pvs.clone(),
158            None,
159        )
160    }
161
162    /// Generate an EVM-compatible proof directly (one-step, no wrapper circuit).
163    pub fn prove_for_evm_unwrapped(
164        &self,
165        params: &Halo2Params,
166        proof: &Proof<RootConfig>,
167    ) -> RawEvmProof {
168        self.shape.assert_onchain_verifier_supported();
169
170        let mut builder = BaseCircuitBuilder::prover(
171            self.pinning.metadata.config_params.clone(),
172            self.pinning.metadata.break_points.clone(),
173        )
174        .use_instance_columns(self.shape.instance_columns);
175
176        let public_inputs = self.circuit.populate(&mut builder, proof);
177        let instances_vec = public_inputs.to_vec();
178
179        let snark = gen_evm_proof_shplonk(
180            params,
181            &self.pinning.pk,
182            builder,
183            vec![instances_vec.clone()],
184        );
185
186        RawEvmProof {
187            instances: instances_vec,
188            proof: snark,
189        }
190    }
191}
192
193/// Verify an EVM proof using a deployed verifier contract.
194///
195/// Returns the gas used on success, or an error message on failure.
196#[cfg(feature = "evm-verify")]
197pub fn evm_verify(deployment_code: &[u8], proof: &RawEvmProof) -> Result<u64, String> {
198    snark_verifier_sdk::evm::evm_verify(
199        deployment_code.to_vec(),
200        vec![proof.instances.clone()],
201        proof.proof.clone(),
202    )
203    .map_err(|e| format!("EVM verification failed: {e}"))
204}