Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion aleph-client/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion aleph-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aleph_client"
version = "2.4.0"
version = "2.5.0"
edition = "2021"
license = "Apache 2.0"

Expand Down
8 changes: 7 additions & 1 deletion aleph-client/src/aleph_zero.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
#[allow(dead_code, unused_imports, non_camel_case_types)]
#[doc(hidden)]
#[allow(
dead_code,
unused_imports,
non_camel_case_types,
rustdoc::broken_intra_doc_links
)]
pub mod api {
use super::api as root_mod;
pub static PALLETS: [&str; 21usize] = [
Expand Down
87 changes: 86 additions & 1 deletion aleph-client/src/connections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,36 @@ use subxt::{

use crate::{api, sp_weights::weight_v2::Weight, BlockHash, Call, Client, KeyPair, TxStatus};

/// Capable of communicating with a live Aleph chain.
#[derive(Clone)]
pub struct Connection {
/// A `subxt` object representing a communication channel to a live chain.
/// Requires an url address for creation.
pub client: Client,
}

/// Any connection that is signed by some key.
pub struct SignedConnection {
/// Composition of [`Connection`] object.
pub connection: Connection,
/// A key which signs any txes send via this connection.
pub signer: KeyPair,
}

/// Specific connection that is singed by the sudo key.
pub struct RootConnection {
/// Composition of [`Connection`] object.
pub connection: Connection,
/// Sudo key pair.
pub root: KeyPair,
}

/// 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 @@ -62,10 +74,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: String) -> Self {
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
pub async fn new_with_retries(address: String, mut retries: u32) -> Self {
loop {
let client = Client::from_url(&address).await;
Expand All @@ -80,6 +98,13 @@ impl Connection {
}
}

/// Retrieves a decoded storage value stored under given storage 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
pub async fn get_storage_entry<T: DecodeWithMetadata, Defaultable, Iterable>(
&self,
addrs: &StaticStorageAddress<T, Yes, Defaultable, Iterable>,
Expand All @@ -90,6 +115,19 @@ impl Connection {
.expect("There should be a value")
}

/// Retrieves a decoded storage value stored under given storage 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
/// ```rust
/// let addrs = api::storage().treasury().proposal_count();
/// get_storage_entry_maybe(&addrs, None).await
/// ```
pub async fn get_storage_entry_maybe<T: DecodeWithMetadata, Defaultable, Iterable>(
&self,
addrs: &StaticStorageAddress<T, Yes, Defaultable, Iterable>,
Expand All @@ -103,6 +141,21 @@ impl Connection {
.expect("Should access storage")
}

/// 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
/// ```rust
/// let func_name = "alephNode_emergencyFinalize";
/// let hash = BlockHash::from_str("0x37841c5a09db7d9f985f2306866f196365f1bb9372efc76086e07b882296e1cc").expect("Hash is properly hex encoded");
/// let signature = key_pair.sign(&hash.encode());
/// let raw_signature: &[u8] = signature.as_ref();
/// let params = rpc_params![raw_signature, hash, number];
/// let _: () = rpc_call(func_name.to_string(), params).await?;
/// ```
pub async fn rpc_call<R: Decode>(
&self,
func_name: String,
Expand All @@ -116,14 +169,31 @@ impl Connection {
}

impl SignedConnection {
/// Creates new signed connection from a given url.
/// * `address` - address in websocket format, e.g. `ws://127.0.0.1:9943`
/// * `signer` - a [`KeyPair`] of signing account
pub async fn new(address: String, 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 }
}

/// Send a transaction to a chain. It waits for a given tx `status`.
/// * `tx` - encoded transaction payload
/// * `status` - tx status
/// # Returns
/// Block hash of block where transaction was put or error
/// # Examples
/// ```rust
/// let tx = api::tx()
/// .balances()
/// .transfer(MultiAddress::Id(dest), amount);
/// send_tx(tx, status).await
/// ```
pub async fn send_tx<Call: TxPayload>(
&self,
tx: Call,
Expand All @@ -133,6 +203,12 @@ impl SignedConnection {
.await
}

/// 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` - tx status
/// # Returns
/// Block hash of block where transaction was put or error
pub async fn send_tx_with_params<Call: TxPayload>(
&self,
tx: Call,
Expand Down Expand Up @@ -162,10 +238,18 @@ impl SignedConnection {
}

impl RootConnection {
/// Creates new root 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`
/// * `root` - a [`KeyPair`] of the Sudo account
pub async fn new(address: String, 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 All @@ -191,6 +275,7 @@ impl RootConnection {
})
}

/// Converts [`RootConnection`] to [`SignedConnection`]
pub fn as_signed(&self) -> SignedConnection {
SignedConnection {
connection: self.connection.clone(),
Expand Down
30 changes: 30 additions & 0 deletions aleph-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
#![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`].
//!
extern crate core;

pub use subxt::ext::sp_core::Pair;
Expand All @@ -13,44 +21,66 @@ 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 use connections::{Connection, RootConnection, SignedConnection, 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::{
Connection, Pair, RootConnection, SudoCall, TxStatus,
};

// TODO replace docs with link to pallet aleph docs, once they are published
/// Any object that implements 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>;
}

/// Any object that implements 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, Connection};

/// Any object that implements `author` RPC.
#[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) -> SessionKeys;
}

Expand Down
Loading