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