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| {
112 (
113 segment_header.segment_index.as_inner(),
114 segment_header.segment_root,
115 )
116 }),
117 iter::empty(),
119 checkpoints,
120 )
121 .map_err(BeaconChainBlockBuilderError::from)?;
122
123 let block_unsealed = block_builder
124 .with_header(
125 &header_prefix,
126 state_root,
127 consensus_info,
128 &consensus_parameters.as_ref(),
129 )
130 .map_err(BeaconChainBlockBuilderError::from)?;
131
132 let seal = seal_block(block_unsealed.pre_seal_hash())
133 .await
134 .ok_or(BlockBuilderError::FailedToSeal)?;
135 let block = block_unsealed.with_seal(seal.as_ref());
136
137 let mut block_mmr = *parent_block_details.mmr_with_block;
138
139 if !block_mmr.add_leaf(&block.header.header().root()) {
140 return Err(BlockBuilderError::CantExtendMmr);
141 }
142
143 Ok(BlockBuilderResult {
144 block,
145 block_details: BlockDetails {
146 mmr_with_block: Arc::new(block_mmr),
147 system_contract_states,
148 },
149 })
150 }
151}
152
153impl<CI> BeaconChainBlockBuilder<CI>
154where
155 CI: ChainInfo<OwnedBeaconChainBlock>,
156{
157 pub fn new(consensus_constants: ConsensusConstants, chain_info: CI) -> Self {
159 Self {
160 consensus_constants,
161 chain_info,
162 }
163 }
164
165 fn create_header_prefix(
166 &self,
167 parent_block_root: &BlockRoot,
168 parent_timestamp: BlockTimestamp,
169 mmr_with_block: &BlockMerkleMountainRange,
170 block_number: BlockNumber,
171 ) -> Result<BlockHeaderPrefix, BlockBuilderError> {
172 let timestamp = SystemTime::now()
173 .duration_since(SystemTime::UNIX_EPOCH)
174 .unwrap_or_default()
175 .as_millis();
176 let mut timestamp =
177 BlockTimestamp::from_millis(u64::try_from(timestamp).unwrap_or(u64::MAX));
178
179 if timestamp <= parent_timestamp {
180 timestamp = BlockTimestamp::from_millis(parent_timestamp.as_millis().saturating_add(1));
181 }
182
183 Ok(BlockHeaderPrefix {
184 number: block_number,
185 shard_index: ShardIndex::BEACON_CHAIN,
186 padding_0: [0; _],
187 timestamp,
188 parent_root: *parent_block_root,
189 mmr_root: Blake3Hash::new(
190 mmr_with_block
191 .root()
192 .ok_or(BlockBuilderError::InvalidParentMmr)?,
193 ),
194 })
195 }
196
197 fn derive_consensus_parameters(
198 &self,
199 parent_block_root: &BlockRoot,
200 parent_header: &BeaconChainHeader<'_>,
201 block_number: BlockNumber,
202 slot: SlotNumber,
203 ) -> Result<OwnedBlockHeaderConsensusParameters, BeaconChainBlockBuilderError> {
204 let derived_consensus_parameters = derive_consensus_parameters(
205 &self.consensus_constants,
206 &self.chain_info,
207 parent_block_root,
208 parent_header.consensus_parameters(),
209 parent_header.consensus_info.slot,
210 block_number,
211 slot,
212 )?;
213
214 Ok(OwnedBlockHeaderConsensusParameters {
215 fixed_parameters: derived_consensus_parameters.fixed_parameters,
216 super_segment_root: None,
218 next_solution_range: derived_consensus_parameters.next_solution_range,
219 pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
220 })
221 }
222
223 fn execute_block(
224 &self,
225 parent_block_details: &BlockDetails,
226 ) -> Result<(Blake3Hash, StdArc<[ContractSlotState]>), BeaconChainBlockBuilderError> {
227 let global_state = GlobalState::new(&parent_block_details.system_contract_states);
228
229 let state_root = global_state.root();
232 let system_contract_states = global_state.to_system_contract_states();
233
234 Ok((state_root, system_contract_states))
235 }
236}