snark_verifier_sdk/
lib.rs

1#[cfg(feature = "display")]
2use ark_std::{end_timer, start_timer};
3use halo2_base::halo2_proofs::{self};
4use halo2_proofs::{
5    halo2curves::{
6        bn256::{Bn256, Fr, G1Affine},
7        group::ff::Field,
8    },
9    plonk::{keygen_pk, keygen_vk, Circuit, ProvingKey, Selector},
10    poly::kzg::commitment::ParamsKZG,
11    SerdeFormat,
12};
13use itertools::Itertools;
14use serde::{Deserialize, Serialize};
15pub use snark_verifier::loader::native::NativeLoader;
16use snark_verifier::{
17    pcs::kzg::{Bdfg21, Gwc19, KzgAs, LimbsEncoding},
18    verifier::{self, plonk::PlonkProtocol},
19};
20use std::{
21    fs::{self, File},
22    io::{self, BufReader, BufWriter},
23    path::Path,
24};
25
26pub use snark_verifier;
27
28#[cfg(feature = "loader_evm")]
29pub mod evm;
30#[cfg(feature = "loader_halo2")]
31pub mod halo2;
32
33pub const LIMBS: usize = 3;
34pub const BITS: usize = 88;
35
36const BUFFER_SIZE: usize = 1024 * 1024; // 1MB
37
38/// AS stands for accumulation scheme.
39/// AS can be either `Kzg<Bn256, Gwc19>` (the original PLONK KZG multi-open) or `Kzg<Bn256, Bdfg21>` (SHPLONK)
40pub type PlonkVerifier<AS> = verifier::plonk::PlonkVerifier<AS, LimbsEncoding<LIMBS, BITS>>;
41pub type PlonkSuccinctVerifier<AS> =
42    verifier::plonk::PlonkSuccinctVerifier<AS, LimbsEncoding<LIMBS, BITS>>;
43pub type SHPLONK = KzgAs<Bn256, Bdfg21>;
44pub type GWC = KzgAs<Bn256, Gwc19>;
45
46#[derive(Clone, Debug, Serialize, Deserialize)]
47pub struct Snark {
48    pub protocol: PlonkProtocol<G1Affine>,
49    pub instances: Vec<Vec<Fr>>,
50    pub proof: Vec<u8>,
51}
52
53impl Snark {
54    pub fn new(protocol: PlonkProtocol<G1Affine>, instances: Vec<Vec<Fr>>, proof: Vec<u8>) -> Self {
55        Self { protocol, instances, proof }
56    }
57
58    pub fn proof(&self) -> &[u8] {
59        &self.proof[..]
60    }
61}
62
63pub trait CircuitExt<F: Field>: Circuit<F> {
64    /// Return the number of instances of the circuit.
65    /// This may depend on extra circuit parameters but NOT on private witnesses.
66    fn num_instance(&self) -> Vec<usize>;
67
68    fn instances(&self) -> Vec<Vec<F>>;
69
70    fn accumulator_indices() -> Option<Vec<(usize, usize)>> {
71        None
72    }
73
74    /// Output the simple selector columns (before selector compression) of the circuit
75    fn selectors(_: &Self::Config) -> Vec<Selector> {
76        vec![]
77    }
78}
79
80pub fn read_pk<C: Circuit<Fr>>(path: &Path, params: C::Params) -> io::Result<ProvingKey<G1Affine>> {
81    read_pk_with_capacity::<C>(BUFFER_SIZE, path, params)
82}
83
84pub fn read_pk_with_capacity<C: Circuit<Fr>>(
85    capacity: usize,
86    path: impl AsRef<Path>,
87    params: C::Params,
88) -> io::Result<ProvingKey<G1Affine>> {
89    let f = File::open(path.as_ref())?;
90    #[cfg(feature = "display")]
91    let read_time = start_timer!(|| format!("Reading pkey from {:?}", path.as_ref()));
92
93    // BufReader is indeed MUCH faster than Read
94    let mut bufreader = BufReader::with_capacity(capacity, f);
95    // But it's even faster to load the whole file into memory first and then process,
96    // HOWEVER this requires twice as much memory to initialize
97    // let initial_buffer_size = f.metadata().map(|m| m.len() as usize + 1).unwrap_or(0);
98    // let mut bufreader = Vec::with_capacity(initial_buffer_size);
99    // f.read_to_end(&mut bufreader)?;
100    let pk =
101        ProvingKey::read::<_, C>(&mut bufreader, SerdeFormat::RawBytesUnchecked, params).unwrap();
102
103    #[cfg(feature = "display")]
104    end_timer!(read_time);
105
106    Ok(pk)
107}
108
109#[allow(clippy::let_and_return)]
110pub fn gen_pk<C: Circuit<Fr>>(
111    params: &ParamsKZG<Bn256>, // TODO: read pk without params
112    circuit: &C,
113    path: Option<&Path>,
114) -> ProvingKey<G1Affine> {
115    if let Some(path) = path {
116        if let Ok(pk) = read_pk::<C>(path, circuit.params()) {
117            return pk;
118        }
119    }
120    #[cfg(feature = "display")]
121    let pk_time = start_timer!(|| "Generating vkey & pkey");
122
123    let vk = keygen_vk(params, circuit).unwrap();
124    let pk = keygen_pk(params, vk, circuit).unwrap();
125
126    #[cfg(feature = "display")]
127    end_timer!(pk_time);
128
129    if let Some(path) = path {
130        #[cfg(feature = "display")]
131        let write_time = start_timer!(|| format!("Writing pkey to {path:?}"));
132
133        path.parent().and_then(|dir| fs::create_dir_all(dir).ok()).unwrap();
134        let mut f = BufWriter::with_capacity(BUFFER_SIZE, File::create(path).unwrap());
135        pk.write(&mut f, SerdeFormat::RawBytesUnchecked).unwrap();
136
137        #[cfg(feature = "display")]
138        end_timer!(write_time);
139    }
140    pk
141}
142
143pub fn read_instances(path: impl AsRef<Path>) -> Result<Vec<Vec<Fr>>, bincode::Error> {
144    let f = File::open(path)?;
145    let reader = BufReader::new(f);
146    let instances: Vec<Vec<[u8; 32]>> = bincode::deserialize_from(reader)?;
147    instances
148        .into_iter()
149        .map(|instance_column| {
150            instance_column
151                .iter()
152                .map(|bytes| {
153                    Option::from(Fr::from_bytes(bytes)).ok_or(Box::new(bincode::ErrorKind::Custom(
154                        "Invalid finite field point".to_owned(),
155                    )))
156                })
157                .collect::<Result<Vec<_>, _>>()
158        })
159        .collect()
160}
161
162pub fn write_instances(instances: &[&[Fr]], path: impl AsRef<Path>) {
163    let instances: Vec<Vec<[u8; 32]>> = instances
164        .iter()
165        .map(|instance_column| instance_column.iter().map(|x| x.to_bytes()).collect_vec())
166        .collect_vec();
167    let f = BufWriter::new(File::create(path).unwrap());
168    bincode::serialize_into(f, &instances).unwrap();
169}