ab_client_block_builder/
beacon_chain.rs

1use crate::{BlockBuilder, BlockBuilderError};
2use ab_client_api::ChainInfo;
3use ab_client_archiving::segment_headers_store::SegmentHeadersStore;
4use ab_client_consensus_common::ConsensusConstants;
5use ab_client_consensus_common::consensus_parameters::{
6    DeriveConsensusParametersError, derive_consensus_parameters,
7};
8use ab_core_primitives::block::body::owned::OwnedBeaconChainBodyError;
9use ab_core_primitives::block::header::owned::{
10    GenericOwnedBlockHeader, OwnedBeaconChainHeader, OwnedBeaconChainHeaderError,
11};
12use ab_core_primitives::block::header::{
13    BeaconChainHeader, BlockHeaderConsensusInfo, BlockHeaderPrefix,
14    OwnedBlockHeaderConsensusParameters, OwnedBlockHeaderSeal,
15};
16use ab_core_primitives::block::owned::OwnedBeaconChainBlock;
17use ab_core_primitives::block::{BlockNumber, BlockRoot, BlockTimestamp};
18use ab_core_primitives::hashes::Blake3Hash;
19use ab_core_primitives::pot::{PotCheckpoints, SlotNumber};
20use ab_core_primitives::shard::ShardIndex;
21use std::iter;
22use std::time::SystemTime;
23
24/// Error for [`BeaconChainBlockBuilder`]
25#[derive(Debug, thiserror::Error)]
26pub enum BeaconChainBlockBuilderError {
27    /// Consensus parameters derivation error
28    #[error("Consensus parameters derivation error: {error}")]
29    ConsensusParametersDerivation {
30        /// Consensus parameters derivation error
31        #[from]
32        error: DeriveConsensusParametersError,
33    },
34    /// Failed to create body
35    #[error("Failed to create body: {error}")]
36    FailedToCreateBody {
37        // Body creation error
38        #[from]
39        error: OwnedBeaconChainBodyError,
40    },
41    /// Failed to create header
42    #[error("Failed to create header: {error}")]
43    FailedToCreateHeader {
44        // Header creation error
45        #[from]
46        error: OwnedBeaconChainHeaderError,
47    },
48}
49
50impl From<BeaconChainBlockBuilderError> for BlockBuilderError {
51    #[inline(always)]
52    fn from(error: BeaconChainBlockBuilderError) -> Self {
53        Self::Custom {
54            error: error.into(),
55        }
56    }
57}
58
59// TODO: Another domain-specific abstraction over `ChainInfo`, which will be implemented for
60//  `ChainInfo`, but could also be implemented in simpler way directly for tests without dealing
61//  with complete headers, etc.
62/// Beacon chain block builder
63#[derive(Debug)]
64pub struct BeaconChainBlockBuilder<CI> {
65    segment_headers_store: SegmentHeadersStore,
66    consensus_constants: ConsensusConstants,
67    chain_info: CI,
68}
69
70impl<CI> BlockBuilder<OwnedBeaconChainBlock> for BeaconChainBlockBuilder<CI>
71where
72    CI: ChainInfo<OwnedBeaconChainBlock>,
73{
74    async fn build<SealBlock, SealBlockFut>(
75        &mut self,
76        parent_block_root: &BlockRoot,
77        parent_header: &<OwnedBeaconChainHeader as GenericOwnedBlockHeader>::Header<'_>,
78        consensus_info: &BlockHeaderConsensusInfo,
79        checkpoints: &[PotCheckpoints],
80        seal_block: SealBlock,
81    ) -> Result<OwnedBeaconChainBlock, BlockBuilderError>
82    where
83        SealBlock: FnOnce(Blake3Hash) -> SealBlockFut + Send,
84        SealBlockFut: Future<Output = Option<OwnedBlockHeaderSeal>> + Send,
85    {
86        let block_number = parent_header.prefix.number.saturating_add(BlockNumber::ONE);
87
88        let header_prefix = self.create_header_prefix(
89            parent_block_root,
90            parent_header.prefix.timestamp,
91            block_number,
92        );
93        let consensus_parameters = self.derive_consensus_parameters(
94            parent_block_root,
95            parent_header,
96            block_number,
97            consensus_info.slot,
98        )?;
99
100        let block_builder = OwnedBeaconChainBlock::init(
101            self.segment_headers_store
102                .segment_headers_for_block(block_number)
103                .into_iter()
104                .map(|segment_header| segment_header.segment_root),
105            // TODO: Real intermediate shard blocks
106            iter::empty(),
107            checkpoints,
108        )
109        .map_err(BeaconChainBlockBuilderError::from)?;
110
111        let block_unsealed = block_builder
112            .with_header(
113                &header_prefix,
114                // TODO: Real state root
115                Default::default(),
116                consensus_info,
117                consensus_parameters.as_ref(),
118            )
119            .map_err(BeaconChainBlockBuilderError::from)?;
120
121        let seal = seal_block(block_unsealed.pre_seal_hash())
122            .await
123            .ok_or(BlockBuilderError::FailedToSeal)?;
124        let block = block_unsealed.with_seal(seal.as_ref());
125
126        Ok(block)
127    }
128}
129
130impl<CI> BeaconChainBlockBuilder<CI>
131where
132    CI: ChainInfo<OwnedBeaconChainBlock>,
133{
134    /// Create a new instance
135    pub fn new(
136        segment_headers_store: SegmentHeadersStore,
137        consensus_constants: ConsensusConstants,
138        chain_info: CI,
139    ) -> Self {
140        Self {
141            segment_headers_store,
142            consensus_constants,
143            chain_info,
144        }
145    }
146
147    fn create_header_prefix(
148        &self,
149        parent_block_root: &BlockRoot,
150        parent_timestamp: BlockTimestamp,
151        block_number: BlockNumber,
152    ) -> BlockHeaderPrefix {
153        let timestamp = SystemTime::now()
154            .duration_since(SystemTime::UNIX_EPOCH)
155            .unwrap_or_default()
156            .as_millis();
157        let mut timestamp = BlockTimestamp::new(u64::try_from(timestamp).unwrap_or(u64::MAX));
158
159        if timestamp <= parent_timestamp {
160            timestamp = BlockTimestamp::new(parent_timestamp.as_ms().saturating_add(1));
161        }
162
163        BlockHeaderPrefix {
164            number: block_number,
165            shard_index: ShardIndex::BEACON_CHAIN,
166            padding_0: [0; _],
167            timestamp,
168            parent_root: *parent_block_root,
169            // TODO: Real MMR root
170            mmr_root: Default::default(),
171        }
172    }
173
174    fn derive_consensus_parameters(
175        &self,
176        parent_block_root: &BlockRoot,
177        parent_header: &BeaconChainHeader<'_>,
178        block_number: BlockNumber,
179        slot: SlotNumber,
180    ) -> Result<OwnedBlockHeaderConsensusParameters, BeaconChainBlockBuilderError> {
181        let derived_consensus_parameters = derive_consensus_parameters(
182            &self.consensus_constants,
183            &self.chain_info,
184            parent_block_root,
185            parent_header.consensus_parameters(),
186            parent_header.consensus_info.slot,
187            block_number,
188            slot,
189        )?;
190
191        Ok(OwnedBlockHeaderConsensusParameters {
192            fixed_parameters: derived_consensus_parameters.fixed_parameters,
193            // TODO: Super segment support
194            super_segment_root: None,
195            next_solution_range: derived_consensus_parameters.next_solution_range,
196            pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
197        })
198    }
199}