ab_client_block_import/
beacon_chain.rs

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