, Error> {
+ let headers = self.rpc.subscribe_finalized_blocks().await?;
+ Ok(headers)
}
/// Create a transaction builder for a private key.
- pub fn xt(
+ pub async fn xt
(
&self,
signer: P,
nonce: Option,
- ) -> impl Future- , Error = Error>
+ ) -> Result, Error>
where
P: Pair,
P::Signature: Codec,
S: Verify,
S::Signer: From + IdentifyAccount,
{
- let client = self.clone();
let account_id = S::Signer::from(signer.public()).into_account();
- match nonce {
- Some(nonce) => Either::A(future::ok(nonce)),
- None => Either::B(self.account_nonce(account_id)),
- }
- .map(|nonce| {
- let genesis_hash = client.genesis_hash;
- let runtime_version = client.runtime_version.clone();
- XtBuilder {
- client,
- nonce,
- runtime_version,
- genesis_hash,
- signer,
- }
+ let nonce = match nonce {
+ Some(nonce) => nonce,
+ None => self.account_nonce(account_id).await?,
+ };
+
+ let genesis_hash = self.genesis_hash;
+ let runtime_version = self.runtime_version.clone();
+ Ok(XtBuilder {
+ client: self.clone(),
+ nonce,
+ runtime_version,
+ genesis_hash,
+ signer,
})
}
}
@@ -294,7 +318,7 @@ pub struct XtBuilder {
signer: P,
}
-impl XtBuilder
+impl XtBuilder
where
P: Pair,
{
@@ -365,17 +389,10 @@ where
}
/// Submits a transaction to the chain.
- pub fn submit(
- &self,
- call: Call,
- ) -> impl Future
- {
- let cli = self.client.connect();
- self.create_and_sign(call)
- .into_future()
- .map_err(Into::into)
- .and_then(move |extrinsic| {
- cli.and_then(move |rpc| rpc.submit_extrinsic(extrinsic))
- })
+ pub async fn submit(&self, call: Call) -> Result {
+ let extrinsic = self.create_and_sign(call)?;
+ let xt_hash = self.client.submit_extrinsic(extrinsic).await?;
+ Ok(xt_hash)
}
/// Submits transaction to the chain and watch for events.
@@ -422,24 +439,17 @@ where
}
/// Submits transaction to the chain and watch for events.
- pub fn submit(
+ pub async fn submit(
self,
call: Call,
- ) -> impl Future
- , Error = Error> {
- let cli = self.client.connect();
- let decoder = self.decoder.into_future().map_err(Into::into);
-
- self.builder
- .create_and_sign(call)
- .into_future()
- .map_err(Into::into)
- .join(decoder)
- .and_then(move |(extrinsic, decoder)| {
- decoder.check_missing_type_sizes();
- cli.and_then(move |rpc| {
- rpc.submit_and_watch_extrinsic(extrinsic, decoder)
- })
- })
+ ) -> Result, Error> {
+ let decoder = self.decoder?;
+ let extrinsic = self.builder.create_and_sign(call)?;
+ let xt_success = self
+ .client
+ .submit_and_watch_extrinsic(extrinsic, decoder)
+ .await?;
+ Ok(xt_success)
}
}
@@ -458,7 +468,6 @@ impl codec::Encode for Encoded {
mod tests {
use codec::Encode;
use frame_support::StorageMap;
- use futures::stream::Stream;
use sp_core::storage::StorageKey;
use sp_keyring::AccountKeyring;
@@ -469,6 +478,7 @@ mod tests {
BalancesStore,
},
DefaultNodeRuntime as Runtime,
+ Error,
};
type Index = ::Index;
@@ -476,85 +486,104 @@ mod tests {
type Address = ::Address;
type Balance = ::Balance;
- pub(crate) fn test_setup() -> (tokio::runtime::Runtime, Client) {
- env_logger::try_init().ok();
- let mut rt = tokio::runtime::Runtime::new().unwrap();
- let client_future = ClientBuilder::::new().build();
- let client = rt.block_on(client_future).unwrap();
- (rt, client)
+ pub(crate) async fn test_client() -> Client {
+ ClientBuilder::::new()
+ .build()
+ .await
+ .expect("Error creating client")
}
#[test]
#[ignore] // requires locally running substrate node
fn test_tx_transfer_balance() {
- let (mut rt, client) = test_setup();
+ env_logger::try_init().ok();
+ let transfer = async_std::task::block_on(async move {
+ let signer = AccountKeyring::Alice.pair();
+ let dest = AccountKeyring::Bob.to_account_id();
- let signer = AccountKeyring::Alice.pair();
- let mut xt = rt.block_on(client.xt(signer, None)).unwrap();
+ let client = test_client().await;
+ let mut xt = client.xt(signer, None).await?;
+ let _ = xt
+ .submit(balances::transfer::(dest.clone().into(), 10_000))
+ .await?;
- let dest = AccountKeyring::Bob.to_account_id();
- let transfer =
- xt.submit(balances::transfer::(dest.clone().into(), 10_000));
- rt.block_on(transfer).unwrap();
+ // check that nonce is handled correctly
+ xt.increment_nonce()
+ .submit(balances::transfer::(dest.clone().into(), 10_000))
+ .await
+ });
- // check that nonce is handled correctly
- let transfer = xt
- .increment_nonce()
- .submit(balances::transfer::(dest.clone().into(), 10_000));
-
- rt.block_on(transfer).unwrap();
+ assert!(transfer.is_ok())
}
#[test]
#[ignore] // requires locally running substrate node
fn test_getting_hash() {
- let (mut rt, client) = test_setup();
- rt.block_on(client.block_hash(None)).unwrap();
+ let result: Result<_, Error> = async_std::task::block_on(async move {
+ let client = test_client().await;
+ let block_hash = client.block_hash(None).await?;
+ Ok(block_hash)
+ });
+
+ assert!(result.is_ok())
}
#[test]
#[ignore] // requires locally running substrate node
fn test_getting_block() {
- let (mut rt, client) = test_setup();
- rt.block_on(client.block_hash(None).and_then(move |h| client.block(h)))
- .unwrap();
+ let result: Result<_, Error> = async_std::task::block_on(async move {
+ let client = test_client().await;
+ let block_hash = client.block_hash(None).await?;
+ let block = client.block(block_hash).await?;
+ Ok(block)
+ });
+
+ assert!(result.is_ok())
}
#[test]
#[ignore] // requires locally running substrate node
fn test_state_read_free_balance() {
- let (mut rt, client) = test_setup();
+ let result: Result<_, Error> = async_std::task::block_on(async move {
+ let account = AccountKeyring::Alice.to_account_id();
+ let client = test_client().await;
+ let balance = client.free_balance(account.into()).await?;
+ Ok(balance)
+ });
- let account = AccountKeyring::Alice.to_account_id();
- rt.block_on(client.free_balance(account.into())).unwrap();
+ assert!(result.is_ok())
}
#[test]
#[ignore] // requires locally running substrate node
fn test_chain_subscribe_blocks() {
- let (mut rt, client) = test_setup();
+ let result: Result<_, Error> = async_std::task::block_on(async move {
+ let client = test_client().await;
+ let mut blocks = client.subscribe_blocks().await?;
+ let block = blocks.next().await;
+ Ok(block)
+ });
- let stream = rt.block_on(client.subscribe_blocks()).unwrap();
- let (_header, _) = rt
- .block_on(stream.into_future().map_err(|(e, _)| e))
- .unwrap();
+ assert!(result.is_ok())
}
#[test]
#[ignore] // requires locally running substrate node
fn test_chain_subscribe_finalized_blocks() {
- let (mut rt, client) = test_setup();
+ let result: Result<_, Error> = async_std::task::block_on(async move {
+ let client = test_client().await;
+ let mut blocks = client.subscribe_finalized_blocks().await?;
+ let block = blocks.next().await;
+ Ok(block)
+ });
- let stream = rt.block_on(client.subscribe_finalized_blocks()).unwrap();
- let (_header, _) = rt
- .block_on(stream.into_future().map_err(|(e, _)| e))
- .unwrap();
+ assert!(result.is_ok())
}
#[test]
#[ignore] // requires locally running substrate node
fn test_chain_read_metadata() {
- let (_, client) = test_setup();
+ let client = async_std::task::block_on(test_client());
let balances = client.metadata().module_with_calls("Balances").unwrap();
let dest = sp_keyring::AccountKeyring::Bob.to_account_id();
diff --git a/src/metadata.rs b/src/metadata.rs
index d84978fc61..c451535d42 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -41,6 +41,8 @@ use crate::Encoded;
#[derive(Debug, thiserror::Error)]
pub enum MetadataError {
+ #[error("Error converting substrate metadata: {0}")]
+ Conversion(#[from] ConversionError),
#[error("Module not found")]
ModuleNotFound(String),
#[error("Module with events not found")]
@@ -284,14 +286,14 @@ pub enum EventArg {
}
impl FromStr for EventArg {
- type Err = Error;
+ type Err = ConversionError;
fn from_str(s: &str) -> Result {
if s.starts_with("Vec<") {
if s.ends_with('>') {
Ok(EventArg::Vec(Box::new(s[4..s.len() - 1].parse()?)))
} else {
- Err(Error::InvalidEventArg(
+ Err(ConversionError::InvalidEventArg(
s.to_string(),
"Expected closing `>` for `Vec`",
))
@@ -305,7 +307,7 @@ impl FromStr for EventArg {
}
Ok(EventArg::Tuple(args))
} else {
- Err(Error::InvalidEventArg(
+ Err(ConversionError::InvalidEventArg(
s.to_string(),
"Expecting closing `)` for tuple",
))
@@ -333,24 +335,28 @@ impl EventArg {
}
}
-#[derive(Debug)]
-pub enum Error {
+#[derive(Debug, thiserror::Error)]
+pub enum ConversionError {
+ #[error("Invalid prefix")]
InvalidPrefix,
+ #[error("Invalid version")]
InvalidVersion,
+ #[error("Expected DecodeDifferent::Decoded")]
ExpectedDecoded,
+ #[error("Invalid event arg {0}")]
InvalidEventArg(String, &'static str),
}
impl TryFrom for Metadata {
- type Error = Error;
+ type Error = MetadataError;
fn try_from(metadata: RuntimeMetadataPrefixed) -> Result {
if metadata.0 != META_RESERVED {
- return Err(Error::InvalidPrefix)
+ return Err(ConversionError::InvalidPrefix.into())
}
let meta = match metadata.1 {
RuntimeMetadata::V10(meta) => meta,
- _ => return Err(Error::InvalidVersion),
+ _ => return Err(ConversionError::InvalidVersion.into()),
};
let mut modules = HashMap::new();
let mut modules_with_calls = HashMap::new();
@@ -417,16 +423,18 @@ impl TryFrom for Metadata {
}
}
-fn convert(dd: DecodeDifferent) -> Result {
+fn convert(
+ dd: DecodeDifferent,
+) -> Result {
match dd {
DecodeDifferent::Decoded(value) => Ok(value),
- _ => Err(Error::ExpectedDecoded),
+ _ => Err(ConversionError::ExpectedDecoded),
}
}
fn convert_event(
event: frame_metadata::EventMetadata,
-) -> Result {
+) -> Result {
let name = convert(event.name)?;
let mut arguments = Vec::new();
for arg in convert(event.arguments)? {
@@ -440,7 +448,7 @@ fn convert_entry(
module_prefix: String,
storage_prefix: String,
entry: frame_metadata::StorageEntryMetadata,
-) -> Result {
+) -> Result {
let default = convert(entry.default)?;
Ok(StorageMetadata {
module_prefix,
diff --git a/src/rpc.rs b/src/rpc.rs
index 069186dd5f..35cf258ad3 100644
--- a/src/rpc.rs
+++ b/src/rpc.rs
@@ -21,35 +21,26 @@ use codec::{
Encode,
Error as CodecError,
};
-use futures::{
- future::{
- self,
- Future,
- IntoFuture,
- },
- stream::{
- self,
- Stream,
+use jsonrpsee::{
+ client::Subscription,
+ core::common::{
+ to_value as to_json_value,
+ Params,
},
+ Client,
};
-use jsonrpc_core_client::{
- RpcChannel,
- TypedSubscriptionStream,
-};
+
use num_traits::bounds::Bounded;
use frame_metadata::RuntimeMetadataPrefixed;
-use sc_rpc_api::{
- author::AuthorClient,
- chain::ChainClient,
- state::StateClient,
-};
use sp_core::{
storage::{
StorageChangeSet,
+ StorageData,
StorageKey,
},
twox_128,
+ Bytes,
};
use sp_rpc::{
list::ListOrValue,
@@ -64,6 +55,7 @@ use sp_runtime::{
};
use sp_transaction_pool::TransactionStatus;
use sp_version::RuntimeVersion;
+use std::marker::PhantomData;
use crate::{
error::Error,
@@ -83,265 +75,251 @@ use crate::{
metadata::Metadata,
};
-pub type ChainBlock = SignedBlock::Header, ::Extrinsic>>;
+pub type ChainBlock =
+ SignedBlock::Header, ::Extrinsic>>;
pub type BlockNumber = NumberOrHex<::BlockNumber>;
/// Client for substrate rpc interfaces
+#[derive(Clone)]
pub struct Rpc {
- state: StateClient,
- chain: ChainClient>,
- author: AuthorClient,
+ client: Client,
+ marker: std::marker::PhantomData,
}
-/// Allows connecting to all inner interfaces on the same RpcChannel
-impl From for Rpc {
- fn from(channel: RpcChannel) -> Self {
- Self {
- state: channel.clone().into(),
- chain: channel.clone().into(),
- author: channel.into(),
- }
+impl Rpc
+where
+ T: System,
+{
+ pub async fn connect_ws(url: &str) -> Result {
+ let raw_client = jsonrpsee::ws::ws_raw_client(&url).await?;
+ Ok(Rpc {
+ client: raw_client.into(),
+ marker: PhantomData,
+ })
}
-}
-impl Rpc {
/// Fetch a storage key
- pub fn storage(
+ pub async fn storage(
&self,
key: StorageKey,
hash: Option,
- ) -> impl Future
- , Error = Error> {
- self.state
- .storage(key, hash)
- .map_err(Into::into)
- .and_then(|data| {
- match data {
- Some(data) => {
- let value = Decode::decode(&mut &data.0[..])?;
- Ok(Some(value))
- }
- None => Ok(None),
- }
- })
+ ) -> Result