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;
12use ab_core_primitives::block::{BlockNumber, BlockRoot};
13use ab_core_primitives::segments::{LocalSegmentIndex, SegmentHeader};
14use ab_merkle_tree::mmr::MerkleMountainRange;
15use rclite::Arc;
16use std::io;
17use std::sync::Arc as StdArc;
18
19// TODO: This is a workaround for https://github.com/rust-lang/rust/issues/139866 that allows the
20//  code to compile. Constant 4294967295 is hardcoded here and below for compilation to succeed.
21#[expect(clippy::assertions_on_constants, reason = "Intentional documentation")]
22#[expect(clippy::eq_op, reason = "Intentional documentation")]
23const {
24    assert!(u32::MAX == 4294967295);
25}
26
27// TODO: Make this a `#[transparent]` struct to improve usability (avoiding the need for
28//  `generic_const_exprs` feature in downstream crates)?
29/// Type alias for Merkle Mountain Range with block roots.
30///
31/// NOTE: `u32` is smaller than `BlockNumber`'s internal `u64` but will be sufficient for a long
32/// time and substantially decrease the size of the data structure.
33pub type BlockMerkleMountainRange = MerkleMountainRange<4294967295>;
34
35/// State of a contract slot
36#[derive(Debug, Clone)]
37pub struct ContractSlotState {
38    /// Owner of the slot
39    pub owner: Address,
40    /// Contract that manages the slot
41    pub contract: Address,
42    /// Slot contents
43    pub contents: SharedAlignedBuffer,
44}
45
46/// Additional details about a block
47#[derive(Debug, Clone)]
48pub struct BlockDetails {
49    /// Merkle Mountain Range with block
50    pub mmr_with_block: Arc<BlockMerkleMountainRange>,
51    /// System contracts state after block
52    pub system_contract_states: StdArc<[ContractSlotState]>,
53}
54
55// TODO: Probably move it elsewhere
56/// Origin
57#[derive(Debug, Clone)]
58pub enum BlockOrigin {
59    // TODO: Take advantage of this in block import
60    /// Created locally by block builder
61    LocalBlockBuilder {
62        /// Additional details about a block
63        block_details: BlockDetails,
64    },
65    /// Received during the sync process
66    Sync,
67    /// Broadcast on the network during normal operation (not sync)
68    Broadcast,
69}
70
71/// Error for [`ChainInfo::block()`]
72#[derive(Debug, thiserror::Error)]
73pub enum ReadBlockError {
74    /// Unknown block root
75    #[error("Unknown block root")]
76    UnknownBlockRoot,
77    /// Failed to decode the block
78    #[error("Failed to decode the block")]
79    FailedToDecode,
80    /// Storage item read error
81    #[error("Storage item read error")]
82    StorageItemReadError {
83        /// Low-level error
84        #[from]
85        error: io::Error,
86    },
87}
88
89/// Error for [`ChainInfoWrite::persist_block()`]
90#[derive(Debug, thiserror::Error)]
91pub enum PersistBlockError {
92    /// Missing parent
93    #[error("Missing parent")]
94    MissingParent,
95    /// Block is outside the acceptable range
96    #[error("Block is outside the acceptable range")]
97    OutsideAcceptableRange,
98    /// Storage item write error
99    #[error("Storage item write error")]
100    StorageItemWriteError {
101        /// Low-level error
102        #[from]
103        error: io::Error,
104    },
105}
106
107/// Error for [`ChainInfoWrite::persist_segment_headers()`]
108#[derive(Debug, thiserror::Error)]
109pub enum PersistSegmentHeadersError {
110    /// Segment index must strictly follow the last segment index, can't store segment header
111    #[error(
112        "Segment index {local_segment_index} must strictly follow last segment index \
113        {last_local_segment_index}, can't store segment header"
114    )]
115    MustFollowLastSegmentIndex {
116        /// Segment index that was attempted to be inserted
117        local_segment_index: LocalSegmentIndex,
118        /// Last segment index
119        last_local_segment_index: LocalSegmentIndex,
120    },
121    /// The first segment index must be zero
122    #[error("First segment index must be zero, found {local_segment_index}")]
123    FirstSegmentIndexZero {
124        /// Segment index that was attempted to be inserted
125        local_segment_index: LocalSegmentIndex,
126    },
127    /// Storage item write error
128    #[error("Storage item write error")]
129    StorageItemWriteError {
130        /// Low-level error
131        #[from]
132        error: io::Error,
133    },
134}
135
136// TODO: Split this into different more narrow traits
137/// Chain info.
138///
139/// NOTE:
140/// <div class="warning">
141/// Blocks or their parts returned from these APIs are reference-counted and cheap to clone.
142/// However, it is not expected that they will be retained in memory for a long time. Blocks and
143/// headers will not be pruned until their reference count goes down to one. This is imported when
144/// there is an ongoing block import happening and its parent must exist until the import
145/// finishes.
146/// </div>
147pub trait ChainInfo<Block>: Clone + Send + Sync + 'static
148where
149    Block: GenericOwnedBlock,
150{
151    /// Best block root
152    fn best_root(&self) -> BlockRoot;
153
154    // TODO: Uncomment if/when necessary
155    // /// Find root of ancestor block number for descendant block root
156    // fn ancestor_root(
157    //     &self,
158    //     ancestor_block_number: BlockNumber,
159    //     descendant_block_root: &BlockRoot,
160    // ) -> Option<BlockRoot>;
161
162    /// Best block header
163    fn best_header(&self) -> Block::Header;
164
165    /// Returns the best block header like [`Self::best_header()`] with additional block details
166    fn best_header_with_details(&self) -> (Block::Header, BlockDetails);
167
168    /// Get header of ancestor block number for descendant block root
169    fn ancestor_header(
170        &self,
171        ancestor_block_number: BlockNumber,
172        descendant_block_root: &BlockRoot,
173    ) -> Option<Block::Header>;
174
175    /// Block header
176    fn header(&self, block_root: &BlockRoot) -> Option<Block::Header>;
177
178    /// Returns a block header like [`Self::header()`] with additional block details
179    fn header_with_details(&self, block_root: &BlockRoot) -> Option<(Block::Header, BlockDetails)>;
180
181    fn block(
182        &self,
183        block_root: &BlockRoot,
184    ) -> impl Future<Output = Result<Block, ReadBlockError>> + Send;
185
186    /// Returns last observed segment header
187    fn last_segment_header(&self) -> Option<SegmentHeader>;
188
189    /// Returns last observed segment index
190    fn max_local_segment_index(&self) -> Option<LocalSegmentIndex>;
191
192    /// Get a single segment header
193    fn get_segment_header(&self, segment_index: LocalSegmentIndex) -> Option<SegmentHeader>;
194
195    /// Get segment headers that are expected to be included at specified block number.
196    fn segment_headers_for_block(&self, block_number: BlockNumber) -> Vec<SegmentHeader>;
197}
198
199/// [`ChainInfo`] extension for writing information
200pub trait ChainInfoWrite<Block>: ChainInfo<Block>
201where
202    Block: GenericOwnedBlock,
203{
204    /// Persist newly imported block
205    fn persist_block(
206        &self,
207        block: Block,
208        block_details: BlockDetails,
209    ) -> impl Future<Output = Result<(), PersistBlockError>> + Send;
210
211    /// Persist segment headers.
212    ///
213    /// Multiple can be inserted for efficiency purposes.
214    fn persist_segment_headers(
215        &self,
216        segment_headers: Vec<SegmentHeader>,
217    ) -> impl Future<Output = Result<(), PersistSegmentHeadersError>> + Send;
218}
219
220/// Chain sync status
221pub trait ChainSyncStatus: Clone + Send + Sync + 'static {
222    /// The block number that the sync process is targeting right now.
223    ///
224    /// Can be zero if not syncing actively.
225    fn target_block_number(&self) -> BlockNumber;
226
227    /// Returns `true` if the chain is currently syncing
228    fn is_syncing(&self) -> bool;
229
230    /// Returns `true` if the node is currently offline
231    fn is_offline(&self) -> bool;
232}