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_archiving::segment_headers_store::SegmentHeadersStore;
6use ab_client_consensus_common::ConsensusConstants;
7use ab_client_consensus_common::consensus_parameters::{
8    DeriveConsensusParametersError, derive_consensus_parameters,
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::shard::ShardIndex;
24use rclite::Arc;
25use std::iter;
26use std::sync::Arc as StdArc;
27use std::time::SystemTime;
28
29/// Error for [`BeaconChainBlockBuilder`]
30#[derive(Debug, thiserror::Error)]
31pub enum BeaconChainBlockBuilderError {
32    /// Consensus parameters derivation error
33    #[error("Consensus parameters derivation error: {error}")]
34    ConsensusParametersDerivation {
35        /// Consensus parameters derivation error
36        #[from]
37        error: DeriveConsensusParametersError,
38    },
39    /// Failed to create body
40    #[error("Failed to create body: {error}")]
41    FailedToCreateBody {
42        // Body creation error
43        #[from]
44        error: OwnedBeaconChainBodyError,
45    },
46    /// Failed to create a header
47    #[error("Failed to create a header: {error}")]
48    FailedToCreateHeader {
49        // Header creation error
50        #[from]
51        error: OwnedBeaconChainHeaderError,
52    },
53}
54
55impl From<BeaconChainBlockBuilderError> for BlockBuilderError {
56    #[inline(always)]
57    fn from(error: BeaconChainBlockBuilderError) -> Self {
58        Self::Custom {
59            error: error.into(),
60        }
61    }
62}
63
64// TODO: Another domain-specific abstraction over `ChainInfo`, which will be implemented for
65//  `ChainInfo`, but could also be implemented in simpler way directly for tests without dealing
66//  with complete headers, etc.
67/// Beacon chain block builder
68#[derive(Debug)]
69pub struct BeaconChainBlockBuilder<CI> {
70    segment_headers_store: SegmentHeadersStore,
71    consensus_constants: ConsensusConstants,
72    chain_info: CI,
73}
74
75impl<CI> BlockBuilder<OwnedBeaconChainBlock> for BeaconChainBlockBuilder<CI>
76where
77    CI: ChainInfo<OwnedBeaconChainBlock>,
78{
79    async fn build<SealBlock>(
80        &mut self,
81        parent_block_root: &BlockRoot,
82        parent_header: &<OwnedBeaconChainHeader as GenericOwnedBlockHeader>::Header<'_>,
83        parent_block_details: &BlockDetails,
84        consensus_info: &BlockHeaderConsensusInfo,
85        checkpoints: &[PotCheckpoints],
86        seal_block: SealBlock,
87    ) -> Result<BlockBuilderResult<OwnedBeaconChainBlock>, BlockBuilderError>
88    where
89        SealBlock: AsyncFnOnce<(Blake3Hash,), Output = Option<OwnedBlockHeaderSeal>, CallOnceFuture: Send>
90            + Send,
91    {
92        let block_number = parent_header.prefix.number.saturating_add(BlockNumber::ONE);
93
94        let header_prefix = self.create_header_prefix(
95            parent_block_root,
96            parent_header.prefix.timestamp,
97            &parent_block_details.mmr_with_block,
98            block_number,
99        )?;
100        let consensus_parameters = self.derive_consensus_parameters(
101            parent_block_root,
102            parent_header,
103            block_number,
104            consensus_info.slot,
105        )?;
106
107        let (state_root, system_contract_states) = self.execute_block(parent_block_details)?;
108
109        let block_builder = OwnedBeaconChainBlock::init(
110            self.segment_headers_store
111                .segment_headers_for_block(block_number)
112                .into_iter()
113                .map(|segment_header| segment_header.segment_root),
114            // TODO: Real intermediate shard blocks
115            iter::empty(),
116            checkpoints,
117        )
118        .map_err(BeaconChainBlockBuilderError::from)?;
119
120        let block_unsealed = block_builder
121            .with_header(
122                &header_prefix,
123                state_root,
124                consensus_info,
125                consensus_parameters.as_ref(),
126            )
127            .map_err(BeaconChainBlockBuilderError::from)?;
128
129        let seal = seal_block(block_unsealed.pre_seal_hash())
130            .await
131            .ok_or(BlockBuilderError::FailedToSeal)?;
132        let block = block_unsealed.with_seal(seal.as_ref());
133
134        let mut block_mmr = *parent_block_details.mmr_with_block;
135
136        if !block_mmr.add_leaf(&block.header.header().root()) {
137            return Err(BlockBuilderError::CantExtendMmr);
138        }
139
140        Ok(BlockBuilderResult {
141            block,
142            block_details: BlockDetails {
143                mmr_with_block: Arc::new(block_mmr),
144                system_contract_states,
145            },
146        })
147    }
148}
149
150impl<CI> BeaconChainBlockBuilder<CI>
151where
152    CI: ChainInfo<OwnedBeaconChainBlock>,
153{
154    /// Create a new instance
155    pub fn new(
156        segment_headers_store: SegmentHeadersStore,
157        consensus_constants: ConsensusConstants,
158        chain_info: CI,
159    ) -> Self {
160        Self {
161            segment_headers_store,
162            consensus_constants,
163            chain_info,
164        }
165    }
166
167    fn create_header_prefix(
168        &self,
169        parent_block_root: &BlockRoot,
170        parent_timestamp: BlockTimestamp,
171        mmr_with_block: &BlockMerkleMountainRange,
172        block_number: BlockNumber,
173    ) -> Result<BlockHeaderPrefix, BlockBuilderError> {
174        let timestamp = SystemTime::now()
175            .duration_since(SystemTime::UNIX_EPOCH)
176            .unwrap_or_default()
177            .as_millis();
178        let mut timestamp =
179            BlockTimestamp::from_millis(u64::try_from(timestamp).unwrap_or(u64::MAX));
180
181        if timestamp <= parent_timestamp {
182            timestamp = BlockTimestamp::from_millis(parent_timestamp.as_millis().saturating_add(1));
183        }
184
185        Ok(BlockHeaderPrefix {
186            number: block_number,
187            shard_index: ShardIndex::BEACON_CHAIN,
188            padding_0: [0; _],
189            timestamp,
190            parent_root: *parent_block_root,
191            mmr_root: Blake3Hash::new(
192                mmr_with_block
193                    .root()
194                    .ok_or(BlockBuilderError::InvalidParentMmr)?,
195            ),
196        })
197    }
198
199    fn derive_consensus_parameters(
200        &self,
201        parent_block_root: &BlockRoot,
202        parent_header: &BeaconChainHeader<'_>,
203        block_number: BlockNumber,
204        slot: SlotNumber,
205    ) -> Result<OwnedBlockHeaderConsensusParameters, BeaconChainBlockBuilderError> {
206        let derived_consensus_parameters = derive_consensus_parameters(
207            &self.consensus_constants,
208            &self.chain_info,
209            parent_block_root,
210            parent_header.consensus_parameters(),
211            parent_header.consensus_info.slot,
212            block_number,
213            slot,
214        )?;
215
216        Ok(OwnedBlockHeaderConsensusParameters {
217            fixed_parameters: derived_consensus_parameters.fixed_parameters,
218            // TODO: Super segment support
219            super_segment_root: None,
220            next_solution_range: derived_consensus_parameters.next_solution_range,
221            pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
222        })
223    }
224
225    fn execute_block(
226        &self,
227        parent_block_details: &BlockDetails,
228    ) -> Result<(Blake3Hash, StdArc<[ContractSlotState]>), BeaconChainBlockBuilderError> {
229        let global_state = GlobalState::new(&parent_block_details.system_contract_states);
230
231        // TODO: Execute block
232
233        let state_root = global_state.root();
234        let system_contract_states = global_state.to_system_contract_states();
235
236        Ok((state_root, system_contract_states))
237    }
238}