1#[cfg(test)]
22mod tests;
23
24use ab_aligned_buffer::SharedAlignedBuffer;
25use ab_archiving::archiver::{Archiver, ArchiverInstantiationError, NewArchivedSegment};
26use ab_client_api::{ChainInfo, ChainInfoWrite, PersistSegmentHeadersError, ReadBlockError};
27use ab_client_consensus_common::{BlockImportingNotification, ConsensusConstants};
28use ab_core_primitives::block::body::owned::GenericOwnedBlockBody;
29use ab_core_primitives::block::header::GenericBlockHeader;
30use ab_core_primitives::block::header::owned::GenericOwnedBlockHeader;
31use ab_core_primitives::block::owned::GenericOwnedBlock;
32use ab_core_primitives::block::{BlockNumber, BlockRoot, GenericBlock};
33use ab_core_primitives::segments::{LocalSegmentIndex, RecordedHistorySegment, SegmentHeader};
34use ab_core_primitives::shard::RealShardKind;
35use ab_erasure_coding::ErasureCoding;
36use bytesize::ByteSize;
37use chacha20::ChaCha8Rng;
38use chacha20::rand_core::{Rng, SeedableRng};
39use futures::channel::mpsc;
40use futures::prelude::*;
41use std::time::Duration;
42use tracing::{debug, info, trace, warn};
43use triomphe::Arc;
44
45const ACKNOWLEDGEMENT_TIMEOUT: Duration = Duration::from_mins(2);
47
48#[derive(Debug)]
61pub struct ArchivedSegmentNotification {
62 pub archived_segment: Arc<NewArchivedSegment>,
64 pub acknowledgement_sender: mpsc::Sender<()>,
68}
69
70async fn find_last_archived_block<Block, CI>(
71 chain_info: &CI,
72 best_block_number_to_archive: BlockNumber,
73 best_block_root: &BlockRoot,
74) -> Option<(SegmentHeader, Block)>
75where
76 Block: GenericOwnedBlock,
77 CI: ChainInfo<Block>,
78{
79 let max_local_segment_index = chain_info.last_segment_header()?.index.as_inner();
80
81 for segment_header in (LocalSegmentIndex::ZERO..=max_local_segment_index)
82 .rev()
83 .filter_map(|segment_index| chain_info.get_segment_header(segment_index))
84 {
85 let last_archived_block_number = segment_header.last_archived_block.number();
86
87 if last_archived_block_number > best_block_number_to_archive {
88 continue;
92 }
93
94 let Some(last_archived_block_header) =
95 chain_info.ancestor_header(last_archived_block_number, best_block_root)
96 else {
97 continue;
100 };
101
102 let last_archived_block_root = &*last_archived_block_header.header().root();
103
104 let Ok(last_archived_block) = chain_info.block(last_archived_block_root).await else {
105 continue;
107 };
108
109 return Some((segment_header, last_archived_block));
110 }
111
112 None
113}
114
115pub fn encode_block<Block>(block: &Block) -> Vec<u8>
117where
118 Block: GenericOwnedBlock,
119{
120 let is_beacon_chain_genesis_block = Block::Block::SHARD_KIND == RealShardKind::BeaconChain
121 && block.header().header().prefix.number == BlockNumber::ZERO;
122 let header_buffer = block.header().buffer();
123 let body_buffer = block.body().buffer();
124
125 let mut encoded_block = Vec::with_capacity(if is_beacon_chain_genesis_block {
127 RecordedHistorySegment::SIZE
128 } else {
129 size_of::<u32>() * 2 + header_buffer.len() as usize + body_buffer.len() as usize
130 });
131
132 encoded_block.extend_from_slice(&header_buffer.len().to_le_bytes());
133 encoded_block.extend_from_slice(&body_buffer.len().to_le_bytes());
134 encoded_block.extend_from_slice(header_buffer);
135 encoded_block.extend_from_slice(body_buffer);
136
137 let encoded_block_length = encoded_block.len();
138 if is_beacon_chain_genesis_block && encoded_block_length < RecordedHistorySegment::SIZE {
139 encoded_block.resize(RecordedHistorySegment::SIZE, 0);
145 let mut rng = ChaCha8Rng::from_seed(*block.header().header().result.state_root);
146 rng.fill_bytes(&mut encoded_block[encoded_block_length..]);
147 }
148
149 encoded_block
150}
151
152pub fn decode_block<Block>(mut encoded_block: &[u8]) -> Option<Block>
154where
155 Block: GenericOwnedBlock,
156{
157 let header_length = {
158 let header_length = encoded_block.split_off(..size_of::<u32>())?;
159 u32::from_le_bytes([
160 header_length[0],
161 header_length[1],
162 header_length[2],
163 header_length[3],
164 ])
165 };
166 let body_length = {
167 let body_length = encoded_block.split_off(..size_of::<u32>())?;
168 u32::from_le_bytes([
169 body_length[0],
170 body_length[1],
171 body_length[2],
172 body_length[3],
173 ])
174 };
175
176 let header_buffer = encoded_block.split_off(..header_length as usize)?;
177 let body_buffer = encoded_block.split_off(..body_length as usize)?;
178
179 let header_buffer = SharedAlignedBuffer::from_bytes(header_buffer);
180 let body_buffer = SharedAlignedBuffer::from_bytes(body_buffer);
181
182 Block::from_buffers(header_buffer, body_buffer)
183}
184
185#[derive(Debug, thiserror::Error)]
187pub enum SegmentArchiverTaskError {
188 #[error("Archiver instantiation error: {error}")]
190 Instantiation {
191 #[from]
193 error: ArchiverInstantiationError,
194 },
195 #[error("Failed to persist a new segment header: {error}")]
197 PersistSegmentHeaders {
198 #[from]
200 error: PersistSegmentHeadersError,
201 },
202 #[error("Block {block_number} that must be archived is missing in the chain")]
204 BlockMissing {
205 block_number: BlockNumber,
207 },
208 #[error("Failed to read block {block_number} that must be archived: {error}")]
210 ReadBlock {
211 block_number: BlockNumber,
213 error: ReadBlockError,
215 },
216 #[error("Encoded block {block_number} is too large to archive: {size} bytes")]
218 BlockTooLarge {
219 block_number: BlockNumber,
221 size: usize,
223 },
224 #[error(
226 "Attempt to switch to a different fork beyond archiving depth: parent block root \
227 {parent_block_root}, best archived block root {best_archived_block_root}"
228 )]
229 ArchivingReorg {
230 parent_block_root: BlockRoot,
232 best_archived_block_root: BlockRoot,
234 },
235 #[error(
237 "Re-initialization left a gap before the block to archive (best archived block number \
238 {best_archived_block_number}, block number to archive {block_number_to_archive}, block \
239 about to be imported {importing_block_number})"
240 )]
241 BlockGap {
242 best_archived_block_number: BlockNumber,
244 block_number_to_archive: BlockNumber,
246 importing_block_number: BlockNumber,
248 },
249}
250
251struct InitializedArchiver {
252 archiver: Archiver,
253 best_archived_block: (BlockRoot, BlockNumber),
254}
255
256async fn initialize_archiver<Block, CI>(
257 chain_info: &CI,
258 block_confirmation_depth: BlockNumber,
259 erasure_coding: ErasureCoding,
260) -> Result<InitializedArchiver, SegmentArchiverTaskError>
261where
262 Block: GenericOwnedBlock,
263 CI: ChainInfoWrite<Block>,
264{
265 let best_block_header = chain_info.best_header();
266 let best_block_root = *best_block_header.header().root();
267 let best_block_number: BlockNumber = best_block_header.header().prefix.number;
268
269 let mut best_block_to_archive = best_block_number.saturating_sub(block_confirmation_depth);
270
271 if (best_block_to_archive..best_block_number).any(|block_number| {
272 chain_info
273 .ancestor_header(block_number, &best_block_root)
274 .is_none()
275 }) {
276 best_block_to_archive = best_block_number;
280 }
281
282 let maybe_last_archived_block =
283 find_last_archived_block(chain_info, best_block_to_archive, &best_block_root).await;
284
285 let have_last_segment_header = maybe_last_archived_block.is_some();
286 let mut best_archived_block = None::<(BlockRoot, BlockNumber)>;
287
288 let mut archiver =
289 if let Some((last_segment_header, last_archived_block)) = maybe_last_archived_block {
290 let last_archived_block_number = last_segment_header.last_archived_block.number;
292 info!(
293 %last_archived_block_number,
294 "Resuming archiver from last archived block",
295 );
296
297 let last_archived_block_header = last_archived_block.header().header();
298 best_archived_block.replace((
301 *last_archived_block_header.root(),
302 last_archived_block_header.prefix.number,
303 ));
304
305 let last_archived_block_encoded = encode_block(&last_archived_block);
306
307 Archiver::with_initial_state(
308 best_block_header.header().prefix.shard_index,
309 erasure_coding,
310 last_segment_header,
311 &last_archived_block_encoded,
312 Vec::new(),
313 )?
314 } else {
315 info!("Starting archiving from genesis");
316
317 Archiver::new(
318 best_block_header.header().prefix.shard_index,
319 erasure_coding,
320 )
321 };
322
323 {
326 let blocks_to_archive_from = archiver
327 .last_archived_block_number()
328 .map_or_default(|n| n + BlockNumber::ONE);
329 let blocks_to_archive_to = best_block_number
330 .checked_sub(block_confirmation_depth)
331 .filter(|&blocks_to_archive_to| blocks_to_archive_to >= blocks_to_archive_from)
332 .or({
333 if have_last_segment_header {
334 None
335 } else {
336 Some(BlockNumber::ZERO)
338 }
339 });
340
341 if let Some(blocks_to_archive_to) = blocks_to_archive_to {
342 info!(
343 "Archiving already produced blocks {}..={}",
344 blocks_to_archive_from, blocks_to_archive_to,
345 );
346
347 for block_number_to_archive in blocks_to_archive_from..=blocks_to_archive_to {
348 let header = chain_info
351 .ancestor_header(block_number_to_archive, &best_block_root)
352 .ok_or(SegmentArchiverTaskError::BlockMissing {
353 block_number: block_number_to_archive,
354 })?;
355
356 let block = chain_info
357 .block(&header.header().root())
358 .await
359 .map_err(|error| SegmentArchiverTaskError::ReadBlock {
360 block_number: block_number_to_archive,
361 error,
362 })?;
363
364 let encoded_block = encode_block(&block);
365
366 debug!(
367 "Encoded block {} has size of {}",
368 block_number_to_archive,
369 ByteSize::b(encoded_block.len() as u64).display().iec(),
370 );
371
372 let encoded_block_size = encoded_block.len();
373 let block_outcome = archiver.add_block(encoded_block, Vec::new()).ok_or(
374 SegmentArchiverTaskError::BlockTooLarge {
375 block_number: block_number_to_archive,
376 size: encoded_block_size,
377 },
378 )?;
379 let new_segment_headers: Vec<SegmentHeader> = block_outcome
380 .archived_segments
381 .iter()
382 .map(|archived_segment| archived_segment.segment_header)
383 .collect();
384
385 if !new_segment_headers.is_empty() {
386 chain_info
387 .persist_segment_headers(new_segment_headers)
388 .await?;
389 }
390
391 if block_number_to_archive == blocks_to_archive_to {
392 best_archived_block.replace((*header.header().root(), block_number_to_archive));
393 }
394 }
395 }
396 }
397
398 Ok(InitializedArchiver {
399 archiver,
400 best_archived_block: best_archived_block
401 .expect("Must always set if there is no logical error; qed"),
402 })
403}
404
405pub async fn create_segment_archiver_task<Block, CI>(
436 chain_info: CI,
437 mut block_importing_notification_receiver: mpsc::Receiver<BlockImportingNotification>,
438 mut archived_segment_notification_sender: mpsc::Sender<ArchivedSegmentNotification>,
439 consensus_constants: ConsensusConstants,
440 erasure_coding: ErasureCoding,
441) -> Result<
442 impl Future<Output = Result<(), SegmentArchiverTaskError>> + Send + 'static,
443 SegmentArchiverTaskError,
444>
445where
446 Block: GenericOwnedBlock,
447 CI: ChainInfoWrite<Block> + 'static,
448{
449 let maybe_archiver = if chain_info.last_segment_header().is_none() {
450 let initialize_archiver_fut = initialize_archiver(
451 &chain_info,
452 consensus_constants.block_confirmation_depth,
453 erasure_coding.clone(),
454 );
455 Some(initialize_archiver_fut.await?)
456 } else {
457 None
458 };
459
460 Ok(async move {
461 let archiver = if let Some(archiver) = maybe_archiver {
462 archiver
463 } else {
464 let initialize_archiver_fut = initialize_archiver(
465 &chain_info,
466 consensus_constants.block_confirmation_depth,
467 erasure_coding.clone(),
468 );
469 initialize_archiver_fut.await?
470 };
471
472 let InitializedArchiver {
473 mut archiver,
474 best_archived_block,
475 } = archiver;
476 let (mut best_archived_block_root, mut best_archived_block_number) = best_archived_block;
477
478 while let Some(block_importing_notification) =
479 block_importing_notification_receiver.next().await
480 {
481 let importing_block_number = block_importing_notification.block_number;
482 let Some(block_number_to_archive) =
483 importing_block_number.checked_sub(consensus_constants.block_confirmation_depth)
484 else {
485 continue;
487 };
488
489 let maybe_last_archived_block_number = chain_info
492 .last_segment_header()
493 .map(|last_segment_header| last_segment_header.last_archived_block.number());
494 trace!(
495 %importing_block_number,
496 %block_number_to_archive,
497 %best_archived_block_number,
498 last_archived_block_number = ?maybe_last_archived_block_number,
499 "Checking if block needs to be skipped"
500 );
501
502 let skip_last_archived_blocks =
504 maybe_last_archived_block_number.is_some_and(|last_archived_block_number| {
505 last_archived_block_number > block_number_to_archive
506 });
507 if best_archived_block_number >= block_number_to_archive || skip_last_archived_blocks {
508 debug!(
510 %importing_block_number,
511 %block_number_to_archive,
512 %best_archived_block_number,
513 last_archived_block_number = ?maybe_last_archived_block_number,
514 "Skipping already archived block",
515 );
516 continue;
517 }
518
519 let best_block_root = chain_info.best_root();
520
521 if best_archived_block_number + BlockNumber::ONE != block_number_to_archive {
527 let initialize_archiver_fut = initialize_archiver(
528 &chain_info,
529 consensus_constants.block_confirmation_depth,
530 erasure_coding.clone(),
531 );
532 InitializedArchiver {
533 archiver,
534 best_archived_block: (best_archived_block_root, best_archived_block_number),
535 } = initialize_archiver_fut.await?;
536
537 if best_archived_block_number + BlockNumber::ONE == block_number_to_archive {
538 } else if best_archived_block_number >= block_number_to_archive {
540 continue;
542 } else {
543 return Err(SegmentArchiverTaskError::BlockGap {
549 best_archived_block_number,
550 block_number_to_archive,
551 importing_block_number,
552 });
553 }
554 }
555
556 (best_archived_block_root, best_archived_block_number) = archive_block(
557 &mut archiver,
558 &chain_info,
559 &mut archived_segment_notification_sender,
560 best_archived_block_root,
561 block_number_to_archive,
562 &best_block_root,
563 )
564 .await?;
565 }
566
567 Ok(())
568 })
569}
570
571async fn archive_block<Block, CI>(
573 archiver: &mut Archiver,
574 chain_info: &CI,
575 archived_segment_notification_sender: &mut mpsc::Sender<ArchivedSegmentNotification>,
576 best_archived_block_root: BlockRoot,
577 block_number_to_archive: BlockNumber,
578 best_block_root: &BlockRoot,
579) -> Result<(BlockRoot, BlockNumber), SegmentArchiverTaskError>
580where
581 Block: GenericOwnedBlock,
582 CI: ChainInfoWrite<Block>,
583{
584 let header = chain_info
585 .ancestor_header(block_number_to_archive, best_block_root)
586 .ok_or(SegmentArchiverTaskError::BlockMissing {
587 block_number: block_number_to_archive,
588 })?;
589
590 let parent_block_root = header.header().prefix.parent_root;
591 if parent_block_root != best_archived_block_root {
592 return Err(SegmentArchiverTaskError::ArchivingReorg {
593 parent_block_root,
594 best_archived_block_root,
595 });
596 }
597
598 let block_root_to_archive = *header.header().root();
599
600 let block = chain_info
601 .block(&block_root_to_archive)
602 .await
603 .map_err(|error| SegmentArchiverTaskError::ReadBlock {
604 block_number: block_number_to_archive,
605 error,
606 })?;
607
608 debug!("Archiving block {block_number_to_archive} ({block_root_to_archive})");
609
610 let encoded_block = encode_block(&block);
611 debug!(
612 "Encoded block {block_number_to_archive} has size of {}",
613 ByteSize::b(encoded_block.len() as u64).display().iec(),
614 );
615
616 let encoded_block_size = encoded_block.len();
617 let block_outcome = archiver.add_block(encoded_block, Vec::new()).ok_or(
618 SegmentArchiverTaskError::BlockTooLarge {
619 block_number: block_number_to_archive,
620 size: encoded_block_size,
621 },
622 )?;
623 for archived_segment in block_outcome.archived_segments {
624 let segment_header = archived_segment.segment_header;
625
626 chain_info
627 .persist_segment_headers(vec![segment_header])
628 .await?;
629
630 send_archived_segment_notification(archived_segment_notification_sender, archived_segment)
631 .await;
632 }
633
634 Ok((block_root_to_archive, block_number_to_archive))
635}
636
637async fn send_archived_segment_notification(
638 archived_segment_notification_sender: &mut mpsc::Sender<ArchivedSegmentNotification>,
639 archived_segment: NewArchivedSegment,
640) {
641 let segment_index = archived_segment.segment_header.index;
642 let (acknowledgement_sender, mut acknowledgement_receiver) = mpsc::channel(1);
643 let archived_segment = Arc::new(archived_segment);
646 let archived_segment_notification = ArchivedSegmentNotification {
647 archived_segment: Arc::clone(&archived_segment),
648 acknowledgement_sender,
649 };
650
651 if let Err(error) = archived_segment_notification_sender
652 .send(archived_segment_notification)
653 .await
654 {
655 warn!(
656 %error,
657 "Failed to send archived segment notification"
658 );
659 }
660
661 let wait_fut = async {
662 while acknowledgement_receiver.next().await.is_some() {
663 debug!(
664 "Archived segment notification acknowledged: {}",
665 segment_index
666 );
667 }
668 };
669
670 if tokio::time::timeout(ACKNOWLEDGEMENT_TIMEOUT, wait_fut)
671 .await
672 .is_err()
673 {
674 warn!(
675 "Archived segment notification was not acknowledged and reached timeout, continue \
676 regardless"
677 );
678 }
679}