ab_client_block_import/
beacon_chain.rs1use 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#[derive(Debug, thiserror::Error)]
17pub enum BeaconChainBlockImportError {
18 #[error("Block verification error: {error}")]
20 VerificationError {
21 #[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 #[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 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 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 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}