Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A0-1608 Add documentation of all public API and traits of aleph-client #832

Merged
merged 13 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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