Skip to main content

poly_commitment/
precomputed_srs.rs

1//! Pre-generated SRS (Structured Reference String) for proofs.
2//!
3//! To prove and verify proofs you need a [Structured Reference
4//! String](https://www.cryptologie.net/article/560/zk-faq-whats-a-trusted-setup-whats-a-structured-reference-string-whats-toxic-waste/)
5//! (SRS).
6//! The generation of this SRS is quite expensive, so we provide a pre-generated
7//! SRS in this repo.
8//! Specifically, two of them, one for each pasta curve.
9//!
10//! We generate the SRS within the test in this module.
11//! If you modify the SRS, you will need to regenerate the SRS by passing the
12//! `SRS_OVERWRITE` env var.
13
14use crate::{ipa::SRS, CommitmentCurve, PolyComm};
15use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
16use mina_curves::named::NamedCurve;
17use serde::{Deserialize, Serialize};
18use serde_with::serde_as;
19use std::{collections::HashMap, fs::File, io::BufReader, path::PathBuf, sync::Arc};
20
21/// We store several different types of SRS objects. This enum parameterizes
22/// them.
23#[derive(Clone, Copy, PartialEq, Eq)]
24pub enum StoredSRSType {
25    Test,
26    Prod,
27}
28
29/// A clone of the SRS struct that is used for serialization, in a
30/// test-optimised way.
31///
32/// NB: Serialization of these fields is unchecked (and fast). If you
33/// want to make sure the data is checked on deserialization, this code
34/// must be changed; or you can check it externally.
35#[serde_as]
36#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
37#[serde(bound = "G: CanonicalDeserialize + CanonicalSerialize")]
38pub struct TestSRS<G> {
39    /// The vector of group elements for committing to polynomials in
40    /// coefficient form.
41    #[serde_as(as = "Vec<o1_utils::serialization::SerdeAsUnchecked>")]
42    pub g: Vec<G>,
43
44    /// A group element used for blinding commitments
45    #[serde_as(as = "o1_utils::serialization::SerdeAsUnchecked")]
46    pub h: G,
47
48    /// Commitments to Lagrange bases, per domain size
49    #[serde_as(as = "HashMap<_,Arc<Vec<PolyComm<o1_utils::serialization::SerdeAsUnchecked>>>>")]
50    pub lagrange_bases: HashMap<usize, Arc<Vec<PolyComm<G>>>>,
51}
52
53/// The size of the SRS that we serialize.
54pub const SERIALIZED_SRS_SIZE: u32 = 16;
55
56/// The path of the serialized SRS.
57fn get_srs_path<G: NamedCurve>(srs_type: StoredSRSType) -> PathBuf {
58    let test_prefix: String = (match srs_type {
59        StoredSRSType::Test => "test_",
60        StoredSRSType::Prod => "",
61    })
62    .to_owned();
63    let base_path = env!("CARGO_MANIFEST_DIR");
64    PathBuf::from(base_path)
65        .join("../srs")
66        .join(test_prefix + &format!("{}.srs", G::NAME))
67}
68
69/// Generic SRS getter function.
70///
71/// # Panics
72///
73/// Panics if the SRS file does not exist on disk or cannot be
74/// deserialized.
75#[must_use]
76pub fn get_srs_generic<G>(srs_type: StoredSRSType) -> SRS<G>
77where
78    G: NamedCurve + CommitmentCurve,
79{
80    let srs_path = get_srs_path::<G>(srs_type);
81    let file = File::open(srs_path.clone())
82        .unwrap_or_else(|_| panic!("missing SRS file: {}", srs_path.display()));
83    let reader = BufReader::new(file);
84    match srs_type {
85        StoredSRSType::Test => {
86            let test_srs: TestSRS<G> = rmp_serde::from_read(reader).unwrap();
87            From::from(test_srs)
88        }
89        StoredSRSType::Prod => rmp_serde::from_read(reader).unwrap(),
90    }
91}
92
93/// Obtains an SRS for a specific curve from disk.
94///
95/// # Panics
96///
97/// Panics if the SRS does not exist on disk or cannot be deserialized.
98#[must_use]
99pub fn get_srs<G>() -> SRS<G>
100where
101    G: NamedCurve + CommitmentCurve,
102{
103    get_srs_generic(StoredSRSType::Prod)
104}
105
106/// Obtains a Test SRS for a specific curve from disk.
107///
108/// # Panics
109///
110/// Panics if the SRS does not exist on disk or cannot be deserialized.
111#[must_use]
112pub fn get_srs_test<G>() -> SRS<G>
113where
114    G: NamedCurve + CommitmentCurve,
115{
116    get_srs_generic(StoredSRSType::Test)
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    use crate::SRS as _;
124    use ark_ec::AffineRepr;
125    use ark_ff::PrimeField;
126    use ark_poly::{EvaluationDomain, Radix2EvaluationDomain};
127    use ark_serialize::Write;
128    use hex;
129    use mina_curves::pasta::{Pallas, Vesta};
130
131    fn test_regression_serialization_srs_with_generators<G: AffineRepr>(exp_output: &str) {
132        let h = G::generator();
133        let g = vec![h];
134        let srs = SRS::new(g, h);
135        let srs_bytes = rmp_serde::to_vec(&srs).unwrap();
136        let output = hex::encode(srs_bytes);
137        assert_eq!(output, exp_output);
138    }
139
140    #[test]
141    fn test_regression_serialization_srs_with_generators_vesta() {
142        // This is the same as Pallas as we encode the coordinate x only.
143        // Generated with commit 4c69a4defdb109b94f1124fe93283e728f1d8758
144        let exp_output = "9291c421010000000000000000000000000000000000000000000000000000000000000000c421010000000000000000000000000000000000000000000000000000000000000000";
145        test_regression_serialization_srs_with_generators::<Vesta>(exp_output);
146    }
147
148    #[test]
149    fn test_regression_serialization_srs_with_generators_pallas() {
150        // This is the same as Pallas as we encode the coordinate x only.
151        // Generated with commit 4c69a4defdb109b94f1124fe93283e728f1d8758
152        let exp_output = "9291c421010000000000000000000000000000000000000000000000000000000000000000c421010000000000000000000000000000000000000000000000000000000000000000";
153        test_regression_serialization_srs_with_generators::<Pallas>(exp_output);
154    }
155
156    fn create_or_check_srs<G>(log2_size: u32, srs_type: StoredSRSType)
157    where
158        G: NamedCurve + CommitmentCurve,
159        G::BaseField: PrimeField,
160    {
161        // generate SRS
162        let domain_size = 1 << log2_size;
163        let srs = SRS::<G>::create(domain_size);
164
165        // Test SRS objects have Lagrange bases precomputed
166        if srs_type == StoredSRSType::Test {
167            // FIXME we should iterate by powers of 2, instead we iterate over all the integers? why?
168            for sub_domain_size in 1..=domain_size {
169                let domain = Radix2EvaluationDomain::new(sub_domain_size).unwrap();
170                srs.get_lagrange_basis(domain);
171            }
172        }
173
174        // overwrite SRS if the env var is set
175        let srs_path = get_srs_path::<G>(srs_type);
176        if std::env::var("SRS_OVERWRITE").is_ok() {
177            let mut file = std::fs::OpenOptions::new()
178                .create(true)
179                .truncate(true)
180                .write(true)
181                .open(srs_path)
182                .expect("failed to open SRS file");
183
184            let srs_bytes = match srs_type {
185                StoredSRSType::Test => {
186                    let srs: TestSRS<G> = From::from(srs.clone());
187                    rmp_serde::to_vec(&srs).unwrap()
188                }
189                StoredSRSType::Prod => rmp_serde::to_vec(&srs).unwrap(),
190            };
191
192            file.write_all(&srs_bytes).expect("failed to write file");
193            file.flush().expect("failed to flush file");
194        }
195
196        // get SRS from disk
197        let srs_on_disk: SRS<G> = get_srs_generic::<G>(srs_type);
198
199        // check that it matches what we just generated
200        assert_eq!(srs, srs_on_disk);
201    }
202
203    /// Checks if `get_srs` (prod) succeeds for Pallas. Can be used for time-profiling.
204    #[test]
205    pub fn heavy_check_get_srs_prod_pallas() {
206        let _ = get_srs::<Pallas>();
207    }
208
209    /// Checks if `get_srs` (prod) succeeds for Vesta. Can be used for time-profiling.
210    #[test]
211    pub fn heavy_check_get_srs_prod_vesta() {
212        let _ = get_srs::<Vesta>();
213    }
214
215    /// Checks if `get_srs` (test) succeeds for Pallas. Can be used for time-profiling.
216    #[test]
217    pub fn check_get_srs_test_pallas() {
218        let _ = get_srs_test::<Pallas>();
219    }
220
221    /// Checks if `get_srs` (test) succeeds for Vesta. Can be used for time-profiling.
222    #[test]
223    pub fn check_get_srs_test_vesta() {
224        let _ = get_srs_test::<Vesta>();
225    }
226
227    /// This test checks that the two serialized SRS on disk are correct.
228    #[test]
229    pub fn heavy_test_srs_serialization() {
230        create_or_check_srs::<Vesta>(SERIALIZED_SRS_SIZE, StoredSRSType::Prod);
231        create_or_check_srs::<Pallas>(SERIALIZED_SRS_SIZE, StoredSRSType::Prod);
232        create_or_check_srs::<Vesta>(SERIALIZED_SRS_SIZE, StoredSRSType::Test);
233        create_or_check_srs::<Pallas>(SERIALIZED_SRS_SIZE, StoredSRSType::Test);
234    }
235}