1use crate::{
2 annotations::{extract::FromStrHex, Annotations},
3 builtins::Builtin,
4 layout::Layout,
5 stark_proof::{self, *},
6};
7use num_bigint::BigUint;
8use serde::Deserialize;
9use starknet_crypto::{pedersen_hash, Felt};
10use std::{
11 collections::{BTreeMap, HashMap},
12 convert::TryFrom,
13};
14
15#[derive(Deserialize, Debug, Clone, PartialEq)]
16pub struct StarkProof {
17 proof_parameters: ProofParameters,
18 annotations: Vec<String>,
19 public_input: PublicInput,
20}
21
22#[derive(Deserialize, Debug, Clone, PartialEq)]
23pub struct ProofParameters {
24 pub stark: Stark,
25 #[serde(default)]
26 pub n_verifier_friendly_commitment_layers: u32,
27}
28
29#[derive(Deserialize, Debug, Clone, PartialEq)]
30pub struct Stark {
31 pub fri: Fri,
32 pub log_n_cosets: u32,
33}
34
35#[derive(Deserialize, Debug, Clone, PartialEq)]
36pub struct Fri {
37 pub fri_step_list: Vec<u32>,
38 pub last_layer_degree_bound: u32,
39 pub n_queries: u32,
40 pub proof_of_work_bits: u32,
41}
42
43#[derive(Deserialize, Debug, Clone, PartialEq)]
44pub struct MemorySegmentAddress {
45 begin_addr: u32,
46 stop_ptr: u32,
47}
48
49#[derive(Deserialize, Debug, Clone, PartialEq)]
50pub struct PublicMemoryElement {
51 address: u32,
52 page: u32,
53 value: String,
54}
55
56#[derive(Deserialize, Debug, Clone, PartialEq)]
57pub struct PublicInput {
58 dynamic_params: Option<BTreeMap<String, u32>>,
59 layout: Layout,
60 memory_segments: BTreeMap<String, MemorySegmentAddress>,
61 n_steps: u32,
62 public_memory: Vec<PublicMemoryElement>,
63 rc_min: u32,
64 rc_max: u32,
65}
66
67impl StarkProof {
68 const COMPONENT_HEIGHT: u32 = 16;
69 pub fn stark_config(&self) -> anyhow::Result<StarkConfig> {
70 let stark = &self.proof_parameters.stark;
71 let n_verifier_friendly_commitment_layers =
72 self.proof_parameters.n_verifier_friendly_commitment_layers;
73
74 let consts =
75 self.public_input.layout.get_dynamics_or_consts(&self.public_input.dynamic_params);
76
77 let log_eval_domain_size = self.log_eval_damain_size(&self.public_input.dynamic_params)?;
78 let traces = TracesConfig {
79 original: TableCommitmentConfig {
80 n_columns: consts.num_columns_first,
81 vector: VectorCommitmentConfig {
82 height: log_eval_domain_size,
83 n_verifier_friendly_commitment_layers,
84 },
85 },
86 interaction: TableCommitmentConfig {
87 n_columns: consts.num_columns_second,
88 vector: VectorCommitmentConfig {
89 height: log_eval_domain_size,
90 n_verifier_friendly_commitment_layers,
91 },
92 },
93 };
94
95 let composition = TableCommitmentConfig {
96 n_columns: consts.constraint_degree,
97 vector: VectorCommitmentConfig {
98 height: log_eval_domain_size,
99 n_verifier_friendly_commitment_layers,
100 },
101 };
102
103 let fri = self.proof_parameters.stark.fri.clone();
104
105 let proof_of_work = ProofOfWorkConfig { n_bits: fri.proof_of_work_bits };
106 let n_queries = fri.n_queries;
107
108 let layer_log_sizes = self.layer_log_sizes(&self.public_input.dynamic_params)?;
109
110 let fri_step_list = fri.fri_step_list;
111 let log_last_layer_degree_bound = log2_if_power_of_2(fri.last_layer_degree_bound)
112 .ok_or(anyhow::anyhow!("Invalid last layer degree bound"))?;
113 let fri = FriConfig {
114 log_input_size: layer_log_sizes[0],
115 n_layers: fri_step_list.len() as u32,
116 inner_layers: fri_step_list[1..]
117 .iter()
118 .zip(layer_log_sizes[2..].iter())
119 .map(|(layer_steps, layer_log_rows)| TableCommitmentConfig {
120 n_columns: 2_u32.pow(*layer_steps),
121 vector: VectorCommitmentConfig {
122 height: *layer_log_rows,
123 n_verifier_friendly_commitment_layers,
124 },
125 })
126 .collect(),
127 fri_step_sizes: fri_step_list,
128 log_last_layer_degree_bound,
129 };
130
131 Ok(StarkConfig {
132 traces,
133 composition,
134 fri,
135 proof_of_work,
136 log_trace_domain_size: self.log_trace_domain_size(&self.public_input.dynamic_params)?,
137 n_queries,
138 log_n_cosets: stark.log_n_cosets,
139 n_verifier_friendly_commitment_layers,
140 })
141 }
142 fn log_trace_domain_size(
143 &self,
144 dynamic_params: &Option<BTreeMap<String, u32>>,
145 ) -> anyhow::Result<u32> {
146 let consts = self.public_input.layout.get_dynamics_or_consts(dynamic_params);
147 let effective_component_height = Self::COMPONENT_HEIGHT * consts.cpu_component_step;
148 log2_if_power_of_2(effective_component_height * self.public_input.n_steps)
149 .ok_or(anyhow::anyhow!("Invalid cpu component step"))
150 }
151 fn log_eval_damain_size(
152 &self,
153 dynamic_params: &Option<BTreeMap<String, u32>>,
154 ) -> anyhow::Result<u32> {
155 Ok(self.log_trace_domain_size(dynamic_params)? + self.proof_parameters.stark.log_n_cosets)
156 }
157 fn layer_log_sizes(
158 &self,
159 dynamic_params: &Option<BTreeMap<String, u32>>,
160 ) -> anyhow::Result<Vec<u32>> {
161 let mut layer_log_sizes = vec![self.log_eval_damain_size(dynamic_params)?];
162 for layer_step in &self.proof_parameters.stark.fri.fri_step_list {
163 layer_log_sizes.push(layer_log_sizes.last().unwrap() - layer_step);
164 }
165 Ok(layer_log_sizes)
166 }
167 fn public_input(
168 public_input: PublicInput,
169 z: BigUint,
170 alpha: BigUint,
171 ) -> anyhow::Result<stark_proof::PublicInput> {
172 let continuous_page_headers =
173 Self::continuous_page_headers(&public_input.public_memory, z, alpha);
174 let main_page = Self::main_page(&public_input.public_memory)?;
175 let dynamic_params = public_input.dynamic_params.unwrap_or_default();
176 let memory_segments = Builtin::sort_segments(public_input.memory_segments)
177 .into_iter()
178 .map(|s| SegmentInfo { begin_addr: s.begin_addr, stop_ptr: s.stop_ptr })
179 .collect::<Vec<_>>();
180 let layout = BigUint::from_bytes_be(&public_input.layout.bytes_encode());
181 let (padding_addr, padding_value) = match public_input.public_memory.first() {
182 Some(m) => (
183 m.address,
184 BigUint::from_str_hex(&m.value).ok_or(anyhow::anyhow!("Invalid memory value"))?,
185 ),
186 None => anyhow::bail!("Invalid public memory"),
187 };
188 Ok(stark_proof::PublicInput {
189 log_n_steps: log2_if_power_of_2(public_input.n_steps)
190 .ok_or(anyhow::anyhow!("Invalid number of steps"))?,
191 range_check_min: public_input.rc_min,
192 range_check_max: public_input.rc_max,
193 layout,
194 dynamic_params: dynamic_params.into_iter().collect(),
195 n_segments: memory_segments.len(),
196 segments: memory_segments,
197 padding_addr,
198 padding_value,
199 main_page_len: main_page.len(),
200 main_page,
201 n_continuous_pages: continuous_page_headers.len(),
202 continuous_page_headers: continuous_page_headers
203 .into_iter()
204 .flat_map(|x| {
205 vec![x.0.to_biguint(), x.1.to_biguint(), x.2.to_biguint(), x.3.to_biguint()]
206 })
207 .collect::<Vec<BigUint>>(),
208 })
209 }
210 fn main_page(public_memory: &[PublicMemoryElement]) -> anyhow::Result<Vec<PubilcMemoryCell>> {
211 public_memory
212 .iter()
213 .filter(|m| m.page == 0)
214 .map(|m| {
215 Ok(PubilcMemoryCell {
216 address: m.address,
217 value: BigUint::from_str_hex(&m.value)
218 .ok_or(anyhow::anyhow!("Invalid memory value"))?,
219 })
220 })
221 .collect::<anyhow::Result<Vec<_>>>()
222 }
223 pub fn continuous_page_headers(
224 public_memory: &[PublicMemoryElement],
225 z: BigUint,
226 alpha: BigUint,
227 ) -> Vec<(Felt, Felt, Felt, Felt)> {
228 let (_pages, page_prods) =
229 Self::get_pages_and_products(public_memory, z.clone(), alpha.clone());
230
231 let mut start_address: HashMap<Felt, Felt> = HashMap::new();
232 let mut size: HashMap<Felt, Felt> = HashMap::new();
233 let mut data: HashMap<Felt, Vec<Felt>> = HashMap::new();
234
235 for access in public_memory {
236 let page_id = Felt::from(access.page);
237 let addr = Felt::from(access.address);
238 let val = Felt::from_hex(&access.value).unwrap();
239
240 start_address.entry(page_id).or_insert(addr);
241 if page_id == Felt::ZERO {
242 continue;
243 }
244
245 let current_size = data.entry(page_id).or_default().len();
247 let expected_address = start_address.get(&page_id).unwrap() + Felt::from(current_size);
248 assert_eq!(addr, expected_address);
249
250 data.get_mut(&page_id).unwrap().push(val);
251 *size.entry(page_id).or_insert(Felt::ZERO) += Felt::ONE;
252 }
253
254 let n_pages = size.len() + 1; assert_eq!(page_prods.len(), n_pages);
256
257 let mut headers = Vec::new();
258 let mut sorted_keys: Vec<_> = size.keys().collect();
259 sorted_keys.sort(); for (i, page_id) in sorted_keys.into_iter().enumerate() {
262 let page_index = i + 1;
263 assert_eq!(Felt::from(page_index), *page_id);
264 let hash_value = Self::compute_hash_on_elements(data.get(page_id).unwrap());
265 let header = (
266 *start_address.get(page_id).unwrap(),
267 *size.get(page_id).unwrap(),
268 hash_value,
269 *page_prods.get(page_id).unwrap(),
270 );
271 headers.push(header);
272 }
273
274 headers
275 }
276 fn compute_hash_on_elements(data: &[Felt]) -> Felt {
277 let hash = data.iter().fold(Felt::ZERO, |acc, value| pedersen_hash(&acc, value));
278 pedersen_hash(&hash, &Felt::from(data.len()))
279 }
280 fn get_pages_and_products(
281 public_memory: &[PublicMemoryElement],
282 z: BigUint,
283 alpha: BigUint,
284 ) -> (HashMap<Felt, Vec<Felt>>, HashMap<Felt, Felt>) {
285 let mut pages = HashMap::new();
286 let mut page_prods = HashMap::new();
287
288 let z = Felt::from(z);
289 let alpha = Felt::from(alpha);
290
291 for cell in public_memory {
292 let page_id = Felt::from(cell.page);
293 let addr = Felt::from(cell.address);
294 let val = Felt::from_hex(&cell.value).unwrap();
295
296 let page = pages.entry(page_id).or_insert_with(Vec::new);
298 page.push(addr);
299 page.push(val);
300
301 let product = z - (addr + alpha * val);
303 let page_prod = page_prods.entry(page_id).or_insert(Felt::ONE);
304 *page_prod *= product;
305 }
306
307 (pages, page_prods)
308 }
309 fn stark_unsent_commitment(&self, annotations: &Annotations) -> StarkUnsentCommitment {
310 StarkUnsentCommitment {
311 traces: TracesUnsentCommitment {
312 original: annotations.original_commitment_hash.clone(),
313 interaction: annotations.interaction_commitment_hash.clone(),
314 },
315 composition: annotations.composition_commitment_hash.clone(),
316 oods_values: annotations.oods_values.clone(),
317 fri: FriUnsentCommitment {
318 inner_layers: annotations.fri_layers_commitments.clone(),
319 last_layer_coefficients: annotations.fri_last_layer_coefficients.clone(),
320 },
321 proof_of_work: ProofOfWorkUnsentCommitment {
322 nonce: annotations.proof_of_work_nonce.clone(),
323 },
324 }
325 }
326 fn stark_witness(&self, annotations: &Annotations) -> StarkWitness {
327 StarkWitness {
328 traces_decommitment: TracesDecommitment {
329 original: TableDecommitment {
330 n_values: annotations.original_witness_leaves.len(),
331 values: annotations.original_witness_leaves.clone(),
332 },
333 interaction: TableDecommitment {
334 n_values: annotations.interaction_witness_leaves.len(),
335 values: annotations.interaction_witness_leaves.clone(),
336 },
337 },
338 traces_witness: TracesWitness {
339 original: TableCommitmentWitness {
340 vector: VectorCommitmentWitness {
341 n_authentications: annotations.original_witness_authentications.len(),
342 authentications: annotations.original_witness_authentications.clone(),
343 },
344 },
345 interaction: TableCommitmentWitness {
346 vector: VectorCommitmentWitness {
347 n_authentications: annotations.interaction_witness_authentications.len(),
348 authentications: annotations.interaction_witness_authentications.clone(),
349 },
350 },
351 },
352 composition_decommitment: TableDecommitment {
353 n_values: annotations.composition_witness_leaves.len(),
354 values: annotations.composition_witness_leaves.clone(),
355 },
356 composition_witness: TableCommitmentWitness {
357 vector: VectorCommitmentWitness {
358 n_authentications: annotations.composition_witness_authentications.len(),
359 authentications: annotations.composition_witness_authentications.clone(),
360 },
361 },
362 fri_witness: FriWitness {
363 layers: annotations
364 .fri_witnesses
365 .iter()
366 .map(|w| FriLayerWitness {
367 n_leaves: w.leaves.len(),
368 leaves: w.leaves.clone(),
369 table_witness: TableCommitmentWitnessFlat {
370 vector: VectorCommitmentWitnessFlat {
371 n_authentications: w.authentications.len(),
372 authentications: w.authentications.clone(),
373 },
374 },
375 })
376 .collect(),
377 },
378 }
379 }
380}
381
382impl TryFrom<StarkProof> for stark_proof::StarkProof {
383 type Error = anyhow::Error;
384 fn try_from(value: StarkProof) -> anyhow::Result<Self> {
385 let config = value.stark_config()?;
386
387 let annotations = Annotations::new(
388 &value.annotations.iter().map(String::as_str).collect::<Vec<_>>(),
389 value.proof_parameters.stark.fri.fri_step_list.len(),
390 )?;
391 let public_input = StarkProof::public_input(
392 value.public_input.clone(),
393 annotations.z.clone(),
394 annotations.alpha.clone(),
395 )?;
396 let unsent_commitment = value.stark_unsent_commitment(&annotations);
397 let witness = value.stark_witness(&annotations);
398
399 Ok(stark_proof::StarkProof { config, public_input, unsent_commitment, witness })
400 }
401}
402
403pub fn log2_if_power_of_2(x: u32) -> Option<u32> {
404 if x != 0 && (x & (x - 1)) == 0 {
405 Some(f64::from(x).log2() as u32)
406 } else {
407 None
408 }
409}