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