Skip to main content

ab_client_block_builder/
beacon_chain.rs

1//! Block building for the beacon chain
2
3use 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/// Error for [`BeaconChainBlockBuilder`]
31#[derive(Debug, thiserror::Error)]
32pub enum BeaconChainBlockBuilderError {
33    /// Consensus parameters derivation error
34    #[error("Consensus parameters derivation error: {error}")]
35    ConsensusParametersDerivation {
36        /// Consensus parameters derivation error
37        #[from]
38        error: DeriveConsensusParametersError,
39    },
40    /// Super segment derivation error
41    #[error("Super segment derivation error: {error}")]
42    SuperSegmentDerivation {
43        /// Super segment derivation error
44        #[from]
45        error: DeriveSuperSegmentForBlockError,
46    },
47    /// Failed to create body
48    #[error("Failed to create body: {error}")]
49    FailedToCreateBody {
50        // Body creation error
51        #[from]
52        error: OwnedBeaconChainBodyError,
53    },
54    /// Failed to create a header
55    #[error("Failed to create a header: {error}")]
56    FailedToCreateHeader {
57        // Header creation error
58        #[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// TODO: Another domain-specific abstraction over `ChainInfo`, which will be implemented for
73//  `ChainInfo`, but could also be implemented in simpler way directly for tests without dealing
74//  with complete headers, etc.
75/// Beacon chain block builder
76#[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        // TODO: Handle super segment error caused by too many segments included in shard blocks.
108        //  While not a concern any time soon, but it is theoretically possible and should be
109        //  handled in a general case.
110        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            // TODO: Real intermediate shard blocks
136            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    /// Create a new instance
177    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        // TODO: Execute block
249
250        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}