ab_client_api/
lib.rs

1//! Client API
2
3#![expect(incomplete_features, reason = "generic_const_exprs")]
4// TODO: This feature is not actually used in this crate, but is added as a workaround for
5//  https://github.com/rust-lang/rust/issues/141492
6#![feature(generic_const_exprs)]
7
8use ab_aligned_buffer::SharedAlignedBuffer;
9use ab_core_primitives::address::Address;
10use ab_core_primitives::block::owned::GenericOwnedBlock;
11use ab_core_primitives::block::{BlockNumber, BlockRoot};
12use ab_merkle_tree::mmr::MerkleMountainRange;
13use rclite::Arc;
14use std::io;
15use std::sync::Arc as StdArc;
16
17// TODO: This is a workaround for https://github.com/rust-lang/rust/issues/139866 that allows the
18//  code to compile. Constant 4294967295 is hardcoded here and below for compilation to succeed.
19const _: () = {
20    assert!(u32::MAX == 4294967295);
21};
22
23// TODO: Make this a `#[transparent]` struct to improve usability (avoiding the need for
24//  `generic_const_exprs` feature in downstream crates)?
25/// Type alias for Merkle Mountain Range with block roots.
26///
27/// NOTE: `u32` is smaller than `BlockNumber`'s internal `u64` but will be sufficient for a long
28/// time and substantially decrease the size of the data structure.
29pub type BlockMerkleMountainRange = MerkleMountainRange<4294967295>;
30
31/// State of a contract slot
32#[derive(Debug, Clone)]
33pub struct ContractSlotState {
34    /// Owner of the slot
35    pub owner: Address,
36    /// Contract that manages the slot
37    pub contract: Address,
38    /// Slot contents
39    pub contents: SharedAlignedBuffer,
40}
41
42/// Additional details about a block
43#[derive(Debug, Clone)]
44pub struct BlockDetails {
45    /// Merkle Mountain Range with block
46    pub mmr_with_block: Arc<BlockMerkleMountainRange>,
47    /// System contracts state after block
48    pub system_contract_states: StdArc<[ContractSlotState]>,
49}
50
51// TODO: Probably move it elsewhere
52/// Origin
53#[derive(Debug, Clone)]
54pub enum BlockOrigin {
55    // TODO: Take advantage of this in block import
56    /// Created locally by block builder
57    LocalBlockBuilder {
58        /// Additional details about a block
59        block_details: BlockDetails,
60    },
61    /// Received during the sync process
62    Sync,
63    /// Broadcast on the network during normal operation (not sync)
64    Broadcast,
65}
66
67/// Error for [`ChainInfoWrite::persist_block()`]
68#[derive(Debug, thiserror::Error)]
69pub enum PersistBlockError {
70    /// Missing parent
71    #[error("Missing parent")]
72    MissingParent,
73    /// Block is outside the acceptable range
74    #[error("Block is outside the acceptable range")]
75    OutsideAcceptableRange,
76    /// Storage item write error
77    #[error("Storage item write error")]
78    StorageItemWriteError {
79        /// Low-level error
80        #[from]
81        error: io::Error,
82    },
83}
84
85// TODO: Split this into different more narrow traits
86/// Chain info.
87///
88/// NOTE:
89/// <div class="warning">
90/// Blocks or their parts returned from these APIs are reference-counted and cheap to clone.
91/// However, it is not expected that they will be retained in memory for a long time. Blocks and
92/// headers will not be pruned until their reference count goes down to one. This is imported when
93/// there is an ongoing block import happening and its parent must exist until the import
94/// finishes.
95/// </div>
96pub trait ChainInfo<Block>: Clone + Send + Sync
97where
98    Block: GenericOwnedBlock,
99{
100    /// Best block root
101    fn best_root(&self) -> BlockRoot;
102
103    // TODO: Uncomment if/when necessary
104    // /// Find root of ancestor block number for descendant block root
105    // fn ancestor_root(
106    //     &self,
107    //     ancestor_block_number: BlockNumber,
108    //     descendant_block_root: &BlockRoot,
109    // ) -> Option<BlockRoot>;
110
111    /// Best block header
112    fn best_header(&self) -> Block::Header;
113
114    /// Returns the best block header like [`Self::best_header()`] with additional block details
115    fn best_header_with_details(&self) -> (Block::Header, BlockDetails);
116
117    /// Get header of ancestor block number for descendant block root
118    fn ancestor_header(
119        &self,
120        ancestor_block_number: BlockNumber,
121        descendant_block_root: &BlockRoot,
122    ) -> Option<Block::Header>;
123
124    /// Block header
125    fn header(&self, block_root: &BlockRoot) -> Option<Block::Header>;
126
127    /// Returns a block header like [`Self::header()`] with additional block details
128    fn header_with_details(&self, block_root: &BlockRoot) -> Option<(Block::Header, BlockDetails)>;
129}
130
131/// [`ChainInfo`] extension for writing information
132pub trait ChainInfoWrite<Block>: ChainInfo<Block>
133where
134    Block: GenericOwnedBlock,
135{
136    /// Persist newly imported block
137    fn persist_block(
138        &self,
139        block: Block,
140        block_details: BlockDetails,
141    ) -> impl Future<Output = Result<(), PersistBlockError>> + Send;
142}
143
144/// Chain sync status
145pub trait ChainSyncStatus: Clone + Send + Sync + 'static {
146    /// The block number that the sync process is targeting right now.
147    ///
148    /// Can be zero if not syncing actively.
149    fn target_block_number(&self) -> BlockNumber;
150
151    /// Returns `true` if the chain is currently syncing
152    fn is_syncing(&self) -> bool;
153
154    /// Returns `true` if the node is currently offline
155    fn is_offline(&self) -> bool;
156}