ab_client_block_import/
beacon_chain.rs

1use crate::importing_blocks::{ImportingBlockEntry, ImportingBlockHandle, ImportingBlocks};
2use crate::{BlockImport, BlockImportError};
3use ab_client_api::{BlockOrigin, ChainInfoWrite};
4use ab_client_block_verification::{BlockVerification, BlockVerificationError};
5use ab_core_primitives::block::header::owned::OwnedBeaconChainHeader;
6use ab_core_primitives::block::owned::OwnedBeaconChainBlock;
7use ab_core_primitives::hashes::Blake3Hash;
8use ab_proof_of_space::Table;
9use rclite::Arc;
10use send_future::SendFuture;
11use std::marker::PhantomData;
12
13/// Errors for [`BeaconChainBlockImport`]
14#[derive(Debug, thiserror::Error)]
15pub enum BeaconChainBlockImportError {
16    /// Block verification error
17    #[error("Block verification error: {error}")]
18    VerificationError {
19        /// Block verification error
20        #[from]
21        error: BlockVerificationError,
22    },
23}
24
25impl From<BeaconChainBlockImportError> for BlockImportError {
26    #[inline(always)]
27    fn from(error: BeaconChainBlockImportError) -> Self {
28        Self::Custom {
29            error: error.into(),
30        }
31    }
32}
33
34#[derive(Debug)]
35pub struct BeaconChainBlockImport<PosTable, CI, BV> {
36    chain_info: CI,
37    block_verification: BV,
38    importing_blocks: ImportingBlocks<OwnedBeaconChainHeader>,
39    _pos_table: PhantomData<PosTable>,
40}
41
42impl<PosTable, CI, BV> BlockImport<OwnedBeaconChainBlock>
43    for BeaconChainBlockImport<PosTable, CI, BV>
44where
45    PosTable: Table,
46    CI: ChainInfoWrite<OwnedBeaconChainBlock>,
47    BV: BlockVerification<OwnedBeaconChainBlock>,
48{
49    fn import(
50        &self,
51        block: OwnedBeaconChainBlock,
52        origin: BlockOrigin,
53    ) -> Result<impl Future<Output = Result<(), BlockImportError>> + Send, BlockImportError> {
54        let parent_root = &block.header.header().prefix.parent_root;
55
56        let (parent_header, parent_block_mmr, maybe_parent_importing_entry) =
57            if let Some(parent_header) = self.chain_info.header(parent_root) {
58                let parent_block_mmr = self
59                    .chain_info
60                    .mmr_with_block(parent_root)
61                    .ok_or(BlockImportError::ParentBlockMmrMissing)?;
62
63                (parent_header, parent_block_mmr, None)
64            } else if let Some(importing_entry) = self.importing_blocks.get(parent_root) {
65                (
66                    importing_entry.header().clone(),
67                    Arc::clone(importing_entry.mmr()),
68                    Some(importing_entry),
69                )
70            } else {
71                return Err(BlockImportError::UnknownParentBlock {
72                    block_root: *parent_root,
73                });
74            };
75
76        let parent_block_mmr_root = Blake3Hash::from(
77            parent_block_mmr
78                .root()
79                .ok_or(BlockImportError::ParentBlockMmrInvalid)?,
80        );
81        let mut block_mmr = *parent_block_mmr;
82
83        if !block_mmr.add_leaf(&block.header.header().root()) {
84            return Err(BlockImportError::CantExtendMmr);
85        }
86
87        let importing_handle = self
88            .importing_blocks
89            .insert(block.header.clone(), Arc::new(block_mmr))
90            .ok_or(BlockImportError::AlreadyImporting)?;
91
92        if self
93            .chain_info
94            .header(&block.header.header().root())
95            .is_some()
96        {
97            return Err(BlockImportError::AlreadyImported);
98        }
99
100        Ok(self.import(
101            parent_header,
102            parent_block_mmr_root,
103            block,
104            origin,
105            importing_handle,
106            maybe_parent_importing_entry,
107        ))
108    }
109}
110
111impl<PosTable, CI, BV> BeaconChainBlockImport<PosTable, CI, BV>
112where
113    PosTable: Table,
114    CI: ChainInfoWrite<OwnedBeaconChainBlock>,
115    BV: BlockVerification<OwnedBeaconChainBlock>,
116{
117    /// Create new instance
118    #[inline(always)]
119    pub fn new(chain_info: CI, block_verification: BV) -> Self {
120        Self {
121            chain_info,
122            block_verification,
123            importing_blocks: ImportingBlocks::new(),
124            _pos_table: PhantomData,
125        }
126    }
127
128    async fn import(
129        &self,
130        parent_header: OwnedBeaconChainHeader,
131        parent_block_mmr_root: Blake3Hash,
132        block: OwnedBeaconChainBlock,
133        origin: BlockOrigin,
134        importing_handle: ImportingBlockHandle<OwnedBeaconChainHeader>,
135        maybe_parent_importing_entry: Option<ImportingBlockEntry<OwnedBeaconChainHeader>>,
136    ) -> Result<(), BlockImportError> {
137        let parent_header = parent_header.header();
138        let header = block.header.header();
139        let body = block.body.body();
140
141        // TODO: `.send()` is a hack for compiler bug, see:
142        //  https://github.com/rust-lang/rust/issues/100013#issuecomment-2210995259
143        self.block_verification
144            .verify(parent_header, &parent_block_mmr_root, header, body, origin)
145            .send()
146            .await
147            .map_err(BeaconChainBlockImportError::from)?;
148
149        if maybe_parent_importing_entry
150            .as_ref()
151            .map(ImportingBlockEntry::has_failed)
152            .unwrap_or_default()
153        {
154            // Early exit to avoid extra work
155            return Err(BlockImportError::ParentBlockImportFailed);
156        }
157
158        // TODO: Execute block
159
160        if maybe_parent_importing_entry
161            .as_ref()
162            .map(ImportingBlockEntry::has_failed)
163            .unwrap_or_default()
164        {
165            // Early exit to avoid extra work
166            return Err(BlockImportError::ParentBlockImportFailed);
167        }
168
169        // TODO: Check state root
170
171        // Wait for parent block to be imported successfully
172        if let Some(parent_importing_entry) = maybe_parent_importing_entry
173            && !parent_importing_entry.wait_success().await
174        {
175            return Err(BlockImportError::ParentBlockImportFailed);
176        }
177
178        self.chain_info
179            .persist_block(block, Arc::clone(importing_handle.mmr()))
180            .await?;
181
182        importing_handle.set_success();
183
184        Ok(())
185    }
186}