use alloy_primitives::{B256, BlockNumber};
use reth_provider::{BlockNumReader, ProviderError};
use std::cmp::Ordering;
/// Errors that can occur in Hl consensus
#[derive(Debug, thiserror::Error)]
pub enum HlConsensusErr {
/// Error from the provider
#[error(transparent)]
Provider(#[from] ProviderError),
/// Head block hash not found
#[error("Head block hash not found")]
HeadHashNotFound,
}
/// Hl consensus implementation
pub struct HlConsensus
{
/// The provider for reading block information
pub provider: P,
}
impl
HlConsensus
where
P: BlockNumReader + Clone,
{
/// Determines the head block hash according to Hl consensus rules:
/// 1. Follow the highest block number
/// 2. For same height blocks, pick the one with lower hash
pub(crate) fn canonical_head(
&self,
hash: B256,
number: BlockNumber,
) -> Result<(B256, B256), HlConsensusErr> {
let current_head = self.provider.best_block_number()?;
let current_hash =
self.provider.block_hash(current_head)?.ok_or(HlConsensusErr::HeadHashNotFound)?;
match number.cmp(¤t_head) {
Ordering::Greater => Ok((hash, current_hash)),
Ordering::Equal => Ok((hash.min(current_hash), current_hash)),
Ordering::Less => Ok((current_hash, current_hash)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::hex;
use reth_chainspec::ChainInfo;
use reth_provider::BlockHashReader;
use std::collections::HashMap;
#[derive(Clone)]
struct MockProvider {
blocks: HashMap,
head_number: BlockNumber,
head_hash: B256,
}
impl MockProvider {
fn new(head_number: BlockNumber, head_hash: B256) -> Self {
let mut blocks = HashMap::new();
blocks.insert(head_number, head_hash);
Self { blocks, head_number, head_hash }
}
}
impl BlockHashReader for MockProvider {
fn block_hash(&self, number: BlockNumber) -> Result