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}