Skip to content

Commit

Permalink
A0-1608 Add documentation of all public API and traits of aleph-client (
Browse files Browse the repository at this point in the history
#832)

* A0-1608 Add documentation of all public API and traits

* A0-1608 Review

* A0-1608 Added basic workflow for building docs

* A0-1608 Removed redundant workflow condition

* A0-1068 Review part 2

* A0-1608 fixes after conflicts

* sneaky fix for outdated runtime data

* revert sneaky fix

* an ugly workaround for the fact we want to ignore rustdoc warnings in generated runtime file
  • Loading branch information
Marcin-Radecki committed Jan 5, 2023
1 parent 36016d7 commit 56c6ff4
Show file tree
Hide file tree
Showing 22 changed files with 553 additions and 5 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/build-docs.yaml
Original file line number Diff line number Diff line change
@@ -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
5 changes: 4 additions & 1 deletion aleph-client/docker/subxt-integration-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions aleph-client/src/aleph_zero.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[doc(hidden)]
#[allow(dead_code, unused_imports, non_camel_case_types)]
pub mod api {
use super::api as root_mod;
Expand Down
90 changes: 90 additions & 0 deletions aleph-client/src/connections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<T: DecodeWithMetadata + Sync, Defaultable: Sync, Iterable: Sync>(
&self,
addrs: &StaticStorageAddress<T, Yes, Defaultable, Iterable>,
at: Option<BlockHash>,
) -> 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,
Expand All @@ -57,32 +81,78 @@ pub trait ConnectionApi: Sync {
at: Option<BlockHash>,
) -> Option<T::Target>;

/// Submit a RPC call.
///
/// * `func_name` - name of a RPC call
/// * `params` - result of calling `rpc_params!` macro, that's `Vec<u8>` 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<R: Decode>(&self, func_name: String, params: RpcParams) -> anyhow::Result<R>;
}

/// 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<Call: TxPayload + Send + Sync>(
&self,
tx: Call,
status: TxStatus,
) -> anyhow::Result<BlockHash>;

/// 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<Call: TxPayload + Send + Sync>(
&self,
tx: Call,
params: BaseExtrinsicParamsBuilder<SubstrateConfig, PlainTip>,
status: TxStatus,
) -> anyhow::Result<BlockHash>;

/// 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<RootConnection>;
}

/// 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<BlockHash>;
/// 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<BlockHash>;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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<Self> {
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,
Expand Down
32 changes: 32 additions & 0 deletions aleph-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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)]

Expand All @@ -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<AlephConfig, sr25519::Pair>;
/// 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<AlephConfig>;
/// An alias for a hash type.
pub type BlockHash = H256;

pub(crate) type SubxtClient = OnlineClient<AlephConfig>;
Expand All @@ -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<P>(keypair: &P) -> AccountId
where
P: Pair,
Expand Down
17 changes: 17 additions & 0 deletions aleph-client/src/pallets/aleph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BlockHash>;

/// 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,
Expand All @@ -29,8 +42,12 @@ pub trait AlephSudoApi {
) -> anyhow::Result<BlockHash>;
}

/// 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,
Expand Down
2 changes: 2 additions & 0 deletions aleph-client/src/pallets/author.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<SessionKeys>;
}

Expand Down
Loading

0 comments on commit 56c6ff4

Please sign in to comment.