1use crate::{BlockBuilder, BlockBuilderError, BlockBuilderResult};
4use ab_client_api::{BlockDetails, BlockMerkleMountainRange, ChainInfo, ContractSlotState};
5use ab_client_consensus_common::ConsensusConstants;
6use ab_client_consensus_common::consensus_parameters::{
7 DeriveConsensusParametersError, derive_consensus_parameters,
8};
9use ab_client_consensus_common::state::GlobalState;
10use ab_core_primitives::block::body::owned::OwnedBeaconChainBodyError;
11use ab_core_primitives::block::header::owned::{
12 GenericOwnedBlockHeader, OwnedBeaconChainHeader, OwnedBeaconChainHeaderError,
13};
14use ab_core_primitives::block::header::{
15 BeaconChainHeader, BlockHeaderConsensusInfo, BlockHeaderPrefix,
16 OwnedBlockHeaderConsensusParameters, OwnedBlockHeaderSeal,
17};
18use ab_core_primitives::block::owned::OwnedBeaconChainBlock;
19use ab_core_primitives::block::{BlockNumber, BlockRoot, BlockTimestamp};
20use ab_core_primitives::hashes::Blake3Hash;
21use ab_core_primitives::pot::{PotCheckpoints, SlotNumber};
22use ab_core_primitives::shard::ShardIndex;
23use rclite::Arc;
24use std::iter;
25use std::sync::Arc as StdArc;
26use std::time::SystemTime;
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("Failed to create body: {error}")]
40 FailedToCreateBody {
41 #[from]
43 error: OwnedBeaconChainBodyError,
44 },
45 #[error("Failed to create a header: {error}")]
47 FailedToCreateHeader {
48 #[from]
50 error: OwnedBeaconChainHeaderError,
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<CI> {
69 consensus_constants: ConsensusConstants,
70 chain_info: CI,
71}
72
73impl<CI> BlockBuilder<OwnedBeaconChainBlock> for BeaconChainBlockBuilder<CI>
74where
75 CI: ChainInfo<OwnedBeaconChainBlock>,
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.saturating_add(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 consensus_parameters = self.derive_consensus_parameters(
99 parent_block_root,
100 parent_header,
101 block_number,
102 consensus_info.slot,
103 )?;
104
105 let (state_root, system_contract_states) = self.execute_block(parent_block_details)?;
106
107 let block_builder = OwnedBeaconChainBlock::init(
108 self.chain_info
109 .segment_headers_for_block(block_number)
110 .into_iter()
111 .map(|segment_header| segment_header.segment_root),
112 iter::empty(),
114 checkpoints,
115 )
116 .map_err(BeaconChainBlockBuilderError::from)?;
117
118 let block_unsealed = block_builder
119 .with_header(
120 &header_prefix,
121 state_root,
122 consensus_info,
123 &consensus_parameters.as_ref(),
124 )
125 .map_err(BeaconChainBlockBuilderError::from)?;
126
127 let seal = seal_block(block_unsealed.pre_seal_hash())
128 .await
129 .ok_or(BlockBuilderError::FailedToSeal)?;
130 let block = block_unsealed.with_seal(seal.as_ref());
131
132 let mut block_mmr = *parent_block_details.mmr_with_block;
133
134 if !block_mmr.add_leaf(&block.header.header().root()) {
135 return Err(BlockBuilderError::CantExtendMmr);
136 }
137
138 Ok(BlockBuilderResult {
139 block,
140 block_details: BlockDetails {
141 mmr_with_block: Arc::new(block_mmr),
142 system_contract_states,
143 },
144 })
145 }
146}
147
148impl<CI> BeaconChainBlockBuilder<CI>
149where
150 CI: ChainInfo<OwnedBeaconChainBlock>,
151{
152 pub fn new(consensus_constants: ConsensusConstants, chain_info: CI) -> Self {
154 Self {
155 consensus_constants,
156 chain_info,
157 }
158 }
159
160 fn create_header_prefix(
161 &self,
162 parent_block_root: &BlockRoot,
163 parent_timestamp: BlockTimestamp,
164 mmr_with_block: &BlockMerkleMountainRange,
165 block_number: BlockNumber,
166 ) -> Result<BlockHeaderPrefix, BlockBuilderError> {
167 let timestamp = SystemTime::now()
168 .duration_since(SystemTime::UNIX_EPOCH)
169 .unwrap_or_default()
170 .as_millis();
171 let mut timestamp =
172 BlockTimestamp::from_millis(u64::try_from(timestamp).unwrap_or(u64::MAX));
173
174 if timestamp <= parent_timestamp {
175 timestamp = BlockTimestamp::from_millis(parent_timestamp.as_millis().saturating_add(1));
176 }
177
178 Ok(BlockHeaderPrefix {
179 number: block_number,
180 shard_index: ShardIndex::BEACON_CHAIN,
181 padding_0: [0; _],
182 timestamp,
183 parent_root: *parent_block_root,
184 mmr_root: Blake3Hash::new(
185 mmr_with_block
186 .root()
187 .ok_or(BlockBuilderError::InvalidParentMmr)?,
188 ),
189 })
190 }
191
192 fn derive_consensus_parameters(
193 &self,
194 parent_block_root: &BlockRoot,
195 parent_header: &BeaconChainHeader<'_>,
196 block_number: BlockNumber,
197 slot: SlotNumber,
198 ) -> Result<OwnedBlockHeaderConsensusParameters, BeaconChainBlockBuilderError> {
199 let derived_consensus_parameters = derive_consensus_parameters(
200 &self.consensus_constants,
201 &self.chain_info,
202 parent_block_root,
203 parent_header.consensus_parameters(),
204 parent_header.consensus_info.slot,
205 block_number,
206 slot,
207 )?;
208
209 Ok(OwnedBlockHeaderConsensusParameters {
210 fixed_parameters: derived_consensus_parameters.fixed_parameters,
211 super_segment_root: None,
213 next_solution_range: derived_consensus_parameters.next_solution_range,
214 pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
215 })
216 }
217
218 fn execute_block(
219 &self,
220 parent_block_details: &BlockDetails,
221 ) -> Result<(Blake3Hash, StdArc<[ContractSlotState]>), BeaconChainBlockBuilderError> {
222 let global_state = GlobalState::new(&parent_block_details.system_contract_states);
223
224 let state_root = global_state.root();
227 let system_contract_states = global_state.to_system_contract_states();
228
229 Ok((state_root, system_contract_states))
230 }
231}