Skip to main content

ab_client_api/
lib.rs

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