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