Skip to content

Commit

Permalink
add state_call to rpc api
Browse files Browse the repository at this point in the history
fix clippy

add call_raw and automatic decoding

add missing await

use vec instead of option

readd Option once again

add some runtime api calls and first test

add authority test

fix build

fix no-std build

fix  build

fix result return values

remove unnecessary result return

add finalize block

add core runtime api

fix build

add RutimeApiClient for clear distinguishion

add transaction, staking , mmr and session_keys api

fix build

fix build

fix clippy

fix naming of session keys function

add mmr tests

add session key tests

fix no-std error by defining types by self for now

add sakintapi test and fix

fix build

fix tets

update tests

add runtime api example

update README

add example of self creation of call

add metadata decoding

add list functions

add some nice printing

fix build

remove mmr

fix async build

update jsonrspee to v21 (#707)
  • Loading branch information
haerdib committed Feb 15, 2024
1 parent 839ba6f commit 0acf8bd
Show file tree
Hide file tree
Showing 20 changed files with 1,430 additions and 3 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,11 @@ jobs:
keystore_tests,
pallet_balances_tests,
pallet_transaction_payment_tests,
state_tests,
runtime_api_tests,
tungstenite_client_test,
ws_client_test,
state_tests,
query_runtime_api,
runtime_update_sync,
runtime_update_async,
]
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ ws = { version = "0.9.2", optional = true, features = ["ssl"] }
# Substrate no_std dependencies
sp-core = { default-features = false, features = ["full_crypto", "serde"], git = "https://github.com/paritytech/polkadot-sdk.git", branch = "master" }
sp-crypto-hashing = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", branch = "master" }
sp-inherents = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", branch = "master" }
sp-runtime = { default-features = false, features = ["serde"], git = "https://github.com/paritytech/polkadot-sdk.git", branch = "master" }
sp-runtime-interface = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", branch = "master" }
sp-storage = { default-features = false, features = ["serde"], git = "https://github.com/paritytech/polkadot-sdk.git", branch = "master" }
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ The following async examples can be found in the [async examples](/examples/asyn
* [get_blocks](/examples/async/examples/get_blocks.rs): Read header, block and signed block from storage.
* [get_storage](/examples/async/examples/get_storage.rs): Read storage values.
* [print_metadata](/examples/async/examples/print_metadata.rs): Print the metadata of the node in a readable way.
* [query_runtime_api](/src/examples/async/examples/query_runtime_api.rs): How to query the runtime api.
* [runtime_update_async](/examples/async/examples/runtime_update_async.rs): How to do an runtime upgrade asynchronously.
* [staking_batch_payout](/examples/async/examples/staking_batch_payout.rs): Batch reward payout for validator.
* [subscribe_events](/examples/async/examples/subscribe_events.rs): Subscribe and react on events.
Expand Down
99 changes: 99 additions & 0 deletions examples/async/examples/query_runtime_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
Copyright 2024 Supercomputing Systems AG
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

//! Very simple example that shows how to query Runtime Api of a Substrate node.

use codec::Encode;
use sp_core::sr25519;
use sp_keyring::AccountKeyring;
use substrate_api_client::{
ac_primitives::AssetRuntimeConfig,
extrinsic::BalancesExtrinsics,
rpc::JsonrpseeClient,
runtime_api::{AuthorityDiscoveryApi, CoreApi, MetadataApi, RuntimeApi, TransactionPaymentApi},
Api, GetChainInfo,
};

// To test this example with CI we run it against the Substrate kitchensink node, which uses the asset pallet.
// Therefore, we need to use the `AssetRuntimeConfig` in this example.
// ! However, most Substrate runtimes do not use the asset pallet at all. So if you run an example against your own node
// you most likely should use `DefaultRuntimeConfig` instead.

#[tokio::main]
async fn main() {
env_logger::init();

// Initialize the api, which retrieves the metadata from the node upon initialization.
let client = JsonrpseeClient::with_default_url().await.unwrap();
let mut api = Api::<AssetRuntimeConfig, _>::new(client).await.unwrap();
let alice_pair = AccountKeyring::Alice.pair();
api.set_signer(alice_pair.into());
let runtime_api = api.runtime_api();

// Query the fee of an extrinsic.
let bob = AccountKeyring::Bob.to_account_id();
let balance_extrinsic = api.balance_transfer_allow_death(bob.clone().into(), 1000).await;
let extrinsic_fee_details = runtime_api
.query_fee_details(balance_extrinsic.clone(), 1000, None)
.await
.unwrap();
let final_fee = extrinsic_fee_details.final_fee();
println!("To exceute the balance extrinsic, the following fee is required: {:?}", final_fee);

// Get the authority Ids.
let authority_ids: Vec<sr25519::Public> = runtime_api.authority_discovery(None).await.unwrap();
println!("The following authorities are currently active:");
for authority in authority_ids {
println!("{:?}", authority);
}

// Query the runtime api version.
let version = runtime_api.version(None).await.unwrap();
println!("{:?}", version);

// Query the available metadata versions.
let metadata_versions = runtime_api.metadata_versions(None).await.unwrap();
assert_eq!(metadata_versions, [14, 15]);

// List all apis and functions thereof.
let trait_names = runtime_api.list_traits(None).await.unwrap();
println!();
println!("Available traits:");
for name in trait_names {
println!("{name}");
}
println!();

let trait_name = "BabeApi";
let method_names = runtime_api.list_methods_of_trait(trait_name, None).await.unwrap();
println!("Available methods of {trait_name}:");
for name in method_names {
println!("{name}");
}
println!();

// Create your own runtime api call.
let parameters = vec![1000.encode()];
let latest_block_hash = api.get_block_hash(None).await.unwrap().unwrap();
let result: Result<u128, substrate_api_client::Error> = runtime_api
.runtime_call(
"TransactionPaymentApi_query_length_to_fee",
parameters,
Some(latest_block_hash),
)
.await;
let output = result.unwrap();
println!("Received the following output: {:?}", output);
}
62 changes: 61 additions & 1 deletion primitives/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
//! Re-defintion of substrate primitives.
//! Needed because substrate pallets compile to wasm in no_std.

use alloc::string::String;
use alloc::{string::String, vec::Vec};
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -299,3 +299,63 @@ pub enum ChainType {
/// Arbitrary properties defined in chain spec as a JSON object
// https://github.com/paritytech/substrate/blob/c172d0f683fab3792b90d876fd6ca27056af9fe9/client/chain-spec/src/lib.rs#L215-L216
pub type Properties = serde_json::map::Map<String, serde_json::Value>;

// Merkle-mountain-range primitives. sp-mmr-primitives does not seem to be no-std compatible as of now.
// Might be caused by the thiserror import in the toml. Parity probably will accept a PR if opened.

/// A type-safe wrapper for the concrete leaf type.
///
/// This structure serves merely to avoid passing raw `Vec<u8>` around.
/// It must be `Vec<u8>`-encoding compatible.
// https://github.com/paritytech/polkadot-sdk/blob/a190e0e9253562fdca9c1b6e9541a7ea0a50c018/substrate/primitives/merkle-mountain-range/src/lib.rs#L138-L146
#[derive(codec::Encode, codec::Decode, PartialEq, Eq, TypeInfo, Clone)]
pub struct EncodableOpaqueLeaf(pub Vec<u8>);

/// An MMR proof data for a group of leaves.
// https://github.com/paritytech/polkadot-sdk/blob/a190e0e9253562fdca9c1b6e9541a7ea0a50c018/substrate/primitives/merkle-mountain-range/src/lib.rs#L351-L360
#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
pub struct Proof<Hash> {
/// The indices of the leaves the proof is for.
pub leaf_indices: Vec<LeafIndex>,
/// Number of leaves in MMR, when the proof was generated.
pub leaf_count: NodeIndex,
/// Proof elements (hashes of siblings of inner nodes on the path to the leaf).
pub items: Vec<Hash>,
}

/// A type to describe leaf position in the MMR.
///
/// Note this is different from [`NodeIndex`], which can be applied to
/// both leafs and inner nodes. Leafs will always have consecutive `LeafIndex`,
/// but might be actually at different positions in the MMR `NodeIndex`.
// https://github.com/paritytech/polkadot-sdk/blob/a190e0e9253562fdca9c1b6e9541a7ea0a50c018/substrate/primitives/merkle-mountain-range/src/lib.rs#L45
pub type LeafIndex = u64;
/// A type to describe node position in the MMR (node index).
// https://github.com/paritytech/polkadot-sdk/blob/a190e0e9253562fdca9c1b6e9541a7ea0a50c018/substrate/primitives/merkle-mountain-range/src/lib.rs#L138-L146
pub type NodeIndex = u64;

/// Merkle Mountain Range operation error.
// https://github.com/paritytech/polkadot-sdk/blob/a190e0e9253562fdca9c1b6e9541a7ea0a50c018/substrate/primitives/merkle-mountain-range/src/lib.rs#L362-L396
#[derive(codec::Encode, codec::Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)]
pub enum MmrError {
/// Error during translation of a block number into a leaf index.
InvalidNumericOp,
/// Error while pushing new node.
Push,
/// Error getting the new root.
GetRoot,
/// Error committing changes.
Commit,
/// Error during proof generation.
GenerateProof,
/// Proof verification error.
Verify,
/// Leaf not found in the storage.
LeafNotFound,
/// Mmr Pallet not included in runtime
PalletNotIncluded,
/// Cannot find the requested leaf index
InvalidLeafIndex,
/// The provided best know block number is invalid.
InvalidBestKnownBlock,
}
13 changes: 12 additions & 1 deletion src/api/api_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
use crate::{
api::error::{Error, Result},
rpc::Request,
runtime_api::RuntimeApiClient,
GetAccountInformation,
};
use ac_compose_macros::rpc_params;
use ac_node_api::metadata::Metadata;
use ac_primitives::{Config, ExtrinsicParams, SignExtrinsic};
#[cfg(not(feature = "sync-api"))]
use alloc::boxed::Box;
use alloc::sync::Arc;
use codec::Decode;
use core::convert::TryFrom;
use frame_metadata::RuntimeMetadataPrefixed;
Expand All @@ -36,7 +38,8 @@ pub struct Api<T: Config, Client> {
genesis_hash: T::Hash,
metadata: Metadata,
runtime_version: RuntimeVersion,
client: Client,
client: Arc<Client>,
runtime_api: RuntimeApiClient<T, Client>,
additional_extrinsic_params:
Option<<T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::AdditionalParams>,
}
Expand All @@ -49,12 +52,15 @@ impl<T: Config, Client> Api<T, Client> {
runtime_version: RuntimeVersion,
client: Client,
) -> Self {
let client = Arc::new(client);
let runtime_api = RuntimeApiClient::new(client.clone());
Self {
signer: None,
genesis_hash,
metadata,
runtime_version,
client,
runtime_api,
additional_extrinsic_params: None,
}
}
Expand Down Expand Up @@ -102,6 +108,11 @@ impl<T: Config, Client> Api<T, Client> {
self.additional_extrinsic_params = Some(add_params);
}

/// Access the RuntimeApi.
pub fn runtime_api(&self) -> &RuntimeApiClient<T, Client> {
&self.runtime_api
}

/// Get the extrinsic params with the set additional params. If no additional params are set,
/// the default is taken.
pub fn extrinsic_params(&self, nonce: T::Index) -> T::ExtrinsicParams {
Expand Down
1 change: 1 addition & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub use rpc_api::{
pub mod api_client;
pub mod error;
pub mod rpc_api;
pub mod runtime_api;

/// Extrinsic report returned upon a submit_and_watch request.
/// Holds as much information as available.
Expand Down
52 changes: 52 additions & 0 deletions src/api/runtime_api/account_nonce.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright 2024 Supercomputing Systems AG
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

use super::{RuntimeApi, RuntimeApiClient};
use crate::{api::Result, rpc::Request};
use ac_primitives::config::Config;
#[cfg(not(feature = "sync-api"))]
use alloc::boxed::Box;
use alloc::vec;
use sp_core::Encode;

#[maybe_async::maybe_async(?Send)]
pub trait AccountNonceApi: RuntimeApi {
type Index;
type AccountId;

/// The API to query account nonce (aka transaction index).
async fn account_nonce(
&self,
account_id: Self::AccountId,
at_block: Option<Self::Hash>,
) -> Result<Self::Index>;
}

#[maybe_async::maybe_async(?Send)]
impl<T, Client> AccountNonceApi for RuntimeApiClient<T, Client>
where
T: Config,
Client: Request,
{
type Index = T::Index;
type AccountId = T::AccountId;

async fn account_nonce(
&self,
account_id: Self::AccountId,
at_block: Option<Self::Hash>,
) -> Result<Self::Index> {
self.runtime_call("AccountNonceApi_account_nonce", vec![account_id.encode()], at_block)
.await
}
}
Loading

0 comments on commit 0acf8bd

Please sign in to comment.