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 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 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 pub only_verify_constraints: bool,
207 pub expected_step_proof: Option<&'static str>,
209 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}