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