diff --git a/.github/workflows/build-docs.yaml b/.github/workflows/build-docs.yaml new file mode 100644 index 0000000000..0b5234af84 --- /dev/null +++ b/.github/workflows/build-docs.yaml @@ -0,0 +1,21 @@ +# this workflow builds rustdoc for aleph-node crates +name: build-docs + +on: + push: + +jobs: + build-aleph-client-docs: + name: Build docs + runs-on: ubuntu-20.04 + steps: + - name: GIT | Checkout source code + uses: actions/checkout@v2 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + + - name: rustdoc | Build aleph-client docs + run: | + cd aleph-client && cargo doc --no-deps + diff --git a/aleph-client/docker/subxt-integration-entrypoint.sh b/aleph-client/docker/subxt-integration-entrypoint.sh index 947010e7a5..3720d936a6 100644 --- a/aleph-client/docker/subxt-integration-entrypoint.sh +++ b/aleph-client/docker/subxt-integration-entrypoint.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash -subxt codegen --derive Clone --derive Debug --derive Eq --derive PartialEq | rustfmt --edition=2021 --config-path aleph-node/rustfmt.toml > aleph_zero.rs +# an ugly workaround for the fact we want to ignore rustdoc warnings in generated runtime file +echo "#[doc(hidden)]" > aleph_zero.rs +subxt codegen --derive Clone --derive Debug --derive Eq --derive PartialEq | rustfmt --edition=2021 --config-path aleph-node/rustfmt.toml >> aleph_zero.rs + diff -y -W 200 --suppress-common-lines aleph_zero.rs aleph-node/aleph-client/src/aleph_zero.rs diff_exit_code=$? if [[ ! $diff_exit_code -eq 0 ]]; then diff --git a/aleph-client/src/aleph_zero.rs b/aleph-client/src/aleph_zero.rs index c3d5c0f443..810b80fb6e 100644 --- a/aleph-client/src/aleph_zero.rs +++ b/aleph-client/src/aleph_zero.rs @@ -1,3 +1,4 @@ +#[doc(hidden)] #[allow(dead_code, unused_imports, non_camel_case_types)] pub mod api { use super::api as root_mod; diff --git a/aleph-client/src/connections.rs b/aleph-client/src/connections.rs index 9011949403..aab0b24a84 100644 --- a/aleph-client/src/connections.rs +++ b/aleph-client/src/connections.rs @@ -16,16 +16,19 @@ use crate::{ api, sp_weights::weight_v2::Weight, AccountId, BlockHash, Call, KeyPair, SubxtClient, TxStatus, }; +/// Capable of communicating with a live Aleph chain. #[derive(Clone)] pub struct Connection { client: SubxtClient, } +/// Any connection that is signed by some key. pub struct SignedConnection { connection: Connection, signer: KeyPair, } +/// Specific connection that is signed by the sudo key. #[derive(Clone)] pub struct RootConnection { connection: SignedConnection, @@ -39,14 +42,35 @@ pub(crate) trait AsSigned { fn as_signed(&self) -> &SignedConnection; } +/// Any connection should be able to request storage and submit RPC calls #[async_trait::async_trait] pub trait ConnectionApi: Sync { + /// Retrieves a decoded storage value stored under given key. + /// + /// # Panic + /// This method `panic`s, in case storage key is invalid, or in case value cannot be decoded, + /// or there is no such value + /// * `addrs` - represents a storage key, see [more info about keys](https://docs.substrate.io/fundamentals/state-transitions-and-storage/#querying-storage) + /// * `at` - optional block hash to query state from async fn get_storage_entry( &self, addrs: &StaticStorageAddress, at: Option, ) -> T::Target; + /// Retrieves a decoded storage value stored under given key. + /// + /// # Panic + /// This method `panic`s, in case storage key is invalid, or in case value cannot be decoded, + /// but does _not_ `panic` if there is no such value + /// * `addrs` - represents a storage key, see [more info about keys](https://docs.substrate.io/fundamentals/state-transitions-and-storage/#querying-storage) + /// * `at` - optional block hash to query state from + /// + /// # Examples + /// ```ignore + /// let addrs = api::storage().treasury().proposal_count(); + /// get_storage_entry_maybe(&addrs, None).await + /// ``` async fn get_storage_entry_maybe< T: DecodeWithMetadata + Sync, Defaultable: Sync, @@ -57,17 +81,55 @@ pub trait ConnectionApi: Sync { at: Option, ) -> Option; + /// Submit a RPC call. + /// + /// * `func_name` - name of a RPC call + /// * `params` - result of calling `rpc_params!` macro, that's `Vec` of encoded data + /// to this rpc call + /// + /// # Examples + /// ```ignore + /// let args = ContractCallArgs { + /// origin: address.clone(), + /// dest: address.clone(), + /// value: 0, + /// gas_limit: None, + /// input_data: payload, + /// storage_deposit_limit: None, + /// }; + /// let params = rpc_params!["ContractsApi_call", Bytes(args.encode())]; + /// rpc_call("state_call".to_string(), params).await; + /// ``` async fn rpc_call(&self, func_name: String, params: RpcParams) -> anyhow::Result; } +/// Signed connection should be able to sends transactions to chain #[async_trait::async_trait] pub trait SignedConnectionApi: ConnectionApi { + /// Send a transaction to a chain. It waits for a given tx `status`. + /// * `tx` - encoded transaction payload + /// * `status` - a [`TxStatus`] for a tx to wait for + /// # Returns + /// Block hash of block where transaction was put or error + /// # Examples + /// ```ignore + /// let tx = api::tx() + /// .balances() + /// .transfer(MultiAddress::Id(dest), amount); + /// send_tx(tx, status).await + /// ``` async fn send_tx( &self, tx: Call, status: TxStatus, ) -> anyhow::Result; + /// Send a transaction to a chain. It waits for a given tx `status`. + /// * `tx` - encoded transaction payload + /// * `params` - optional tx params e.g. tip + /// * `status` - a [`TxStatus`] of a tx to wait for + /// # Returns + /// Block hash of block where transaction was put or error async fn send_tx_with_params( &self, tx: Call, @@ -75,14 +137,22 @@ pub trait SignedConnectionApi: ConnectionApi { status: TxStatus, ) -> anyhow::Result; + /// Returns account id which signs this connection fn account_id(&self) -> &AccountId; + + /// Returns a [`KeyPair`] which signs this connection fn signer(&self) -> &KeyPair; + + /// Tries to convert [`SignedConnection`] as [`RootConnection`] async fn try_as_root(&self) -> anyhow::Result; } +/// API for [sudo pallet](https://paritytech.github.io/substrate/master/pallet_sudo/index.html). #[async_trait::async_trait] pub trait SudoCall { + /// API for [`sudo_unchecked_weight`](https://paritytech.github.io/substrate/master/pallet_sudo/pallet/enum.Call.html#variant.sudo_unchecked_weight) call. async fn sudo_unchecked(&self, call: Call, status: TxStatus) -> anyhow::Result; + /// API for [`sudo`](https://paritytech.github.io/substrate/master/pallet_sudo/pallet/enum.Call.html#variant.sudo) call. async fn sudo(&self, call: Call, status: TxStatus) -> anyhow::Result; } @@ -243,10 +313,16 @@ impl Connection { const DEFAULT_RETRIES: u32 = 10; const RETRY_WAIT_SECS: u64 = 1; + /// Creates new connection from a given url. + /// By default, it tries to connect 10 times, waiting 1 second between each unsuccessful attempt. + /// * `address` - address in websocket format, e.g. `ws://127.0.0.1:9943` pub async fn new(address: &str) -> Connection { Self::new_with_retries(address, Self::DEFAULT_RETRIES).await } + /// Creates new connection from a given url and given number of connection attempts. + /// * `address` - address in websocket format, e.g. `ws://127.0.0.1:9943` + /// * `retries` - number of connection attempts async fn new_with_retries(address: &str, mut retries: u32) -> Connection { loop { let client = SubxtClient::from_url(&address).await; @@ -267,20 +343,34 @@ impl Connection { } impl SignedConnection { + /// Creates new signed connection from existing [`Connection`] object. + /// * `connection` - existing connection + /// * `signer` - a [`KeyPair`] of signing account pub async fn new(address: &str, signer: KeyPair) -> Self { Self::from_connection(Connection::new(address).await, signer) } + /// Creates new signed connection from existing [`Connection`] object. + /// * `connection` - existing connection + /// * `signer` - a [`KeyPair`] of signing account pub fn from_connection(connection: Connection, signer: KeyPair) -> Self { Self { connection, signer } } } impl RootConnection { + /// Creates new root connection from a given url. + /// It tries to connect 10 times, waiting 1 second between each unsuccessful attempt. + /// * `address` - address in websocket format, e.g. `ws://127.0.0.1:9943` + /// * `root` - a [`KeyPair`] of the Sudo account pub async fn new(address: &str, root: KeyPair) -> anyhow::Result { RootConnection::try_from_connection(Connection::new(address).await, root).await } + /// Creates new root connection from a given [`Connection`] object. It validates whether given + /// key is really a sudo account + /// * `connection` - existing connection + /// * `signer` - a [`KeyPair`] of the Sudo account pub async fn try_from_connection( connection: Connection, signer: KeyPair, diff --git a/aleph-client/src/lib.rs b/aleph-client/src/lib.rs index 4dbdf8cfc1..313f30d6df 100644 --- a/aleph-client/src/lib.rs +++ b/aleph-client/src/lib.rs @@ -1,3 +1,12 @@ +#![warn(missing_docs)] +//! API for [aleph-node](https://github.com/Cardinal-Cryptography/aleph-node) chain. +//! +//! This crate provides a Rust application interface for submitting transactions to `aleph-node` chain. +//! Most of the [pallets](https://docs.substrate.io/reference/frame-pallets/) are common to any +//! [Substrate](https://github.com/paritytech/substrate) chain, but there are some unique to `aleph-node`, +//! e.g. [`pallets::elections::ElectionsApi`]. +//! + #![feature(auto_traits)] #![feature(negative_impls)] @@ -17,19 +26,30 @@ use crate::api::runtime_types::aleph_runtime::RuntimeCall as Call; mod aleph_zero; mod connections; pub mod contract; +/// API for pallets. pub mod pallets; mod runtime_types; +/// Block / session / era API. pub mod utility; +/// Waiting for some events API. pub mod waiting; pub use aleph_zero::api; pub use runtime_types::*; +/// An alias for a configuration of live chain, e.g. block index type, hash type. pub type AlephConfig = PolkadotConfig; +/// An alias for a pallet aleph keys. pub type AlephKeyPair = ed25519::Pair; +/// An alias for a type of a key pair that signs chain transactions. pub type RawKeyPair = sr25519::Pair; +/// An alias for a signer object that constructs [`sr25519::Pair`] from given account id type. pub type KeyPair = PairSigner; +/// An alias for an account id type. pub type AccountId = subxt::ext::sp_core::crypto::AccountId32; +/// An alias for a client type. +pub type Client = OnlineClient; +/// An alias for a hash type. pub type BlockHash = H256; pub(crate) type SubxtClient = OnlineClient; @@ -38,26 +58,38 @@ pub use connections::{ Connection, ConnectionApi, RootConnection, SignedConnection, SignedConnectionApi, SudoCall, }; +/// When submitting a transaction, wait for given status before proceeding. #[derive(Copy, Clone)] pub enum TxStatus { + /// A tx must be included in some block. InBlock, + /// A tx must be included in some finalized block. Finalized, + /// A tx must be successfully submitted. Submitted, } +/// Converts given seed phrase to a sr25519 [`KeyPair`] object. +/// * `seed` - a 12 or 24 word seed phrase pub fn keypair_from_string(seed: &str) -> KeyPair { let pair = sr25519::Pair::from_string(seed, None).expect("Can't create pair from seed value"); KeyPair::new(pair) } +/// Converts given seed phrase to a sr25519 [`RawKeyPair`] object. +/// * `seed` - a 12 or 24 word seed phrase pub fn raw_keypair_from_string(seed: &str) -> RawKeyPair { sr25519::Pair::from_string(seed, None).expect("Can't create pair from seed value") } +/// Converts given seed phrase to a ed25519 [`AlephKeyPair`] object. +/// * `seed` - a 12 or 24 word seed phrase pub fn aleph_keypair_from_string(seed: &str) -> AlephKeyPair { ed25519::Pair::from_string(seed, None).expect("Can't create pair from seed value") } +/// Converts a key pair object to `AccountId`. +/// * `keypair` - a key-pair object, e.g. [`ed25519::Pair`] or [`sr25519::Pair`] pub fn account_from_keypair

(keypair: &P) -> AccountId where P: Pair, diff --git a/aleph-client/src/pallets/aleph.rs b/aleph-client/src/pallets/aleph.rs index 3f0c8530b5..5e06267322 100644 --- a/aleph-client/src/pallets/aleph.rs +++ b/aleph-client/src/pallets/aleph.rs @@ -13,14 +13,27 @@ use crate::{ ConnectionApi, Pair, RootConnection, SudoCall, TxStatus, }; +// TODO replace docs with link to pallet aleph docs, once they are published +/// Pallet aleph API that requires sudo. #[async_trait::async_trait] pub trait AlephSudoApi { + /// Sets the emergency finalization key. + /// * `finalizer` - a new finalizer key + /// * `status` - a [`TxStatus`] of a tx to wait for + /// # Returns + /// Block hash of block where transaction was put or error async fn set_emergency_finalizer( &self, finalizer: AccountId, status: TxStatus, ) -> anyhow::Result; + /// Schedules a finality version change for a future session. + /// * `version` - next version of the finalizer + /// * `session` - from which session the next version applies + /// * `status` - a [`TxStatus`] of a tx to wait for + /// # Returns + /// Block hash of block where transaction was put or error async fn schedule_finality_version_change( &self, version: u32, @@ -29,8 +42,12 @@ pub trait AlephSudoApi { ) -> anyhow::Result; } +/// Pallet aleph RPC api. #[async_trait::async_trait] pub trait AlephRpc { + /// Finalize the block with given hash and number using attached signature. + /// # Returns + /// Block hash of block where transaction was put or error async fn emergency_finalize( &self, number: BlockNumber, diff --git a/aleph-client/src/pallets/author.rs b/aleph-client/src/pallets/author.rs index 56bc511782..7e8006e194 100644 --- a/aleph-client/src/pallets/author.rs +++ b/aleph-client/src/pallets/author.rs @@ -2,8 +2,10 @@ use codec::Decode; use crate::{aleph_runtime::SessionKeys, connections::AsConnection}; +/// Implements RPC calls for [`author`](https://paritytech.github.io/substrate/master/sc_rpc/author/struct.Author.html) pallet #[async_trait::async_trait] pub trait AuthorRpc { + /// API for [`rotate_keys`](https://paritytech.github.io/substrate/master/sc_rpc/author/struct.Author.html#method.rotate_keys) call async fn author_rotate_keys(&self) -> anyhow::Result; } diff --git a/aleph-client/src/pallets/balances.rs b/aleph-client/src/pallets/balances.rs index e4d44e6f6b..b4e4be0aae 100644 --- a/aleph-client/src/pallets/balances.rs +++ b/aleph-client/src/pallets/balances.rs @@ -10,29 +10,44 @@ use crate::{ ConnectionApi, SignedConnectionApi, TxStatus, }; +/// Pallet balances read-only API. #[async_trait::async_trait] pub trait BalanceApi { + /// API for [`locks`](https://paritytech.github.io/substrate/master/pallet_balances/pallet/struct.Pallet.html#method.locks) call. + /// * `account` - an account to query locked balance for + /// * `at` - optional hash of a block to query state from async fn locks_for_account( &self, account: AccountId, at: Option, ) -> Vec>; + + /// API for [`locks`](https://paritytech.github.io/substrate/master/pallet_balances/pallet/struct.Pallet.html#method.locks) call. + /// * `accounts` - a list of accounts to query locked balance for + /// * `at` - optional hash of a block to query state from async fn locks( &self, accounts: &[AccountId], at: Option, ) -> Vec>>; + + /// Returns [`total_issuance`](https://paritytech.github.io/substrate/master/pallet_balances/pallet/type.TotalIssuance.html). async fn total_issuance(&self, at: Option) -> Balance; } +/// Pallet balances API #[async_trait::async_trait] pub trait BalanceUserApi { + /// API for [`transfer`](https://paritytech.github.io/substrate/master/pallet_balances/pallet/struct.Pallet.html#method.transfer) call. async fn transfer( &self, dest: AccountId, amount: Balance, status: TxStatus, ) -> anyhow::Result; + + /// API for [`transfer`](https://paritytech.github.io/substrate/master/pallet_balances/pallet/struct.Pallet.html#method.transfer) call. + /// Include tip in the tx. async fn transfer_with_tip( &self, dest: AccountId, @@ -42,8 +57,23 @@ pub trait BalanceUserApi { ) -> anyhow::Result; } +/// Pallet balances logic not directly related to any pallet call. #[async_trait::async_trait] pub trait BalanceUserBatchExtApi { + /// Performs batch of `balances.transfer` calls. + /// * `dest` - a list of accounts to send tokens to + /// * `amount` - an amount to transfer + /// * `status` - a [`TxStatus`] for a tx to wait for + /// + /// # Examples + /// ```ignore + /// for chunk in stash_accounts.chunks(1024) { + /// connection + /// .batch_transfer(chunk, 1_000_000_000_000u128, TxStatus::InBlock) + /// .await + /// .unwrap(); + /// } + /// ``` async fn batch_transfer( &self, dest: &[AccountId], diff --git a/aleph-client/src/pallets/contract.rs b/aleph-client/src/pallets/contract.rs index 1eb7fd807e..eeebce4a3e 100644 --- a/aleph-client/src/pallets/contract.rs +++ b/aleph-client/src/pallets/contract.rs @@ -8,18 +8,29 @@ use crate::{ ConnectionApi, SignedConnectionApi, TxStatus, }; +/// Arguments to [`ContractRpc::call_and_get`]. #[derive(Encode)] pub struct ContractCallArgs { + /// Who is singing a tx. pub origin: AccountId, + /// Address of the contract to call. pub dest: AccountId, + /// The balance to transfer from the `origin` to `dest`. pub value: Balance, + /// The gas limit enforced when executing the constructor. pub gas_limit: Option, + /// The maximum amount of balance that can be charged from the caller to pay for the storage consumed. pub storage_deposit_limit: Option, + /// The input data to pass to the contract. pub input_data: Vec, } +/// Pallet contracts read-only api. #[async_trait::async_trait] pub trait ContractsApi { + /// Returns `contracts.owner_info_of` storage for a given code hash. + /// * `code_hash` - a code hash + /// * `at` - optional hash of a block to query state from async fn get_owner_info( &self, code_hash: BlockHash, @@ -27,14 +38,18 @@ pub trait ContractsApi { ) -> Option; } +/// Pallet contracts api. #[async_trait::async_trait] pub trait ContractsUserApi { + /// API for [`upload_code`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/struct.Pallet.html#method.upload_code) call. async fn upload_code( &self, code: Vec, storage_limit: Option>, status: TxStatus, ) -> anyhow::Result; + + /// API for [`instantiate`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/struct.Pallet.html#method.instantiate) call. #[allow(clippy::too_many_arguments)] async fn instantiate( &self, @@ -46,6 +61,8 @@ pub trait ContractsUserApi { salt: Vec, status: TxStatus, ) -> anyhow::Result; + + /// API for [`instantiate_with_code`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/struct.Pallet.html#method.instantiate_with_code) call. #[allow(clippy::too_many_arguments)] async fn instantiate_with_code( &self, @@ -57,6 +74,8 @@ pub trait ContractsUserApi { salt: Vec, status: TxStatus, ) -> anyhow::Result; + + /// API for [`call`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/struct.Pallet.html#method.call) call. async fn call( &self, destination: AccountId, @@ -66,6 +85,8 @@ pub trait ContractsUserApi { data: Vec, status: TxStatus, ) -> anyhow::Result; + + /// API for [`remove_code`](https://paritytech.github.io/substrate/master/pallet_contracts/pallet/struct.Pallet.html#method.remove_code) call. async fn remove_code( &self, code_hash: BlockHash, @@ -73,8 +94,10 @@ pub trait ContractsUserApi { ) -> anyhow::Result; } +/// RPC for runtime ContractsApi #[async_trait::async_trait] pub trait ContractRpc { + /// API for [`call`](https://paritytech.github.io/substrate/master/pallet_contracts/trait.ContractsApi.html#method.call) call. async fn call_and_get( &self, args: ContractCallArgs, diff --git a/aleph-client/src/pallets/elections.rs b/aleph-client/src/pallets/elections.rs index 5c04534657..4ed650412a 100644 --- a/aleph-client/src/pallets/elections.rs +++ b/aleph-client/src/pallets/elections.rs @@ -16,39 +16,82 @@ use crate::{ ConnectionApi, RootConnection, SudoCall, TxStatus, }; +// TODO once pallet elections docs are published, replace api docs with links to public docs +/// Pallet elections read-only api. #[async_trait::async_trait] pub trait ElectionsApi { + /// Returns `elections.ban_config` storage of the elections pallet. + /// * `at` - optional hash of a block to query state from async fn get_ban_config(&self, at: Option) -> BanConfig; + + /// Returns `elections.committee_size` storage of the elections pallet. + /// * `at` - optional hash of a block to query state from async fn get_committee_seats(&self, at: Option) -> CommitteeSeats; + + /// Returns `elections.next_era_committee_seats` storage of the elections pallet. + /// * `at` - optional hash of a block to query state from async fn get_next_era_committee_seats(&self, at: Option) -> CommitteeSeats; + + /// Returns `elections.session_validator_block_count` of a given validator. + /// * `validator` - a validator stash account id + /// * `at` - optional hash of a block to query state from async fn get_validator_block_count( &self, validator: AccountId, at: Option, ) -> Option; + + /// Returns `elections.current_era_validators` storage of the elections pallet. + /// * `at` - optional hash of a block to query state from async fn get_current_era_validators(&self, at: Option) -> EraValidators; + + /// Returns `elections.next_era_reserved_validators` storage of the elections pallet. + /// * `at` - optional hash of a block to query state from async fn get_next_era_reserved_validators(&self, at: Option) -> Vec; + + /// Returns `elections.next_era_non_reserved_validators` storage of the elections pallet. + /// * `at` - optional hash of a block to query state from async fn get_next_era_non_reserved_validators(&self, at: Option) -> Vec; + + /// Returns `elections.underperformed_validator_session_count` storage of a given validator. + /// * `validator` - a validator stash account id + /// * `at` - optional hash of a block to query state from async fn get_underperformed_validator_session_count( &self, validator: AccountId, at: Option, ) -> Option; + + /// Returns `elections.banned.reason` storage of a given validator. + /// * `validator` - a validator stash account id + /// * `at` - optional hash of a block to query state from async fn get_ban_reason_for_validator( &self, validator: AccountId, at: Option, ) -> Option; + + /// Returns `elections.banned` storage of a given validator. + /// * `validator` - a validator stash account id + /// * `at` - optional hash of a block to query state from async fn get_ban_info_for_validator( &self, validator: AccountId, at: Option, ) -> Option; + /// Returns `elections.session_period` const of the elections pallet. async fn get_session_period(&self) -> anyhow::Result; } +/// any object that implements pallet elections api that requires sudo #[async_trait::async_trait] pub trait ElectionsSudoApi { + /// Issues `elections.set_ban_config`. It has an immediate effect. + /// * `minimal_expected_performance` - performance ratio threshold in a session + /// * `underperformed_session_count_threshold` - how many bad uptime sessions force validator to be removed from the committee + /// * `clean_session_counter_delay` - underperformed session counter is cleared every subsequent `clean_session_counter_delay` sessions + /// * `ban_period` - how many eras a validator is banned for + /// * `status` - a [`TxStatus`] for a tx to wait for async fn set_ban_config( &self, minimal_expected_performance: Option, @@ -58,6 +101,11 @@ pub trait ElectionsSudoApi { status: TxStatus, ) -> anyhow::Result; + /// Issues `elections.change_validators` that sets the committee for the next era. + /// * `new_reserved_validators` - reserved validators to be in place in the next era; optional + /// * `new_non_reserved_validators` - non reserved validators to be in place in the next era; optional + /// * `committee_size` - committee size to be in place in the next era; optional + /// * `status` - a [`TxStatus`] for a tx to wait for async fn change_validators( &self, new_reserved_validators: Option>, @@ -65,12 +113,21 @@ pub trait ElectionsSudoApi { committee_size: Option, status: TxStatus, ) -> anyhow::Result; + + /// Schedule a non-reserved node to be banned out from the committee at the end of the era. + /// * `account` - account to be banned, + /// * `ben_reason` - reaons for ban, expressed as raw bytes + /// * `status` - a [`TxStatus`] for a tx to wait for async fn ban_from_committee( &self, account: AccountId, ban_reason: Vec, status: TxStatus, ) -> anyhow::Result; + + /// Set openness of the elections. + /// * `mode` - new elections openness mode + /// * `status` - a [`TxStatus`] for a tx to wait for async fn set_election_openness( &self, mode: ElectionOpenness, diff --git a/aleph-client/src/pallets/fee.rs b/aleph-client/src/pallets/fee.rs index 330b8239c0..1cab7bfd66 100644 --- a/aleph-client/src/pallets/fee.rs +++ b/aleph-client/src/pallets/fee.rs @@ -1,9 +1,12 @@ use crate::{api, BlockHash, ConnectionApi}; +/// An alias for a fee multiplier. pub type FeeMultiplier = u128; +/// Transaction payment pallet API. #[async_trait::async_trait] pub trait TransactionPaymentApi { + /// API for [`next_fee_multiplier`](https://paritytech.github.io/substrate/master/pallet_transaction_payment/pallet/struct.Pallet.html#method.next_fee_multiplier) call. async fn get_next_fee_multiplier(&self, at: Option) -> FeeMultiplier; } diff --git a/aleph-client/src/pallets/mod.rs b/aleph-client/src/pallets/mod.rs index eaa9e11277..047ea35857 100644 --- a/aleph-client/src/pallets/mod.rs +++ b/aleph-client/src/pallets/mod.rs @@ -1,13 +1,26 @@ +/// Pallet aleph API pub mod aleph; +/// Pallet author API pub mod author; +/// Pallet balances API pub mod balances; +/// Pallet contracts API pub mod contract; +/// Pallet elections API pub mod elections; +/// Pallet transaction payment API pub mod fee; +/// Pallet multisig API pub mod multisig; +/// Pallet session API pub mod session; +/// Pallet staking API pub mod staking; +/// Pallet system API pub mod system; +/// Pallet treasury API pub mod treasury; +/// Pallet utility API pub mod utility; +/// Pallet vesting API pub mod vesting; diff --git a/aleph-client/src/pallets/multisig.rs b/aleph-client/src/pallets/multisig.rs index e92bdbe122..62454d450c 100644 --- a/aleph-client/src/pallets/multisig.rs +++ b/aleph-client/src/pallets/multisig.rs @@ -1,17 +1,20 @@ -use primitives::{Balance, BlockNumber}; +use primitives::BlockNumber; use crate::{ - api, api::runtime_types, sp_weights::weight_v2::Weight, AccountId, BlockHash, - SignedConnectionApi, TxStatus, + api, sp_weights::weight_v2::Weight, AccountId, BlockHash, SignedConnectionApi, TxStatus, }; +/// An alias for a call hash. pub type CallHash = [u8; 32]; +/// An alias for a call. pub type Call = Vec; +/// An alias for a timepoint. pub type Timepoint = api::runtime_types::pallet_multisig::Timepoint; -pub type Multisig = runtime_types::pallet_multisig::Multisig; +/// Pallet multisig api. #[async_trait::async_trait] pub trait MultisigUserApi { + /// API for [`approve_as_multi`](https://paritytech.github.io/substrate/master/pallet_multisig/pallet/struct.Pallet.html#method.approve_as_multi) call. async fn approve_as_multi( &self, threshold: u16, @@ -21,6 +24,8 @@ pub trait MultisigUserApi { call_hash: CallHash, status: TxStatus, ) -> anyhow::Result; + + /// API for [`cancel_as_multi`](https://paritytech.github.io/substrate/master/pallet_multisig/pallet/struct.Pallet.html#method.cancel_as_multi) call. async fn cancel_as_multi( &self, threshold: u16, diff --git a/aleph-client/src/pallets/session.rs b/aleph-client/src/pallets/session.rs index d9176ada4a..4f2f2351e1 100644 --- a/aleph-client/src/pallets/session.rs +++ b/aleph-client/src/pallets/session.rs @@ -5,19 +5,27 @@ use crate::{ SignedConnectionApi, TxStatus, }; +/// Pallet session read-only api. #[async_trait::async_trait] pub trait SessionApi { + /// API for [`next_keys`](https://paritytech.github.io/substrate/master/pallet_session/pallet/type.NextKeys.html) call. async fn get_next_session_keys( &self, account: AccountId, at: Option, ) -> Option; + + /// API for [`current_index`](https://paritytech.github.io/substrate/master/pallet_session/pallet/struct.Pallet.html#method.current_index) call. async fn get_session(&self, at: Option) -> SessionIndex; + + /// API for [`validators`](https://paritytech.github.io/substrate/master/pallet_session/pallet/struct.Pallet.html#method.validators) call. async fn get_validators(&self, at: Option) -> Vec; } +/// any object that implements pallet session api #[async_trait::async_trait] pub trait SessionUserApi { + /// API for [`set_keys`](https://paritytech.github.io/substrate/master/pallet_session/pallet/struct.Pallet.html#method.set_keys) call. async fn set_keys(&self, new_keys: SessionKeys, status: TxStatus) -> anyhow::Result; } diff --git a/aleph-client/src/pallets/staking.rs b/aleph-client/src/pallets/staking.rs index cae30b052c..6008a19636 100644 --- a/aleph-client/src/pallets/staking.rs +++ b/aleph-client/src/pallets/staking.rs @@ -26,53 +26,96 @@ use crate::{ ConnectionApi, RootConnection, SignedConnectionApi, SudoCall, TxStatus, }; +/// Any object that implemnts pallet staking read-only api. #[async_trait::async_trait] pub trait StakingApi { + /// Returns [`active_era`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.active_era). + /// * `at` - optional hash of a block to query state from async fn get_active_era(&self, at: Option) -> EraIndex; + + /// Returns [`current_era`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.current_era). + /// * `at` - optional hash of a block to query state from async fn get_current_era(&self, at: Option) -> EraIndex; + + /// Returns [`bonded`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.bonded) for a given stash account. + /// * `stash` - a stash account id + /// * `at` - optional hash of a block to query state from async fn get_bonded(&self, stash: AccountId, at: Option) -> Option; + + /// Returns [`ledger`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.ledger) for a given controller account. + /// * `controller` - a controller account id + /// * `at` - optional hash of a block to query state from async fn get_ledger(&self, controller: AccountId, at: Option) -> StakingLedger; + + /// Returns [`eras_validator_reward`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.eras_validator_reward) for a given era. + /// * `era` - an era index + /// * `at` - optional hash of a block to query state from async fn get_payout_for_era(&self, era: EraIndex, at: Option) -> u128; + + /// Returns [`eras_stakers`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.eras_stakers) for a given era and account id. + /// * `era` - an era index + /// * `account_id` - an account id + /// * `at` - optional hash of a block to query state from async fn get_exposure( &self, era: EraIndex, account_id: &AccountId, at: Option, ) -> Exposure; + + /// Returns [`eras_reward_points`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.eras_reward_points) for a given era. + /// * `era` - an era index + /// * `at` - optional hash of a block to query state from async fn get_era_reward_points( &self, era: EraIndex, at: Option, ) -> Option>; + + /// Returns [`minimum_validator_count`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.minimum_validator_count). + /// * `at` - optional hash of a block to query state from async fn get_minimum_validator_count(&self, at: Option) -> u32; + /// Returns [`SessionsPerEra`](https://paritytech.github.io/substrate/master/pallet_staking/trait.Config.html#associatedtype.SessionsPerEra) const. async fn get_session_per_era(&self) -> anyhow::Result; } +/// Pallet staking api #[async_trait::async_trait] pub trait StakingUserApi { + /// API for [`bond`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.bond) call. async fn bond( &self, initial_stake: Balance, controller_id: AccountId, status: TxStatus, ) -> anyhow::Result; + + /// API for [`validate`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.validate) call. async fn validate( &self, validator_commission_percentage: u8, status: TxStatus, ) -> anyhow::Result; + + /// API for [`payout_stakers`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.payout_stakers) call. async fn payout_stakers( &self, stash_account: AccountId, era: EraIndex, status: TxStatus, ) -> anyhow::Result; + + /// API for [`nominate`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.nominate) call. async fn nominate( &self, nominee_account_id: AccountId, status: TxStatus, ) -> anyhow::Result; + + /// API for [`chill`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.chill) call. async fn chill(&self, status: TxStatus) -> anyhow::Result; + + /// API for [`bond_extra`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.bond_extra) call. async fn bond_extra_stake( &self, extra_stake: Balance, @@ -80,14 +123,65 @@ pub trait StakingUserApi { ) -> anyhow::Result; } +/// Pallet staking logic, not directly related to any particular pallet call. #[async_trait::async_trait] pub trait StakingApiExt { + /// Send batch of [`bond`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.bond) calls. + /// * `accounts` - a slice of account ids pairs (stash, controller) + /// * `stake` - what amount should be bonded, + /// * `status` - a [`TxStatus`] of a tx to wait for + /// + /// # Examples + /// ```ignore + /// async fn nominate_validator( + /// connection: &RootConnection, + /// nominator_controller_accounts: Vec, + /// nominator_stash_accounts: Vec, + /// nominee_account: AccountId, + /// ) { + /// let stash_controller_accounts = nominator_stash_accounts + /// .iter() + /// .cloned() + /// .zip(nominator_controller_accounts.iter().cloned()) + /// .collect::>(); + /// + /// let mut rng = thread_rng(); + /// for chunk in stash_controller_accounts + /// .chunks(256) + /// .map(|c| c.to_vec()) + /// { + /// let stake = 100 * 1_000_000_000_000u128; + /// connection + /// .batch_bond(&chunk, stake, TxStatus::Submitted) + /// .await + /// .unwrap(); + /// } + /// let nominator_nominee_accounts = nominator_controller_accounts + /// .iter() + /// .cloned() + /// .zip(iter::repeat(&nominee_account).cloned()) + /// .collect::>(); + /// for chunks in nominator_nominee_accounts.chunks(128) { + /// connection + /// .batch_nominate(chunks, TxStatus::InBlock) + /// .await + /// .unwrap(); + /// } + /// } + /// ``` async fn batch_bond( &self, accounts: &[(AccountId, AccountId)], stake: Balance, status: TxStatus, ) -> anyhow::Result; + + /// Send batch of [`nominate`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.nominate) calls. + /// * `nominator_nominee_pairs` - a slice of account ids pairs (nominator, nominee) + /// * `status` - a [`TxStatus`] of a tx to wait for + /// + /// # Examples + /// see [`Self::batch_bond`] example above async fn batch_nominate( &self, nominator_nominee_pairs: &[(AccountId, AccountId)], @@ -95,9 +189,13 @@ pub trait StakingApiExt { ) -> anyhow::Result; } +/// Pallet staking api that requires sudo. #[async_trait::async_trait] pub trait StakingSudoApi { + /// API for [`force_new_era`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.force_new_era) call. async fn force_new_era(&self, status: TxStatus) -> anyhow::Result; + + /// API for [`set_staking_config`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.set_staking_configs) call. async fn set_staking_config( &self, minimal_nominator_bond: Option, @@ -108,13 +206,33 @@ pub trait StakingSudoApi { ) -> anyhow::Result; } +/// Logic for retrieving raw storage keys or values from a pallet staking. #[async_trait::async_trait] pub trait StakingRawApi { + /// Returns all encoded [`eras_stakers`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.eras_stakers). + /// storage keys for a given era + /// * `era` - an era index + /// * `at` - optional hash of a block to query state from + /// + /// # Examples + /// ```ignore + /// let stakers = connection + /// .get_stakers_storage_keys(current_era, None) + /// .await + /// .into_iter() + /// .map(|key| key.0); + /// ``` async fn get_stakers_storage_keys( &self, era: EraIndex, at: Option, ) -> anyhow::Result>; + + /// Returns encoded [`eras_stakers`](https://paritytech.github.io/substrate/master/pallet_staking/struct.Pallet.html#method.eras_stakers). + /// storage keys for a given era and given account ids + /// * `era` - an era index + /// * `accounts` - list of account ids + /// * `at` - optional hash of a block to query state from async fn get_stakers_storage_keys_from_accounts( &self, era: EraIndex, diff --git a/aleph-client/src/pallets/system.rs b/aleph-client/src/pallets/system.rs index 5c2bb270b4..10a9e792f8 100644 --- a/aleph-client/src/pallets/system.rs +++ b/aleph-client/src/pallets/system.rs @@ -10,14 +10,26 @@ use crate::{ ConnectionApi, RootConnection, SudoCall, TxStatus, }; +/// Pallet system read-only api. #[async_trait::async_trait] pub trait SystemApi { + /// returns free balance of a given account + /// * `account` - account id + /// * `at` - optional hash of a block to query state from + /// + /// it uses [`system.account`](https://paritytech.github.io/substrate/master/frame_system/pallet/struct.Pallet.html#method.account) storage async fn get_free_balance(&self, account: AccountId, at: Option) -> Balance; } +/// Pallet system api. #[async_trait::async_trait] pub trait SystemSudoApi { + /// API for [`set_code`](https://paritytech.github.io/substrate/master/frame_system/pallet/struct.Pallet.html#method.set_code) call. async fn set_code(&self, code: Vec, status: TxStatus) -> anyhow::Result; + + /// A dispatch that will fill the block weight up to the given ratio. + /// * `target_ratio_percent` - ratio to fill block + /// `status` - a [`TxStatus`] to wait for async fn fill_block( &self, target_ratio_percent: u8, diff --git a/aleph-client/src/pallets/treasury.rs b/aleph-client/src/pallets/treasury.rs index 8e4deace36..ef4b4c4251 100644 --- a/aleph-client/src/pallets/treasury.rs +++ b/aleph-client/src/pallets/treasury.rs @@ -13,33 +13,56 @@ use crate::{ ConnectionApi, RootConnection, SignedConnectionApi, SudoCall, TxStatus, }; +/// Pallet treasury read-only api. #[async_trait::async_trait] pub trait TreasuryApi { + /// Returns an unique account id for all treasury transfers. async fn treasury_account(&self) -> AccountId; + + /// Returns storage `proposals_count`. + /// * `at` - an optional block hash to query state from async fn proposals_count(&self, at: Option) -> Option; + + /// Returns storage `approvals`. + /// * `at` - an optional block hash to query state from async fn approvals(&self, at: Option) -> Vec; } +/// Pallet treasury api. #[async_trait::async_trait] pub trait TreasuryUserApi { + /// API for [`propose_spend`](https://paritytech.github.io/substrate/master/pallet_treasury/pallet/struct.Pallet.html#method.propose_spend) call. async fn propose_spend( &self, amount: Balance, beneficiary: AccountId, status: TxStatus, ) -> anyhow::Result; + + /// API for [`approve_proposal`](https://paritytech.github.io/substrate/master/pallet_treasury/pallet/struct.Pallet.html#method.approve_proposal) call. async fn approve(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result; + + /// API for [`reject_proposal`](https://paritytech.github.io/substrate/master/pallet_treasury/pallet/struct.Pallet.html#method.reject_proposal) call. async fn reject(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result; } +/// Pallet treasury funcionality that is not directly related to any pallet call. #[async_trait::async_trait] pub trait TreasureApiExt { + /// When `staking.payout_stakers` is done, what amount of AZERO is transferred to. + /// the treasury async fn possible_treasury_payout(&self) -> anyhow::Result; } +/// Pallet treasury api issued by the sudo account. #[async_trait::async_trait] pub trait TreasurySudoApi { + /// API for [`approve_proposal`](https://paritytech.github.io/substrate/master/pallet_treasury/pallet/struct.Pallet.html#method.approve_proposal) call. + /// wrapped in [`sudo_unchecked_weight`](https://paritytech.github.io/substrate/master/pallet_sudo/pallet/struct.Pallet.html#method.sudo_unchecked_weight) async fn approve(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result; + + /// API for [`reject_proposal`](https://paritytech.github.io/substrate/master/pallet_treasury/pallet/struct.Pallet.html#method.reject_proposal) call. + /// wrapped [`sudo_unchecked_weight`](https://paritytech.github.io/substrate/master/pallet_sudo/pallet/struct.Pallet.html#method.sudo_unchecked_weight) async fn reject(&self, proposal_id: u32, status: TxStatus) -> anyhow::Result; } diff --git a/aleph-client/src/pallets/utility.rs b/aleph-client/src/pallets/utility.rs index 884325508c..974ca4bedd 100644 --- a/aleph-client/src/pallets/utility.rs +++ b/aleph-client/src/pallets/utility.rs @@ -1,7 +1,9 @@ use crate::{api, BlockHash, Call, SignedConnectionApi, TxStatus}; +/// Pallet utility api. #[async_trait::async_trait] pub trait UtilityApi { + /// API for [`batch`](https://paritytech.github.io/substrate/master/pallet_utility/pallet/struct.Pallet.html#method.batch) call. async fn batch_call(&self, calls: Vec, status: TxStatus) -> anyhow::Result; } diff --git a/aleph-client/src/pallets/vesting.rs b/aleph-client/src/pallets/vesting.rs index c6faa1d7a7..287ce17164 100644 --- a/aleph-client/src/pallets/vesting.rs +++ b/aleph-client/src/pallets/vesting.rs @@ -5,8 +5,12 @@ use crate::{ SignedConnectionApi, TxStatus, }; +/// Read only pallet vesting API. #[async_trait::async_trait] pub trait VestingApi { + /// Returns [`VestingInfo`] of the given account. + /// * `who` - an account id + /// * `at` - optional hash of a block to query state from async fn get_vesting( &self, who: AccountId, @@ -14,16 +18,24 @@ pub trait VestingApi { ) -> Vec>; } +/// Pallet vesting api. #[async_trait::async_trait] pub trait VestingUserApi { + /// API for [`vest`](https://paritytech.github.io/substrate/master/pallet_vesting/pallet/enum.Call.html#variant.vest) call. async fn vest(&self, status: TxStatus) -> anyhow::Result; + + /// API for [`vest_other`](https://paritytech.github.io/substrate/master/pallet_vesting/pallet/enum.Call.html#variant.vest_other) call. async fn vest_other(&self, status: TxStatus, other: AccountId) -> anyhow::Result; + + /// API for [`vested_transfer`](https://paritytech.github.io/substrate/master/pallet_vesting/pallet/enum.Call.html#variant.vested_transfer) call. async fn vested_transfer( &self, receiver: AccountId, schedule: VestingInfo, status: TxStatus, ) -> anyhow::Result; + + /// API for [`merge_schedules`](https://paritytech.github.io/substrate/master/pallet_vesting/pallet/enum.Call.html#variant.merge_schedules) call. async fn merge_schedules( &self, idx1: u32, diff --git a/aleph-client/src/runtime_types.rs b/aleph-client/src/runtime_types.rs index 2c1eceddbe..5787c1a23d 100644 --- a/aleph-client/src/runtime_types.rs +++ b/aleph-client/src/runtime_types.rs @@ -51,6 +51,7 @@ impl TryFrom for SessionKeys { } impl Weight { + /// Returns new instance of weight v2 object. pub fn new(ref_time: u64, proof_size: u64) -> Self { Self { ref_time, diff --git a/aleph-client/src/utility.rs b/aleph-client/src/utility.rs index e02b1e2cb8..15821dbe9d 100644 --- a/aleph-client/src/utility.rs +++ b/aleph-client/src/utility.rs @@ -7,24 +7,44 @@ use crate::{ BlockHash, }; +/// Block info API. #[async_trait::async_trait] pub trait BlocksApi { + /// Returns the first block of a session. + /// * `session` - number of the session to query the first block from async fn first_block_of_session( &self, session: SessionIndex, ) -> anyhow::Result>; + + /// Returns hash of a given block if the given block exists, otherwise `None` + /// * `block` - number of the block async fn get_block_hash(&self, block: BlockNumber) -> anyhow::Result>; + + /// Returns the most recent block from the current best chain. async fn get_best_block(&self) -> anyhow::Result>; + + /// Returns the most recent block from the finalized chain. async fn get_finalized_block_hash(&self) -> anyhow::Result; + + /// Returns number of a given block hash, if the given block exists, otherwise `None` + /// This is version that returns `Result` + /// * `block` - hash of the block to query its number async fn get_block_number(&self, block: BlockHash) -> anyhow::Result>; + + /// Returns number of a given block hash, if the given block exists, otherwise `None` + /// * `block` - hash of the block to query its number async fn get_block_number_opt( &self, block: Option, ) -> anyhow::Result>; } +/// Interaction logic between pallet session and pallet staking. #[async_trait::async_trait] pub trait SessionEraApi { + /// Returns which era given session is. + /// * `session` - session index async fn get_active_era_for_session(&self, session: SessionIndex) -> anyhow::Result; } diff --git a/aleph-client/src/waiting.rs b/aleph-client/src/waiting.rs index e06fdd09d2..559c5ed506 100644 --- a/aleph-client/src/waiting.rs +++ b/aleph-client/src/waiting.rs @@ -10,26 +10,81 @@ use crate::{ pallets::{session::SessionApi, staking::StakingApi}, }; +/// When using waiting API, what kind of block status we should wait for. pub enum BlockStatus { + /// Wait for event or block to be in the best chain. Best, + /// Wait for the event or block to be in the finalized chain. Finalized, } +/// Waiting _for_ various events API #[async_trait::async_trait] pub trait AlephWaiting { + /// Wait for a particular block to be in a [`BlockStatus`]. + /// Block number must match given predicate. + /// * `predicate` - a `u32` -> `bool` functor, first argument is a block number + /// * `status` - a [`BlockStatus`] of the block we wait for + /// + /// # Examples + /// ```ignore + /// let finalized = connection.connection.get_finalized_block_hash().await; + /// let finalized_number = connection + /// .connection + /// .get_block_number(finalized) + /// .await + /// .unwrap(); + /// connection + /// .connection + /// .wait_for_block(|n| n > finalized_number, BlockStatus::Finalized) + /// .await; + /// ``` async fn wait_for_block bool + Send>(&self, predicate: P, status: BlockStatus); + + /// Wait for a particular event to be emitted on chain. + /// * `predicate` - a predicate that has one argument (ref to an emitted event) + /// * `status` - a [`BlockStatus`] of the event we wait for + /// + /// # Examples + /// ```ignore + /// let event = connection + /// .wait_for_event( + /// |event: &BanValidators| { + /// info!("Received BanValidators event: {:?}", event.0); + /// true + /// }, + /// BlockStatus::Best, + /// ) + /// .await; + /// ``` async fn wait_for_event bool + Send>( &self, predicate: P, status: BlockStatus, ) -> T; + + /// Wait for given era to happen. + /// * `era` - number of the era to wait for + /// * `status` - a [`BlockStatus`] of the era we wait for async fn wait_for_era(&self, era: EraIndex, status: BlockStatus); + + /// Wait for given session to happen. + /// * `session` - number of the session to wait for + /// * `status` - a [`BlockStatus`] of the session we wait for async fn wait_for_session(&self, session: SessionIndex, status: BlockStatus); } +/// nWaiting _from_ the current moment of time API #[async_trait::async_trait] pub trait WaitingExt { + /// Wait for a given number of sessions to wait from a current session. + /// `n` - number of sessions to wait from now + /// * `status` - a [`BlockStatus`] of the session we wait for async fn wait_for_n_sessions(&self, n: SessionIndex, status: BlockStatus); + + /// Wait for a given number of eras to wait from a current era. + /// `n` - number of eras to wait from now + /// * `status` - a [`BlockStatus`] of the era we wait for async fn wait_for_n_eras(&self, n: EraIndex, status: BlockStatus); }