diff --git a/crates/header-accumulator/src/epoch.rs b/crates/header-accumulator/src/epoch.rs index 3a50efbb..89543274 100644 --- a/crates/header-accumulator/src/epoch.rs +++ b/crates/header-accumulator/src/epoch.rs @@ -1,7 +1,7 @@ use std::array::IntoIter; use alloy_primitives::map::HashSet; -use ethportal_api::types::execution::accumulator::HeaderRecord; +use ethportal_api::types::execution::accumulator::{EpochAccumulator, HeaderRecord}; use crate::{errors::EraValidateError, types::ExtHeaderRecord}; @@ -30,6 +30,7 @@ pub const MERGE_BLOCK: u64 = 15537394; /// 0 must start from block 0 to block 8191. /// /// All blocks must be at the same epoch +#[derive(Clone)] pub struct Epoch { number: usize, data: Box<[HeaderRecord; MAX_EPOCH_SIZE]>, @@ -81,6 +82,13 @@ impl TryFrom> for Epoch { } } +impl From for EpochAccumulator { + fn from(value: Epoch) -> Self { + let vec: Vec = value.data.into_iter().collect(); + EpochAccumulator::from(vec) + } +} + impl Epoch { pub fn number(&self) -> usize { self.number diff --git a/crates/header-accumulator/src/errors.rs b/crates/header-accumulator/src/errors.rs index 1c4605ec..1ff5e0fd 100644 --- a/crates/header-accumulator/src/errors.rs +++ b/crates/header-accumulator/src/errors.rs @@ -9,6 +9,19 @@ pub enum EraValidateError { #[error("Era accumulator mismatch")] EraAccumulatorMismatch, + #[error("Block epoch {block_epoch} (block number {block_number}) could not be proven with provided epoch {epoch_number}.")] + EpochNotMatchForHeader { + epoch_number: usize, + block_number: u64, + block_epoch: usize, + }, + + #[error("Expected epoch {block_epoch} was not found in the provided epoch list. Epochs provided: {epoch_list:?}.")] + EpochNotFoundInProvidedList { + block_epoch: usize, + epoch_list: Vec, + }, + #[error("Error generating inclusion proof")] ProofGenerationFailure, #[error("Error validating inclusion proof")] diff --git a/crates/header-accumulator/src/inclusion_proof.rs b/crates/header-accumulator/src/inclusion_proof.rs index 0c8100e6..1d27e930 100644 --- a/crates/header-accumulator/src/inclusion_proof.rs +++ b/crates/header-accumulator/src/inclusion_proof.rs @@ -1,4 +1,6 @@ -use crate::{epoch::MAX_EPOCH_SIZE, errors::EraValidateError, types::ExtHeaderRecord}; +use std::ops::Range; + +use crate::{epoch::MAX_EPOCH_SIZE, errors::EraValidateError, types::ExtHeaderRecord, Epoch}; use alloy_primitives::FixedBytes; use ethportal_api::{ @@ -15,6 +17,8 @@ use trin_validation::{ historical_roots_acc::HistoricalRootsAccumulator, }; +type InclusionProof = [Hash256; 15]; + /// generates an inclusion proof over headers, given blocks between `start_block` and `end_block` /// /// # Arguments @@ -23,50 +27,49 @@ use trin_validation::{ /// to function without error /// * `start_block` - The starting point of blocks that are to be included in the proofs. This interval is inclusive. /// * `end_epoch` - The ending point of blocks that are to be included in the proofs. This interval is inclusive. -pub fn generate_inclusion_proof( - mut ext_headers: Vec, - start_block: u64, - end_block: u64, -) -> Result, EraValidateError> { - if start_block > end_block { - return Err(EraValidateError::InvalidBlockRange(start_block, end_block)); - } - - // Compute the epoch accumulator for the blocks - // The epochs start on a multiple of 8192 blocks, so we need to round down to the nearest 8192 - let epoch_start = start_block / MAX_EPOCH_SIZE as u64; - - // The epochs end on a multiple of 8192 blocks, so we need to round up to the nearest 8192 - let epoch_end = ((end_block as f32) / MAX_EPOCH_SIZE as f32).ceil() as u64; - +pub fn generate_inclusion_proofs( + epochs: Vec, + headers_to_prove: Vec
, +) -> Result, EraValidateError> { // We need to load blocks from an entire epoch to be able to generate inclusion proofs // First compute epoch accumulators and the Merkle tree for all the epochs of interest - let mut epoch_accumulators = Vec::new(); - let mut inclusion_proof_vec: Vec<[FixedBytes<32>; 15]> = Vec::new(); - let mut headers: Vec
= Vec::new(); + // let mut epoch_accumulators = Vec::new(); + // let mut headers: Vec
= Vec::new(); + let mut inclusion_proof_vec: Vec = Vec::new(); + let epoch_list: Vec<_> = epochs.iter().map(|epoch| epoch.number()).collect(); - for _ in epoch_start..epoch_end { - let epoch_headers: Vec = ext_headers.drain(0..MAX_EPOCH_SIZE).collect(); - let header_records: Vec = epoch_headers.iter().map(Into::into).collect(); - let tmp_headers: Vec
= epoch_headers - .into_iter() - .map(ExtHeaderRecord::try_into) - .collect::>()?; - headers.extend(tmp_headers); - epoch_accumulators.push(EpochAccumulator::from(header_records)); + for header in headers_to_prove { + let block_epoch = (header.number / MAX_EPOCH_SIZE as u64) as usize; + let epoch = epochs + .iter() + .find(|epoch| epoch.number() == block_epoch) + .ok_or(EraValidateError::EpochNotFoundInProvidedList { + block_epoch, + epoch_list: epoch_list.clone(), + })?; + inclusion_proof_vec.push(generate_inclusion_proof(header, epoch.clone())?); } - for block_idx in start_block..=end_block { - let epoch = block_idx / MAX_EPOCH_SIZE as u64; - let epoch_acc = epoch_accumulators[epoch as usize].clone(); - let header = headers[block_idx as usize].clone(); - inclusion_proof_vec.push( - PreMergeAccumulator::construct_proof(&header, &epoch_acc) - .map_err(|_| EraValidateError::ProofGenerationFailure)?, - ); + Ok(inclusion_proof_vec) +} + +pub fn generate_inclusion_proof( + header: Header, + epoch: Epoch, +) -> Result { + let block_epoch = (header.number / MAX_EPOCH_SIZE as u64) as usize; + if block_epoch != epoch.number() { + return Err(EraValidateError::EpochNotMatchForHeader { + epoch_number: epoch.number(), + block_number: header.number, + block_epoch, + }); } - Ok(inclusion_proof_vec) + let epoch_accumulator = EpochAccumulator::from(epoch); + + PreMergeAccumulator::construct_proof(&header, &epoch_accumulator) + .map_err(|_| EraValidateError::ProofGenerationFailure) } /// verifies an inclusion proof generate by [`generate_inclusion_proof`] @@ -75,32 +78,34 @@ pub fn generate_inclusion_proof( /// * `pre_merge_accumulator_file`- An instance of [`PreMergeAccumulator`] which is a file that maintains a record of historical epoch /// it is used to verify canonical-ness of headers accumulated from the `blocks` /// * `inclusion_proof` - The inclusion proof generated from [`generate_inclusion_proof`]. -pub fn verify_inclusion_proof( - blocks: Vec, +pub fn verify_inclusion_proofs( pre_merge_accumulator_file: Option, - inclusion_proof: Vec<[Hash256; 15]>, + block_proofs: Vec<(Block, InclusionProof)>, ) -> Result<(), EraValidateError> { let pre_merge_acc = pre_merge_accumulator_file.unwrap_or_default(); - let header_validator = HeaderValidator { pre_merge_acc, historical_roots_acc: HistoricalRootsAccumulator::default(), }; - for (block_idx, _) in blocks.iter().enumerate() { - let bhp = BlockHeaderProof::PreMergeAccumulatorProof(PreMergeAccumulatorProof { - proof: inclusion_proof[block_idx], - }); - - let hwp = HeaderWithProof { - header: Header::try_from(&blocks[block_idx])?, - proof: bhp, - }; - - header_validator - .validate_header_with_proof(&hwp) - .map_err(|_| EraValidateError::ProofValidationFailure)?; + for (block, proof) in block_proofs { + let header = Header::try_from(&block)?; + verify_inclusion_proof(&header_validator, header, proof)?; } Ok(()) } + +pub fn verify_inclusion_proof( + header_validator: &HeaderValidator, + header: Header, + proof: InclusionProof, +) -> Result<(), EraValidateError> { + let proof = BlockHeaderProof::PreMergeAccumulatorProof(PreMergeAccumulatorProof { proof }); + + let hwp = HeaderWithProof { header, proof }; + + header_validator + .validate_header_with_proof(&hwp) + .map_err(|_| EraValidateError::ProofValidationFailure) +} diff --git a/crates/header-accumulator/tests/inclusion_proof.rs b/crates/header-accumulator/tests/inclusion_proof.rs index 0074159d..9fa173e8 100644 --- a/crates/header-accumulator/tests/inclusion_proof.rs +++ b/crates/header-accumulator/tests/inclusion_proof.rs @@ -33,8 +33,8 @@ fn test_inclusion_proof() -> Result<(), EraValidateError> { let start_block = 301; let end_block = 402; - let inclusion_proof = - generate_inclusion_proof(headers, start_block, end_block).unwrap_or_else(|e| { + let inclusion_proof = generate_inclusion_proofs(headers, start_block, end_block) + .unwrap_or_else(|e| { println!("Error occurred: {}", e); // Handle the error, e.g., by exiting the program or returning a default value std::process::exit(1); // Exiting the program, for example