1use crate::{BlockBuilder, BlockBuilderError, BlockBuilderResult};
4use ab_client_api::{BeaconChainInfo, BlockDetails, BlockMerkleMountainRange, ContractSlotState};
5use ab_client_consensus_common::ConsensusConstants;
6use ab_client_consensus_common::consensus_parameters::{
7 DeriveConsensusParametersError, DeriveSuperSegmentForBlockError, derive_consensus_parameters,
8 derive_super_segments_for_block,
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::segments::SuperSegmentRoot;
24use ab_core_primitives::shard::ShardIndex;
25use rclite::Arc;
26use std::iter;
27use std::sync::Arc as StdArc;
28use std::time::SystemTime;
29
30#[derive(Debug, thiserror::Error)]
32pub enum BeaconChainBlockBuilderError {
33 #[error("Consensus parameters derivation error: {error}")]
35 ConsensusParametersDerivation {
36 #[from]
38 error: DeriveConsensusParametersError,
39 },
40 #[error("Super segment derivation error: {error}")]
42 SuperSegmentDerivation {
43 #[from]
45 error: DeriveSuperSegmentForBlockError,
46 },
47 #[error("Failed to create body: {error}")]
49 FailedToCreateBody {
50 #[from]
52 error: OwnedBeaconChainBodyError,
53 },
54 #[error("Failed to create a header: {error}")]
56 FailedToCreateHeader {
57 #[from]
59 error: OwnedBeaconChainHeaderError,
60 },
61}
62
63impl From<BeaconChainBlockBuilderError> for BlockBuilderError {
64 #[inline(always)]
65 fn from(error: BeaconChainBlockBuilderError) -> Self {
66 Self::Custom {
67 error: error.into(),
68 }
69 }
70}
71
72#[derive(Debug)]
77pub struct BeaconChainBlockBuilder<BCI> {
78 consensus_constants: ConsensusConstants,
79 chain_info: BCI,
80}
81
82impl<CI> BlockBuilder<OwnedBeaconChainBlock> for BeaconChainBlockBuilder<CI>
83where
84 CI: BeaconChainInfo,
85{
86 async fn build<SealBlock>(
87 &mut self,
88 parent_block_root: &BlockRoot,
89 parent_header: &<OwnedBeaconChainHeader as GenericOwnedBlockHeader>::Header<'_>,
90 parent_block_details: &BlockDetails,
91 consensus_info: &BlockHeaderConsensusInfo,
92 checkpoints: &[PotCheckpoints],
93 seal_block: SealBlock,
94 ) -> Result<BlockBuilderResult<OwnedBeaconChainBlock>, BlockBuilderError>
95 where
96 SealBlock: AsyncFnOnce<(Blake3Hash,), Output = Option<OwnedBlockHeaderSeal>, CallOnceFuture: Send>
97 + Send,
98 {
99 let block_number = parent_header.prefix.number + BlockNumber::ONE;
100
101 let header_prefix = self.create_header_prefix(
102 parent_block_root,
103 parent_header.prefix.timestamp,
104 &parent_block_details.mmr_with_block,
105 block_number,
106 )?;
107 let maybe_super_segment = derive_super_segments_for_block(
111 &self.chain_info,
112 parent_header.prefix.number,
113 self.consensus_constants.block_confirmation_depth,
114 self.consensus_constants.shard_confirmation_depth,
115 )
116 .map_err(BeaconChainBlockBuilderError::from)?;
117
118 let consensus_parameters = self.derive_consensus_parameters(
119 parent_block_root,
120 parent_header,
121 block_number,
122 consensus_info.slot,
123 maybe_super_segment
124 .as_ref()
125 .map(|super_segment| super_segment.header.root),
126 )?;
127
128 let (state_root, system_contract_states) = self.execute_block(parent_block_details)?;
129
130 let block_builder = OwnedBeaconChainBlock::init(
131 self.chain_info
132 .segment_headers_for_block(block_number)
133 .into_iter()
134 .map(|segment_header| {
135 (
136 segment_header.segment_index.as_inner(),
137 segment_header.segment_root,
138 )
139 }),
140 iter::empty(),
142 checkpoints,
143 )
144 .map_err(BeaconChainBlockBuilderError::from)?;
145
146 let block_unsealed = block_builder
147 .with_header(
148 &header_prefix,
149 state_root,
150 consensus_info,
151 &consensus_parameters.as_ref(),
152 )
153 .map_err(BeaconChainBlockBuilderError::from)?;
154
155 let seal = seal_block(block_unsealed.pre_seal_hash())
156 .await
157 .ok_or(BlockBuilderError::FailedToSeal)?;
158 let block = block_unsealed.with_seal(seal.as_ref());
159
160 let mut block_mmr = *parent_block_details.mmr_with_block;
161
162 if !block_mmr.add_leaf(&block.header.header().root()) {
163 return Err(BlockBuilderError::CantExtendMmr);
164 }
165
166 Ok(BlockBuilderResult {
167 block,
168 block_details: BlockDetails {
169 mmr_with_block: Arc::new(block_mmr),
170 system_contract_states,
171 },
172 extra: (),
173 })
174 }
175}
176
177impl<BCI> BeaconChainBlockBuilder<BCI>
178where
179 BCI: BeaconChainInfo,
180{
181 pub fn new(consensus_constants: ConsensusConstants, chain_info: BCI) -> Self {
183 Self {
184 consensus_constants,
185 chain_info,
186 }
187 }
188
189 fn create_header_prefix(
190 &self,
191 parent_block_root: &BlockRoot,
192 parent_timestamp: BlockTimestamp,
193 mmr_with_block: &BlockMerkleMountainRange,
194 block_number: BlockNumber,
195 ) -> Result<BlockHeaderPrefix, BlockBuilderError> {
196 let timestamp = SystemTime::now()
197 .duration_since(SystemTime::UNIX_EPOCH)
198 .unwrap_or_default()
199 .as_millis();
200 let mut timestamp =
201 BlockTimestamp::from_millis(u64::try_from(timestamp).unwrap_or(u64::MAX));
202
203 if timestamp <= parent_timestamp {
204 timestamp = BlockTimestamp::from_millis(parent_timestamp.as_millis().saturating_add(1));
205 }
206
207 Ok(BlockHeaderPrefix {
208 number: block_number,
209 shard_index: ShardIndex::BEACON_CHAIN,
210 padding_0: [0; _],
211 timestamp,
212 parent_root: *parent_block_root,
213 mmr_root: Blake3Hash::new(
214 mmr_with_block
215 .root()
216 .ok_or(BlockBuilderError::InvalidParentMmr)?,
217 ),
218 })
219 }
220
221 fn derive_consensus_parameters(
222 &self,
223 parent_block_root: &BlockRoot,
224 parent_header: &BeaconChainHeader<'_>,
225 block_number: BlockNumber,
226 slot: SlotNumber,
227 super_segment_root: Option<SuperSegmentRoot>,
228 ) -> Result<OwnedBlockHeaderConsensusParameters, BeaconChainBlockBuilderError> {
229 let derived_consensus_parameters = derive_consensus_parameters(
230 &self.consensus_constants,
231 &self.chain_info,
232 parent_block_root,
233 parent_header.consensus_parameters(),
234 parent_header.consensus_info.slot,
235 block_number,
236 slot,
237 )?;
238
239 Ok(OwnedBlockHeaderConsensusParameters {
240 fixed_parameters: derived_consensus_parameters.fixed_parameters,
241 super_segment_root,
242 next_solution_range: derived_consensus_parameters.next_solution_range,
243 pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
244 })
245 }
246
247 fn execute_block(
248 &self,
249 parent_block_details: &BlockDetails,
250 ) -> Result<(Blake3Hash, StdArc<[ContractSlotState]>), BeaconChainBlockBuilderError> {
251 let global_state = GlobalState::new(&parent_block_details.system_contract_states);
252
253 let state_root = global_state.root();
256 let system_contract_states = global_state.to_system_contract_states();
257
258 Ok((state_root, system_contract_states))
259 }
260}