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