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::{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/// Error for [`BeaconChainBlockBuilder`]
29#[derive(Debug, thiserror::Error)]
30pub enum BeaconChainBlockBuilderError {
31    /// Consensus parameters derivation error
32    #[error("Consensus parameters derivation error: {error}")]
33    ConsensusParametersDerivation {
34        /// Consensus parameters derivation error
35        #[from]
36        error: DeriveConsensusParametersError,
37    },
38    /// Failed to create body
39    #[error("Failed to create body: {error}")]
40    FailedToCreateBody {
41        // Body creation error
42        #[from]
43        error: OwnedBeaconChainBodyError,
44    },
45    /// Failed to create a header
46    #[error("Failed to create a header: {error}")]
47    FailedToCreateHeader {
48        // Header creation error
49        #[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// TODO: Another domain-specific abstraction over `ChainInfo`, which will be implemented for
64//  `ChainInfo`, but could also be implemented in simpler way directly for tests without dealing
65//  with complete headers, etc.
66/// Beacon chain block builder
67#[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            // TODO: Real intermediate shard blocks
118            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    /// Create a new instance
158    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            // TODO: Super segment support
217            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        // TODO: Execute block
230
231        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}