mina_tree/proofs/public_input/
prepared_statement.rs

1use ark_ff::{BigInteger256, Zero};
2use mina_curves::pasta::{Fp, Fq};
3use mina_p2p_messages::v2::{
4    CompositionTypesBranchDataStableV1, PicklesBaseProofsVerifiedStableV1,
5};
6
7use crate::proofs::{
8    field::{CircuitVar, FieldWitness},
9    step::{FeatureFlags, OptFlag, Packed},
10    to_field_elements::ToFieldElements,
11    util::{four_u64_to_field, two_u64_to_field},
12};
13
14#[derive(Clone, Debug)]
15pub struct Plonk<F: FieldWitness> {
16    pub alpha: [u64; 2],
17    pub beta: [u64; 2],
18    pub gamma: [u64; 2],
19    pub zeta: [u64; 2],
20    pub zeta_to_srs_length: F::Shifting,
21    pub zeta_to_domain_size: F::Shifting,
22    pub perm: F::Shifting,
23    pub lookup: Option<[u64; 2]>,
24    pub feature_flags: FeatureFlags<bool>,
25}
26
27#[derive(Clone, Debug)]
28pub struct DeferredValues<F: FieldWitness> {
29    pub plonk: Plonk<F>,
30    pub combined_inner_product: F::Shifting,
31    pub b: F::Shifting,
32    pub xi: [u64; 2],
33    pub bulletproof_challenges: Vec<F>,
34    pub branch_data: CompositionTypesBranchDataStableV1,
35}
36
37#[derive(Clone, Debug)]
38pub struct ProofState {
39    pub deferred_values: DeferredValues<Fp>,
40    pub sponge_digest_before_evaluations: [u64; 4],
41    pub messages_for_next_wrap_proof: [u64; 4],
42}
43
44#[derive(Debug)]
45pub struct PreparedStatement {
46    pub proof_state: ProofState,
47    pub messages_for_next_step_proof: [u64; 4],
48}
49
50impl PreparedStatement {
51    /// Implementation of `tock_unpadded_public_input_of_statement`
52    /// <https://github.com/MinaProtocol/mina/blob/32a91613c388a71f875581ad72276e762242f802/src/lib/pickles/common.ml#L202>
53    pub fn to_public_input(&self, npublic_input: usize) -> anyhow::Result<Vec<Fq>> {
54        let PreparedStatement {
55            proof_state:
56                ProofState {
57                    deferred_values:
58                        DeferredValues {
59                            plonk:
60                                Plonk {
61                                    alpha,
62                                    beta,
63                                    gamma,
64                                    zeta,
65                                    zeta_to_srs_length,
66                                    zeta_to_domain_size,
67                                    perm,
68                                    lookup,
69                                    feature_flags,
70                                },
71                            combined_inner_product,
72                            b,
73                            xi,
74                            bulletproof_challenges,
75                            branch_data:
76                                CompositionTypesBranchDataStableV1 {
77                                    proofs_verified,
78                                    domain_log2,
79                                },
80                        },
81                    sponge_digest_before_evaluations,
82                    messages_for_next_wrap_proof,
83                },
84            messages_for_next_step_proof,
85        } = &self;
86
87        // We sort the fields in the same order as here:
88        // <https://github.com/MinaProtocol/mina/blob/c824be7d80db1d290e0d48cbc920182d07de0330/src/lib/pickles/composition_types/composition_types.ml#L739>
89
90        let mut fields: Vec<Fq> = Vec::with_capacity(npublic_input);
91
92        let to_fq = |fp: Fp| -> Fq {
93            let bigint: BigInteger256 = fp.into();
94            bigint.try_into().unwrap() // Never fail, `Fq` is larger than `Fp`
95        };
96
97        // Fp
98        {
99            fields.push(to_fq(combined_inner_product.shifted));
100            fields.push(to_fq(b.shifted));
101            fields.push(to_fq(zeta_to_srs_length.shifted));
102            fields.push(to_fq(zeta_to_domain_size.shifted));
103            fields.push(to_fq(perm.shifted));
104        }
105
106        // Challenge
107        {
108            fields.push(two_u64_to_field(beta));
109            fields.push(two_u64_to_field(gamma));
110        }
111
112        // Scalar challenge
113        {
114            fields.push(two_u64_to_field(alpha));
115            fields.push(two_u64_to_field(zeta));
116            fields.push(two_u64_to_field(xi));
117        }
118
119        // Digest
120        {
121            fields.push(four_u64_to_field(sponge_digest_before_evaluations)?);
122            fields.push(four_u64_to_field(messages_for_next_wrap_proof)?);
123            fields.push(four_u64_to_field(messages_for_next_step_proof)?);
124        }
125
126        fields.extend(bulletproof_challenges.iter().copied().map(to_fq));
127
128        // Index
129        {
130            // <https://github.com/MinaProtocol/mina/blob/32a91613c388a71f875581ad72276e762242f802/src/lib/pickles_base/proofs_verified.ml#L58>
131            let proofs_verified = match proofs_verified {
132                PicklesBaseProofsVerifiedStableV1::N0 => 0b00,
133                PicklesBaseProofsVerifiedStableV1::N1 => 0b10,
134                PicklesBaseProofsVerifiedStableV1::N2 => 0b11,
135            };
136            // <https://github.com/MinaProtocol/mina/blob/c824be7d80db1d290e0d48cbc920182d07de0330/src/lib/pickles/composition_types/branch_data.ml#L63>
137            let domain_log2: u8 = domain_log2.0.into();
138            let domain_log2: u64 = domain_log2 as u64;
139            let branch_data: u64 = (domain_log2 << 2) | proofs_verified;
140
141            fields.push(Fq::from(branch_data));
142        }
143
144        let lookup_value = lookup.as_ref().map(two_u64_to_field);
145
146        feature_flags.to_field_elements(&mut fields);
147
148        let FeatureFlags {
149            range_check0,
150            range_check1,
151            foreign_field_add: _,
152            foreign_field_mul,
153            xor,
154            rot,
155            lookup,
156            runtime_tables: _,
157        } = feature_flags;
158
159        // <https://github.com/MinaProtocol/mina/blob/dc6bf78b8ddbbca3a1a248971b76af1514bf05aa/src/lib/pickles/composition_types/composition_types.ml#L146>
160        let uses_lookup = [
161            range_check0,
162            range_check1,
163            foreign_field_mul,
164            xor,
165            rot,
166            lookup,
167        ]
168        .iter()
169        .any(|b| **b);
170
171        uses_lookup.to_field_elements(&mut fields);
172
173        if uses_lookup {
174            fields.push(lookup_value.unwrap_or(Fq::zero()));
175        } else {
176            fields.push(Fq::zero());
177        }
178
179        assert_eq!(fields.len(), npublic_input);
180
181        Ok(fields)
182    }
183
184    pub fn to_public_input_cvar(
185        &self,
186        hack_feature_flags: OptFlag,
187        npublic_input: usize,
188    ) -> anyhow::Result<Vec<Packed>> {
189        let PreparedStatement {
190            proof_state:
191                ProofState {
192                    deferred_values:
193                        DeferredValues {
194                            plonk:
195                                Plonk {
196                                    alpha,
197                                    beta,
198                                    gamma,
199                                    zeta,
200                                    zeta_to_srs_length,
201                                    zeta_to_domain_size,
202                                    perm,
203                                    lookup: _,
204                                    feature_flags: _,
205                                },
206                            combined_inner_product,
207                            b,
208                            xi,
209                            bulletproof_challenges,
210                            branch_data:
211                                CompositionTypesBranchDataStableV1 {
212                                    proofs_verified,
213                                    domain_log2,
214                                },
215                        },
216                    sponge_digest_before_evaluations,
217                    messages_for_next_wrap_proof,
218                },
219            messages_for_next_step_proof,
220        } = &self;
221
222        // We sort the fields in the same order as here:
223        // <https://github.com/MinaProtocol/mina/blob/c824be7d80db1d290e0d48cbc920182d07de0330/src/lib/pickles/composition_types/composition_types.ml#L739>
224
225        let mut fields: Vec<Packed> = Vec::with_capacity(npublic_input);
226
227        let to_fq = |fp: Fp| -> Fq {
228            let bigint: BigInteger256 = fp.into();
229            bigint.try_into().unwrap() // Never fail, `Fq` is larger than `Fp`
230        };
231
232        let var = |x| Packed::Field(CircuitVar::Var(x));
233        let bits = |n, x| Packed::PackedBits(CircuitVar::Var(x), n);
234
235        // Fp
236        {
237            fields.push(var(to_fq(combined_inner_product.shifted)));
238            fields.push(var(to_fq(b.shifted)));
239            fields.push(var(to_fq(zeta_to_srs_length.shifted)));
240            fields.push(var(to_fq(zeta_to_domain_size.shifted)));
241            fields.push(var(to_fq(perm.shifted)));
242        }
243
244        // Challenge
245        {
246            fields.push(bits(128, two_u64_to_field(beta)));
247            fields.push(bits(128, two_u64_to_field(gamma)));
248        }
249
250        // Scalar challenge
251        {
252            fields.push(bits(128, two_u64_to_field(alpha)));
253            fields.push(bits(128, two_u64_to_field(zeta)));
254            fields.push(bits(128, two_u64_to_field(xi)));
255        }
256
257        // Digest
258        {
259            fields.push(bits(
260                255,
261                four_u64_to_field(sponge_digest_before_evaluations)?,
262            ));
263            fields.push(bits(255, four_u64_to_field(messages_for_next_wrap_proof)?));
264            fields.push(bits(255, four_u64_to_field(messages_for_next_step_proof)?));
265        }
266
267        fields.extend(
268            bulletproof_challenges
269                .iter()
270                .copied()
271                .map(|v| bits(128, to_fq(v))),
272        );
273
274        // Index
275        {
276            // <https://github.com/MinaProtocol/mina/blob/32a91613c388a71f875581ad72276e762242f802/src/lib/pickles_base/proofs_verified.ml#L58>
277            let proofs_verified = match proofs_verified {
278                PicklesBaseProofsVerifiedStableV1::N0 => 0b00,
279                PicklesBaseProofsVerifiedStableV1::N1 => 0b10,
280                PicklesBaseProofsVerifiedStableV1::N2 => 0b11,
281            };
282            // <https://github.com/MinaProtocol/mina/blob/c824be7d80db1d290e0d48cbc920182d07de0330/src/lib/pickles/composition_types/branch_data.ml#L63>
283            let domain_log2: u8 = domain_log2.0.into();
284            let domain_log2: u64 = domain_log2 as u64;
285            let branch_data: u64 = (domain_log2 << 2) | proofs_verified;
286
287            fields.push(bits(10, Fq::from(branch_data)));
288        }
289
290        // TODO: Find out how this padding works, it's probably related to features/lookup
291        let zero = Fq::zero();
292        let circuit_var = match hack_feature_flags {
293            OptFlag::Yes => todo!(),
294            OptFlag::No => CircuitVar::Constant,
295            OptFlag::Maybe => CircuitVar::Var,
296        };
297        while fields.len() < npublic_input - 1 {
298            fields.push(Packed::PackedBits(circuit_var(zero), 1));
299        }
300        fields.push(Packed::PackedBits(circuit_var(zero), 128));
301
302        Ok(fields)
303    }
304}