Skip to main content

ab_client_api/
lib.rs

1//! Client API
2
3#![feature(const_convert, const_trait_impl)]
4
5use ab_aligned_buffer::SharedAlignedBuffer;
6use ab_core_primitives::address::Address;
7use ab_core_primitives::block::owned::{GenericOwnedBlock, OwnedBeaconChainBlock};
8use ab_core_primitives::block::{BlockNumber, BlockRoot};
9use ab_core_primitives::segments::{
10    LocalSegmentIndex, SegmentHeader, SegmentIndex, SegmentRoot, SuperSegmentHeader,
11    SuperSegmentIndex,
12};
13use ab_core_primitives::shard::ShardIndex;
14use ab_merkle_tree::mmr::MerkleMountainRange;
15use std::io;
16use triomphe::Arc;
17
18const MAX_U32_AS_U64: u64 = u64::from(u32::MAX);
19/// Type alias for Merkle Mountain Range with block roots.
20///
21/// NOTE: `u32` is smaller than `BlockNumber`'s internal `u64` but will be sufficient for a long
22/// time and substantially decrease the size of the in-memory data structure.
23pub type BlockMerkleMountainRange = MerkleMountainRange<MAX_U32_AS_U64>;
24
25/// State of a contract slot
26#[derive(Debug, Clone)]
27pub struct ContractSlotState {
28    /// Owner of the slot
29    pub owner: Address,
30    /// Contract that manages the slot
31    pub contract: Address,
32    /// Slot contents
33    pub contents: SharedAlignedBuffer,
34}
35
36/// Additional details about a block
37#[derive(Debug, Clone)]
38pub struct BlockDetails {
39    /// Merkle Mountain Range with block
40    pub mmr_with_block: Arc<BlockMerkleMountainRange>,
41    /// System contracts state after block
42    pub system_contract_states: Arc<[ContractSlotState]>,
43}
44
45// TODO: Probably move it elsewhere
46/// Origin
47#[derive(Debug, Clone)]
48pub enum BlockOrigin {
49    // TODO: Take advantage of this in block import
50    /// Created locally by block builder
51    LocalBlockBuilder {
52        /// Additional details about a block
53        block_details: BlockDetails,
54    },
55    /// Received during the sync process
56    Sync,
57    /// Broadcast on the network during normal operation (not sync)
58    Broadcast,
59}
60
61/// Intermediate or leaf shard segment root information
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct ShardSegmentRoot {
64    /// Shard index
65    pub shard_index: ShardIndex,
66    /// Local segment index
67    pub segment_index: LocalSegmentIndex,
68    /// Segment root
69    pub segment_root: SegmentRoot,
70}
71
72/// Error for [`ChainInfo::block()`]
73#[derive(Debug, thiserror::Error)]
74pub enum ReadBlockError {
75    /// Unknown block root
76    #[error("Unknown block root")]
77    UnknownBlockRoot,
78    /// Failed to decode the block
79    #[error("Failed to decode the block")]
80    FailedToDecode,
81    /// Storage item read error
82    #[error("Storage item read error")]
83    StorageItemReadError {
84        /// Low-level error
85        #[from]
86        error: io::Error,
87    },
88}
89
90/// Error for [`ChainInfoWrite::persist_block()`]
91#[derive(Debug, thiserror::Error)]
92pub enum PersistBlockError {
93    /// Missing parent
94    #[error("Missing parent")]
95    MissingParent,
96    /// Block is outside the acceptable range
97    #[error("Block is outside the acceptable range")]
98    OutsideAcceptableRange,
99    /// Storage item write error
100    #[error("Storage item write error")]
101    StorageItemWriteError {
102        /// Low-level error
103        #[from]
104        error: io::Error,
105    },
106}
107
108/// Error for [`ChainInfoWrite::persist_segment_headers()`]
109#[derive(Debug, thiserror::Error)]
110pub enum PersistSegmentHeadersError {
111    /// Segment index must strictly follow the last segment index, can't store segment header
112    #[error(
113        "Segment index {local_segment_index} must strictly follow last segment index \
114        {last_local_segment_index}, can't store segment header"
115    )]
116    MustFollowLastSegmentIndex {
117        /// Segment index that was attempted to be inserted
118        local_segment_index: LocalSegmentIndex,
119        /// Last segment index
120        last_local_segment_index: LocalSegmentIndex,
121    },
122    /// The first segment index must be zero
123    #[error("First segment index must be zero, found {local_segment_index}")]
124    FirstSegmentIndexZero {
125        /// Segment index that was attempted to be inserted
126        local_segment_index: LocalSegmentIndex,
127    },
128    /// Storage item write error
129    #[error("Storage item write error")]
130    StorageItemWriteError {
131        /// Low-level error
132        #[from]
133        error: io::Error,
134    },
135}
136
137/// Error for [`BeaconChainInfo::shard_segment_roots()`]
138#[derive(Debug, thiserror::Error)]
139pub enum ShardSegmentRootsError {
140    /// Block missing
141    #[error("Block {block_number} is missing")]
142    BlockMissing {
143        /// The block number that is missing in the database
144        block_number: BlockNumber,
145    },
146}
147
148/// Error for [`BeaconChainInfoWrite::persist_super_segment_headers()`]
149#[derive(Debug, thiserror::Error)]
150pub enum PersistSuperSegmentHeadersError {
151    /// Super segment index must strictly follow the last super segment index, can't store super
152    /// segment header
153    #[error(
154        "Super segment index {super_segment_index} must strictly follow last super segment index \
155        {last_super_segment_index}, can't store super segment header"
156    )]
157    MustFollowLastSegmentIndex {
158        /// Super segment index that was attempted to be inserted
159        super_segment_index: SuperSegmentIndex,
160        /// Last super segment index
161        last_super_segment_index: SuperSegmentIndex,
162    },
163    /// The first super segment index must be zero
164    #[error("First super segment index must be zero, found {super_segment_index}")]
165    FirstSegmentIndexZero {
166        /// Super segment index that was attempted to be inserted
167        super_segment_index: SuperSegmentIndex,
168    },
169    /// Storage item write error
170    #[error("Storage item write error")]
171    StorageItemWriteError {
172        /// Low-level error
173        #[from]
174        error: io::Error,
175    },
176}
177
178// TODO: Split this into different more narrow traits
179/// Chain info.
180///
181/// NOTE:
182/// <div class="warning">
183/// Blocks or their parts returned from these APIs are reference-counted and cheap to clone.
184/// However, it is not expected that they will be retained in memory for a long time. Blocks and
185/// headers will not be pruned until their reference count goes down to one. This is imported when
186/// there is an ongoing block import happening and its parent must exist until the import
187/// finishes.
188/// </div>
189pub trait ChainInfo<Block>: Clone + Send + Sync + 'static
190where
191    Block: GenericOwnedBlock,
192{
193    /// Best block root
194    fn best_root(&self) -> BlockRoot;
195
196    // TODO: Uncomment if/when necessary
197    // /// Find root of ancestor block number for descendant block root
198    // fn ancestor_root(
199    //     &self,
200    //     ancestor_block_number: BlockNumber,
201    //     descendant_block_root: &BlockRoot,
202    // ) -> Option<BlockRoot>;
203
204    /// Best block header
205    fn best_header(&self) -> Block::Header;
206
207    /// Returns the best block header like [`Self::best_header()`] with additional block details
208    fn best_header_with_details(&self) -> (Block::Header, BlockDetails);
209
210    /// Get header of ancestor block number for descendant block root
211    fn ancestor_header(
212        &self,
213        ancestor_block_number: BlockNumber,
214        descendant_block_root: &BlockRoot,
215    ) -> Option<Block::Header>;
216
217    /// Block header
218    fn header(&self, block_root: &BlockRoot) -> Option<Block::Header>;
219
220    /// Returns a block header like [`Self::header()`] with additional block details
221    fn header_with_details(&self, block_root: &BlockRoot) -> Option<(Block::Header, BlockDetails)>;
222
223    fn block(
224        &self,
225        block_root: &BlockRoot,
226    ) -> impl Future<Output = Result<Block, ReadBlockError>> + Send;
227
228    /// Returns the last observed local segment header of this shard
229    fn last_segment_header(&self) -> Option<SegmentHeader>;
230
231    /// Get a single segment header
232    fn get_segment_header(&self, segment_index: LocalSegmentIndex) -> Option<SegmentHeader>;
233
234    /// Get segment headers that are expected to be included at specified block number
235    fn segment_headers_for_block(&self, block_number: BlockNumber) -> Vec<SegmentHeader>;
236}
237
238/// [`ChainInfo`] extension for writing information
239pub trait ChainInfoWrite<Block>: ChainInfo<Block>
240where
241    Block: GenericOwnedBlock,
242{
243    /// Persist newly imported block
244    fn persist_block(
245        &self,
246        block: Block,
247        block_details: BlockDetails,
248    ) -> impl Future<Output = Result<(), PersistBlockError>> + Send;
249
250    /// Persist segment headers.
251    ///
252    /// Multiple can be inserted for efficiency purposes.
253    fn persist_segment_headers(
254        &self,
255        segment_headers: Vec<SegmentHeader>,
256    ) -> impl Future<Output = Result<(), PersistSegmentHeadersError>> + Send;
257}
258
259/// Beacon chain info
260pub trait BeaconChainInfo: ChainInfo<OwnedBeaconChainBlock> {
261    /// Returns intermediate and leaf shard segment roots included in the specified block number.
262    ///
263    /// NOTE: Since blocks at this depth are already confirmed, only a block number is needed as a
264    /// reference.
265    fn shard_segment_roots(
266        &self,
267        block_number: BlockNumber,
268    ) -> Result<Arc<[ShardSegmentRoot]>, ShardSegmentRootsError>;
269
270    /// Returns the last observed super segment header
271    fn last_super_segment_header(&self) -> Option<SuperSegmentHeader>;
272
273    /// Returns the previous super segment header for the block built with the specified target
274    /// block number.
275    ///
276    /// `None` is returned for blocks <= 1.
277    fn previous_super_segment_header(
278        &self,
279        block_number: BlockNumber,
280    ) -> Option<SuperSegmentHeader>;
281
282    /// Get a single super segment header
283    fn get_super_segment_header(
284        &self,
285        super_segment_index: SuperSegmentIndex,
286    ) -> Option<SuperSegmentHeader>;
287
288    /// Get a single super segment header for a segment index
289    fn get_super_segment_header_for_segment_index(
290        &self,
291        segment_index: SegmentIndex,
292    ) -> Option<SuperSegmentHeader>;
293}
294
295/// [`BeaconChainInfo`] extension for writing information
296pub trait BeaconChainInfoWrite: BeaconChainInfo + ChainInfoWrite<OwnedBeaconChainBlock> {
297    /// Persist a new super segment header.
298    ///
299    /// Returns `Ok(true)` if the header was inserted, `Ok(false)` if it was already present.
300    fn persist_super_segment_header(
301        &self,
302        super_segment_header: SuperSegmentHeader,
303    ) -> impl Future<Output = Result<bool, PersistSuperSegmentHeadersError>> + Send;
304
305    /// Persist super segment headers.
306    ///
307    /// Multiple can be inserted for efficiency purposes.
308    fn persist_super_segment_headers(
309        &self,
310        super_segment_headers: Vec<SuperSegmentHeader>,
311    ) -> impl Future<Output = Result<(), PersistSuperSegmentHeadersError>> + Send;
312}
313
314/// Chain sync status
315pub trait ChainSyncStatus: Clone + Send + Sync + 'static {
316    /// The block number that the sync process is targeting right now.
317    ///
318    /// Can be zero if not syncing actively.
319    fn target_block_number(&self) -> BlockNumber;
320
321    /// Returns `true` if the chain is currently syncing
322    fn is_syncing(&self) -> bool;
323
324    /// Returns `true` if the node is currently offline
325    fn is_offline(&self) -> bool;
326}