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
19pub 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(), Fp::one(), 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}