saffron_og_flow/
main.rs

1use ark_ff::{One, Zero};
2use ark_poly::{EvaluationDomain, Evaluations, Polynomial, Radix2EvaluationDomain};
3use kimchi::groupmap::GroupMap;
4use mina_curves::pasta::{Fp, Vesta, VestaParameters};
5use mina_poseidon::{
6    constants::PlonkSpongeConstantsKimchi, sponge::DefaultFqSponge, FqSponge as _,
7};
8use o1_utils::field_helpers::pows;
9use poly_commitment::{
10    commitment::{absorb_commitment, BatchEvaluationProof, CommitmentCurve, Evaluation},
11    ipa::SRS,
12    utils::DensePolynomialOrEvaluations,
13    PolyComm, SRS as _,
14};
15use rayon::iter::{IntoParallelIterator, ParallelIterator};
16use saffron::utils::encode_for_domain;
17use std::{fs::File, io::Read, process::ExitCode};
18
19// To run:
20// ```
21// cargo run --bin saffron-og-flow --release -- <path-to-file>
22// ```
23
24pub fn main() -> ExitCode {
25    let input_file = std::env::args()
26        .nth(1)
27        .ok_or("Missing data filepath argument")
28        .unwrap();
29
30    println!("Startup time (cacheable, 1-time cost)");
31
32    println!("- Generate SRS");
33    let now = std::time::Instant::now();
34    let domain = Radix2EvaluationDomain::new(saffron::SRS_SIZE).unwrap();
35    let srs = SRS::<Vesta>::create(saffron::SRS_SIZE);
36    let duration = now.elapsed();
37    println!(
38        "  - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
39        duration.as_secs(),
40        duration.as_millis(),
41        duration.as_micros(),
42        duration.as_nanos(),
43    );
44    println!("- Generate SRS lagrange basis");
45    let basis = srs.get_lagrange_basis(domain);
46    let duration = now.elapsed();
47    println!(
48        "  - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
49        duration.as_secs(),
50        duration.as_millis(),
51        duration.as_micros(),
52        duration.as_nanos(),
53    );
54
55    println!("- Generate 'group map' parameters");
56    let now = std::time::Instant::now();
57    let group_map = <Vesta as CommitmentCurve>::Map::setup();
58    let duration = now.elapsed();
59    println!(
60        "  - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
61        duration.as_secs(),
62        duration.as_millis(),
63        duration.as_micros(),
64        duration.as_nanos(),
65    );
66
67    let now = std::time::Instant::now();
68    let rng = &mut rand::rngs::OsRng;
69
70    println!("Reading data from {}", input_file);
71    let data: Vec<Fp> = {
72        let mut file = File::open(input_file).unwrap();
73        let mut buf = Vec::new();
74        file.read_to_end(&mut buf).unwrap();
75        encode_for_domain(domain.size(), &buf)
76            .into_iter()
77            .flatten()
78            .collect()
79    };
80
81    let duration = now.elapsed();
82    println!(
83        "  - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
84        duration.as_secs(),
85        duration.as_millis(),
86        duration.as_micros(),
87        duration.as_nanos(),
88    );
89
90    println!("Main protocol");
91
92    println!("- One-time setup for newly-stored data");
93    println!("  - Generate cryptographic commitments");
94    let now = std::time::Instant::now();
95    let committed_chunks = (0..data.len() / saffron::SRS_SIZE)
96        .into_par_iter()
97        .map(|idx| {
98            PolyComm::multi_scalar_mul(
99                &basis.iter().collect::<Vec<_>>(),
100                &data[saffron::SRS_SIZE * idx..saffron::SRS_SIZE * (idx + 1)],
101            )
102        })
103        .collect::<Vec<_>>();
104    let duration = now.elapsed();
105    println!(
106        "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
107        duration.as_secs(),
108        duration.as_millis(),
109        duration.as_micros(),
110        duration.as_nanos(),
111    );
112
113    println!(" - Convert to affine coordinates");
114    let now = std::time::Instant::now();
115    let duration = now.elapsed();
116    println!(
117        "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
118        duration.as_secs(),
119        duration.as_millis(),
120        duration.as_micros(),
121        duration.as_nanos(),
122    );
123
124    println!("  - Combine the commitments");
125    println!("    - Using a merkle commitment (poseidon hashing)");
126    let now = std::time::Instant::now();
127    let mut fq_sponge = DefaultFqSponge::<VestaParameters, PlonkSpongeConstantsKimchi>::new(
128        mina_poseidon::pasta::fq_kimchi::static_params(),
129    );
130    committed_chunks.iter().for_each(|commitment| {
131        absorb_commitment(&mut fq_sponge, commitment);
132    });
133    let challenge = fq_sponge.squeeze(2);
134    let duration = now.elapsed();
135    println!(
136        "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
137        duration.as_secs(),
138        duration.as_millis(),
139        duration.as_micros(),
140        duration.as_nanos(),
141    );
142
143    for i in 0..2 {
144        println!("- Storage protocol iteration {i}");
145        println!("  - Computing randomizers for data chunks");
146        let now = std::time::Instant::now();
147        let powers = pows(committed_chunks.len(), challenge);
148        let duration = now.elapsed();
149        println!(
150            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
151            duration.as_secs(),
152            duration.as_millis(),
153            duration.as_micros(),
154            duration.as_nanos(),
155        );
156
157        println!("  - Combining the data chunk commitments");
158        let now = std::time::Instant::now();
159        let final_commitment = PolyComm::multi_scalar_mul(
160            &committed_chunks.iter().collect::<Vec<_>>(),
161            powers.as_slice(),
162        );
163        let duration = now.elapsed();
164        println!(
165            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
166            duration.as_secs(),
167            duration.as_millis(),
168            duration.as_micros(),
169            duration.as_nanos(),
170        );
171
172        println!("  - Convert data");
173        println!("    - Temporary step until we have Montgomery representation preprocessing");
174        let now = std::time::Instant::now();
175        let mongomeryized_data = data.iter().map(|x| Fp::from(*x)).collect::<Vec<_>>();
176        let duration = now.elapsed();
177        println!(
178            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
179            duration.as_secs(),
180            duration.as_millis(),
181            duration.as_micros(),
182            duration.as_nanos(),
183        );
184
185        println!("  - Combine randomized data chunks");
186        let now = std::time::Instant::now();
187        let final_chunk = (mongomeryized_data.len() / saffron::SRS_SIZE) - 1;
188        let randomized_data = (0..saffron::SRS_SIZE)
189            .into_par_iter()
190            .map(|idx| {
191                let mut acc = mongomeryized_data[final_chunk * saffron::SRS_SIZE + idx];
192                (0..final_chunk).rev().for_each(|chunk| {
193                    acc *= challenge;
194                    acc += mongomeryized_data[chunk * saffron::SRS_SIZE + idx];
195                });
196                acc
197            })
198            .collect::<Vec<_>>();
199        let duration = now.elapsed();
200        println!(
201            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
202            duration.as_secs(),
203            duration.as_millis(),
204            duration.as_micros(),
205            duration.as_nanos(),
206        );
207
208        println!("  - Sample evaluation point");
209        let now = std::time::Instant::now();
210        let mut fq_sponge = DefaultFqSponge::<VestaParameters, PlonkSpongeConstantsKimchi>::new(
211            mina_poseidon::pasta::fq_kimchi::static_params(),
212        );
213        absorb_commitment(&mut fq_sponge, &final_commitment);
214        let evaluation_point = fq_sponge.squeeze(2);
215        let duration = now.elapsed();
216        println!(
217            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
218            duration.as_secs(),
219            duration.as_millis(),
220            duration.as_micros(),
221            duration.as_nanos(),
222        );
223
224        println!("  - Interpolate polynomial");
225        println!("    - Fixed cost regardless of data size");
226        let now = std::time::Instant::now();
227        let randomized_data_poly =
228            Evaluations::from_vec_and_domain(randomized_data, domain).interpolate();
229        let duration = now.elapsed();
230        println!(
231            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
232            duration.as_secs(),
233            duration.as_millis(),
234            duration.as_micros(),
235            duration.as_nanos(),
236        );
237
238        println!("  - Evaluate polynomial and absorb evaluation");
239        println!("    - Fixed cost regardless of data size");
240        let now = std::time::Instant::now();
241        let randomized_data_eval = randomized_data_poly.evaluate(&evaluation_point);
242        let mut opening_proof_sponge =
243            DefaultFqSponge::<VestaParameters, PlonkSpongeConstantsKimchi>::new(
244                mina_poseidon::pasta::fq_kimchi::static_params(),
245            );
246        opening_proof_sponge.absorb_fr(&[randomized_data_eval]);
247        let duration = now.elapsed();
248        println!(
249            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
250            duration.as_secs(),
251            duration.as_millis(),
252            duration.as_micros(),
253            duration.as_nanos(),
254        );
255
256        println!("  - Opening proof");
257        println!("    - Fixed cost regardless of data size");
258        let now = std::time::Instant::now();
259        let opening_proof = srs.open(
260            &group_map,
261            &[(
262                DensePolynomialOrEvaluations::<_, Radix2EvaluationDomain<_>>::DensePolynomial(
263                    &randomized_data_poly,
264                ),
265                PolyComm {
266                    chunks: vec![Fp::zero()],
267                },
268            )],
269            &[evaluation_point],
270            Fp::one(), // Single polynomial, so we don't care
271            Fp::one(), // Single polynomial, so we don't care
272            opening_proof_sponge.clone(),
273            rng,
274        );
275        let duration = now.elapsed();
276        println!(
277            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
278            duration.as_secs(),
279            duration.as_millis(),
280            duration.as_micros(),
281            duration.as_nanos(),
282        );
283
284        println!("- Verifier protocol iteration {i}");
285        println!("  - Verify opening proof");
286        let now = std::time::Instant::now();
287        let opening_proof_verifies = srs.verify(
288            &group_map,
289            &mut [BatchEvaluationProof {
290                sponge: opening_proof_sponge.clone(),
291                evaluation_points: vec![evaluation_point],
292                polyscale: Fp::one(),
293                evalscale: Fp::one(),
294                evaluations: vec![Evaluation {
295                    commitment: final_commitment,
296                    evaluations: vec![vec![randomized_data_eval]],
297                }],
298                opening: &opening_proof,
299                combined_inner_product: randomized_data_eval,
300            }],
301            rng,
302        );
303        let duration = now.elapsed();
304        println!("    - Verifies: {}", opening_proof_verifies);
305        println!(
306            "    - Took {:?}s / {:?}ms / {:?}us / {:?}ns",
307            duration.as_secs(),
308            duration.as_millis(),
309            duration.as_micros(),
310            duration.as_nanos(),
311        );
312    }
313
314    ExitCode::SUCCESS
315}