1use crate::importing_blocks::{ImportingBlockHandle, ImportingBlocks, ParentBlockImportStatus};
2use crate::{BlockImport, BlockImportError};
3use ab_client_api::{
4 BeaconChainInfoWrite, BlockDetails, BlockOrigin, ChainInfo, PersistSuperSegmentHeadersError,
5};
6use ab_client_block_verification::{BlockVerification, BlockVerificationError};
7use ab_client_consensus_common::BlockImportingNotification;
8use ab_client_consensus_common::consensus_parameters::{
9 DeriveConsensusParametersChainInfo, DeriveConsensusParametersConsensusInfo,
10 ShardMembershipEntropySourceChainInfo,
11};
12use ab_client_consensus_common::state::GlobalState;
13use ab_core_primitives::block::header::owned::OwnedBeaconChainHeader;
14use ab_core_primitives::block::owned::OwnedBeaconChainBlock;
15use ab_core_primitives::block::{BlockNumber, BlockRoot};
16use ab_core_primitives::hashes::Blake3Hash;
17use ab_core_primitives::pot::PotOutput;
18use ab_core_primitives::segments::SuperSegment;
19use ab_proof_of_space::Table;
20use async_lock::Mutex as AsyncMutex;
21use futures::channel::mpsc;
22use futures::prelude::*;
23use send_future::SendFuture;
24use std::marker::PhantomData;
25use tracing::{info, warn};
26use triomphe::Arc;
27
28#[derive(Debug, thiserror::Error)]
30pub enum BeaconChainBlockImportError {
31 #[error("Block verification error: {error}")]
33 VerificationError {
34 #[from]
36 error: BlockVerificationError,
37 },
38 #[error("Failed to persist a new super segment header: {error}")]
40 PersistSuperSegmentHeaders {
41 #[from]
43 error: PersistSuperSegmentHeadersError,
44 },
45}
46
47impl From<BeaconChainBlockImportError> for BlockImportError {
48 #[inline(always)]
49 fn from(error: BeaconChainBlockImportError) -> Self {
50 Self::Custom {
51 error: error.into(),
52 }
53 }
54}
55
56#[derive(Debug)]
59struct VerificationChainInfo<'a, CI> {
60 chain_info: &'a CI,
61 importing_blocks: &'a ImportingBlocks<OwnedBeaconChainHeader>,
62}
63
64impl<CI> DeriveConsensusParametersChainInfo for VerificationChainInfo<'_, CI>
65where
66 CI: ChainInfo<OwnedBeaconChainBlock>,
67{
68 fn ancestor_header_consensus_info(
69 &self,
70 ancestor_block_number: BlockNumber,
71 descendant_block_root: &BlockRoot,
72 ) -> Option<DeriveConsensusParametersConsensusInfo> {
73 if let Some(consensus_info) = self
74 .chain_info
75 .ancestor_header_consensus_info(ancestor_block_number, descendant_block_root)
76 {
77 return Some(consensus_info);
78 }
79
80 let mut current_block_root = *descendant_block_root;
81 loop {
82 let Some(importing_entry) = self.importing_blocks.get(¤t_block_root) else {
83 break;
84 };
85 let header = importing_entry.header().header();
86
87 if header.prefix.number == ancestor_block_number {
88 return Some(DeriveConsensusParametersConsensusInfo::from_consensus_info(
89 header.consensus_info,
90 ));
91 }
92
93 current_block_root = *header.root();
94 }
95
96 self.chain_info
99 .ancestor_header_consensus_info(ancestor_block_number, descendant_block_root)
100 }
101}
102
103impl<CI> ShardMembershipEntropySourceChainInfo for VerificationChainInfo<'_, CI>
104where
105 CI: ChainInfo<OwnedBeaconChainBlock>,
106{
107 fn ancestor_header_proof_of_time(
108 &self,
109 ancestor_block_number: BlockNumber,
110 descendant_block_root: &BlockRoot,
111 ) -> Option<PotOutput> {
112 if let Some(header) = self
113 .chain_info
114 .ancestor_header(ancestor_block_number, descendant_block_root)
115 {
116 return Some(header.header().consensus_info.proof_of_time);
117 }
118
119 let mut current_block_root = *descendant_block_root;
120 loop {
121 let Some(importing_entry) = self.importing_blocks.get(¤t_block_root) else {
122 break;
123 };
124 let header = importing_entry.header();
125
126 if header.header().prefix.number == ancestor_block_number {
127 return Some(header.header().consensus_info.proof_of_time);
128 }
129
130 current_block_root = *header.header().root();
131 }
132
133 self.chain_info
136 .ancestor_header_proof_of_time(ancestor_block_number, descendant_block_root)
137 }
138}
139
140#[derive(Debug)]
141pub struct BeaconChainBlockImport<PosTable, CI, BV> {
142 chain_info: CI,
143 block_verification: BV,
144 importing_blocks: ImportingBlocks<OwnedBeaconChainHeader>,
145 block_importing_notification_sender: mpsc::Sender<BlockImportingNotification>,
146 super_segments_sender: AsyncMutex<mpsc::Sender<SuperSegment>>,
147 block_import_notification_sender: mpsc::Sender<OwnedBeaconChainBlock>,
148 _pos_table: PhantomData<PosTable>,
149}
150
151impl<PosTable, CI, BV> BlockImport<OwnedBeaconChainBlock>
152 for BeaconChainBlockImport<PosTable, CI, BV>
153where
154 PosTable: Table,
155 CI: BeaconChainInfoWrite,
156 BV: BlockVerification<OwnedBeaconChainBlock, Option<SuperSegment>>,
157{
158 fn import(
159 &self,
160 block: OwnedBeaconChainBlock,
161 origin: BlockOrigin,
162 ) -> Result<impl Future<Output = Result<(), BlockImportError>> + Send, BlockImportError> {
163 let parent_root = &block.header.header().prefix.parent_root;
164
165 let (parent_header, parent_block_mmr, parent_block_import_status) =
166 if let Some((parent_header, parent_block_details)) =
167 self.chain_info.header_with_details(parent_root)
168 {
169 (
170 parent_header,
171 parent_block_details.mmr_with_block,
172 ParentBlockImportStatus::Imported {
173 system_contract_states: parent_block_details.system_contract_states,
174 },
175 )
176 } else if let Some(importing_entry) = self.importing_blocks.get(parent_root) {
177 (
178 importing_entry.header().clone(),
179 Arc::clone(importing_entry.mmr()),
180 ParentBlockImportStatus::Importing {
181 entry: importing_entry,
182 },
183 )
184 } else {
185 return Err(BlockImportError::UnknownParentBlock {
186 block_root: *parent_root,
187 });
188 };
189
190 let parent_block_mmr_root = Blake3Hash::from(
191 parent_block_mmr
192 .root()
193 .ok_or(BlockImportError::ParentBlockMmrInvalid)?,
194 );
195 let mut block_mmr = *parent_block_mmr;
196
197 if !block_mmr.add_leaf(&block.header.header().root()) {
198 return Err(BlockImportError::CantExtendMmr);
199 }
200
201 let importing_handle = self
202 .importing_blocks
203 .insert(block.header.clone(), Arc::new(block_mmr))
204 .ok_or(BlockImportError::AlreadyImporting)?;
205
206 if self
207 .chain_info
208 .header(&block.header.header().root())
209 .is_some()
210 {
211 return Err(BlockImportError::AlreadyImported);
212 }
213
214 Ok(self.import(
215 parent_header,
216 parent_block_mmr_root,
217 block,
218 origin,
219 importing_handle,
220 parent_block_import_status,
221 ))
222 }
223}
224
225impl<PosTable, CI, BV> BeaconChainBlockImport<PosTable, CI, BV>
226where
227 PosTable: Table,
228 CI: BeaconChainInfoWrite,
229 BV: BlockVerification<OwnedBeaconChainBlock, Option<SuperSegment>>,
230{
231 #[inline(always)]
233 pub fn new(
234 chain_info: CI,
235 block_verification: BV,
236 block_importing_notification_sender: mpsc::Sender<BlockImportingNotification>,
237 super_segments_sender: mpsc::Sender<SuperSegment>,
238 block_import_notification_sender: mpsc::Sender<OwnedBeaconChainBlock>,
239 ) -> Self {
240 Self {
241 chain_info,
242 block_verification,
243 importing_blocks: ImportingBlocks::new(),
244 block_importing_notification_sender,
245 super_segments_sender: AsyncMutex::new(super_segments_sender),
246 block_import_notification_sender,
247 _pos_table: PhantomData,
248 }
249 }
250
251 async fn import(
252 &self,
253 parent_header: OwnedBeaconChainHeader,
254 parent_block_mmr_root: Blake3Hash,
255 block: OwnedBeaconChainBlock,
256 origin: BlockOrigin,
257 importing_handle: ImportingBlockHandle<OwnedBeaconChainHeader>,
258 parent_block_import_status: ParentBlockImportStatus<OwnedBeaconChainHeader>,
259 ) -> Result<(), BlockImportError> {
260 let parent_header = parent_header.header();
261 let header = block.header.header();
262 let body = block.body.body();
263
264 #[expect(
265 clippy::rest_pattern_accessible_field,
266 reason = "Do not care about fields"
267 )]
268 let log_block_import = match origin {
269 BlockOrigin::LocalBlockBuilder { .. } => true,
270 BlockOrigin::Sync => false,
271 BlockOrigin::Broadcast => true,
272 };
273
274 self.block_verification
277 .verify_concurrent(
278 parent_header,
279 &parent_block_mmr_root,
280 header,
281 body,
282 &origin,
283 &VerificationChainInfo {
284 chain_info: &self.chain_info,
285 importing_blocks: &self.importing_blocks,
286 },
287 )
288 .send()
289 .await
290 .map_err(BeaconChainBlockImportError::from)?;
291
292 let Some(system_contract_states) = parent_block_import_status.wait().await else {
293 return Err(BlockImportError::ParentBlockImportFailed);
294 };
295
296 let maybe_super_segment = self
299 .block_verification
300 .verify_sequential(parent_header, &parent_block_mmr_root, header, body, &origin)
301 .send()
302 .await
303 .map_err(BeaconChainBlockImportError::from)?;
304
305 let global_state = GlobalState::new(&system_contract_states);
306
307 let state_root = global_state.root();
310
311 if header.result.state_root != state_root {
312 return Err(BlockImportError::InvalidStateRoot {
313 expected: state_root,
314 actual: header.result.state_root,
315 });
316 }
317
318 let system_contract_states = global_state.to_system_contract_states();
319
320 let (acknowledgement_sender, mut acknowledgement_receiver) = mpsc::channel(0);
321 if let Err(error) = self
322 .block_importing_notification_sender
323 .clone()
324 .send(BlockImportingNotification {
325 block_number: header.prefix.number,
326 acknowledgement_sender,
327 })
328 .await
329 {
330 warn!(%error, "Failed to send block importing notification");
331 }
332
333 while acknowledgement_receiver.next().await.is_some() {
334 }
336
337 let number = header.prefix.number;
338 let root = *header.root();
339
340 if let Some(super_segment) = maybe_super_segment {
341 let mut super_segment_sender = self.super_segments_sender.lock().await;
342 if self
343 .chain_info
344 .persist_super_segment_header(super_segment.header)
345 .await
346 .map_err(
347 |error| BeaconChainBlockImportError::PersistSuperSegmentHeaders { error },
348 )?
349 && let Err(error) = super_segment_sender.send(super_segment).await
350 {
351 warn!(%error, "Failed to send a super segment notification");
352 }
353 }
354
355 self.chain_info
356 .persist_block(
357 block.clone(),
358 BlockDetails {
359 mmr_with_block: Arc::clone(importing_handle.mmr()),
360 system_contract_states: Arc::clone(&system_contract_states),
361 },
362 )
363 .await?;
364
365 importing_handle.set_success(system_contract_states);
366
367 if let Err(error) = self
368 .block_import_notification_sender
369 .clone()
370 .send(block)
371 .await
372 {
373 warn!(
374 %error,
375 block_number = %number,
376 block_root = %root,
377 "Failed to send block import notification"
378 );
379 }
380
381 if log_block_import {
382 info!(
383 %number,
384 %root,
385 "🏆 Imported block",
386 );
387 }
388
389 Ok(())
390 }
391}