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| (segment_header.index.as_inner(), segment_header.root)),
135 iter::empty(),
137 checkpoints,
138 )
139 .map_err(BeaconChainBlockBuilderError::from)?;
140
141 let block_unsealed = block_builder
142 .with_header(
143 &header_prefix,
144 state_root,
145 consensus_info,
146 &consensus_parameters.as_ref(),
147 )
148 .map_err(BeaconChainBlockBuilderError::from)?;
149
150 let seal = seal_block(block_unsealed.pre_seal_hash())
151 .await
152 .ok_or(BlockBuilderError::FailedToSeal)?;
153 let block = block_unsealed.with_seal(seal.as_ref());
154
155 let mut block_mmr = *parent_block_details.mmr_with_block;
156
157 if !block_mmr.add_leaf(&block.header.header().root()) {
158 return Err(BlockBuilderError::CantExtendMmr);
159 }
160
161 Ok(BlockBuilderResult {
162 block,
163 block_details: BlockDetails {
164 mmr_with_block: Arc::new(block_mmr),
165 system_contract_states,
166 },
167 extra: (),
168 })
169 }
170}
171
172impl<BCI> BeaconChainBlockBuilder<BCI>
173where
174 BCI: BeaconChainInfo,
175{
176 pub fn new(consensus_constants: ConsensusConstants, chain_info: BCI) -> Self {
178 Self {
179 consensus_constants,
180 chain_info,
181 }
182 }
183
184 fn create_header_prefix(
185 &self,
186 parent_block_root: &BlockRoot,
187 parent_timestamp: BlockTimestamp,
188 mmr_with_block: &BlockMerkleMountainRange,
189 block_number: BlockNumber,
190 ) -> Result<BlockHeaderPrefix, BlockBuilderError> {
191 let timestamp = SystemTime::now()
192 .duration_since(SystemTime::UNIX_EPOCH)
193 .unwrap_or_default()
194 .as_millis();
195 let mut timestamp =
196 BlockTimestamp::from_millis(u64::try_from(timestamp).unwrap_or(u64::MAX));
197
198 if timestamp <= parent_timestamp {
199 timestamp = BlockTimestamp::from_millis(parent_timestamp.as_millis().saturating_add(1));
200 }
201
202 Ok(BlockHeaderPrefix {
203 number: block_number,
204 shard_index: ShardIndex::BEACON_CHAIN,
205 padding_0: [0; _],
206 timestamp,
207 parent_root: *parent_block_root,
208 mmr_root: Blake3Hash::new(
209 mmr_with_block
210 .root()
211 .ok_or(BlockBuilderError::InvalidParentMmr)?,
212 ),
213 })
214 }
215
216 fn derive_consensus_parameters(
217 &self,
218 parent_block_root: &BlockRoot,
219 parent_header: &BeaconChainHeader<'_>,
220 block_number: BlockNumber,
221 slot: SlotNumber,
222 super_segment_root: Option<SuperSegmentRoot>,
223 ) -> Result<OwnedBlockHeaderConsensusParameters, BeaconChainBlockBuilderError> {
224 let derived_consensus_parameters = derive_consensus_parameters(
225 &self.consensus_constants,
226 &self.chain_info,
227 parent_block_root,
228 parent_header.consensus_parameters(),
229 parent_header.consensus_info.slot,
230 block_number,
231 slot,
232 )?;
233
234 Ok(OwnedBlockHeaderConsensusParameters {
235 fixed_parameters: derived_consensus_parameters.fixed_parameters,
236 super_segment_root,
237 next_solution_range: derived_consensus_parameters.next_solution_range,
238 pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
239 })
240 }
241
242 fn execute_block(
243 &self,
244 parent_block_details: &BlockDetails,
245 ) -> Result<(Blake3Hash, StdArc<[ContractSlotState]>), BeaconChainBlockBuilderError> {
246 let global_state = GlobalState::new(&parent_block_details.system_contract_states);
247
248 let state_root = global_state.root();
251 let system_contract_states = global_state.to_system_contract_states();
252
253 Ok((state_root, system_contract_states))
254 }
255}