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| segment_header.segment_root),
112            // TODO: Real intermediate shard blocks
113            iter::empty(),
114            checkpoints,
115        )
116        .map_err(BeaconChainBlockBuilderError::from)?;
117
118        let block_unsealed = block_builder
119            .with_header(
120                &header_prefix,
121                state_root,
122                consensus_info,
123                &consensus_parameters.as_ref(),
124            )
125            .map_err(BeaconChainBlockBuilderError::from)?;
126
127        let seal = seal_block(block_unsealed.pre_seal_hash())
128            .await
129            .ok_or(BlockBuilderError::FailedToSeal)?;
130        let block = block_unsealed.with_seal(seal.as_ref());
131
132        let mut block_mmr = *parent_block_details.mmr_with_block;
133
134        if !block_mmr.add_leaf(&block.header.header().root()) {
135            return Err(BlockBuilderError::CantExtendMmr);
136        }
137
138        Ok(BlockBuilderResult {
139            block,
140            block_details: BlockDetails {
141                mmr_with_block: Arc::new(block_mmr),
142                system_contract_states,
143            },
144        })
145    }
146}
147
148impl<CI> BeaconChainBlockBuilder<CI>
149where
150    CI: ChainInfo<OwnedBeaconChainBlock>,
151{
152    /// Create a new instance
153    pub fn new(consensus_constants: ConsensusConstants, chain_info: CI) -> Self {
154        Self {
155            consensus_constants,
156            chain_info,
157        }
158    }
159
160    fn create_header_prefix(
161        &self,
162        parent_block_root: &BlockRoot,
163        parent_timestamp: BlockTimestamp,
164        mmr_with_block: &BlockMerkleMountainRange,
165        block_number: BlockNumber,
166    ) -> Result<BlockHeaderPrefix, BlockBuilderError> {
167        let timestamp = SystemTime::now()
168            .duration_since(SystemTime::UNIX_EPOCH)
169            .unwrap_or_default()
170            .as_millis();
171        let mut timestamp =
172            BlockTimestamp::from_millis(u64::try_from(timestamp).unwrap_or(u64::MAX));
173
174        if timestamp <= parent_timestamp {
175            timestamp = BlockTimestamp::from_millis(parent_timestamp.as_millis().saturating_add(1));
176        }
177
178        Ok(BlockHeaderPrefix {
179            number: block_number,
180            shard_index: ShardIndex::BEACON_CHAIN,
181            padding_0: [0; _],
182            timestamp,
183            parent_root: *parent_block_root,
184            mmr_root: Blake3Hash::new(
185                mmr_with_block
186                    .root()
187                    .ok_or(BlockBuilderError::InvalidParentMmr)?,
188            ),
189        })
190    }
191
192    fn derive_consensus_parameters(
193        &self,
194        parent_block_root: &BlockRoot,
195        parent_header: &BeaconChainHeader<'_>,
196        block_number: BlockNumber,
197        slot: SlotNumber,
198    ) -> Result<OwnedBlockHeaderConsensusParameters, BeaconChainBlockBuilderError> {
199        let derived_consensus_parameters = derive_consensus_parameters(
200            &self.consensus_constants,
201            &self.chain_info,
202            parent_block_root,
203            parent_header.consensus_parameters(),
204            parent_header.consensus_info.slot,
205            block_number,
206            slot,
207        )?;
208
209        Ok(OwnedBlockHeaderConsensusParameters {
210            fixed_parameters: derived_consensus_parameters.fixed_parameters,
211            // TODO: Super segment support
212            super_segment_root: None,
213            next_solution_range: derived_consensus_parameters.next_solution_range,
214            pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
215        })
216    }
217
218    fn execute_block(
219        &self,
220        parent_block_details: &BlockDetails,
221    ) -> Result<(Blake3Hash, StdArc<[ContractSlotState]>), BeaconChainBlockBuilderError> {
222        let global_state = GlobalState::new(&parent_block_details.system_contract_states);
223
224        // TODO: Execute block
225
226        let state_root = global_state.root();
227        let system_contract_states = global_state.to_system_contract_states();
228
229        Ok((state_root, system_contract_states))
230    }
231}