swiftness_proof_parser/
json_parser.rs

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            // Ensure the address is correct
246            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; // +1 because size does not count page 0
255        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(); // Ensure keys are sorted
260
261        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            // Insert or get the vector for the current page_id
297            let page = pages.entry(page_id).or_insert_with(Vec::new);
298            page.push(addr);
299            page.push(val);
300
301            // Calculate the product for the current page_id
302            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}