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