sidechain_block_search/
lib.rs

1//! Binary search queries for Partner Chain slots and epochs
2//!
3//! # Purpose of this crate
4//!
5//! Standard Substrate block storage allows for retrieving blocks based on their number and hash.
6//! However, Partner Chains toolkit introduces two new categories that are not supported by this
7//! lookup: slot and epoch. This crate provides a mechanism to quickly query for blocks based on
8//! their Partner Chain epoch or slot by applying a binary search over historical blocks.
9//!
10//! # Usage
11//!
12//! The binary search feature is provided via the [FindSidechainBlock] trait. This trait is
13//! implemented for any runtime client that implements the [GetSidechainStatus] runtime API.
14//! To query the blockchain, a predicate must be passed to the query that defines the searched
15//! block. Some predefined targets are defined in the [predicates] module, otherwise a new target
16//! type can be defined by implementing the [CompareStrategy] trait.
17//!
18//! Given a runtime client that satisfies the trait bounds, the blockchain can be queried like this:
19//!
20//! ```rust
21//! use sidechain_block_search::predicates::AnyBlockInEpoch;
22//! use sidechain_block_search::{ FindSidechainBlock, Client };
23//! use sidechain_domain::*;
24//! use sp_api::ProvideRuntimeApi;
25//! use sp_runtime::traits::{ Block as BlockT, NumberFor };
26//! use sp_sidechain::GetSidechainStatus;
27//!
28//! fn query_example<B, C>(client: C)
29//! where
30//!     B: BlockT,
31//!     NumberFor<B>: From<u32> + Into<u32>,
32//!     C: ProvideRuntimeApi<B> + Client<B> + Send + Sync + 'static,
33//!     C::Api: GetSidechainStatus<B>
34//! {
35//!     let search_target = AnyBlockInEpoch {
36//!         epoch: ScEpochNumber(42)
37//!     };
38//!     let result = client.find_block(search_target);
39//! }
40//! ```
41
42#![deny(missing_docs)]
43
44mod binary_search;
45mod impl_block_info;
46mod impl_compare_strategy;
47mod impl_find_block;
48
49pub use binary_search::binary_search_by;
50
51use sidechain_domain::{ScEpochNumber, ScSlotNumber};
52use sp_api::ProvideRuntimeApi;
53use sp_blockchain::HeaderBackend;
54use sp_runtime::traits::Block as BlockT;
55use sp_runtime::traits::NumberFor;
56#[allow(deprecated)]
57use sp_sidechain::GetSidechainStatus;
58use std::cmp::Ordering;
59use std::ops::Range;
60
61#[cfg(test)]
62mod tests;
63
64/// Types of binary search queries over Partner Chain blocks
65pub mod predicates {
66	use super::*;
67
68	/// Query for any block in given Partner Chain epoch
69	pub struct AnyBlockInEpoch {
70		/// Queried Partner Chain epoch
71		pub epoch: ScEpochNumber,
72	}
73
74	/// Query for the first block in given Partner Chain epoch
75	pub struct FirstBlockInEpoch {
76		/// Queried Partner Chain epoch
77		pub epoch: ScEpochNumber,
78	}
79
80	/// Query for the last block in given Partner Chain epoch
81	pub struct LastBlockInEpoch {
82		/// Queried Partner Chain epoch
83		pub epoch: ScEpochNumber,
84	}
85
86	/// Query for any block in given slot range
87	pub struct AnyBlockInSlotRange {
88		/// Queried slot range. Left-inclusive, right-exclusive
89		pub slot_range: Range<ScSlotNumber>,
90	}
91
92	/// Query for the last block in given slot range with upper block number bound
93	pub struct LatestBlockInSlotRange<Block: BlockT> {
94		/// Queried slot range. Left-inclusive, right-exclusive
95		pub slot_range: Range<ScSlotNumber>,
96		/// Upper bound for the number of returned block
97		pub latest_block: NumberFor<Block>,
98	}
99}
100use predicates::*;
101
102/// Runtime API client used by the block queries in this crate
103pub trait Client<Block: BlockT>: HeaderBackend<Block> + ProvideRuntimeApi<Block> {}
104
105impl<C: HeaderBackend<Block> + ProvideRuntimeApi<Block>, Block: BlockT> Client<Block> for C {}
106
107/// Interface for retrieving information about slot and epoch of Partner Chain blocks
108pub trait SidechainInfo<Block: BlockT>: Client<Block> {
109	/// Error type
110	type Error: std::error::Error;
111
112	/// Finds the Partner Chain slot number for a given block number
113	fn get_slot_of_block(
114		&self,
115		block_number: NumberFor<Block>,
116	) -> Result<ScSlotNumber, Self::Error>;
117
118	/// Finds the Partner Chain eopch number for a given block number
119	fn get_epoch_of_block(
120		&self,
121		block_number: NumberFor<Block>,
122	) -> Result<ScEpochNumber, Self::Error>;
123}
124
125/// Comparator used for binary searching the block history
126///
127/// Types implementing this trait represent some _search target_, which is to be found through
128/// binary search over block history. Note that this search target can be a single block defined
129/// by its _slot_ or some other monotonically increasing block property, or a _range_ of blocks
130/// defined by a range of slots or other property.
131pub trait CompareStrategy<Block: BlockT, BlockInfo: Client<Block>> {
132	/// Error type
133	type Error: std::error::Error;
134
135	/// Compares a block against a search target.
136	///
137	/// # Returns
138	/// - `Ok(Ordering::Less)` if the block is below the target
139	/// - `Ok(Ordering::Equal)` if the block is at target
140	/// - `Ok(Ordering::Greater)` if the block is above the target
141	/// - `Err` if an error occured
142	fn compare_block(
143		&self,
144		block: NumberFor<Block>,
145		block_info: &BlockInfo,
146	) -> Result<Ordering, Self::Error>;
147}
148
149/// Runtime client capable of finding Partner Chain blocks via binary search using some [CompareStrategy].
150pub trait FindSidechainBlock<Block: BlockT, CS: CompareStrategy<Block, Self>>:
151	Client<Block> + Sized
152{
153	/// Error type
154	type Error: std::error::Error;
155
156	/// Finds the number of the block satisfying `compare_strategy`
157	fn find_block_number(&self, compare_strategy: CS) -> Result<NumberFor<Block>, Self::Error>;
158
159	/// Finds the hash of the block satisfying `compare_strategy`
160	fn find_block(&self, compare_strategy: CS) -> Result<Block::Hash, Self::Error>;
161}