use crate::SyncStartError;
use alloy_eips::{BlockId, BlockNumberOrTag};
use alloy_provider::{Network, Provider, RootProvider};
use alloy_transport::TransportResult;
use kona_genesis::RollupConfig;
use kona_protocol::L2BlockInfo;
use op_alloy_network::Optimism;
use std::fmt::Display;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct L2ForkchoiceState {
pub un_safe: L2BlockInfo,
pub safe: L2BlockInfo,
pub finalized: L2BlockInfo,
}
impl Display for L2ForkchoiceState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"FINALIZED: {} (#{}) | SAFE: {} (#{}) | UNSAFE: {} (#{})",
self.finalized.block_info.hash,
self.finalized.block_info.number,
self.safe.block_info.hash,
self.safe.block_info.number,
self.un_safe.block_info.hash,
self.un_safe.block_info.number,
)
}
}
impl L2ForkchoiceState {
pub async fn current(
cfg: &RollupConfig,
l2_provider: &RootProvider<Optimism>,
) -> Result<Self, SyncStartError> {
let finalized = {
let rpc_block = match get_block_compat(l2_provider, BlockNumberOrTag::Finalized.into())
.await
{
Ok(Some(block)) => block,
Ok(None) => l2_provider
.get_block(cfg.genesis.l2.number.into())
.full()
.await?
.ok_or(SyncStartError::BlockNotFound(cfg.genesis.l2.number.into()))?,
Err(e) => return Err(e.into()),
}
.into_consensus();
L2BlockInfo::from_block_and_genesis(&rpc_block, &cfg.genesis)?
};
let safe = match get_block_compat(l2_provider, BlockNumberOrTag::Safe.into()).await {
Ok(Some(block)) => {
L2BlockInfo::from_block_and_genesis(&block.into_consensus(), &cfg.genesis)?
}
Ok(None) => finalized,
Err(e) => return Err(e.into()),
};
let un_safe = {
let rpc_block =
get_block_compat(l2_provider, BlockNumberOrTag::Latest.into())
.await?
.ok_or(SyncStartError::BlockNotFound(BlockNumberOrTag::Latest.into()))?;
L2BlockInfo::from_block_and_genesis(&rpc_block.into_consensus(), &cfg.genesis)?
};
Ok(Self { un_safe, safe, finalized })
}
}
async fn get_block_compat<P: Provider<Optimism>>(
provider: &P,
block_id: BlockId,
) -> TransportResult<Option<<Optimism as Network>::BlockResponse>> {
match provider.get_block(block_id).full().await {
Err(e) => {
let err_str = e.to_string();
if err_str.contains("block not found") || err_str.contains("Unknown block") {
Ok(None)
} else {
Err(e)
}
}
r => r,
}
}