1use crate::{BlockBuilder, BlockBuilderError, BlockBuilderResult};
4use ab_client_api::{BlockDetails, BlockMerkleMountainRange, ChainInfo, ContractSlotState};
5use ab_client_archiving::segment_headers_store::SegmentHeadersStore;
6use ab_client_consensus_common::ConsensusConstants;
7use ab_client_consensus_common::consensus_parameters::{
8 DeriveConsensusParametersError, derive_consensus_parameters,
9};
10use ab_client_consensus_common::state::GlobalState;
11use ab_core_primitives::block::body::owned::OwnedBeaconChainBodyError;
12use ab_core_primitives::block::header::owned::{
13 GenericOwnedBlockHeader, OwnedBeaconChainHeader, OwnedBeaconChainHeaderError,
14};
15use ab_core_primitives::block::header::{
16 BeaconChainHeader, BlockHeaderConsensusInfo, BlockHeaderPrefix,
17 OwnedBlockHeaderConsensusParameters, OwnedBlockHeaderSeal,
18};
19use ab_core_primitives::block::owned::OwnedBeaconChainBlock;
20use ab_core_primitives::block::{BlockNumber, BlockRoot, BlockTimestamp};
21use ab_core_primitives::hashes::Blake3Hash;
22use ab_core_primitives::pot::{PotCheckpoints, SlotNumber};
23use ab_core_primitives::shard::ShardIndex;
24use rclite::Arc;
25use std::iter;
26use std::sync::Arc as StdArc;
27use std::time::SystemTime;
28
29#[derive(Debug, thiserror::Error)]
31pub enum BeaconChainBlockBuilderError {
32 #[error("Consensus parameters derivation error: {error}")]
34 ConsensusParametersDerivation {
35 #[from]
37 error: DeriveConsensusParametersError,
38 },
39 #[error("Failed to create body: {error}")]
41 FailedToCreateBody {
42 #[from]
44 error: OwnedBeaconChainBodyError,
45 },
46 #[error("Failed to create a header: {error}")]
48 FailedToCreateHeader {
49 #[from]
51 error: OwnedBeaconChainHeaderError,
52 },
53}
54
55impl From<BeaconChainBlockBuilderError> for BlockBuilderError {
56 #[inline(always)]
57 fn from(error: BeaconChainBlockBuilderError) -> Self {
58 Self::Custom {
59 error: error.into(),
60 }
61 }
62}
63
64#[derive(Debug)]
69pub struct BeaconChainBlockBuilder<CI> {
70 segment_headers_store: SegmentHeadersStore,
71 consensus_constants: ConsensusConstants,
72 chain_info: CI,
73}
74
75impl<CI> BlockBuilder<OwnedBeaconChainBlock> for BeaconChainBlockBuilder<CI>
76where
77 CI: ChainInfo<OwnedBeaconChainBlock>,
78{
79 async fn build<SealBlock>(
80 &mut self,
81 parent_block_root: &BlockRoot,
82 parent_header: &<OwnedBeaconChainHeader as GenericOwnedBlockHeader>::Header<'_>,
83 parent_block_details: &BlockDetails,
84 consensus_info: &BlockHeaderConsensusInfo,
85 checkpoints: &[PotCheckpoints],
86 seal_block: SealBlock,
87 ) -> Result<BlockBuilderResult<OwnedBeaconChainBlock>, BlockBuilderError>
88 where
89 SealBlock: AsyncFnOnce<(Blake3Hash,), Output = Option<OwnedBlockHeaderSeal>, CallOnceFuture: Send>
90 + Send,
91 {
92 let block_number = parent_header.prefix.number.saturating_add(BlockNumber::ONE);
93
94 let header_prefix = self.create_header_prefix(
95 parent_block_root,
96 parent_header.prefix.timestamp,
97 &parent_block_details.mmr_with_block,
98 block_number,
99 )?;
100 let consensus_parameters = self.derive_consensus_parameters(
101 parent_block_root,
102 parent_header,
103 block_number,
104 consensus_info.slot,
105 )?;
106
107 let (state_root, system_contract_states) = self.execute_block(parent_block_details)?;
108
109 let block_builder = OwnedBeaconChainBlock::init(
110 self.segment_headers_store
111 .segment_headers_for_block(block_number)
112 .into_iter()
113 .map(|segment_header| segment_header.segment_root),
114 iter::empty(),
116 checkpoints,
117 )
118 .map_err(BeaconChainBlockBuilderError::from)?;
119
120 let block_unsealed = block_builder
121 .with_header(
122 &header_prefix,
123 state_root,
124 consensus_info,
125 consensus_parameters.as_ref(),
126 )
127 .map_err(BeaconChainBlockBuilderError::from)?;
128
129 let seal = seal_block(block_unsealed.pre_seal_hash())
130 .await
131 .ok_or(BlockBuilderError::FailedToSeal)?;
132 let block = block_unsealed.with_seal(seal.as_ref());
133
134 let mut block_mmr = *parent_block_details.mmr_with_block;
135
136 if !block_mmr.add_leaf(&block.header.header().root()) {
137 return Err(BlockBuilderError::CantExtendMmr);
138 }
139
140 Ok(BlockBuilderResult {
141 block,
142 block_details: BlockDetails {
143 mmr_with_block: Arc::new(block_mmr),
144 system_contract_states,
145 },
146 })
147 }
148}
149
150impl<CI> BeaconChainBlockBuilder<CI>
151where
152 CI: ChainInfo<OwnedBeaconChainBlock>,
153{
154 pub fn new(
156 segment_headers_store: SegmentHeadersStore,
157 consensus_constants: ConsensusConstants,
158 chain_info: CI,
159 ) -> Self {
160 Self {
161 segment_headers_store,
162 consensus_constants,
163 chain_info,
164 }
165 }
166
167 fn create_header_prefix(
168 &self,
169 parent_block_root: &BlockRoot,
170 parent_timestamp: BlockTimestamp,
171 mmr_with_block: &BlockMerkleMountainRange,
172 block_number: BlockNumber,
173 ) -> Result<BlockHeaderPrefix, BlockBuilderError> {
174 let timestamp = SystemTime::now()
175 .duration_since(SystemTime::UNIX_EPOCH)
176 .unwrap_or_default()
177 .as_millis();
178 let mut timestamp =
179 BlockTimestamp::from_millis(u64::try_from(timestamp).unwrap_or(u64::MAX));
180
181 if timestamp <= parent_timestamp {
182 timestamp = BlockTimestamp::from_millis(parent_timestamp.as_millis().saturating_add(1));
183 }
184
185 Ok(BlockHeaderPrefix {
186 number: block_number,
187 shard_index: ShardIndex::BEACON_CHAIN,
188 padding_0: [0; _],
189 timestamp,
190 parent_root: *parent_block_root,
191 mmr_root: Blake3Hash::new(
192 mmr_with_block
193 .root()
194 .ok_or(BlockBuilderError::InvalidParentMmr)?,
195 ),
196 })
197 }
198
199 fn derive_consensus_parameters(
200 &self,
201 parent_block_root: &BlockRoot,
202 parent_header: &BeaconChainHeader<'_>,
203 block_number: BlockNumber,
204 slot: SlotNumber,
205 ) -> Result<OwnedBlockHeaderConsensusParameters, BeaconChainBlockBuilderError> {
206 let derived_consensus_parameters = derive_consensus_parameters(
207 &self.consensus_constants,
208 &self.chain_info,
209 parent_block_root,
210 parent_header.consensus_parameters(),
211 parent_header.consensus_info.slot,
212 block_number,
213 slot,
214 )?;
215
216 Ok(OwnedBlockHeaderConsensusParameters {
217 fixed_parameters: derived_consensus_parameters.fixed_parameters,
218 super_segment_root: None,
220 next_solution_range: derived_consensus_parameters.next_solution_range,
221 pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
222 })
223 }
224
225 fn execute_block(
226 &self,
227 parent_block_details: &BlockDetails,
228 ) -> Result<(Blake3Hash, StdArc<[ContractSlotState]>), BeaconChainBlockBuilderError> {
229 let global_state = GlobalState::new(&parent_block_details.system_contract_states);
230
231 let state_root = global_state.root();
234 let system_contract_states = global_state.to_system_contract_states();
235
236 Ok((state_root, system_contract_states))
237 }
238}