1use std::{collections::BTreeMap, path::Path, sync::Arc};
2
3use axiom_eth::{
4 block_header::GENESIS_BLOCK_RLP,
5 halo2_base::{
6 gates::circuit::CircuitBuilderStage,
7 utils::halo2::{KeygenCircuitIntent, ProvingKeyGenerator},
8 },
9 halo2_proofs::{
10 plonk::{Circuit, ProvingKey},
11 poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG},
12 },
13 halo2curves::bn256::{Bn256, Fr, G1Affine},
14 snark_verifier_sdk::{
15 halo2::{
16 aggregation::AggregationCircuit,
17 utils::{
18 AggregationDependencyIntent, AggregationDependencyIntentOwned,
19 KeygenAggregationCircuitIntent,
20 },
21 },
22 CircuitExt, Snark,
23 },
24 utils::{
25 build_utils::{
26 aggregation::get_dummy_aggregation_params,
27 keygen::{
28 compile_agg_dep_to_protocol, get_dummy_rlc_keccak_params, read_srs_from_dir,
29 write_pk_and_pinning,
30 },
31 pinning::aggregation::{AggTreeId, GenericAggParams, GenericAggPinning},
32 },
33 merkle_aggregation::keygen::AggIntentMerkle,
34 DEFAULT_RLC_CACHE_BITS,
35 },
36};
37use serde::{Deserialize, Serialize};
38
39use crate::{
40 aggregation::{
41 final_merkle::{
42 EthBlockHeaderChainRootAggregationCircuit, EthBlockHeaderChainRootAggregationInput,
43 },
44 intermediate::EthBlockHeaderChainIntermediateAggregationInput,
45 },
46 header_chain::{EthBlockHeaderChainCircuit, EthBlockHeaderChainInput},
47 types::{
48 CoreNodeParams, CoreNodeType, CorePinningIntermediate, CorePinningLeaf, CorePinningRoot,
49 },
50};
51
52#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
54pub struct RecursiveCoreIntent {
55 pub k_at_depth: Vec<u32>,
58 pub max_extra_data_bytes: usize,
61 pub params: CoreNodeParams,
62}
63
64impl RecursiveCoreIntent {
65 pub fn new(k_at_depth: Vec<u32>, max_extra_data_bytes: usize, params: CoreNodeParams) -> Self {
66 Self { k_at_depth, max_extra_data_bytes, params }
67 }
68 pub fn child(&self) -> Option<Self> {
70 assert!(!self.k_at_depth.is_empty());
71 self.params.child(Some(self.max_extra_data_bytes)).map(|params| Self {
72 k_at_depth: self.k_at_depth[1..].to_vec(),
73 max_extra_data_bytes: self.max_extra_data_bytes,
74 params,
75 })
76 }
77}
78
79#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
80pub struct CoreIntentLeaf {
81 pub k: u32,
82 pub max_extra_data_bytes: usize,
85 pub depth: usize,
87}
88
89#[derive(Clone, Debug)]
90pub(crate) struct CoreIntentIntermediate {
91 pub k: u32,
92 pub kzg_params: Arc<ParamsKZG<Bn256>>,
94 pub to_agg: Vec<AggTreeId>,
96 pub child_intent: AggregationDependencyIntentOwned,
98 pub depth: usize,
100 pub initial_depth: usize,
102}
103
104#[derive(Clone, Debug)]
105pub(crate) struct CoreIntentRoot {
106 pub k: u32,
107 pub(crate) kzg_params: Arc<ParamsKZG<Bn256>>,
109 pub to_agg: Vec<AggTreeId>,
111 pub child_intent: AggregationDependencyIntentOwned,
113 pub depth: usize,
115 pub initial_depth: usize,
117}
118
119#[derive(Clone, Debug)]
122pub(crate) struct CoreIntentEvm {
123 pub k: u32,
124 pub kzg_params: Arc<ParamsKZG<Bn256>>,
126 pub to_agg: AggTreeId,
128 pub child_intent: AggregationDependencyIntentOwned,
130}
131
132impl KeygenCircuitIntent<Fr> for CoreIntentLeaf {
133 type ConcreteCircuit = EthBlockHeaderChainCircuit<Fr>;
134 type Pinning = CorePinningLeaf;
135 fn get_k(&self) -> u32 {
136 self.k
137 }
138 fn build_keygen_circuit(self) -> Self::ConcreteCircuit {
139 let dummy_input = EthBlockHeaderChainInput::<Fr>::new(
140 vec![GENESIS_BLOCK_RLP.to_vec(); 1 << self.depth], 1,
142 self.depth,
143 self.max_extra_data_bytes,
144 );
145 let circuit_params = get_dummy_rlc_keccak_params(self.k as usize, 8);
146 let mut circuit = EthBlockHeaderChainCircuit::new_impl(
147 CircuitBuilderStage::Keygen,
148 dummy_input,
149 circuit_params,
150 DEFAULT_RLC_CACHE_BITS,
151 );
152 circuit.calculate_params();
153 circuit
154 }
155 fn get_pinning_after_keygen(
156 self,
157 kzg_params: &ParamsKZG<Bn256>,
158 circuit: &Self::ConcreteCircuit,
159 ) -> Self::Pinning {
160 let params = circuit.params();
161 let break_points = circuit.break_points();
162 let num_instance = circuit.num_instance();
163 let dk = (kzg_params.get_g()[0], kzg_params.g2(), kzg_params.s_g2());
164 CorePinningLeaf { params, break_points, num_instance, dk: dk.into() }
165 }
166}
167
168impl KeygenAggregationCircuitIntent for CoreIntentIntermediate {
169 fn intent_of_dependencies(&self) -> Vec<AggregationDependencyIntent> {
170 vec![(&self.child_intent).into(); 2]
171 }
172 fn build_keygen_circuit_from_snarks(self, snarks: Vec<Snark>) -> Self::AggregationCircuit {
173 assert_eq!(snarks.len(), 2);
174
175 let input = EthBlockHeaderChainIntermediateAggregationInput::new(
176 snarks,
177 1,
178 self.depth,
179 self.initial_depth,
180 );
181 let circuit_params = get_dummy_aggregation_params(self.k as usize);
182 let mut circuit =
183 input.build(CircuitBuilderStage::Keygen, circuit_params, &self.kzg_params).unwrap();
184 circuit.0.calculate_params(Some(20));
185 circuit.0
186 }
187}
188
189impl KeygenCircuitIntent<Fr> for CoreIntentIntermediate {
190 type ConcreteCircuit = AggregationCircuit;
191 type Pinning = CorePinningIntermediate;
192 fn get_k(&self) -> u32 {
193 self.k
194 }
195 fn build_keygen_circuit(self) -> Self::ConcreteCircuit {
196 self.build_keygen_circuit_shplonk()
197 }
198 fn get_pinning_after_keygen(
199 self,
200 kzg_params: &ParamsKZG<Bn256>,
201 circuit: &Self::ConcreteCircuit,
202 ) -> Self::Pinning {
203 let to_agg = compile_agg_dep_to_protocol(kzg_params, &self.child_intent, false);
204 let dk = (kzg_params.get_g()[0], kzg_params.g2(), kzg_params.s_g2());
205 CorePinningIntermediate {
206 params: circuit.params(),
207 to_agg: vec![to_agg; self.to_agg.len()],
208 break_points: circuit.break_points(),
209 num_instance: circuit.num_instance(),
210 dk: dk.into(),
211 }
212 }
213}
214
215impl KeygenAggregationCircuitIntent for CoreIntentRoot {
216 type AggregationCircuit = EthBlockHeaderChainRootAggregationCircuit;
217 fn intent_of_dependencies(&self) -> Vec<AggregationDependencyIntent> {
218 vec![(&self.child_intent).into(); 2]
219 }
220 fn build_keygen_circuit_from_snarks(self, snarks: Vec<Snark>) -> Self::AggregationCircuit {
221 assert_eq!(snarks.len(), 2);
222
223 let input = EthBlockHeaderChainRootAggregationInput::new(
224 snarks,
225 1,
226 self.depth,
227 self.initial_depth,
228 &self.kzg_params,
229 )
230 .unwrap();
231 let circuit_params = get_dummy_rlc_keccak_params(self.k as usize, self.k as usize - 1);
233 let mut circuit = EthBlockHeaderChainRootAggregationCircuit::new_impl(
235 CircuitBuilderStage::Keygen,
236 input,
237 circuit_params,
238 0, );
240 circuit.calculate_params();
241 circuit
242 }
243}
244
245impl KeygenCircuitIntent<Fr> for CoreIntentRoot {
246 type ConcreteCircuit = EthBlockHeaderChainRootAggregationCircuit;
247 type Pinning = CorePinningRoot;
248 fn get_k(&self) -> u32 {
249 self.k
250 }
251 fn build_keygen_circuit(self) -> Self::ConcreteCircuit {
252 self.build_keygen_circuit_shplonk()
253 }
254 fn get_pinning_after_keygen(
255 self,
256 kzg_params: &ParamsKZG<Bn256>,
257 circuit: &Self::ConcreteCircuit,
258 ) -> Self::Pinning {
259 let params = circuit.params();
260 let break_points = circuit.break_points();
261 let to_agg = compile_agg_dep_to_protocol(kzg_params, &self.child_intent, false);
262 let dk = (kzg_params.get_g()[0], kzg_params.g2(), kzg_params.s_g2());
263 CorePinningRoot {
264 params,
265 to_agg: vec![to_agg; self.to_agg.len()],
266 num_instance: circuit.num_instance(),
267 break_points,
268 dk: dk.into(),
269 }
270 }
271}
272
273impl From<CoreIntentEvm> for AggIntentMerkle {
274 fn from(value: CoreIntentEvm) -> Self {
275 AggIntentMerkle {
276 kzg_params: value.kzg_params,
277 to_agg: vec![value.to_agg],
278 deps: vec![value.child_intent],
279 k: value.k,
280 }
281 }
282}
283
284impl KeygenCircuitIntent<Fr> for CoreIntentEvm {
285 type ConcreteCircuit = AggregationCircuit;
286 type Pinning = GenericAggPinning<GenericAggParams>;
287 fn get_k(&self) -> u32 {
288 self.k
289 }
290 fn build_keygen_circuit(self) -> Self::ConcreteCircuit {
291 AggIntentMerkle::from(self).build_keygen_circuit()
292 }
293 fn get_pinning_after_keygen(
294 self,
295 kzg_params: &ParamsKZG<Bn256>,
296 circuit: &Self::ConcreteCircuit,
297 ) -> Self::Pinning {
298 AggIntentMerkle::from(self).get_pinning_after_keygen(kzg_params, circuit)
299 }
300}
301
302impl RecursiveCoreIntent {
303 pub fn create_and_serialize_proving_key(
311 self,
312 srs_dir: &Path,
313 data_dir: &Path,
314 cid_repo: &mut BTreeMap<CoreNodeParams, String>,
315 ) -> anyhow::Result<(AggTreeId, ProvingKey<G1Affine>, serde_json::Value)> {
316 let child = if let Some(child_intent) = self.child() {
318 let is_aggregation = !matches!(child_intent.params.node_type, CoreNodeType::Leaf(_));
319 let (child_id, child_pk, child_pinning) =
320 child_intent.create_and_serialize_proving_key(srs_dir, data_dir, cid_repo)?;
321 let num_instance: Vec<usize> =
322 serde_json::from_value(child_pinning["num_instance"].clone())?;
323 let agg_intent = AggregationDependencyIntentOwned {
326 vk: child_pk.get_vk().clone(),
327 num_instance,
328 accumulator_indices: is_aggregation
329 .then(|| AggregationCircuit::accumulator_indices().unwrap()),
330 agg_vk_hash_data: None,
331 };
332 Some((child_id, agg_intent))
333 } else {
334 None
335 };
336 assert!(!self.k_at_depth.is_empty());
337 let k = self.k_at_depth[0];
338 let kzg_params = Arc::new(read_srs_from_dir(srs_dir, k)?);
339 let ((pk, pinning), children) = match self.params.node_type {
340 CoreNodeType::Leaf(max_extra_data_bytes) => {
341 let intent =
342 CoreIntentLeaf { k, max_extra_data_bytes, depth: self.params.initial_depth };
343 (intent.create_pk_and_pinning(&kzg_params), vec![])
344 }
345 CoreNodeType::Intermediate => {
346 let (child_id, child_intent) = child.unwrap();
347 let to_agg = vec![child_id; 2];
348 let intent = CoreIntentIntermediate {
349 k,
350 kzg_params: kzg_params.clone(),
351 to_agg: to_agg.clone(),
352 child_intent,
353 depth: self.params.depth,
354 initial_depth: self.params.initial_depth,
355 };
356 (intent.create_pk_and_pinning(&kzg_params), to_agg)
357 }
358 CoreNodeType::Root => {
359 let (child_id, child_intent) = child.unwrap();
360 let to_agg = vec![child_id; 2];
361 let intent = CoreIntentRoot {
362 k,
363 kzg_params: kzg_params.clone(),
364 to_agg: to_agg.clone(),
365 child_intent,
366 depth: self.params.depth,
367 initial_depth: self.params.initial_depth,
368 };
369 (intent.create_pk_and_pinning(&kzg_params), to_agg)
370 }
371 CoreNodeType::Evm(_) => {
372 let (child_id, child_intent) = child.unwrap();
373 let to_agg = vec![child_id.clone()];
374 let intent = CoreIntentEvm {
375 k,
376 to_agg: child_id,
377 child_intent,
378 kzg_params: kzg_params.clone(),
379 };
380 (intent.create_pk_and_pinning(&kzg_params), to_agg)
381 }
382 };
383 let circuit_id = write_pk_and_pinning(data_dir, &pk, &pinning)?;
384 if let Some(old_cid) = cid_repo.insert(self.params, circuit_id.clone()) {
385 if old_cid != circuit_id {
386 anyhow::bail!("Different circuit ID for the same node params")
387 }
388 }
389 let tree_id = AggTreeId { circuit_id, children, aggregate_vk_hash: None };
390 Ok((tree_id, pk, pinning))
391 }
392}