axiom_core/
header_chain.rs1use std::{iter, marker::PhantomData};
2
3use axiom_eth::{
4 block_header::{
5 get_block_header_rlp_max_lens_from_extra, get_boundary_block_data, EthBlockHeaderChip,
6 EthBlockHeaderWitness,
7 },
8 halo2_base::{
9 gates::{GateInstructions, RangeInstructions},
10 AssignedValue,
11 QuantumCell::Constant,
12 },
13 mpt::MPTChip,
14 rlc::circuit::builder::RlcCircuitBuilder,
15 utils::assign_vec,
16 utils::{
17 build_utils::aggregation::CircuitMetadata, eth_circuit::EthCircuitInstructions,
18 keccak::decorator::RlcKeccakCircuitImpl,
19 },
20};
21use itertools::Itertools;
22
23use crate::Field;
24
25pub type EthBlockHeaderChainCircuit<F> = RlcKeccakCircuitImpl<F, EthBlockHeaderChainInput<F>>;
26
27#[derive(Clone, Debug)]
35pub struct EthBlockHeaderChainInput<F> {
36 header_rlp_encodings: Vec<Vec<u8>>,
38 num_blocks: u32, max_depth: usize,
40 max_extra_data_bytes: usize,
43 _marker: PhantomData<F>,
44}
45
46impl<F: Field> EthBlockHeaderChainInput<F> {
47 pub fn new(
50 mut header_rlp_encodings: Vec<Vec<u8>>,
51 num_blocks: u32,
52 max_depth: usize,
53 max_extra_data_bytes: usize,
54 ) -> Self {
55 let (header_rlp_max_bytes, _) =
56 get_block_header_rlp_max_lens_from_extra(max_extra_data_bytes);
57 let dummy_block_rlp = header_rlp_encodings[0].clone();
59 header_rlp_encodings.resize(1 << max_depth, dummy_block_rlp);
60 for header_rlp in header_rlp_encodings.iter_mut() {
61 header_rlp.resize(header_rlp_max_bytes, 0u8);
62 }
63 Self {
64 header_rlp_encodings,
65 num_blocks,
66 max_depth,
67 max_extra_data_bytes,
68 _marker: PhantomData,
69 }
70 }
71}
72
73#[derive(Clone, Debug)]
75pub struct EthBlockHeaderchainWitness<F: Field> {
76 pub max_extra_data_bytes: usize,
77 pub block_chain: Vec<EthBlockHeaderWitness<F>>,
79 pub num_blocks_minus_one: AssignedValue<F>,
80 pub indicator: Vec<AssignedValue<F>>,
81}
82
83impl<F: Field> EthCircuitInstructions<F> for EthBlockHeaderChainInput<F> {
84 type FirstPhasePayload = EthBlockHeaderchainWitness<F>;
85
86 fn virtual_assign_phase0(
87 &self,
88 builder: &mut RlcCircuitBuilder<F>,
89 mpt: &MPTChip<F>,
90 ) -> Self::FirstPhasePayload {
91 let chip = EthBlockHeaderChip::new(mpt.rlp, self.max_extra_data_bytes);
92 let keccak = mpt.keccak();
93
94 let ctx = builder.base.main(0);
96 let num_blocks = ctx.load_witness(F::from(self.num_blocks as u64));
98 let num_blocks_minus_one = chip.gate().sub(ctx, num_blocks, Constant(F::ONE));
99 chip.range().range_check(ctx, num_blocks_minus_one, self.max_depth);
102
103 let max_len = get_block_header_rlp_max_lens_from_extra(self.max_extra_data_bytes).0;
105 let block_headers = self
106 .header_rlp_encodings
107 .iter()
108 .map(|header| assign_vec(ctx, header.clone(), max_len))
109 .collect_vec();
110 let block_chain_witness =
111 chip.decompose_block_header_chain_phase0(builder, keccak, block_headers);
112 let ctx = builder.base.main(0);
114 let num_leaves_bits = chip.gate().num_to_bits(ctx, num_blocks, self.max_depth + 1);
115 let block_hashes = block_chain_witness
116 .iter()
117 .map(|witness| witness.block_hash.output_bytes.as_ref().to_vec())
118 .collect_vec();
119 let mountain_range = keccak.merkle_mountain_range(ctx, &block_hashes, &num_leaves_bits);
121 let mountain_range = mountain_range
122 .into_iter()
123 .zip(num_leaves_bits.into_iter().rev())
124 .flat_map(|((_hash_bytes, hash_u128s), bit)| {
125 hash_u128s.map(|hash_u128| chip.gate().mul(ctx, hash_u128, bit))
127 })
128 .collect_vec();
129
130 let indicator =
131 chip.gate().idx_to_indicator(ctx, num_blocks_minus_one, block_chain_witness.len());
132 let (prev_block_hash, end_block_hash, block_numbers) =
133 get_boundary_block_data(ctx, chip.gate(), &block_chain_witness, &indicator);
134 let assigned_instances = iter::empty()
135 .chain(prev_block_hash)
136 .chain(end_block_hash)
137 .chain(iter::once(block_numbers))
138 .chain(mountain_range)
139 .collect_vec();
140 builder.base.assigned_instances[0] = assigned_instances;
141
142 EthBlockHeaderchainWitness {
143 max_extra_data_bytes: self.max_extra_data_bytes,
144 block_chain: block_chain_witness,
145 num_blocks_minus_one,
146 indicator,
147 }
148 }
149
150 fn virtual_assign_phase1(
151 &self,
152 builder: &mut RlcCircuitBuilder<F>,
153 mpt: &MPTChip<F>,
154 witness: Self::FirstPhasePayload,
155 ) {
156 let chip = EthBlockHeaderChip::new(mpt.rlp, witness.max_extra_data_bytes);
157 let _block_chain_trace = chip.decompose_block_header_chain_phase1(
158 builder,
159 witness.block_chain,
160 Some((witness.num_blocks_minus_one, witness.indicator)),
161 );
162 }
163}
164
165impl<F: Field> CircuitMetadata for EthBlockHeaderChainInput<F> {
166 const HAS_ACCUMULATOR: bool = false;
167 fn num_instance(&self) -> Vec<usize> {
168 vec![2 + 2 + 1 + 2 * (self.max_depth + 1)]
169 }
170}