Skip to main content

axiom_eth/block_header/
mod.rs

1use crate::Field;
2use crate::{
3    keccak::{types::KeccakVarLenQuery, KeccakChip},
4    rlc::{
5        chip::RlcChip,
6        circuit::builder::{RlcCircuitBuilder, RlcContextPair},
7        types::RlcTrace,
8    },
9    rlp::{
10        evaluate_byte_array, max_rlp_encoding_len,
11        types::{RlpArrayWitness, RlpFieldTrace, RlpFieldWitness},
12        RlpChip,
13    },
14    utils::{bytes_be_to_u128, AssignedH256},
15};
16use core::iter::once;
17use ethers_core::types::Chain;
18use halo2_base::{
19    gates::{
20        flex_gate::threads::parallelize_core, GateChip, GateInstructions, RangeChip,
21        RangeInstructions,
22    },
23    safe_types::{left_pad_var_array_to_fixed, FixLenBytes, SafeTypeChip},
24    AssignedValue, Context,
25    QuantumCell::{Constant, Existing},
26};
27use itertools::Itertools;
28
29#[cfg(test)]
30mod tests;
31
32// extra data max byte length is different for different networks
33pub const MAINNET_EXTRA_DATA_MAX_BYTES: usize = 32;
34pub const GOERLI_EXTRA_DATA_MAX_BYTES: usize = 300;
35
36/// This is the minimum possible RLP byte length of a block header *at any block* (including pre EIPs)
37pub const BLOCK_HEADER_RLP_MIN_BYTES: usize = 479;
38
39pub const MIN_NUM_BLOCK_HEADER_FIELDS: usize = 15;
40pub const NUM_BLOCK_HEADER_FIELDS: usize = 20;
41pub const MAINNET_HEADER_FIELDS_MAX_BYTES: [usize; NUM_BLOCK_HEADER_FIELDS] = [
42    32,                           // parent_hash
43    32,                           // ommers_hash
44    20,                           // coinbase [beneficiary]
45    32,                           // state_root
46    32,                           // txs_root
47    32,                           // receipts_root
48    256,                          // logs_bloom
49    7,                            // difficulty
50    4,                            // number
51    4,                            // gas_limit
52    4,                            // gas_used
53    4,                            // timestamp
54    MAINNET_EXTRA_DATA_MAX_BYTES, // extradata
55    32,                           // mix_hash or prev_randao
56    8,                            // nonce
57    32,                           // base_fee_per_gas
58    32,                           // withdrawals_root
59    8,                            // data_gas_used
60    8,                            // excess_data_gas
61    32,                           // parent_beacon_block_root
62];
63pub const BLOCK_HEADER_FIELD_IS_VAR_LEN: [bool; NUM_BLOCK_HEADER_FIELDS] = [
64    false, false, false, false, false, false, false, true, true, true, true, true, true, false,
65    false, true, false, true, true, false,
66];
67/// The maximum number of bytes it takes to represent a block number, without any RLP encoding.
68pub const BLOCK_NUMBER_MAX_BYTES: usize = MAINNET_HEADER_FIELDS_MAX_BYTES[BLOCK_NUMBER_INDEX];
69pub const STATE_ROOT_INDEX: usize = 3;
70pub const TX_ROOT_INDEX: usize = 4;
71pub const RECEIPT_ROOT_INDEX: usize = 5;
72pub const LOGS_BLOOM_INDEX: usize = 6;
73pub const BLOCK_NUMBER_INDEX: usize = 8;
74pub const EXTRA_DATA_INDEX: usize = 12;
75pub const WITHDRAWALS_ROOT_INDEX: usize = 16;
76
77/**
78| Field                        | Type            | Size (bytes)    | RLP size (bytes) | RLP size (bits) |
79|------------------------------|-----------------|-----------------|------------------|-----------------|
80| parentHash                   | 256 bits        | 32              | 33               | 264             |
81| ommersHash                   | 256 bits        | 32              | 33               | 264             |
82| beneficiary                  | 160 bits        | 20              | 21               | 168             |
83| stateRoot                    | 256 bits        | 32              | 33               | 264             |
84| transactionsRoot             | 256 bits        | 32              | 33               | 264             |
85| receiptsRoot                 | 256 bits        | 32              | 33               | 264             |
86| logsBloom                    | 256 bytes       | 256             | 259              | 2072            |
87| difficulty                   | big int scalar  | variable        | 8                | 64              |
88| number                       | big int scalar  | variable        | <= 5             | <= 40           |
89| gasLimit                     | big int scalar  | variable        | 5                | 40              |
90| gasUsed                      | big int scalar  | variable        | <= 5             | <= 40           |
91| timestamp                    | big int scalar  | variable        | 5                | 40              |
92| extraData (Mainnet)          | up to 256 bits  | variable, <= 32 | <= 33            | <= 264          |
93| mixHash                      | 256 bits        | 32              | 33               | 264             |
94| nonce                        | 64 bits         | 8               | 9                | 72              |
95| basefee (post-1559)          | big int scalar  | variable, <=32  | <= 33            | <= 264          |
96| withdrawalsRoot (post-4895)  | 256 bits        | 32              | 33               | 264             |
97| blobGasUsed (post-4844)      | 64 bits         | <= 8            | <= 9             | <= 72           |
98| excessBlobGas (post-4844)    | 64 bits         | <= 8            | <= 9             | <= 72           |
99| parentBeaconBlockRoot (post-4788) | 256 bits   | 32              | 33               | 264             |
100*/
101#[allow(dead_code)]
102#[derive(Clone, Debug)]
103pub struct EthBlockHeaderTrace<F: Field> {
104    // pub rlp_trace: RlcTrace<F>,
105    pub parent_hash: RlpFieldTrace<F>,
106    pub ommers_hash: RlpFieldTrace<F>,
107    pub beneficiary: RlpFieldTrace<F>,
108    pub state_root: RlpFieldTrace<F>,
109    pub transactions_root: RlpFieldTrace<F>,
110    pub receipts_root: RlpFieldTrace<F>,
111    pub logs_bloom: RlpFieldTrace<F>,
112    pub difficulty: RlpFieldTrace<F>,
113    pub number: RlpFieldTrace<F>,
114    pub gas_limit: RlpFieldTrace<F>,
115    pub gas_used: RlpFieldTrace<F>,
116    pub timestamp: RlpFieldTrace<F>,
117    pub extra_data: RlpFieldTrace<F>,
118    pub mix_hash: RlpFieldTrace<F>,
119    pub nonce: RlpFieldTrace<F>,
120    pub basefee: RlpFieldTrace<F>, // this is 0 (or undefined) for pre-EIP1559 (London) blocks
121    pub withdrawals_root: RlpFieldTrace<F>, // this is 0 (or undefined) for pre-EIP4895 (Shapella) blocks (before block number 1681338455)
122    // the user will have to separately determine whether the block is EIP1559 or not
123    pub block_hash: KeccakVarLenQuery<F>,
124
125    // pub prefix: AssignedValue<F>,
126    pub len_trace: RlcTrace<F>,
127}
128
129#[derive(Clone, Debug)]
130pub struct EthBlockHeaderWitness<F: Field> {
131    pub rlp_witness: RlpArrayWitness<F>,
132    pub block_hash: KeccakVarLenQuery<F>,
133}
134
135impl<F: Field> EthBlockHeaderWitness<F> {
136    /// Returns block number as bytes4 (left padded with zeros, big endian)
137    pub fn get_number_fixed(
138        &self,
139        ctx: &mut Context<F>,
140        gate: &impl GateInstructions<F>,
141    ) -> FixLenBytes<F, BLOCK_NUMBER_MAX_BYTES> {
142        let block_num_bytes = &self.get_number().field_cells;
143        let block_num_len = self.get_number().field_len;
144        SafeTypeChip::unsafe_to_fix_len_bytes(
145            left_pad_var_array_to_fixed(
146                ctx,
147                gate,
148                block_num_bytes,
149                block_num_len,
150                BLOCK_NUMBER_MAX_BYTES,
151            )
152            .try_into()
153            .unwrap(),
154        )
155    }
156    /// Returns block number as a field element
157    pub fn get_number_value(
158        &self,
159        ctx: &mut Context<F>,
160        gate: &impl GateInstructions<F>,
161    ) -> AssignedValue<F> {
162        let block_num_bytes = &self.get_number().field_cells;
163        let block_num_len = self.get_number().field_len;
164        evaluate_byte_array(ctx, gate, block_num_bytes, block_num_len)
165    }
166    pub fn get_block_hash_hi_lo(&self) -> AssignedH256<F> {
167        self.block_hash.hi_lo()
168    }
169    pub fn get_parent_hash(&self) -> &RlpFieldWitness<F> {
170        &self.rlp_witness.field_witness[0]
171    }
172    pub fn get_ommers_hash(&self) -> &RlpFieldWitness<F> {
173        &self.rlp_witness.field_witness[1]
174    }
175    pub fn get_beneficiary(&self) -> &RlpFieldWitness<F> {
176        &self.rlp_witness.field_witness[2]
177    }
178    pub fn get_state_root(&self) -> &RlpFieldWitness<F> {
179        &self.rlp_witness.field_witness[3]
180    }
181    pub fn get_transactions_root(&self) -> &RlpFieldWitness<F> {
182        &self.rlp_witness.field_witness[4]
183    }
184    pub fn get_receipts_root(&self) -> &RlpFieldWitness<F> {
185        &self.rlp_witness.field_witness[5]
186    }
187    pub fn get_logs_bloom(&self) -> &RlpFieldWitness<F> {
188        &self.rlp_witness.field_witness[6]
189    }
190    pub fn get_difficulty(&self) -> &RlpFieldWitness<F> {
191        &self.rlp_witness.field_witness[7]
192    }
193    pub fn get_number(&self) -> &RlpFieldWitness<F> {
194        &self.rlp_witness.field_witness[8]
195    }
196    pub fn get_gas_limit(&self) -> &RlpFieldWitness<F> {
197        &self.rlp_witness.field_witness[9]
198    }
199    pub fn get_gas_used(&self) -> &RlpFieldWitness<F> {
200        &self.rlp_witness.field_witness[10]
201    }
202    pub fn get_timestamp(&self) -> &RlpFieldWitness<F> {
203        &self.rlp_witness.field_witness[11]
204    }
205    pub fn get_extra_data(&self) -> &RlpFieldWitness<F> {
206        &self.rlp_witness.field_witness[12]
207    }
208    pub fn get_mix_hash(&self) -> &RlpFieldWitness<F> {
209        &self.rlp_witness.field_witness[13]
210    }
211    pub fn get_nonce(&self) -> &RlpFieldWitness<F> {
212        &self.rlp_witness.field_witness[14]
213    }
214    pub fn get_basefee(&self) -> &RlpFieldWitness<F> {
215        &self.rlp_witness.field_witness[15]
216    }
217    pub fn get_withdrawals_root(&self) -> &RlpFieldWitness<F> {
218        &self.rlp_witness.field_witness[16]
219    }
220    pub fn get_index(&self, idx: usize) -> Option<&RlpFieldWitness<F>> {
221        self.rlp_witness.field_witness.get(idx)
222    }
223    /// Returns the number of fields in the block header
224    pub fn get_list_len(&self) -> AssignedValue<F> {
225        self.rlp_witness.list_len.unwrap()
226    }
227}
228
229pub struct EthBlockHeaderChip<'chip, F: Field> {
230    pub rlp: RlpChip<'chip, F>,
231    pub max_extra_data_bytes: usize,
232}
233
234impl<'chip, F: Field> EthBlockHeaderChip<'chip, F> {
235    pub fn new(rlp: RlpChip<'chip, F>, max_extra_data_bytes: usize) -> Self {
236        Self { rlp, max_extra_data_bytes }
237    }
238
239    pub fn new_from_network(rlp: RlpChip<'chip, F>, chain: Chain) -> Self {
240        let max_extra_data_bytes = get_block_header_extra_bytes(chain);
241        Self { rlp, max_extra_data_bytes }
242    }
243
244    pub fn gate(&self) -> &GateChip<F> {
245        self.rlp.gate()
246    }
247
248    pub fn range(&self) -> &RangeChip<F> {
249        self.rlp.range()
250    }
251
252    pub fn rlc(&self) -> &RlcChip<F> {
253        self.rlp.rlc()
254    }
255
256    pub fn rlp(&self) -> &RlpChip<F> {
257        &self.rlp
258    }
259
260    /// Takes the variable length RLP encoded block header, padded with 0s to the maximum possible block header RLP length, and outputs the decomposition into block header fields.
261    ///
262    /// This function _will_ range check that `block_header` consists of bytes (8 bits).
263    ///
264    /// In addition, the keccak block hash of the block is calculated.
265    ///
266    /// This is the preparation step that computes the witnesses. This MUST be done in `FirstPhase`.
267    /// The accompanying `decompose_block_header_phase1` must be called in `SecondPhase` to constrain the RLCs associated to the RLP decoding.
268    pub fn decompose_block_header_phase0(
269        &self,
270        ctx: &mut Context<F>, // ctx_gate in FirstPhase
271        keccak: &KeccakChip<F>,
272        block_header: &[AssignedValue<F>],
273    ) -> EthBlockHeaderWitness<F> {
274        let (max_len, max_field_lens) =
275            get_block_header_rlp_max_lens_from_extra(self.max_extra_data_bytes);
276        assert_eq!(block_header.len(), max_len);
277        // range check that block_header consists of bytes
278        for b in block_header {
279            self.range().range_check(ctx, *b, 8);
280        }
281
282        let rlp_witness = self.rlp().decompose_rlp_array_phase0(
283            ctx,
284            block_header.to_vec(),
285            &max_field_lens,
286            true,
287        ); // `is_variable_len = true` because RLP can have >=15 fields, depending on which EIPs are active at that block
288
289        let block_hash = keccak.keccak_var_len(
290            ctx,
291            rlp_witness.rlp_array.clone(), // this is `block_header_assigned`
292            rlp_witness.rlp_len,
293            BLOCK_HEADER_RLP_MIN_BYTES,
294        );
295        EthBlockHeaderWitness { rlp_witness, block_hash }
296    }
297
298    /// Takes the variable length RLP encoded block header, padded with 0s to the maximum possible block header RLP length, and outputs the decomposition into block header fields.
299    ///
300    /// In addition, the keccak block hash of the block is calculated.
301    ///
302    /// This is the finalization step that constrains RLC concatenations.
303    /// This should be called after `decompose_block_header_phase0`.
304    /// This MUST be done in `SecondPhase`.
305    pub fn decompose_block_header_phase1(
306        &self,
307        ctx: RlcContextPair<F>,
308        witness: EthBlockHeaderWitness<F>,
309    ) -> EthBlockHeaderTrace<F> {
310        let trace = self.rlp().decompose_rlp_array_phase1(ctx, witness.rlp_witness, true);
311        let block_hash = witness.block_hash;
312
313        // Base fee per unit gas only after London
314        let [parent_hash, ommers_hash, beneficiary, state_root, transactions_root, receipts_root, logs_bloom, difficulty, number, gas_limit, gas_used, timestamp, extra_data, mix_hash, nonce, basefee, withdrawals_root, ..]: [RlpFieldTrace<F>; NUM_BLOCK_HEADER_FIELDS] =
315            trace.field_trace.try_into().unwrap();
316        EthBlockHeaderTrace {
317            parent_hash,
318            ommers_hash,
319            beneficiary,
320            state_root,
321            transactions_root,
322            receipts_root,
323            logs_bloom,
324            difficulty,
325            number,
326            gas_limit,
327            gas_used,
328            timestamp,
329            extra_data,
330            mix_hash,
331            nonce,
332            basefee,
333            withdrawals_root,
334            block_hash,
335            len_trace: trace.len_trace,
336        }
337    }
338
339    /// Makes multiple calls to `decompose_block_header_phase0` in parallel threads. Should be called in FirstPhase.
340    pub fn decompose_block_headers_phase0(
341        &self,
342        builder: &mut RlcCircuitBuilder<F>,
343        keccak: &KeccakChip<F>,
344        block_headers: Vec<Vec<AssignedValue<F>>>,
345    ) -> Vec<EthBlockHeaderWitness<F>> {
346        parallelize_core(builder.base.pool(0), block_headers, |ctx, block_header| {
347            self.decompose_block_header_phase0(ctx, keccak, &block_header)
348        })
349    }
350
351    /// Makes multiple calls to `decompose_block_header_phase1` in parallel threads. Should be called in SecondPhase.
352    pub fn decompose_block_headers_phase1(
353        &self,
354        builder: &mut RlcCircuitBuilder<F>,
355        witnesses: Vec<EthBlockHeaderWitness<F>>,
356    ) -> Vec<EthBlockHeaderTrace<F>> {
357        assert!(!witnesses.is_empty());
358        // `load_rlc_cache` no longer called here: it should be called globally when `RlcCircuitBuilder` is constructed
359        builder.parallelize_phase1(witnesses, |(ctx_gate, ctx_rlc), witness| {
360            self.decompose_block_header_phase1((ctx_gate, ctx_rlc), witness)
361        })
362    }
363
364    /// Takes a list of (purported) RLP encoded block headers and
365    /// decomposes each header into it's fields.
366    /// `headers[0]` is the earliest block.
367    ///
368    /// This is the preparation step that computes the witnesses. This MUST be done in `FirstPhase`.
369    /// The accompanying `decompose_block_header_chain_phase1` must be called in `SecondPhase` to constrain the RLCs associated to the RLP decoding.
370    pub fn decompose_block_header_chain_phase0(
371        &self,
372        builder: &mut RlcCircuitBuilder<F>,
373        keccak: &KeccakChip<F>,
374        block_headers: Vec<Vec<AssignedValue<F>>>,
375    ) -> Vec<EthBlockHeaderWitness<F>> {
376        self.decompose_block_headers_phase0(builder, keccak, block_headers)
377    }
378
379    /// Takes a list of `2^max_depth` (purported) RLP encoded block headers.
380    /// Decomposes each header into it's fields.
381    /// `headers[0]` is the earliest block
382    ///
383    /// - If `num_blocks_minus_one = (num_blocks_minus_one, indicator)` is not None, then the circuit checks that the first `num_blocks := num_blocks_minus_one + 1` block headers form a chain: meaning that the parent hash of block i + 1 equals the hash of block i.
384    /// - `indicator` is a vector with index `i` equal to `i == num_blocks - 1 ? 1 : 0`.
385    /// - Otherwise if `num_blocks` is None, the circuit checks that all `headers` form a hash chain.
386    ///
387    /// Assumes that `0 <= num_blocks_minus_one < 2^max_depth`.
388    ///
389    /// This is the finalization step that constrains RLC concatenations. In this step the hash chain is actually constrained.
390    /// This should be called after `decompose_block_header_chain_phase0`.
391    /// This MUST be done in `SecondPhase`.
392    pub fn decompose_block_header_chain_phase1(
393        &self,
394        builder: &mut RlcCircuitBuilder<F>,
395        witnesses: Vec<EthBlockHeaderWitness<F>>,
396        num_blocks_minus_one: Option<(AssignedValue<F>, Vec<AssignedValue<F>>)>,
397    ) -> Vec<EthBlockHeaderTrace<F>> {
398        assert!(!witnesses.is_empty());
399        let traces = self.decompose_block_headers_phase1(builder, witnesses);
400        let (ctx_gate, ctx_rlc) = builder.rlc_ctx_pair();
401        let thirty_two = F::from(32);
402        // record for each idx whether hash of headers[idx] is in headers[idx + 1]
403        if let Some((num_blocks_minus_one, indicator)) = num_blocks_minus_one {
404            let mut hash_checks = Vec::with_capacity(traces.len() - 1);
405            for idx in 0..traces.len() - 1 {
406                let block_hash_bytes = traces[idx].block_hash.output_bytes.as_ref().iter().copied();
407                let block_hash = self.rlc().compute_rlc_fixed_len(ctx_rlc, block_hash_bytes);
408                let hash_check = self.gate().is_equal(
409                    ctx_gate,
410                    block_hash.rlc_val,
411                    traces[idx + 1].parent_hash.field_trace.rlc_val,
412                );
413                hash_checks.push(hash_check);
414                self.gate().assert_is_const(
415                    ctx_gate,
416                    &traces[idx + 1].parent_hash.field_trace.len,
417                    &thirty_two,
418                );
419            }
420            let hash_check_sums =
421                self.gate().partial_sums(ctx_gate, hash_checks.iter().copied()).collect_vec();
422            let hash_check_sum = self.gate().select_by_indicator(
423                ctx_gate,
424                once(Constant(F::ZERO)).chain(hash_check_sums.into_iter().map(Existing)),
425                indicator,
426            );
427            ctx_gate.constrain_equal(&hash_check_sum, &num_blocks_minus_one);
428        } else {
429            for idx in 0..traces.len() - 1 {
430                let block_hash_bytes = traces[idx].block_hash.output_bytes.as_ref().iter().copied();
431                let block_hash = self.rlc().compute_rlc_fixed_len(ctx_rlc, block_hash_bytes);
432                ctx_gate.constrain_equal(
433                    &block_hash.rlc_val,
434                    &traces[idx + 1].parent_hash.field_trace.rlc_val,
435                );
436                self.gate().assert_is_const(
437                    ctx_gate,
438                    &traces[idx + 1].parent_hash.field_trace.len,
439                    &thirty_two,
440                );
441            }
442        }
443
444        traces
445    }
446}
447
448/// Given a block header chain `chain` of fixed length `2^max_depth`, returns
449/// ```
450/// (prev_block_hash, end_block_hash, start_block_number || end_block_number)
451/// ```
452/// where
453/// ```
454/// prev_block_hash = chain[0].parent_hash,
455/// end_block_hash = chain[num_blocks_minus_one].block_hash,
456/// start_block_number = chain[0].number,
457/// end_block_number = chain[num_blocks_minus_one].number
458/// ```
459/// The hashes are H256 that are represented as two u128
460/// (we need them in 128 bits to fit in Bn254 scalar field).
461///
462/// The numbers are left padded by zeros to be exactly 4 bytes (u32); the two padded numbers are concatenated together to a u64.
463///
464/// `indicator` is the indicator for `num_blocks_minus_one`, where `indicator[i] = (i == end_block_number - start_block_number ? 1 : 0)`.
465///
466/// This function should be called in `FirstPhase`.
467pub fn get_boundary_block_data<F: Field>(
468    ctx: &mut Context<F>, // ctx_gate in FirstPhase
469    gate: &impl GateInstructions<F>,
470    chain: &[EthBlockHeaderWitness<F>],
471    indicator: &[AssignedValue<F>],
472) -> ([AssignedValue<F>; 2], [AssignedValue<F>; 2], AssignedValue<F>) {
473    let parent_hash_bytes = SafeTypeChip::unsafe_to_fix_len_bytes_vec(
474        chain[0].get_parent_hash().field_cells.clone(),
475        32,
476    );
477    let prev_block_hash: [_; 2] =
478        bytes_be_to_u128(ctx, gate, parent_hash_bytes.bytes()).try_into().unwrap();
479    let end_block_hash: [_; 2] = [0, 1].map(|idx| {
480        gate.select_by_indicator(
481            ctx,
482            chain.iter().map(|header| header.block_hash.hi_lo()[idx]),
483            indicator.iter().copied(),
484        )
485    });
486
487    // start_block_number || end_block_number
488    let block_numbers = {
489        debug_assert_eq!(chain[0].get_number().max_field_len, BLOCK_NUMBER_MAX_BYTES);
490        let start_block_number_bytes = chain[0].get_number_fixed(ctx, gate);
491        let end_block_number_bytes = {
492            // TODO: is there a way to do this without so many selects
493            let bytes: [_; BLOCK_NUMBER_MAX_BYTES] = core::array::from_fn(|i| i).map(|idx| {
494                gate.select_by_indicator(
495                    ctx,
496                    chain.iter().map(|header| header.get_number().field_cells[idx]),
497                    indicator.iter().copied(),
498                )
499            });
500            let len = gate.select_by_indicator(
501                ctx,
502                chain.iter().map(|header| header.get_number().field_len),
503                indicator.iter().copied(),
504            );
505            let var_bytes = SafeTypeChip::unsafe_to_var_len_bytes(bytes, len);
506            var_bytes.left_pad_to_fixed(ctx, gate)
507        };
508        let block_numbers_bytes =
509            [start_block_number_bytes.into_bytes(), end_block_number_bytes.into_bytes()].concat();
510        let [block_numbers]: [_; 1] =
511            bytes_be_to_u128(ctx, gate, &block_numbers_bytes).try_into().unwrap();
512        block_numbers
513    };
514
515    (prev_block_hash, end_block_hash, block_numbers)
516}
517
518pub fn get_block_header_rlp_max_lens(chain: Chain) -> (usize, [usize; NUM_BLOCK_HEADER_FIELDS]) {
519    get_block_header_rlp_max_lens_from_chain_id(chain as u64)
520}
521
522pub fn get_block_header_rlp_max_lens_from_extra(
523    max_extra_data_bytes: usize,
524) -> (usize, [usize; NUM_BLOCK_HEADER_FIELDS]) {
525    let mut field_lens = [0usize; NUM_BLOCK_HEADER_FIELDS];
526    for (a, b) in field_lens.iter_mut().zip_eq(MAINNET_HEADER_FIELDS_MAX_BYTES.iter()) {
527        *a = *b;
528    }
529    field_lens[EXTRA_DATA_INDEX] = max_extra_data_bytes;
530    let mut list_payload_len = 0;
531    for &field_len in &field_lens {
532        list_payload_len += max_rlp_encoding_len(field_len);
533    }
534    let rlp_len = max_rlp_encoding_len(list_payload_len);
535    (rlp_len, field_lens)
536}
537
538pub fn get_block_header_extra_bytes(chain: Chain) -> usize {
539    get_block_header_extra_bytes_from_chain_id(chain as u64)
540}
541
542pub fn get_block_header_rlp_max_lens_from_chain_id(
543    chain_id: u64,
544) -> (usize, [usize; NUM_BLOCK_HEADER_FIELDS]) {
545    let max_extra_data_bytes = get_block_header_extra_bytes_from_chain_id(chain_id);
546    get_block_header_rlp_max_lens_from_extra(max_extra_data_bytes)
547}
548
549pub fn get_block_header_extra_bytes_from_chain_id(chain_id: u64) -> usize {
550    match chain_id {
551        5 => GOERLI_EXTRA_DATA_MAX_BYTES,
552        _ => MAINNET_EXTRA_DATA_MAX_BYTES, // for now everything besides Goerli assumed to be mainnet equivalent
553    }
554}
555
556/// RLP of block number 0 on mainnet
557pub const GENESIS_BLOCK_RLP: &[u8] = &[
558    249, 2, 20, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
559    0, 0, 0, 0, 0, 0, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212,
560    26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 148, 0, 0, 0, 0,
561    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 215, 248, 151, 79, 181, 172, 120, 217,
562    172, 9, 155, 154, 213, 1, 139, 237, 194, 206, 10, 114, 218, 209, 130, 122, 23, 9, 218, 48, 88,
563    15, 5, 68, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91,
564    72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 86, 232, 31, 23, 27,
565    204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1,
566    98, 47, 181, 227, 99, 180, 33, 185, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
567    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
568    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
569    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
570    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
571    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
572    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
573    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
574    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 4, 0, 0, 0, 0, 128, 130, 19, 136, 128, 128, 160,
575    17, 187, 232, 219, 78, 52, 123, 78, 140, 147, 124, 28, 131, 112, 228, 181, 237, 51, 173, 179,
576    219, 105, 203, 219, 122, 56, 225, 229, 11, 27, 130, 250, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
577    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 0, 0, 0, 0, 0, 0, 0, 66,
578];