Skip to content

Commit

Permalink
Support Balance (#10)
Browse files Browse the repository at this point in the history
* Add StateApi

* RuntimeVersion Api works

* `state_getMetadata` dev

* Add account command

* Add get_storage api

* Add AccountId type

* Fix compile 1

* The first version

* Fix clippy warning

* Code clean

* Query the account balance success

* Code clean
  • Loading branch information
boundless-forest authored Aug 4, 2023
1 parent 7e8922b commit b86cd08
Show file tree
Hide file tree
Showing 15 changed files with 296 additions and 30 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# will have compiled files and executables
debug/
target/
# TODO: Remove this later
metadata.yml

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Expand Down
32 changes: 19 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,24 @@ version = "0.0.1"

[dependencies]
# crate.io
jsonrpsee = { version = "0.18.2", features = ["http-client", "async-client", "client-ws-transport-native-tls"] }
tokio = { version = "1.29.0", features = ["rt", "macros", "rt-multi-thread"] }
rustyline = { version = "12.0.0" }
clap = { version = "4.3.8", features = ["derive"] }
dirs = { version = "5.0.1" }
colored = { version = "2.0.0" }
figlet-rs = { version = "0.1.5" }
shell-words = { version = "1.1.0" }
async-trait = { version = "0.1.69" }
serde_json = { version = "1.0.100" }
serde = { version = "1.0.166" }
jsonrpsee = { version = "0.18.2", features = ["http-client", "async-client", "client-ws-transport-native-tls"] }
tokio = { version = "1.29.0", features = ["rt", "macros", "rt-multi-thread"] }
rustyline = { version = "12.0.0" }
clap = { version = "4.3.8", features = ["derive"] }
dirs = { version = "5.0.1" }
colored = { version = "2.0.0" }
figlet-rs = { version = "0.1.5" }
shell-words = { version = "1.1.0" }
async-trait = { version = "0.1.69" }
serde_json = { version = "1.0.100" }
serde = { version = "1.0.166" }
clap_complete_command = { version = "0.5.1" }
frame-metadata = { version = "16.0.0", features = ["decode"] }
# substrate
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-storage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-version = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
fp-account = { git = "https://github.com/boundless-forest/frontier", branch = "bear-polkadot-v1.0.0" }
29 changes: 27 additions & 2 deletions src/app/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@ pub enum AppCommand {
/// RPC interfaces
#[command(subcommand)]
Rpc(RpcCommand),
/// Chain interfaces
#[command(subcommand)]
Chain(ChainCommand),
/// Chain state interfaces
#[command(subcommand)]
State(StateCommand),
/// Account interfaces
#[command(subcommand)]
AccountInfo(AccountInfoCommand),
// TODO: ADD HELP COMMAND
}

#[derive(Subcommand, Clone, Debug)]
#[command(name = "rpc")]
pub enum RpcCommand {
/// Get RPC methods
// RpcMethods,
/// Get the node name
SysName,
/// Get System Properties
Expand Down Expand Up @@ -61,3 +66,23 @@ pub enum ChainCommand {
hash: String,
},
}

#[derive(Subcommand, Clone, Debug)]
#[command(name = "state")]
pub enum StateCommand {
RuntimeVersion {
#[arg(long)]
hash: Option<String>,
},
}

#[derive(Subcommand, Clone, Debug)]
#[command(name = "account-info")]
pub enum AccountInfoCommand {
Balances {
#[arg(name = "account-id", long)]
account_id: String,
#[arg(name = "at-block", long)]
at_block: Option<String>,
},
}
2 changes: 1 addition & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod command;
mod helper;

pub use command::{AppCommand, ChainCommand, RpcCommand};
pub use command::{AccountInfoCommand, AppCommand, ChainCommand, RpcCommand, StateCommand};
pub use helper::{create_editor, history_path, print_welcome_message, Config, EditorHelper};
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub enum RpcError {
WsHandshakeError,
JsonRpseeError(Error),
InvalidCommandParams,
DecodeError,
StorageKeyFailed,
}

impl From<RpcError> for AppError {
Expand Down
44 changes: 42 additions & 2 deletions src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
// std
use std::str::FromStr;
// crates.io
use frame_system::AccountInfo;
use pallet_balances::AccountData;
// this crate
use crate::{
app::{AppCommand, ChainCommand, RpcCommand},
app::{AccountInfoCommand, AppCommand, ChainCommand, RpcCommand, StateCommand},
errors::{AppError, RpcError},
networks::{ChainInfo, Network},
rpc::{print_format_json, ChainApi, RpcClient, SystemApi},
rpc::{
print_format_json, single_map_storage_key, AccountBalances, ChainApi, RpcClient, StateApi,
SystemApi,
},
};

/// The APP's command execution result.
Expand Down Expand Up @@ -83,6 +89,40 @@ pub async fn handle_commands<CI: ChainInfo>(
print_format_json(res);
},
},
AppCommand::State(sub_command) => match sub_command {
StateCommand::RuntimeVersion { hash } => {
let hash = if let Some(hash) = hash {
<CI as ChainInfo>::Hash::from_str(hash.as_str())
.map_err(|_| RpcError::InvalidCommandParams)?
} else {
client.get_finalized_head().await?.expect("Failed to get finalized head")
};

let res = client.runtime_version(hash).await?;
print_format_json(res);
},
},
AppCommand::AccountInfo(sub_command) => match sub_command {
AccountInfoCommand::Balances { account_id, at_block } => {
let metadata = client.runtime_metadata().await?;
let hash = at_block.and_then(|s| <CI as ChainInfo>::Hash::from_str(&s).ok());

let key = <CI as ChainInfo>::AccountId::from_str(account_id.as_str())
.map_err(|_| RpcError::InvalidCommandParams)?;
let storage_key = single_map_storage_key(&metadata, "System", "Account", key)
.map_err(|_| RpcError::StorageKeyFailed)?;

let account: Option<AccountInfo<CI::Nonce, AccountData<CI::Balance>>> =
client.get_storage(storage_key, hash).await?;
if let Some(a) = account {
print_format_json(AccountBalances {
free: a.data.free,
reserved: a.data.reserved,
frozen: a.data.frozen,
});
}
},
},
}

Ok(ExecutionResult::Success)
Expand Down
16 changes: 16 additions & 0 deletions src/networks/darwinia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@ use sp_runtime::{
// this crate
use super::{ChainInfo, Network};

type Signature = fp_account::EthereumSignature;
type AccountPublic = <Signature as sp_runtime::traits::Verify>::Signer;
pub type AccountId = <AccountPublic as sp_runtime::traits::IdentifyAccount>::AccountId;

/// Pangolin Chain information
pub struct PangolinChain;

impl ChainInfo for PangolinChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Pangolin;
const WS_PORT: &'static str = "wss://pangolin-rpc.darwinia.network:443";
Expand All @@ -25,10 +32,13 @@ impl ChainInfo for PangolinChain {
pub struct PangoroChain;

impl ChainInfo for PangoroChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Pangoro;
const WS_PORT: &'static str = "wss://pangoro-rpc.darwinia.network:443";
Expand All @@ -38,10 +48,13 @@ impl ChainInfo for PangoroChain {
pub struct CrabChain;

impl ChainInfo for CrabChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Crab;
const WS_PORT: &'static str = "wss://crab-rpc.darwinia.network:443";
Expand All @@ -51,10 +64,13 @@ impl ChainInfo for CrabChain {
pub struct DarwiniaChain;

impl ChainInfo for DarwiniaChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Darwinia;
const WS_PORT: &'static str = "wss://rpc.darwinia.network:443";
Expand Down
17 changes: 12 additions & 5 deletions src/networks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ pub use node_template::NoteTemplate;
pub use polkadot::{KusamaChain, PolkadotChain};

// std
use std::{marker::Sync, str::FromStr};
use std::{fmt::Debug, marker::Sync, str::FromStr};
// crates.io
use clap::Subcommand;
use serde::{Deserialize, Serialize};
use sp_core::{Decode, Encode};
use sp_runtime::DeserializeOwned;

/// The ChainInfo API
Expand All @@ -20,14 +21,20 @@ pub trait ChainInfo: Sync + Send {
/// The network name of this chain
const NET_WORK: Network;

/// The hash type of the chain
type Hash: Serialize + DeserializeOwned + Send + FromStr;
/// The account id type of the chain
type AccountId: Serialize + DeserializeOwned + Encode + FromStr + AsRef<[u8]>;
/// The balance type of the chain
type Balance: Serialize + DeserializeOwned + Decode + Debug;
/// The block type of the chain
type Block: Serialize + DeserializeOwned;
/// The block number type of the chain
type BlockNumber: Serialize + DeserializeOwned + Send + From<u32>;
/// The hash type of the chain
type Hash: Serialize + DeserializeOwned + Send + FromStr;
/// The header type of the chain
type Header: Serialize + DeserializeOwned;
/// The block type of the chain
type Block: Serialize + DeserializeOwned;
/// The nonce type of the chain
type Nonce: Serialize + DeserializeOwned + Decode + Debug;
}

#[derive(Subcommand, Clone, Debug, Serialize, Deserialize, Default)]
Expand Down
11 changes: 9 additions & 2 deletions src/networks/node_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@
use sp_core::H256;
use sp_runtime::{
generic::{Block, Header},
traits::BlakeTwo256,
OpaqueExtrinsic as UncheckedExtrinsic,
traits::{BlakeTwo256, IdentifyAccount, Verify},
MultiSignature, OpaqueExtrinsic as UncheckedExtrinsic,
};
// this crate
use super::{ChainInfo, Network};

pub type Signature = MultiSignature;
pub type AccountPublic = <Signature as Verify>::Signer;
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;

pub struct NoteTemplate;

impl ChainInfo for NoteTemplate {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Local;
const WS_PORT: &'static str = "ws://127.0.0.1:9944";
Expand Down
14 changes: 12 additions & 2 deletions src/networks/polkadot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,27 @@
use sp_core::H256;
use sp_runtime::{
generic::{Block, Header},
traits::BlakeTwo256,
OpaqueExtrinsic as UncheckedExtrinsic,
traits::{BlakeTwo256, IdentifyAccount, Verify},
MultiSignature, OpaqueExtrinsic as UncheckedExtrinsic,
};
// this crate
use super::{ChainInfo, Network};

type Signature = MultiSignature;
type AccountPublic = <Signature as Verify>::Signer;
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;

/// Polkadot Chain information
pub struct PolkadotChain;

impl ChainInfo for PolkadotChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Polkadot;
const WS_PORT: &'static str = "wss://rpc.polkadot.io:443";
Expand All @@ -25,10 +32,13 @@ impl ChainInfo for PolkadotChain {
pub struct KusamaChain;

impl ChainInfo for KusamaChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Kusama;
const WS_PORT: &'static str = "wss://kusama-rpc.polkadot.io:443";
Expand Down
27 changes: 27 additions & 0 deletions src/rpc/api.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// crates.io
use async_trait::async_trait;
use frame_metadata::RuntimeMetadataPrefixed;
use sp_core::Decode;
use sp_runtime::generic::SignedBlock;
use sp_storage::StorageKey;
use sp_version::RuntimeVersion;
// this crate
use super::{
client::RpcResult,
Expand Down Expand Up @@ -65,3 +69,26 @@ pub trait ChainApi {
hash: HashForChain<Self::ChainInfo>,
) -> RpcResult<HeaderForChain<Self::ChainInfo>>;
}

/// The State API provides access to chain state and storage.
#[async_trait]
pub trait StateApi {
/// The chain info type
type ChainInfo: ChainInfo;

/// Get the runtime version
async fn runtime_version(
&self,
hash: HashForChain<Self::ChainInfo>,
) -> RpcResult<RuntimeVersion>;

/// Get the runtime metadata
async fn runtime_metadata(&self) -> RpcResult<RuntimeMetadataPrefixed>;

/// Retrieves the storage for a key
async fn get_storage<R: Decode>(
&self,
storage_key: StorageKey,
at_block: Option<HashForChain<Self::ChainInfo>>,
) -> RpcResult<Option<R>>;
}
Loading

0 comments on commit b86cd08

Please sign in to comment.