poly_commitment/
precomputed_srs.rs

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