mina_tree/proofs/
merge.rs

1use std::rc::Rc;
2
3use crate::proofs::{
4    constants::{make_step_transaction_data, StepMergeProof},
5    step::{step, StepParams},
6    util::sha256_sum,
7    wrap::{wrap, WrapParams},
8};
9use ark_ff::fields::arithmetic::InvalidBigInt;
10use mina_curves::pasta::{Fp, Fq};
11use mina_p2p_messages::v2;
12
13use crate::{
14    proofs::transaction::transaction_snark::assert_equal_local_state,
15    scan_state::{
16        fee_excess::FeeExcess,
17        pending_coinbase,
18        scan_state::transaction_snark::{
19            validate_ledgers_at_merge_checked, SokDigest, SokMessage, Statement, StatementLedgers,
20        },
21    },
22};
23
24use super::{
25    constants::WrapMergeProof,
26    field::{Boolean, CircuitVar, FieldWitness},
27    public_input::plonk_checks::PlonkMinimal,
28    step::{
29        extract_recursion_challenges, InductiveRule, OptFlag, PreviousProofStatement, StepProof,
30    },
31    transaction::{PlonkVerificationKeyEvals, Prover},
32    util::two_u64_to_field,
33    witness::Witness,
34    wrap::WrapProof,
35};
36
37fn merge_main(
38    statement: &Statement<SokDigest>,
39    proofs: &[v2::LedgerProofProdStableV2; 2],
40    w: &mut Witness<Fp>,
41) -> anyhow::Result<(Statement<SokDigest>, Statement<SokDigest>)> {
42    let (s1, s2) = w.exists({
43        let [p1, p2] = proofs;
44        let (s1, s2) = (&p1.0.statement, &p2.0.statement);
45        let s1: Statement<SokDigest> = s1.try_into()?;
46        let s2: Statement<SokDigest> = s2.try_into()?;
47        (s1, s2)
48    });
49
50    let _fee_excess = FeeExcess::combine_checked(&s1.fee_excess, &s2.fee_excess, w);
51
52    pending_coinbase::Stack::check_merge(
53        (
54            &s1.source.pending_coinbase_stack,
55            &s1.target.pending_coinbase_stack,
56        ),
57        (
58            &s2.source.pending_coinbase_stack,
59            &s2.target.pending_coinbase_stack,
60        ),
61        w,
62    );
63
64    let _supply_increase = {
65        let s1 = s1.supply_increase.to_checked::<Fp>();
66        let s2 = s2.supply_increase.to_checked::<Fp>();
67        s1.add(&s2, w)
68    };
69
70    assert_equal_local_state(&statement.source.local_state, &s1.source.local_state, w);
71    assert_equal_local_state(&statement.target.local_state, &s2.target.local_state, w);
72
73    let _valid_ledger = validate_ledgers_at_merge_checked(
74        &StatementLedgers::of_statement(&s1),
75        &StatementLedgers::of_statement(&s2),
76        w,
77    );
78
79    {
80        // Only `Statement.fee_excess`, not `fee_excess`
81        let FeeExcess {
82            fee_excess_l,
83            fee_excess_r,
84            ..
85        } = statement.fee_excess;
86        fee_excess_l.to_checked::<Fp>().value(w);
87        fee_excess_r.to_checked::<Fp>().value(w);
88
89        // Only `Statement.supply_increase`, not `supply_increase`
90        let supply_increase = statement.supply_increase;
91        supply_increase.to_checked::<Fp>().value(w);
92    }
93
94    Ok((s1, s2))
95}
96
97pub fn dlog_plonk_index(wrap_prover: &Prover<Fq>) -> PlonkVerificationKeyEvals<Fp> {
98    PlonkVerificationKeyEvals::from(&**wrap_prover.index.verifier_index.as_ref().unwrap())
99}
100
101impl From<&v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValuesPlonkFeatureFlags> for crate::proofs::step::FeatureFlags::<bool> {
102    fn from(value: &v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValuesPlonkFeatureFlags) -> Self {
103        let v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValuesPlonkFeatureFlags {
104            range_check0,
105            range_check1,
106            foreign_field_add,
107            foreign_field_mul,
108            xor,
109            rot,
110            lookup,
111            runtime_tables,
112        } = value;
113
114        Self {
115            range_check0: *range_check0,
116            range_check1: *range_check1,
117            foreign_field_add: *foreign_field_add,
118            foreign_field_mul: *foreign_field_mul,
119            xor: *xor,
120            rot: *rot,
121            lookup: *lookup,
122            runtime_tables: *runtime_tables,
123        }
124    }
125}
126
127impl From<&crate::proofs::step::FeatureFlags::<bool>> for v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValuesPlonkFeatureFlags {
128    fn from(value: &crate::proofs::step::FeatureFlags::<bool>) -> Self {
129        let crate::proofs::step::FeatureFlags::<bool> {
130            range_check0,
131            range_check1,
132            foreign_field_add,
133            foreign_field_mul,
134            xor,
135            rot,
136            lookup,
137            runtime_tables,
138        } = value;
139
140        Self {
141            range_check0: *range_check0,
142            range_check1: *range_check1,
143            foreign_field_add: *foreign_field_add,
144            foreign_field_mul: *foreign_field_mul,
145            xor: *xor,
146            rot: *rot,
147            lookup: *lookup,
148            runtime_tables: *runtime_tables,
149        }
150    }
151}
152
153impl<F: FieldWitness>
154    TryFrom<&v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValuesPlonk>
155    for PlonkMinimal<F>
156{
157    type Error = InvalidBigInt;
158
159    fn try_from(
160        value: &v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValuesPlonk,
161    ) -> Result<Self, Self::Error> {
162        let v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValuesPlonk {
163            alpha,
164            beta,
165            gamma,
166            zeta,
167            joint_combiner,
168            feature_flags,
169        } = value;
170
171        let to_bytes = |v: &v2::LimbVectorConstantHex64StableV1| v.as_u64();
172
173        let alpha_bytes = alpha.inner.each_ref().map(to_bytes);
174        let beta_bytes = beta.each_ref().map(to_bytes);
175        let gamma_bytes = gamma.each_ref().map(to_bytes);
176        let zeta_bytes = zeta.inner.each_ref().map(to_bytes);
177
178        Ok(PlonkMinimal::<F, 2> {
179            alpha: two_u64_to_field(&alpha_bytes),
180            beta: two_u64_to_field(&beta_bytes),
181            gamma: two_u64_to_field(&gamma_bytes),
182            zeta: two_u64_to_field(&zeta_bytes),
183            joint_combiner: joint_combiner
184                .as_ref()
185                .map(|f| two_u64_to_field(&f.inner.each_ref().map(to_bytes))),
186            alpha_bytes,
187            beta_bytes,
188            gamma_bytes,
189            zeta_bytes,
190            joint_combiner_bytes: joint_combiner
191                .as_ref()
192                .map(|f| f.inner.each_ref().map(to_bytes)),
193            feature_flags: feature_flags.into(),
194        })
195    }
196}
197
198pub struct MergeParams<'a> {
199    pub statement: Statement<()>,
200    pub proofs: &'a [v2::LedgerProofProdStableV2; 2],
201    pub message: &'a SokMessage,
202    pub step_prover: &'a Prover<Fp>,
203    pub wrap_prover: &'a Prover<Fq>,
204    /// When set to `true`, `generate_block_proof` will not create a proof, but only
205    /// verify constraints in the step witnesses
206    pub only_verify_constraints: bool,
207    /// For debugging only
208    pub expected_step_proof: Option<&'static str>,
209    /// For debugging only
210    pub ocaml_wrap_witness: Option<Vec<Fq>>,
211}
212
213const MERGE_N_PREVIOUS_PROOFS: usize = 2;
214
215pub(super) fn generate_merge_proof(
216    params: MergeParams,
217    w: &mut Witness<Fp>,
218) -> anyhow::Result<WrapProof> {
219    let MergeParams {
220        statement,
221        proofs,
222        message,
223        step_prover,
224        wrap_prover,
225        only_verify_constraints,
226        expected_step_proof,
227        ocaml_wrap_witness,
228    } = params;
229
230    let sok_digest = message.digest();
231    let statement_with_sok = Rc::new(statement.with_digest(sok_digest));
232
233    w.exists(&*statement_with_sok);
234
235    let (s1, s2) = merge_main(&statement_with_sok, proofs, w)?;
236
237    let [p1, p2]: [&v2::PicklesProofProofsVerified2ReprStableV2; 2] = {
238        let [p1, p2] = proofs;
239        [&p1.0.proof, &p2.0.proof]
240    };
241
242    let prev_challenge_polynomial_commitments = extract_recursion_challenges(&[p1, p2])?;
243
244    let rule = InductiveRule {
245        previous_proof_statements: [
246            PreviousProofStatement {
247                public_input: Rc::new(s1),
248                proof: p1,
249                proof_must_verify: CircuitVar::Constant(Boolean::True),
250            },
251            PreviousProofStatement {
252                public_input: Rc::new(s2),
253                proof: p2,
254                proof_must_verify: CircuitVar::Constant(Boolean::True),
255            },
256        ],
257        public_output: (),
258        auxiliary_output: (),
259    };
260
261    let dlog_plonk_index = dlog_plonk_index(wrap_prover);
262    let dlog_plonk_index_cvar = dlog_plonk_index.to_cvar(CircuitVar::Var);
263    let verifier_index = &**wrap_prover.index.verifier_index.as_ref().unwrap();
264
265    let tx_data = make_step_transaction_data(&dlog_plonk_index_cvar);
266    let for_step_datas = [&tx_data, &tx_data];
267
268    let indexes = [
269        (verifier_index, &dlog_plonk_index_cvar),
270        (verifier_index, &dlog_plonk_index_cvar),
271    ];
272
273    let StepProof {
274        statement,
275        prev_evals,
276        proof_with_public: proof,
277    } = step::<StepMergeProof, MERGE_N_PREVIOUS_PROOFS>(
278        StepParams {
279            app_state: Rc::clone(&statement_with_sok) as _,
280            rule,
281            for_step_datas,
282            indexes,
283            wrap_prover,
284            prev_challenge_polynomial_commitments,
285            step_prover,
286            hack_feature_flags: OptFlag::No,
287            only_verify_constraints,
288        },
289        w,
290    )?;
291
292    if let Some(expected) = expected_step_proof {
293        let proof_json = serde_json::to_vec(&proof.proof).unwrap();
294        assert_eq!(sha256_sum(&proof_json), expected);
295    };
296
297    let mut w = Witness::new::<WrapMergeProof>();
298
299    if let Some(ocaml_aux) = ocaml_wrap_witness {
300        w.ocaml_aux = ocaml_aux;
301    };
302
303    wrap::<WrapMergeProof>(
304        WrapParams {
305            app_state: statement_with_sok,
306            proof_with_public: &proof,
307            step_statement: statement,
308            prev_evals: &prev_evals,
309            dlog_plonk_index: &dlog_plonk_index,
310            step_prover_index: &step_prover.index,
311            wrap_prover,
312        },
313        &mut w,
314    )
315}