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