|
1 |
| -use std::cmp; |
2 |
| - |
3 |
| -use alloy::{primitives::B256, transports::Transport}; |
4 |
| -use tracing::info; |
5 |
| - |
6 | 1 | use crate::{
|
7 |
| - clients::{ |
8 |
| - beacon::types::{BlockHeader, BlockId, HeadEventData}, |
9 |
| - blobscan::types::BlockchainSyncState, |
10 |
| - common::ClientError, |
11 |
| - }, |
12 |
| - context::CommonContext, |
| 2 | + clients::beacon::types::{BlockId, HeadEventData}, |
13 | 3 | synchronizer::{error::SynchronizerError, CommonSynchronizer},
|
14 | 4 | };
|
15 | 5 |
|
16 | 6 | #[derive(Debug, thiserror::Error)]
|
17 | 7 | pub enum HeadEventHandlerError {
|
18 | 8 | #[error(transparent)]
|
19 | 9 | EventDeserializationFailure(#[from] serde_json::Error),
|
20 |
| - #[error("failed to retrieve header for block \"{0}\"")] |
21 |
| - BlockHeaderRetrievalError(BlockId, #[source] ClientError), |
22 |
| - #[error("header for block \"{0}\" not found")] |
23 |
| - BlockHeaderNotFound(BlockId), |
24 | 10 | #[error("failed to index head block")]
|
25 | 11 | BlockSyncedError(#[from] SynchronizerError),
|
26 |
| - #[error("failed to handle reorged slots")] |
27 |
| - BlobscanReorgedSlotsFailure(#[source] ClientError), |
28 |
| - #[error("failed to update blobscan's sync state")] |
29 |
| - BlobscanSyncStateUpdateError(#[source] ClientError), |
30 | 12 | }
|
31 | 13 |
|
32 |
| -pub struct HeadEventHandler<T> { |
33 |
| - context: Box<dyn CommonContext<T>>, |
| 14 | +pub struct HeadEventHandler { |
34 | 15 | synchronizer: Box<dyn CommonSynchronizer>,
|
35 |
| - start_block_id: BlockId, |
36 |
| - last_block_hash: Option<B256>, |
| 16 | + is_first_event: bool, |
| 17 | + custom_start_block_id: Option<BlockId>, |
37 | 18 | }
|
38 | 19 |
|
39 |
| -impl<T> HeadEventHandler<T> |
40 |
| -where |
41 |
| - T: Transport + Send + Sync + 'static, |
42 |
| -{ |
| 20 | +impl HeadEventHandler { |
43 | 21 | pub fn new(
|
44 |
| - context: Box<dyn CommonContext<T>>, |
45 | 22 | synchronizer: Box<dyn CommonSynchronizer>,
|
46 |
| - start_block_id: BlockId, |
| 23 | + custom_start_block_id: Option<BlockId>, |
47 | 24 | ) -> Self {
|
48 | 25 | HeadEventHandler {
|
49 |
| - context, |
50 | 26 | synchronizer,
|
51 |
| - start_block_id, |
52 |
| - last_block_hash: None, |
| 27 | + is_first_event: true, |
| 28 | + custom_start_block_id, |
53 | 29 | }
|
54 | 30 | }
|
55 | 31 |
|
56 | 32 | pub async fn handle(&mut self, event_data: String) -> Result<(), HeadEventHandlerError> {
|
57 | 33 | let head_block_data = serde_json::from_str::<HeadEventData>(&event_data)?;
|
| 34 | + let head_slot = head_block_data.slot; |
58 | 35 |
|
59 |
| - let head_block_slot = head_block_data.slot; |
60 |
| - let head_block_hash = head_block_data.block; |
| 36 | + // If this is the first event being processed, ensure the synchronizer is fully up to date |
| 37 | + if self.is_first_event { |
| 38 | + self.is_first_event = false; |
61 | 39 |
|
62 |
| - let head_block_id = BlockId::Slot(head_block_data.slot); |
63 |
| - let initial_block_id = if self.last_block_hash.is_none() { |
64 |
| - self.start_block_id.clone() |
65 |
| - } else { |
66 |
| - head_block_id.clone() |
67 |
| - }; |
| 40 | + let start_block_id = self.custom_start_block_id.clone().or(self |
| 41 | + .synchronizer |
| 42 | + .get_last_synced_block() |
| 43 | + .map(|block| (block.slot + 1).into())); |
68 | 44 |
|
69 |
| - let head_block_header = self.get_block_header(head_block_id).await?; |
| 45 | + if let Some(start_block_id) = start_block_id { |
| 46 | + if self.custom_start_block_id.is_some() { |
| 47 | + self.synchronizer.clear_last_synced_block(); |
| 48 | + } |
70 | 49 |
|
71 |
| - if let Some(last_block_hash) = self.last_block_hash { |
72 |
| - if last_block_hash != head_block_header.parent_root { |
73 |
| - let parent_block_header = self |
74 |
| - .get_block_header(head_block_header.parent_root.into()) |
| 50 | + self.synchronizer |
| 51 | + .sync_blocks(start_block_id, head_slot.into()) |
75 | 52 | .await?;
|
76 |
| - let parent_block_slot = parent_block_header.slot; |
77 |
| - let reorg_start_slot = parent_block_slot + 1; |
78 |
| - let reorg_final_slot = head_block_slot; |
79 |
| - let reorged_slots = (reorg_start_slot..reorg_final_slot).collect::<Vec<u32>>(); |
80 |
| - |
81 |
| - let result: Result<(), HeadEventHandlerError> = async { |
82 |
| - let total_updated_slots = self.context |
83 |
| - .blobscan_client() |
84 |
| - .handle_reorged_slots(reorged_slots.as_slice()) |
85 |
| - .await |
86 |
| - .map_err(HeadEventHandlerError::BlobscanReorgedSlotsFailure)?; |
87 |
| - |
88 |
| - |
89 |
| - info!(slot=head_block_slot, "Reorganization detected. Found the following reorged slots: {:#?}. Total slots marked as reorged: {total_updated_slots}", reorged_slots); |
90 |
| - |
91 |
| - // Re-index parent block as it may be mark as reorged and not indexed |
92 |
| - self.synchronizer |
93 |
| - .run( |
94 |
| - parent_block_slot.into(), |
95 |
| - (parent_block_slot + 1).into(), |
96 |
| - ) |
97 |
| - .await?; |
98 |
| - |
99 |
| - Ok(()) |
100 |
| - } |
101 |
| - .await; |
102 |
| - |
103 |
| - if let Err(err) = result { |
104 |
| - // If an error occurred while handling the reorg try to update the latest synced slot to the last known slot before the reorg |
105 |
| - self.context |
106 |
| - .blobscan_client() |
107 |
| - .update_sync_state(BlockchainSyncState { |
108 |
| - last_finalized_block: None, |
109 |
| - last_lower_synced_slot: None, |
110 |
| - last_upper_synced_slot: Some(cmp::max(parent_block_slot - 1, 0)), |
111 |
| - }) |
112 |
| - .await |
113 |
| - .map_err(HeadEventHandlerError::BlobscanSyncStateUpdateError)?; |
114 |
| - |
115 |
| - return Err(err); |
116 |
| - } |
117 | 53 | }
|
118 | 54 | }
|
119 | 55 |
|
120 |
| - self.synchronizer |
121 |
| - .run(initial_block_id, (head_block_slot + 1).into()) |
122 |
| - .await?; |
123 |
| - |
124 |
| - self.last_block_hash = Some(head_block_hash); |
| 56 | + self.synchronizer.sync_block(head_slot.into()).await?; |
125 | 57 |
|
126 | 58 | Ok(())
|
127 | 59 | }
|
128 |
| - |
129 |
| - async fn get_block_header( |
130 |
| - &self, |
131 |
| - block_id: BlockId, |
132 |
| - ) -> Result<BlockHeader, HeadEventHandlerError> { |
133 |
| - match self |
134 |
| - .context |
135 |
| - .beacon_client() |
136 |
| - .get_block_header(block_id.clone()) |
137 |
| - .await |
138 |
| - .map_err(|err| { |
139 |
| - HeadEventHandlerError::BlockHeaderRetrievalError(block_id.clone(), err) |
140 |
| - })? { |
141 |
| - Some(block) => Ok(block.into()), |
142 |
| - None => Err(HeadEventHandlerError::BlockHeaderNotFound(block_id.clone())), |
143 |
| - } |
144 |
| - } |
145 | 60 | }
|
146 | 61 |
|
147 | 62 | // #[cfg(test)]
|
|
0 commit comments