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