use crate::{
block::{error::SealedBlockRecoveryError, RecoveredBlock, SealedBlock},
Block,
};
use alloc::sync::Arc;
use core::ops::Deref;
#[derive(Debug, Clone)]
pub enum SealedOrRecoveredBlock<B: Block> {
Sealed(Arc<SealedBlock<B>>),
Recovered(Arc<RecoveredBlock<B>>),
}
impl<B: Block> SealedOrRecoveredBlock<B> {
#[inline]
pub fn sealed(block: SealedBlock<B>) -> Self {
Self::Sealed(Arc::new(block))
}
#[inline]
pub const fn sealed_arc(block: Arc<SealedBlock<B>>) -> Self {
Self::Sealed(block)
}
#[inline]
pub fn recovered(block: RecoveredBlock<B>) -> Self {
Self::Recovered(Arc::new(block))
}
#[inline]
pub const fn recovered_arc(block: Arc<RecoveredBlock<B>>) -> Self {
Self::Recovered(block)
}
#[inline]
pub fn sealed_block(&self) -> &SealedBlock<B> {
match self {
Self::Sealed(block) => block,
Self::Recovered(block) => block.sealed_block(),
}
}
#[inline]
pub fn recovered_block(&self) -> Option<&RecoveredBlock<B>> {
match self {
Self::Sealed(_) => None,
Self::Recovered(block) => Some(block),
}
}
pub fn into_sealed_block(self) -> SealedBlock<B> {
match self {
Self::Sealed(block) => Arc::unwrap_or_clone(block),
Self::Recovered(block) => match Arc::try_unwrap(block) {
Ok(block) => block.into_sealed_block(),
Err(block) => block.clone_sealed_block(),
},
}
}
pub fn into_recovered_block(self) -> Result<RecoveredBlock<B>, SealedBlockRecoveryError<B>> {
match self {
Self::Sealed(block) => Arc::unwrap_or_clone(block).try_recover(),
Self::Recovered(block) => Ok(Arc::unwrap_or_clone(block)),
}
}
}
impl<B: Block> PartialEq for SealedOrRecoveredBlock<B> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.sealed_block().eq(other.sealed_block())
}
}
impl<B: Block> Eq for SealedOrRecoveredBlock<B> {}
impl<B: Block> From<SealedBlock<B>> for SealedOrRecoveredBlock<B> {
#[inline]
fn from(block: SealedBlock<B>) -> Self {
Self::sealed(block)
}
}
impl<B: Block> From<Arc<SealedBlock<B>>> for SealedOrRecoveredBlock<B> {
#[inline]
fn from(block: Arc<SealedBlock<B>>) -> Self {
Self::sealed_arc(block)
}
}
impl<B: Block> From<RecoveredBlock<B>> for SealedOrRecoveredBlock<B> {
#[inline]
fn from(block: RecoveredBlock<B>) -> Self {
Self::recovered(block)
}
}
impl<B: Block> From<Arc<RecoveredBlock<B>>> for SealedOrRecoveredBlock<B> {
#[inline]
fn from(block: Arc<RecoveredBlock<B>>) -> Self {
Self::recovered_arc(block)
}
}
impl<B: Block> Deref for SealedOrRecoveredBlock<B> {
type Target = SealedBlock<B>;
#[inline]
fn deref(&self) -> &Self::Target {
self.sealed_block()
}
}
#[cfg(feature = "serde")]
impl<B> serde::Serialize for SealedOrRecoveredBlock<B>
where
B: Block,
SealedBlock<B>: serde::Serialize,
{
#[inline]
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.sealed_block().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, B> serde::Deserialize<'de> for SealedOrRecoveredBlock<B>
where
B: Block,
SealedBlock<B>: serde::Deserialize<'de>,
{
#[inline]
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
SealedBlock::<B>::deserialize(deserializer).map(Self::sealed)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
use alloy_consensus::{Block as AlloyBlock, Header, TxEnvelope};
type TestBlock = AlloyBlock<TxEnvelope, Header>;
fn sealed_block() -> SealedBlock<TestBlock> {
SealedBlock::seal_slow(TestBlock::default())
}
#[test]
fn sealed_variant_returns_sealed_block() {
let sealed = sealed_block();
let hash = sealed.hash();
let block = SealedOrRecoveredBlock::sealed(sealed);
assert_eq!(block.hash(), hash);
assert!(block.recovered_block().is_none());
}
#[test]
fn recovered_variant_returns_recovered_block() {
let recovered = sealed_block().with_senders(Vec::new());
let hash = recovered.hash();
let block = SealedOrRecoveredBlock::recovered(recovered);
assert_eq!(block.hash(), hash);
assert!(block.recovered_block().is_some());
assert_eq!(block.into_sealed_block().hash(), hash);
}
#[test]
fn sealed_and_recovered_variants_compare_by_sealed_block() {
let sealed = sealed_block();
let recovered = sealed.clone().with_senders(Vec::new());
assert_eq!(
SealedOrRecoveredBlock::sealed(sealed),
SealedOrRecoveredBlock::recovered(recovered)
);
}
}