Use the generated DispatchError instead of the hardcoded Substrate one (#394)

* WIP DispatchError generic param

* main crate now compiling with new E generic param for DispatchError

* Remove E param from RpcClient since it doesn't really need it

* Point to generated DispatchError in codegen so no need for additional param there

* More error hunting; cargo check --all-targets passes now

* Use our own RuntimeVersion struct (for now) to avoid error decoding into sp_version::RuntimeVersion

* cargo fmt

* fix docs and expose private documented thing that I think should be pub

* move error info to compile time so that we can make DispatchError a little nicer to work with

* cargo fmt

* clippy

* Rework error handling to remove <E> param in most cases

* fix Error doc ambiguity (hopefully)

* doc tweaks

* docs: remove dismbiguation thing that isn't needed now

* One more Error<E> that can be a BasicError

* rewrite pallet errors thing into normal loops to tidy

* tidy errors codegen a little

* tidy examples/custom_type_derives.rs a little

* cargo fmt

* silcnce clippy in example
This commit is contained in:
James Wilson
2022-01-20 16:35:42 +00:00
committed by GitHub
parent 79bf32a0a9
commit 643795919f
24 changed files with 3471 additions and 2453 deletions
+8 -5
View File
@@ -68,7 +68,7 @@ pub fn generate_calls(
pub fn #fn_name(
&self,
#( #call_fn_args, )*
) -> ::subxt::SubmittableExtrinsic<'a, T, E, A, #call_struct_name> {
) -> ::subxt::SubmittableExtrinsic<'a, T, X, A, #call_struct_name, DispatchError> {
let call = #call_struct_name { #( #call_args, )* };
::subxt::SubmittableExtrinsic::new(self.client, call)
}
@@ -80,17 +80,20 @@ pub fn generate_calls(
quote! {
pub mod calls {
use super::#types_mod_ident;
type DispatchError = #types_mod_ident::sp_runtime::DispatchError;
#( #call_structs )*
pub struct TransactionApi<'a, T: ::subxt::Config, E, A> {
pub struct TransactionApi<'a, T: ::subxt::Config, X, A> {
client: &'a ::subxt::Client<T>,
marker: ::core::marker::PhantomData<(E, A)>,
marker: ::core::marker::PhantomData<(X, A)>,
}
impl<'a, T, E, A> TransactionApi<'a, T, E, A>
impl<'a, T, X, A> TransactionApi<'a, T, X, A>
where
T: ::subxt::Config,
E: ::subxt::SignedExtra<T>,
X: ::subxt::SignedExtra<T>,
A: ::subxt::AccountData<T>,
{
pub fn new(client: &'a ::subxt::Client<T>) -> Self {
+162
View File
@@ -0,0 +1,162 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is part of subxt.
//
// subxt is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// subxt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
use frame_metadata::v14::RuntimeMetadataV14;
use proc_macro2::{
Span as Span2,
TokenStream as TokenStream2,
};
use quote::quote;
/// Tokens which allow us to provide static error information in the generated output.
pub struct ErrorDetails {
/// This type definition will be used in the `dispatch_error_impl_fn` and is
/// expected to be generated somewhere in scope for that to be possible.
pub type_def: TokenStream2,
// A function which will live in an impl block for our `DispatchError`,
// to statically return details for known error types:
pub dispatch_error_impl_fn: TokenStream2,
}
impl ErrorDetails {
fn emit_compile_error(err: &str) -> ErrorDetails {
let err_lit_str = syn::LitStr::new(err, Span2::call_site());
ErrorDetails {
type_def: quote!(),
dispatch_error_impl_fn: quote!(compile_error!(#err_lit_str)),
}
}
}
/// The purpose of this is to enumerate all of the possible `(module_index, error_index)` error
/// variants, so that we can convert `u8` error codes inside a generated `DispatchError` into
/// nicer error strings with documentation. To do this, we emit the type we'll return instances of,
/// and a function that returns such an instance for all of the error codes seen in the metadata.
pub fn generate_error_details(metadata: &RuntimeMetadataV14) -> ErrorDetails {
let errors = match pallet_errors(metadata) {
Ok(errors) => errors,
Err(e) => {
let err_string =
format!("Failed to generate error details from metadata: {}", e);
return ErrorDetails::emit_compile_error(&err_string)
}
};
let match_body_items = errors.into_iter().map(|err| {
let docs = err.docs;
let pallet_index = err.pallet_index;
let error_index = err.error_index;
let pallet_name = err.pallet;
let error_name = err.error;
quote! {
(#pallet_index, #error_index) => Some(ErrorDetails {
pallet: #pallet_name,
error: #error_name,
docs: #docs
})
}
});
ErrorDetails {
type_def: quote! {
pub struct ErrorDetails {
pub pallet: &'static str,
pub error: &'static str,
pub docs: &'static str,
}
},
dispatch_error_impl_fn: quote! {
pub fn details(&self) -> Option<ErrorDetails> {
if let Self::Module { index, error } = self {
match (index, error) {
#( #match_body_items ),*,
_ => None
}
} else {
None
}
}
},
}
}
fn pallet_errors(
metadata: &RuntimeMetadataV14,
) -> Result<Vec<ErrorMetadata>, InvalidMetadataError> {
let get_type_def_variant = |type_id: u32| {
let ty = metadata
.types
.resolve(type_id)
.ok_or(InvalidMetadataError::MissingType(type_id))?;
if let scale_info::TypeDef::Variant(var) = ty.type_def() {
Ok(var)
} else {
Err(InvalidMetadataError::TypeDefNotVariant(type_id))
}
};
let mut pallet_errors = vec![];
for pallet in &metadata.pallets {
let error = match &pallet.error {
Some(err) => err,
None => continue,
};
let type_def_variant = get_type_def_variant(error.ty.id())?;
for var in type_def_variant.variants().iter() {
pallet_errors.push(ErrorMetadata {
pallet_index: pallet.index,
error_index: var.index(),
pallet: pallet.name.clone(),
error: var.name().clone(),
docs: var.docs().join("\n"),
});
}
}
Ok(pallet_errors)
}
/// Information about each error that we find in the metadata;
/// used to generate the static error information.
#[derive(Clone, Debug)]
struct ErrorMetadata {
pub pallet_index: u8,
pub error_index: u8,
pub pallet: String,
pub error: String,
pub docs: String,
}
#[derive(Debug)]
enum InvalidMetadataError {
MissingType(u32),
TypeDefNotVariant(u32),
}
impl std::fmt::Display for InvalidMetadataError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InvalidMetadataError::MissingType(n) => {
write!(f, "Type {} missing from type registry", n)
}
InvalidMetadataError::TypeDefNotVariant(n) => {
write!(f, "Type {} was not a variant/enum type", n)
}
}
}
}
+26 -12
View File
@@ -15,6 +15,7 @@
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
mod calls;
mod errors;
mod events;
mod storage;
@@ -216,6 +217,10 @@ impl RuntimeGenerator {
pallet.calls.as_ref().map(|_| pallet_mod_name)
});
let error_details = errors::generate_error_details(&self.metadata);
let error_type = error_details.type_def;
let error_fn = error_details.dispatch_error_impl_fn;
quote! {
#[allow(dead_code, unused_imports, non_camel_case_types)]
pub mod #mod_ident {
@@ -227,6 +232,15 @@ impl RuntimeGenerator {
/// constructing a transaction.
pub type DefaultAccountData = self::system::storage::Account;
/// The default error type returned when there is a runtime issue.
pub type DispatchError = self::runtime_types::sp_runtime::DispatchError;
// Statically generate error information so that we don't need runtime metadata for it.
#error_type
impl DispatchError {
#error_fn
}
impl ::subxt::AccountData<::subxt::DefaultConfig> for DefaultAccountData {
fn nonce(result: &<Self as ::subxt::StorageEntry>::Value) -> <::subxt::DefaultConfig as ::subxt::Config>::Index {
result.nonce
@@ -236,31 +250,31 @@ impl RuntimeGenerator {
}
}
pub struct RuntimeApi<T: ::subxt::Config, E> {
pub struct RuntimeApi<T: ::subxt::Config, X> {
pub client: ::subxt::Client<T>,
marker: ::core::marker::PhantomData<E>,
marker: ::core::marker::PhantomData<X>,
}
impl<T, E> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, E>
impl<T, X> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X>
where
T: ::subxt::Config,
E: ::subxt::SignedExtra<T>,
X: ::subxt::SignedExtra<T>,
{
fn from(client: ::subxt::Client<T>) -> Self {
Self { client, marker: ::core::marker::PhantomData }
}
}
impl<'a, T, E> RuntimeApi<T, E>
impl<'a, T, X> RuntimeApi<T, X>
where
T: ::subxt::Config,
E: ::subxt::SignedExtra<T>,
X: ::subxt::SignedExtra<T>,
{
pub fn storage(&'a self) -> StorageApi<'a, T> {
StorageApi { client: &self.client }
}
pub fn tx(&'a self) -> TransactionApi<'a, T, E, DefaultAccountData> {
pub fn tx(&'a self) -> TransactionApi<'a, T, X, DefaultAccountData> {
TransactionApi { client: &self.client, marker: ::core::marker::PhantomData }
}
}
@@ -280,19 +294,19 @@ impl RuntimeGenerator {
)*
}
pub struct TransactionApi<'a, T: ::subxt::Config, E, A> {
pub struct TransactionApi<'a, T: ::subxt::Config, X, A> {
client: &'a ::subxt::Client<T>,
marker: ::core::marker::PhantomData<(E, A)>,
marker: ::core::marker::PhantomData<(X, A)>,
}
impl<'a, T, E, A> TransactionApi<'a, T, E, A>
impl<'a, T, X, A> TransactionApi<'a, T, X, A>
where
T: ::subxt::Config,
E: ::subxt::SignedExtra<T>,
X: ::subxt::SignedExtra<T>,
A: ::subxt::AccountData<T>,
{
#(
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, E, A> {
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, X, A> {
#pallets_with_calls::calls::TransactionApi::new(self.client)
}
)*
+3 -2
View File
@@ -50,6 +50,7 @@ pub fn generate_storage(
quote! {
pub mod storage {
use super::#types_mod_ident;
#( #storage_structs )*
pub struct StorageApi<'a, T: ::subxt::Config> {
@@ -195,7 +196,7 @@ fn generate_storage_entry_fns(
pub async fn #fn_name_iter(
&self,
hash: ::core::option::Option<T::Hash>,
) -> ::core::result::Result<::subxt::KeyIter<'a, T, #entry_struct_ident>, ::subxt::Error> {
) -> ::core::result::Result<::subxt::KeyIter<'a, T, #entry_struct_ident>, ::subxt::BasicError> {
self.client.storage().iter(hash).await
}
)
@@ -211,7 +212,7 @@ fn generate_storage_entry_fns(
&self,
#( #key_args, )*
hash: ::core::option::Option<T::Hash>,
) -> ::core::result::Result<#return_ty, ::subxt::Error> {
) -> ::core::result::Result<#return_ty, ::subxt::BasicError> {
let entry = #constructor;
self.client.storage().#fetch(&entry, hash).await
}
+1
View File
@@ -38,6 +38,7 @@ impl Default for GeneratedTypeDerives {
let mut derives = Punctuated::new();
derives.push(syn::parse_quote!(::subxt::codec::Encode));
derives.push(syn::parse_quote!(::subxt::codec::Decode));
derives.push(syn::parse_quote!(Debug));
Self::new(derives)
}
}
+34 -34
View File
@@ -64,7 +64,7 @@ fn generate_struct_with_primitives() {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct S {
pub a: ::core::primitive::bool,
pub b: ::core::primitive::u32,
@@ -110,12 +110,12 @@ fn generate_struct_with_a_struct_field() {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Child {
pub a: ::core::primitive::i32,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Parent {
pub a: ::core::primitive::bool,
pub b: root::subxt_codegen::types::tests::Child,
@@ -155,10 +155,10 @@ fn generate_tuple_struct() {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Child(pub ::core::primitive::i32,);
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Parent(pub ::core::primitive::bool, pub root::subxt_codegen::types::tests::Child,);
}
}
@@ -238,43 +238,43 @@ fn derive_compact_as_for_uint_wrapper_structs() {
use super::root;
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Su128 { pub a: ::core::primitive::u128, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Su16 { pub a: ::core::primitive::u16, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Su32 { pub a: ::core::primitive::u32, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Su64 { pub a: ::core::primitive::u64, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Su8 { pub a: ::core::primitive::u8, }
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct TSu128(pub ::core::primitive::u128,);
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct TSu16(pub ::core::primitive::u16,);
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct TSu32(pub ::core::primitive::u32,);
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct TSu64(pub ::core::primitive::u64,);
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct TSu8(pub ::core::primitive::u8,);
}
}
@@ -310,7 +310,7 @@ fn generate_enum() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub enum E {
# [codec (index = 0)]
A,
@@ -368,7 +368,7 @@ fn compact_fields() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub enum E {
# [codec (index = 0)]
A {
@@ -379,12 +379,12 @@ fn compact_fields() {
B( #[codec(compact)] ::core::primitive::u32,),
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct S {
#[codec(compact)] pub a: ::core::primitive::u32,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct TupleStruct(#[codec(compact)] pub ::core::primitive::u32,);
}
}
@@ -418,7 +418,7 @@ fn generate_array_field() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct S {
pub a: [::core::primitive::u8; 32usize],
}
@@ -455,7 +455,7 @@ fn option_fields() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct S {
pub a: ::core::option::Option<::core::primitive::bool>,
pub b: ::core::option::Option<::core::primitive::u32>,
@@ -495,7 +495,7 @@ fn box_fields_struct() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct S {
pub a: ::std::boxed::Box<::core::primitive::bool>,
pub b: ::std::boxed::Box<::core::primitive::u32>,
@@ -535,7 +535,7 @@ fn box_fields_enum() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub enum E {
# [codec (index = 0)]
A(::std::boxed::Box<::core::primitive::bool>,),
@@ -575,7 +575,7 @@ fn range_fields() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct S {
pub a: ::core::ops::Range<::core::primitive::u32>,
pub b: ::core::ops::RangeInclusive<::core::primitive::u32>,
@@ -619,12 +619,12 @@ fn generics() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Bar {
pub b: root::subxt_codegen::types::tests::Foo<::core::primitive::u32>,
pub c: root::subxt_codegen::types::tests::Foo<::core::primitive::u8>,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Foo<_0> {
pub a: _0,
}
@@ -667,12 +667,12 @@ fn generics_nested() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Bar<_0> {
pub b: root::subxt_codegen::types::tests::Foo<_0, ::core::primitive::u32>,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Foo<_0, _1> {
pub a: _0,
pub b: ::core::option::Option<(_0, _1,)>,
@@ -718,7 +718,7 @@ fn generate_bitvec() {
quote! {
pub mod tests {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct S {
pub lsb: ::subxt::bitvec::vec::BitVec<root::bitvec::order::Lsb0, ::core::primitive::u8>,
pub msb: ::subxt::bitvec::vec::BitVec<root::bitvec::order::Msb0, ::core::primitive::u16>,
@@ -772,12 +772,12 @@ fn generics_with_alias_adds_phantom_data_marker() {
pub mod tests {
use super::root;
#[derive(::subxt::codec::CompactAs)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct NamedFields<_0> {
pub b: ::core::primitive::u32,
#[codec(skip)] pub __subxt_unused_type_params: ::core::marker::PhantomData<_0>,
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct UnnamedFields<_0, _1> (
pub (::core::primitive::u32, ::core::primitive::u32,),
#[codec(skip)] pub ::core::marker::PhantomData<(_0, _1)>,
@@ -840,20 +840,20 @@ fn modules() {
pub mod b {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Bar {
pub a: root::subxt_codegen::types::tests::m::a::Foo,
}
}
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Foo {}
}
pub mod c {
use super::root;
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode)]
#[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)]
pub struct Foo {
pub a: root::subxt_codegen::types::tests::m::a::b::Bar,
}
+6 -2
View File
@@ -14,9 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
#![allow(clippy::redundant_clone)]
#[subxt::subxt(
runtime_metadata_path = "examples/polkadot_metadata.scale",
generated_type_derives = "Clone, Debug"
// We can add (certain) custom derives to the generated types by providing
// a comma separated list to the below attribute. Most useful for adding `Clone`:
generated_type_derives = "Clone, Hash"
)]
pub mod polkadot {}
@@ -25,6 +29,6 @@ use polkadot::runtime_types::frame_support::PalletId;
#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pallet_id = PalletId([1u8; 8]);
let _ = <PalletId as Clone>::clone(&pallet_id);
let _ = pallet_id.clone();
Ok(())
}
+20 -16
View File
@@ -19,7 +19,7 @@ use sp_runtime::traits::Hash;
pub use sp_runtime::traits::SignedExtension;
use crate::{
error::Error,
error::BasicError,
events::EventsDecoder,
extrinsic::{
self,
@@ -40,6 +40,7 @@ use crate::{
Config,
Metadata,
};
use codec::Decode;
use derivative::Derivative;
use std::sync::Arc;
@@ -80,7 +81,7 @@ impl ClientBuilder {
}
/// Creates a new Client.
pub async fn build<T: Config>(self) -> Result<Client<T>, Error> {
pub async fn build<T: Config>(self) -> Result<Client<T>, BasicError> {
let client = if let Some(client) = self.client {
client
} else {
@@ -186,18 +187,19 @@ impl<T: Config> Client<T> {
}
/// A constructed call ready to be signed and submitted.
pub struct SubmittableExtrinsic<'client, T: Config, E, A, C> {
pub struct SubmittableExtrinsic<'client, T: Config, X, A, C, E: Decode> {
client: &'client Client<T>,
call: C,
marker: std::marker::PhantomData<(E, A)>,
marker: std::marker::PhantomData<(X, A, E)>,
}
impl<'client, T, E, A, C> SubmittableExtrinsic<'client, T, E, A, C>
impl<'client, T, X, A, C, E> SubmittableExtrinsic<'client, T, X, A, C, E>
where
T: Config,
E: SignedExtra<T>,
X: SignedExtra<T>,
A: AccountData<T>,
C: Call + Send + Sync,
E: Decode,
{
/// Create a new [`SubmittableExtrinsic`].
pub fn new(client: &'client Client<T>, call: C) -> Self {
@@ -214,16 +216,18 @@ where
/// and obtain details about it, once it has made it into a block.
pub async fn sign_and_submit_then_watch(
self,
signer: &(dyn Signer<T, E> + Send + Sync),
) -> Result<TransactionProgress<'client, T>, Error>
signer: &(dyn Signer<T, X> + Send + Sync),
) -> Result<TransactionProgress<'client, T, E>, BasicError>
where
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
{
// Sign the call data to create our extrinsic.
let extrinsic = self.create_signed(signer, Default::default()).await?;
// Get a hash of the extrinsic (we'll need this later).
let ext_hash = T::Hashing::hash_of(&extrinsic);
// Submit and watch for transaction progress.
let sub = self.client.rpc().watch_extrinsic(extrinsic).await?;
@@ -240,10 +244,10 @@ where
/// and has been included in the transaction pool.
pub async fn sign_and_submit(
self,
signer: &(dyn Signer<T, E> + Send + Sync),
) -> Result<T::Hash, Error>
signer: &(dyn Signer<T, X> + Send + Sync),
) -> Result<T::Hash, BasicError>
where
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
{
let extrinsic = self.create_signed(signer, Default::default()).await?;
@@ -253,11 +257,11 @@ where
/// Creates a signed extrinsic.
pub async fn create_signed(
&self,
signer: &(dyn Signer<T, E> + Send + Sync),
additional_params: E::Parameters,
) -> Result<UncheckedExtrinsic<T, E>, Error>
signer: &(dyn Signer<T, X> + Send + Sync),
additional_params: X::Parameters,
) -> Result<UncheckedExtrinsic<T, X>, BasicError>
where
<<E as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
{
let account_nonce = if let Some(nonce) = signer.nonce() {
+93 -88
View File
@@ -20,19 +20,23 @@ use crate::{
InvalidMetadataError,
MetadataError,
},
Metadata,
};
use core::fmt::Debug;
use jsonrpsee::core::error::Error as RequestError;
use sp_core::crypto::SecretStringError;
use sp_runtime::{
transaction_validity::TransactionValidityError,
DispatchError,
};
use thiserror::Error;
use sp_runtime::transaction_validity::TransactionValidityError;
/// Error enum.
#[derive(Debug, Error)]
pub enum Error {
/// An error that may contain some runtime error `E`
pub type Error<E> = GenericError<RuntimeError<E>>;
/// An error that will never contain a runtime error.
pub type BasicError = GenericError<std::convert::Infallible>;
/// The underlying error enum, generic over the type held by the `Runtime`
/// variant. Prefer to use the [`Error<E>`] and [`BasicError`] aliases over
/// using this type directly.
#[derive(Debug, thiserror::Error)]
pub enum GenericError<E> {
/// Io error.
#[error("Io error: {0}")]
Io(#[from] std::io::Error),
@@ -58,8 +62,8 @@ pub enum Error {
#[error("Metadata: {0}")]
Metadata(#[from] MetadataError),
/// Runtime error.
#[error("Runtime error: {0}")]
Runtime(#[from] RuntimeError),
#[error("Runtime error: {0:?}")]
Runtime(E),
/// Events decoding error.
#[error("Events decoding error: {0}")]
EventsDecoding(#[from] EventsDecodingError),
@@ -71,87 +75,88 @@ pub enum Error {
Other(String),
}
impl From<SecretStringError> for Error {
fn from(error: SecretStringError) -> Self {
Error::SecretString(error)
}
}
impl From<TransactionValidityError> for Error {
fn from(error: TransactionValidityError) -> Self {
Error::Invalid(error)
}
}
impl From<&str> for Error {
fn from(error: &str) -> Self {
Error::Other(error.into())
}
}
impl From<String> for Error {
fn from(error: String) -> Self {
Error::Other(error)
}
}
/// Runtime error.
#[derive(Clone, Debug, Eq, Error, PartialEq)]
pub enum RuntimeError {
/// Module error.
#[error("Runtime module error: {0}")]
Module(PalletError),
/// At least one consumer is remaining so the account cannot be destroyed.
#[error("At least one consumer is remaining so the account cannot be destroyed.")]
ConsumerRemaining,
/// There are no providers so the account cannot be created.
#[error("There are no providers so the account cannot be created.")]
NoProviders,
/// Bad origin.
#[error("Bad origin: throw by ensure_signed, ensure_root or ensure_none.")]
BadOrigin,
/// Cannot lookup.
#[error("Cannot lookup some information required to validate the transaction.")]
CannotLookup,
/// Other error.
#[error("Other error: {0}")]
Other(String),
}
impl RuntimeError {
/// Converts a `DispatchError` into a subxt error.
pub fn from_dispatch(
metadata: &Metadata,
error: DispatchError,
) -> Result<Self, Error> {
match error {
DispatchError::Module {
index,
error,
message: _,
} => {
let error = metadata.error(index, error)?;
Ok(Self::Module(PalletError {
pallet: error.pallet().to_string(),
error: error.error().to_string(),
description: error.description().to_vec(),
}))
}
DispatchError::BadOrigin => Ok(Self::BadOrigin),
DispatchError::CannotLookup => Ok(Self::CannotLookup),
DispatchError::ConsumerRemaining => Ok(Self::ConsumerRemaining),
DispatchError::NoProviders => Ok(Self::NoProviders),
DispatchError::Arithmetic(_math_error) => {
Ok(Self::Other("math_error".into()))
}
DispatchError::Token(_token_error) => Ok(Self::Other("token error".into())),
DispatchError::Other(msg) => Ok(Self::Other(msg.to_string())),
impl<E> GenericError<E> {
/// [`GenericError`] is parameterised over the type that it holds in the `Runtime`
/// variant. This function allows us to map the Runtime error contained within (if present)
/// to a different type.
pub fn map_runtime_err<F, NewE>(self, f: F) -> GenericError<NewE>
where
F: FnOnce(E) -> NewE,
{
match self {
GenericError::Io(e) => GenericError::Io(e),
GenericError::Codec(e) => GenericError::Codec(e),
GenericError::Rpc(e) => GenericError::Rpc(e),
GenericError::Serialization(e) => GenericError::Serialization(e),
GenericError::SecretString(e) => GenericError::SecretString(e),
GenericError::Invalid(e) => GenericError::Invalid(e),
GenericError::InvalidMetadata(e) => GenericError::InvalidMetadata(e),
GenericError::Metadata(e) => GenericError::Metadata(e),
GenericError::EventsDecoding(e) => GenericError::EventsDecoding(e),
GenericError::Transaction(e) => GenericError::Transaction(e),
GenericError::Other(e) => GenericError::Other(e),
// This is the only branch we really care about:
GenericError::Runtime(e) => GenericError::Runtime(f(e)),
}
}
}
impl BasicError {
/// Convert an [`BasicError`] into any
/// arbitrary [`Error<E>`].
pub fn into_error<E>(self) -> Error<E> {
self.map_runtime_err(|e| match e {})
}
}
impl<E> From<BasicError> for Error<E> {
fn from(err: BasicError) -> Self {
err.into_error()
}
}
impl<E> From<SecretStringError> for GenericError<E> {
fn from(error: SecretStringError) -> Self {
GenericError::SecretString(error)
}
}
impl<E> From<TransactionValidityError> for GenericError<E> {
fn from(error: TransactionValidityError) -> Self {
GenericError::Invalid(error)
}
}
impl<E> From<&str> for GenericError<E> {
fn from(error: &str) -> Self {
GenericError::Other(error.into())
}
}
impl<E> From<String> for GenericError<E> {
fn from(error: String) -> Self {
GenericError::Other(error)
}
}
/// This is used in the place of the `E` in [`GenericError<E>`] when we may have a
/// Runtime Error. We use this wrapper so that it is possible to implement
/// `From<Error<Infallible>` for `Error<RuntimeError<E>>`.
///
/// This should not be used as a type; prefer to use the alias [`Error<E>`] when referring
/// to errors which may contain some Runtime error `E`.
#[derive(Clone, Debug, PartialEq)]
pub struct RuntimeError<E>(pub E);
impl<E> RuntimeError<E> {
/// Extract the actual runtime error from this struct.
pub fn inner(self) -> E {
self.0
}
}
/// Module error.
#[derive(Clone, Debug, Eq, Error, PartialEq)]
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
#[error("{error} from {pallet}")]
pub struct PalletError {
/// The module where the error originated.
@@ -163,7 +168,7 @@ pub struct PalletError {
}
/// Transaction error.
#[derive(Clone, Debug, Eq, Error, PartialEq)]
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
pub enum TransactionError {
/// The finality subscription expired (after ~512 blocks we give up if the
/// block hasn't yet been finalized).
+6 -6
View File
@@ -15,12 +15,12 @@
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
use crate::{
error::BasicError,
metadata::{
EventMetadata,
MetadataError,
},
Config,
Error,
Event,
Metadata,
PhantomDataSendSync,
@@ -89,7 +89,7 @@ impl<T: Config> EventsDecoder<T> {
pub fn decode_events(
&self,
input: &mut &[u8],
) -> Result<Vec<(Phase, RawEvent)>, Error> {
) -> Result<Vec<(Phase, RawEvent)>, BasicError> {
let compact_len = <Compact<u32>>::decode(input)?;
let len = compact_len.0 as usize;
log::debug!("decoding {} events", len);
@@ -142,7 +142,7 @@ impl<T: Config> EventsDecoder<T> {
event_metadata: &EventMetadata,
input: &mut &[u8],
output: &mut Vec<u8>,
) -> Result<(), Error> {
) -> Result<(), BasicError> {
log::debug!(
"Decoding Event '{}::{}'",
event_metadata.pallet(),
@@ -160,7 +160,7 @@ impl<T: Config> EventsDecoder<T> {
type_id: u32,
input: &mut &[u8],
output: &mut Vec<u8>,
) -> Result<(), Error> {
) -> Result<(), BasicError> {
let ty = self
.metadata
.resolve_type(type_id)
@@ -169,7 +169,7 @@ impl<T: Config> EventsDecoder<T> {
fn decode_raw<T: Codec>(
input: &mut &[u8],
output: &mut Vec<u8>,
) -> Result<(), Error> {
) -> Result<(), BasicError> {
let decoded = T::decode(input)?;
decoded.encode_to(output);
Ok(())
@@ -190,7 +190,7 @@ impl<T: Config> EventsDecoder<T> {
.iter()
.find(|v| v.index() == variant_index)
.ok_or_else(|| {
Error::Other(format!("Variant {} not found", variant_index))
BasicError::Other(format!("Variant {} not found", variant_index))
})?;
for field in variant.fields() {
self.decode_type(field.ty().id(), input, output)?;
+14 -13
View File
@@ -22,6 +22,7 @@ mod signer;
pub use self::{
extra::{
ChargeAssetTxPayment,
ChargeTransactionPayment,
CheckGenesis,
CheckMortality,
CheckNonce,
@@ -41,48 +42,48 @@ pub use self::{
use sp_runtime::traits::SignedExtension;
use crate::{
error::BasicError,
rpc::RuntimeVersion,
Config,
Encoded,
Error,
};
/// UncheckedExtrinsic type.
pub type UncheckedExtrinsic<T, E> = sp_runtime::generic::UncheckedExtrinsic<
pub type UncheckedExtrinsic<T, X> = sp_runtime::generic::UncheckedExtrinsic<
<T as Config>::Address,
Encoded,
<T as Config>::Signature,
<E as SignedExtra<T>>::Extra,
<X as SignedExtra<T>>::Extra,
>;
/// SignedPayload type.
pub type SignedPayload<T, E> =
sp_runtime::generic::SignedPayload<Encoded, <E as SignedExtra<T>>::Extra>;
pub type SignedPayload<T, X> =
sp_runtime::generic::SignedPayload<Encoded, <X as SignedExtra<T>>::Extra>;
/// Creates a signed extrinsic
pub async fn create_signed<T, E>(
pub async fn create_signed<T, X>(
runtime_version: &RuntimeVersion,
genesis_hash: T::Hash,
nonce: T::Index,
call: Encoded,
signer: &(dyn Signer<T, E> + Send + Sync),
additional_params: E::Parameters,
) -> Result<UncheckedExtrinsic<T, E>, Error>
signer: &(dyn Signer<T, X> + Send + Sync),
additional_params: X::Parameters,
) -> Result<UncheckedExtrinsic<T, X>, BasicError>
where
T: Config,
E: SignedExtra<T>,
<E::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
X: SignedExtra<T>,
<X::Extra as SignedExtension>::AdditionalSigned: Send + Sync,
{
let spec_version = runtime_version.spec_version;
let tx_version = runtime_version.transaction_version;
let extra = E::new(
let extra = X::new(
spec_version,
tx_version,
nonce,
genesis_hash,
additional_params,
);
let payload = SignedPayload::<T, E>::new(call, extra.extra())?;
let payload = SignedPayload::<T, X>::new(call, extra.extra())?;
let signed = signer.sign(payload).await?;
Ok(signed)
}
+1 -1
View File
@@ -79,9 +79,9 @@ pub use crate::{
DefaultConfig,
},
error::{
BasicError,
Error,
PalletError,
RuntimeError,
TransactionError,
},
events::{
-64
View File
@@ -84,7 +84,6 @@ pub struct Metadata {
metadata: RuntimeMetadataLastVersion,
pallets: HashMap<String, PalletMetadata>,
events: HashMap<(u8, u8), EventMetadata>,
errors: HashMap<(u8, u8), ErrorMetadata>,
}
impl Metadata {
@@ -108,19 +107,6 @@ impl Metadata {
Ok(event)
}
/// Returns the metadata for the error at the given pallet and error indices.
pub fn error(
&self,
pallet_index: u8,
error_index: u8,
) -> Result<&ErrorMetadata, MetadataError> {
let error = self
.errors
.get(&(pallet_index, error_index))
.ok_or(MetadataError::ErrorNotFound(pallet_index, error_index))?;
Ok(error)
}
/// Resolve a type definition.
pub fn resolve_type(&self, id: u32) -> Option<&Type<PortableForm>> {
self.metadata.types.resolve(id)
@@ -207,30 +193,6 @@ impl EventMetadata {
}
}
#[derive(Clone, Debug)]
pub struct ErrorMetadata {
pallet: String,
error: String,
variant: Variant<PortableForm>,
}
impl ErrorMetadata {
/// Get the name of the pallet from which the error originates.
pub fn pallet(&self) -> &str {
&self.pallet
}
/// Get the name of the specific pallet error.
pub fn error(&self) -> &str {
&self.error
}
/// Get the description of the specific pallet error.
pub fn description(&self) -> &[String] {
self.variant.docs()
}
}
#[derive(Debug, thiserror::Error)]
pub enum InvalidMetadataError {
#[error("Invalid prefix")]
@@ -331,36 +293,10 @@ impl TryFrom<RuntimeMetadataPrefixed> for Metadata {
})
.collect();
let pallet_errors = metadata
.pallets
.iter()
.filter_map(|pallet| {
pallet.error.as_ref().map(|error| {
let type_def_variant = get_type_def_variant(error.ty.id())?;
Ok((pallet, type_def_variant))
})
})
.collect::<Result<Vec<_>, _>>()?;
let errors = pallet_errors
.iter()
.flat_map(|(pallet, type_def_variant)| {
type_def_variant.variants().iter().map(move |var| {
let key = (pallet.index, var.index());
let value = ErrorMetadata {
pallet: pallet.name.clone(),
error: var.name().clone(),
variant: var.clone(),
};
(key, value)
})
})
.collect();
Ok(Self {
metadata,
pallets,
events,
errors,
})
}
}
+55 -54
View File
@@ -26,6 +26,17 @@ use std::{
sync::Arc,
};
use crate::{
error::BasicError,
storage::StorageKeyPrefix,
subscription::{
EventStorageSubscription,
FinalizedEventStorageSubscription,
SystemEvents,
},
Config,
Metadata,
};
use codec::{
Decode,
Encode,
@@ -72,18 +83,6 @@ use sp_runtime::generic::{
SignedBlock,
};
use crate::{
error::Error,
storage::StorageKeyPrefix,
subscription::{
EventStorageSubscription,
FinalizedEventStorageSubscription,
SystemEvents,
},
Config,
Metadata,
};
/// A number type that can be serialized both as a number or a string that encodes a number in a
/// string.
///
@@ -211,7 +210,7 @@ impl RpcClient {
/// Infers the protocol from the URL, supports:
/// - Websockets (`ws://`, `wss://`)
/// - Http (`http://`, `https://`)
pub async fn try_from_url(url: &str) -> Result<Self, Error> {
pub async fn try_from_url(url: &str) -> Result<Self, RpcError> {
if url.starts_with("ws://") || url.starts_with("wss://") {
let client = WsClientBuilder::default()
.max_notifs_per_subscription(4096)
@@ -229,14 +228,12 @@ impl RpcClient {
&self,
method: &str,
params: &[JsonValue],
) -> Result<T, Error> {
) -> Result<T, RpcError> {
let params = Some(params.into());
log::debug!("request {}: {:?}", method, params);
let data = match self {
Self::WebSocket(inner) => {
inner.request(method, params).await.map_err(Into::into)
}
Self::Http(inner) => inner.request(method, params).await.map_err(Into::into),
RpcClient::WebSocket(inner) => inner.request(method, params).await,
RpcClient::Http(inner) => inner.request(method, params).await,
};
data
}
@@ -247,20 +244,18 @@ impl RpcClient {
subscribe_method: &str,
params: &[JsonValue],
unsubscribe_method: &str,
) -> Result<Subscription<T>, Error> {
) -> Result<Subscription<T>, RpcError> {
let params = Some(params.into());
match self {
Self::WebSocket(inner) => {
RpcClient::WebSocket(inner) => {
inner
.subscribe(subscribe_method, params, unsubscribe_method)
.await
.map_err(Into::into)
}
Self::Http(_) => {
RpcClient::Http(_) => {
Err(RpcError::Custom(
"Subscriptions not supported on HTTP transport".to_owned(),
)
.into())
))
}
}
}
@@ -335,7 +330,7 @@ impl<T: Config> Rpc<T> {
&self,
key: &StorageKey,
hash: Option<T::Hash>,
) -> Result<Option<StorageData>, Error> {
) -> Result<Option<StorageData>, BasicError> {
let params = &[to_json_value(key)?, to_json_value(hash)?];
let data = self.client.request("state_getStorage", params).await?;
Ok(data)
@@ -350,7 +345,7 @@ impl<T: Config> Rpc<T> {
count: u32,
start_key: Option<StorageKey>,
hash: Option<T::Hash>,
) -> Result<Vec<StorageKey>, Error> {
) -> Result<Vec<StorageKey>, BasicError> {
let prefix = prefix.map(|p| p.to_storage_key());
let params = &[
to_json_value(prefix)?,
@@ -368,7 +363,7 @@ impl<T: Config> Rpc<T> {
keys: Vec<StorageKey>,
from: T::Hash,
to: Option<T::Hash>,
) -> Result<Vec<StorageChangeSet<T::Hash>>, Error> {
) -> Result<Vec<StorageChangeSet<T::Hash>>, BasicError> {
let params = &[
to_json_value(keys)?,
to_json_value(from)?,
@@ -385,7 +380,7 @@ impl<T: Config> Rpc<T> {
&self,
keys: &[StorageKey],
at: Option<T::Hash>,
) -> Result<Vec<StorageChangeSet<T::Hash>>, Error> {
) -> Result<Vec<StorageChangeSet<T::Hash>>, BasicError> {
let params = &[to_json_value(keys)?, to_json_value(at)?];
self.client
.request("state_queryStorageAt", params)
@@ -394,7 +389,7 @@ impl<T: Config> Rpc<T> {
}
/// Fetch the genesis hash
pub async fn genesis_hash(&self) -> Result<T::Hash, Error> {
pub async fn genesis_hash(&self) -> Result<T::Hash, BasicError> {
let block_zero = Some(ListOrValue::Value(NumberOrHex::Number(0)));
let params = &[to_json_value(block_zero)?];
let list_or_value: ListOrValue<Option<T::Hash>> =
@@ -408,7 +403,7 @@ impl<T: Config> Rpc<T> {
}
/// Fetch the metadata
pub async fn metadata(&self) -> Result<Metadata, Error> {
pub async fn metadata(&self) -> Result<Metadata, BasicError> {
let bytes: Bytes = self.client.request("state_getMetadata", &[]).await?;
let meta: RuntimeMetadataPrefixed = Decode::decode(&mut &bytes[..])?;
let metadata: Metadata = meta.try_into()?;
@@ -416,22 +411,22 @@ impl<T: Config> Rpc<T> {
}
/// Fetch system properties
pub async fn system_properties(&self) -> Result<SystemProperties, Error> {
pub async fn system_properties(&self) -> Result<SystemProperties, BasicError> {
Ok(self.client.request("system_properties", &[]).await?)
}
/// Fetch system chain
pub async fn system_chain(&self) -> Result<String, Error> {
pub async fn system_chain(&self) -> Result<String, BasicError> {
Ok(self.client.request("system_chain", &[]).await?)
}
/// Fetch system name
pub async fn system_name(&self) -> Result<String, Error> {
pub async fn system_name(&self) -> Result<String, BasicError> {
Ok(self.client.request("system_name", &[]).await?)
}
/// Fetch system version
pub async fn system_version(&self) -> Result<String, Error> {
pub async fn system_version(&self) -> Result<String, BasicError> {
Ok(self.client.request("system_version", &[]).await?)
}
@@ -439,7 +434,7 @@ impl<T: Config> Rpc<T> {
pub async fn header(
&self,
hash: Option<T::Hash>,
) -> Result<Option<T::Header>, Error> {
) -> Result<Option<T::Header>, BasicError> {
let params = &[to_json_value(hash)?];
let header = self.client.request("chain_getHeader", params).await?;
Ok(header)
@@ -449,7 +444,7 @@ impl<T: Config> Rpc<T> {
pub async fn block_hash(
&self,
block_number: Option<BlockNumber>,
) -> Result<Option<T::Hash>, Error> {
) -> Result<Option<T::Hash>, BasicError> {
let block_number = block_number.map(ListOrValue::Value);
let params = &[to_json_value(block_number)?];
let list_or_value = self.client.request("chain_getBlockHash", params).await?;
@@ -460,7 +455,7 @@ impl<T: Config> Rpc<T> {
}
/// Get a block hash of the latest finalized block
pub async fn finalized_head(&self) -> Result<T::Hash, Error> {
pub async fn finalized_head(&self) -> Result<T::Hash, BasicError> {
let hash = self.client.request("chain_getFinalizedHead", &[]).await?;
Ok(hash)
}
@@ -469,7 +464,7 @@ impl<T: Config> Rpc<T> {
pub async fn block(
&self,
hash: Option<T::Hash>,
) -> Result<Option<ChainBlock<T>>, Error> {
) -> Result<Option<ChainBlock<T>>, BasicError> {
let params = &[to_json_value(hash)?];
let block = self.client.request("chain_getBlock", params).await?;
Ok(block)
@@ -480,7 +475,7 @@ impl<T: Config> Rpc<T> {
&self,
keys: Vec<StorageKey>,
hash: Option<T::Hash>,
) -> Result<ReadProof<T::Hash>, Error> {
) -> Result<ReadProof<T::Hash>, BasicError> {
let params = &[to_json_value(keys)?, to_json_value(hash)?];
let proof = self.client.request("state_getReadProof", params).await?;
Ok(proof)
@@ -490,7 +485,7 @@ impl<T: Config> Rpc<T> {
pub async fn runtime_version(
&self,
at: Option<T::Hash>,
) -> Result<RuntimeVersion, Error> {
) -> Result<RuntimeVersion, BasicError> {
let params = &[to_json_value(at)?];
let version = self
.client
@@ -503,7 +498,9 @@ impl<T: Config> Rpc<T> {
///
/// *WARNING* these may not be included in the finalized chain, use
/// `subscribe_finalized_events` to ensure events are finalized.
pub async fn subscribe_events(&self) -> Result<EventStorageSubscription<T>, Error> {
pub async fn subscribe_events(
&self,
) -> Result<EventStorageSubscription<T>, BasicError> {
let keys = Some(vec![StorageKey::from(SystemEvents::new())]);
let params = &[to_json_value(keys)?];
@@ -517,7 +514,7 @@ impl<T: Config> Rpc<T> {
/// Subscribe to finalized events.
pub async fn subscribe_finalized_events(
&self,
) -> Result<EventStorageSubscription<T>, Error> {
) -> Result<EventStorageSubscription<T>, BasicError> {
Ok(EventStorageSubscription::Finalized(
FinalizedEventStorageSubscription::new(
self.clone(),
@@ -527,7 +524,7 @@ impl<T: Config> Rpc<T> {
}
/// Subscribe to blocks.
pub async fn subscribe_blocks(&self) -> Result<Subscription<T::Header>, Error> {
pub async fn subscribe_blocks(&self) -> Result<Subscription<T::Header>, BasicError> {
let subscription = self
.client
.subscribe("chain_subscribeNewHeads", &[], "chain_unsubscribeNewHeads")
@@ -539,7 +536,7 @@ impl<T: Config> Rpc<T> {
/// Subscribe to finalized blocks.
pub async fn subscribe_finalized_blocks(
&self,
) -> Result<Subscription<T::Header>, Error> {
) -> Result<Subscription<T::Header>, BasicError> {
let subscription = self
.client
.subscribe(
@@ -552,10 +549,10 @@ impl<T: Config> Rpc<T> {
}
/// Create and submit an extrinsic and return corresponding Hash if successful
pub async fn submit_extrinsic<E: Encode>(
pub async fn submit_extrinsic<X: Encode>(
&self,
extrinsic: E,
) -> Result<T::Hash, Error> {
extrinsic: X,
) -> Result<T::Hash, BasicError> {
let bytes: Bytes = extrinsic.encode().into();
let params = &[to_json_value(bytes)?];
let xt_hash = self
@@ -566,10 +563,11 @@ impl<T: Config> Rpc<T> {
}
/// Create and submit an extrinsic and return a subscription to the events triggered.
pub async fn watch_extrinsic<E: Encode>(
pub async fn watch_extrinsic<X: Encode>(
&self,
extrinsic: E,
) -> Result<Subscription<SubstrateTransactionStatus<T::Hash, T::Hash>>, Error> {
extrinsic: X,
) -> Result<Subscription<SubstrateTransactionStatus<T::Hash, T::Hash>>, BasicError>
{
let bytes: Bytes = extrinsic.encode().into();
let params = &[to_json_value(bytes)?];
let subscription = self
@@ -589,7 +587,7 @@ impl<T: Config> Rpc<T> {
key_type: String,
suri: String,
public: Bytes,
) -> Result<(), Error> {
) -> Result<(), BasicError> {
let params = &[
to_json_value(key_type)?,
to_json_value(suri)?,
@@ -600,7 +598,7 @@ impl<T: Config> Rpc<T> {
}
/// Generate new session keys and returns the corresponding public keys.
pub async fn rotate_keys(&self) -> Result<Bytes, Error> {
pub async fn rotate_keys(&self) -> Result<Bytes, BasicError> {
Ok(self.client.request("author_rotateKeys", &[]).await?)
}
@@ -609,7 +607,10 @@ impl<T: Config> Rpc<T> {
/// `session_keys` is the SCALE encoded session keys object from the runtime.
///
/// Returns `true` iff all private keys could be found.
pub async fn has_session_keys(&self, session_keys: Bytes) -> Result<bool, Error> {
pub async fn has_session_keys(
&self,
session_keys: Bytes,
) -> Result<bool, BasicError> {
let params = &[to_json_value(session_keys)?];
Ok(self.client.request("author_hasSessionKeys", params).await?)
}
@@ -621,7 +622,7 @@ impl<T: Config> Rpc<T> {
&self,
public_key: Bytes,
key_type: String,
) -> Result<bool, Error> {
) -> Result<bool, BasicError> {
let params = &[to_json_value(public_key)?, to_json_value(key_type)?];
Ok(self.client.request("author_hasKey", params).await?)
}
+9 -9
View File
@@ -30,13 +30,13 @@ pub use sp_version::RuntimeVersion;
use std::marker::PhantomData;
use crate::{
error::BasicError,
metadata::{
Metadata,
MetadataError,
},
rpc::Rpc,
Config,
Error,
StorageHasher,
};
@@ -163,7 +163,7 @@ impl<'a, T: Config> StorageClient<'a, T> {
&self,
key: StorageKey,
hash: Option<T::Hash>,
) -> Result<Option<V>, Error> {
) -> Result<Option<V>, BasicError> {
if let Some(data) = self.rpc.storage(&key, hash).await? {
Ok(Some(Decode::decode(&mut &data.0[..])?))
} else {
@@ -176,7 +176,7 @@ impl<'a, T: Config> StorageClient<'a, T> {
&self,
key: StorageKey,
hash: Option<T::Hash>,
) -> Result<Option<StorageData>, Error> {
) -> Result<Option<StorageData>, BasicError> {
self.rpc.storage(&key, hash).await
}
@@ -185,7 +185,7 @@ impl<'a, T: Config> StorageClient<'a, T> {
&self,
store: &F,
hash: Option<T::Hash>,
) -> Result<Option<F::Value>, Error> {
) -> Result<Option<F::Value>, BasicError> {
let prefix = StorageKeyPrefix::new::<F>();
let key = store.key().final_key(prefix);
self.fetch_unhashed::<F::Value>(key, hash).await
@@ -196,7 +196,7 @@ impl<'a, T: Config> StorageClient<'a, T> {
&self,
store: &F,
hash: Option<T::Hash>,
) -> Result<F::Value, Error> {
) -> Result<F::Value, BasicError> {
if let Some(data) = self.fetch(store, hash).await? {
Ok(data)
} else {
@@ -214,7 +214,7 @@ impl<'a, T: Config> StorageClient<'a, T> {
keys: Vec<StorageKey>,
from: T::Hash,
to: Option<T::Hash>,
) -> Result<Vec<StorageChangeSet<T::Hash>>, Error> {
) -> Result<Vec<StorageChangeSet<T::Hash>>, BasicError> {
self.rpc.query_storage(keys, from, to).await
}
@@ -226,7 +226,7 @@ impl<'a, T: Config> StorageClient<'a, T> {
count: u32,
start_key: Option<StorageKey>,
hash: Option<T::Hash>,
) -> Result<Vec<StorageKey>, Error> {
) -> Result<Vec<StorageKey>, BasicError> {
let prefix = StorageKeyPrefix::new::<F>();
let keys = self
.rpc
@@ -239,7 +239,7 @@ impl<'a, T: Config> StorageClient<'a, T> {
pub async fn iter<F: StorageEntry>(
&self,
hash: Option<T::Hash>,
) -> Result<KeyIter<'a, T, F>, Error> {
) -> Result<KeyIter<'a, T, F>, BasicError> {
let hash = if let Some(hash) = hash {
hash
} else {
@@ -271,7 +271,7 @@ pub struct KeyIter<'a, T: Config, F: StorageEntry> {
impl<'a, T: Config, F: StorageEntry> KeyIter<'a, T, F> {
/// Returns the next key value pair from a map.
pub async fn next(&mut self) -> Result<Option<(StorageKey, F::Value)>, Error> {
pub async fn next(&mut self) -> Result<Option<(StorageKey, F::Value)>, BasicError> {
loop {
if let Some((k, v)) = self.buffer.pop() {
return Ok(Some((k, Decode::decode(&mut &v.0[..])?)))
+18 -17
View File
@@ -14,6 +14,17 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
use crate::{
error::BasicError,
events::{
EventsDecoder,
RawEvent,
},
rpc::Rpc,
Config,
Event,
Phase,
};
use jsonrpsee::core::{
client::Subscription,
DeserializeOwned,
@@ -28,18 +39,6 @@ use sp_core::{
use sp_runtime::traits::Header;
use std::collections::VecDeque;
use crate::{
error::Error,
events::{
EventsDecoder,
RawEvent,
},
rpc::Rpc,
Config,
Event,
Phase,
};
/// Event subscription simplifies filtering a storage change set stream for
/// events of interest.
pub struct EventSubscription<'a, T: Config> {
@@ -58,11 +57,13 @@ enum BlockReader<'a, T: Config> {
},
/// Mock event listener for unit tests
#[cfg(test)]
Mock(Box<dyn Iterator<Item = (T::Hash, Result<Vec<(Phase, RawEvent)>, Error>)>>),
Mock(Box<dyn Iterator<Item = (T::Hash, Result<Vec<(Phase, RawEvent)>, BasicError>)>>),
}
impl<'a, T: Config> BlockReader<'a, T> {
async fn next(&mut self) -> Option<(T::Hash, Result<Vec<(Phase, RawEvent)>, Error>)> {
async fn next(
&mut self,
) -> Option<(T::Hash, Result<Vec<(Phase, RawEvent)>, BasicError>)> {
match self {
BlockReader::Decoder {
subscription,
@@ -117,12 +118,12 @@ impl<'a, T: Config> EventSubscription<'a, T> {
}
/// Filters events by type.
pub fn filter_event<E: Event>(&mut self) {
self.event = Some((E::PALLET, E::EVENT));
pub fn filter_event<Ev: Event>(&mut self) {
self.event = Some((Ev::PALLET, Ev::EVENT));
}
/// Gets the next event.
pub async fn next(&mut self) -> Option<Result<RawEvent, Error>> {
pub async fn next(&mut self) -> Option<Result<RawEvent, BasicError>> {
loop {
if let Some(raw_event) = self.events.pop_front() {
return Some(Ok(raw_event))
+66 -48
View File
@@ -16,10 +16,19 @@
use std::task::Poll;
use crate::PhantomDataSendSync;
use codec::Decode;
use sp_core::storage::StorageKey;
use sp_runtime::traits::Hash;
pub use sp_runtime::traits::SignedExtension;
pub use sp_version::RuntimeVersion;
use crate::{
client::Client,
error::{
BasicError,
Error,
RuntimeError,
TransactionError,
},
rpc::SubstrateTransactionStatus,
@@ -36,27 +45,24 @@ use jsonrpsee::core::{
client::Subscription as RpcSubscription,
Error as RpcError,
};
use sp_core::storage::StorageKey;
use sp_runtime::traits::Hash;
pub use sp_runtime::traits::SignedExtension;
pub use sp_version::RuntimeVersion;
/// This struct represents a subscription to the progress of some transaction, and is
/// returned from [`crate::SubmittableExtrinsic::sign_and_submit_then_watch()`].
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct TransactionProgress<'client, T: Config> {
pub struct TransactionProgress<'client, T: Config, E: Decode> {
sub: Option<RpcSubscription<SubstrateTransactionStatus<T::Hash, T::Hash>>>,
ext_hash: T::Hash,
client: &'client Client<T>,
_error: PhantomDataSendSync<E>,
}
// The above type is not `Unpin` by default unless the generic param `T` is,
// so we manually make it clear that Unpin is actually fine regardless of `T`
// (we don't care if this moves around in memory while it's "pinned").
impl<'client, T: Config> Unpin for TransactionProgress<'client, T> {}
impl<'client, T: Config, E: Decode> Unpin for TransactionProgress<'client, T, E> {}
impl<'client, T: Config> TransactionProgress<'client, T> {
impl<'client, T: Config, E: Decode> TransactionProgress<'client, T, E> {
/// Instantiate a new [`TransactionProgress`] from a custom subscription.
pub fn new(
sub: RpcSubscription<SubstrateTransactionStatus<T::Hash, T::Hash>>,
@@ -67,6 +73,7 @@ impl<'client, T: Config> TransactionProgress<'client, T> {
sub: Some(sub),
client,
ext_hash,
_error: PhantomDataSendSync::new(),
}
}
@@ -75,7 +82,7 @@ impl<'client, T: Config> TransactionProgress<'client, T> {
/// avoid importing that trait if you don't otherwise need it.
pub async fn next_item(
&mut self,
) -> Option<Result<TransactionStatus<'client, T>, Error>> {
) -> Option<Result<TransactionStatus<'client, T, E>, BasicError>> {
self.next().await
}
@@ -92,7 +99,7 @@ impl<'client, T: Config> TransactionProgress<'client, T> {
/// level [`TransactionProgress::next_item()`] API if you'd like to handle these statuses yourself.
pub async fn wait_for_in_block(
mut self,
) -> Result<TransactionInBlock<'client, T>, Error> {
) -> Result<TransactionInBlock<'client, T, E>, BasicError> {
while let Some(status) = self.next_item().await {
match status? {
// Finalized or otherwise in a block! Return.
@@ -122,7 +129,7 @@ impl<'client, T: Config> TransactionProgress<'client, T> {
/// level [`TransactionProgress::next_item()`] API if you'd like to handle these statuses yourself.
pub async fn wait_for_finalized(
mut self,
) -> Result<TransactionInBlock<'client, T>, Error> {
) -> Result<TransactionInBlock<'client, T, E>, BasicError> {
while let Some(status) = self.next_item().await {
match status? {
// Finalized! Return.
@@ -149,14 +156,16 @@ impl<'client, T: Config> TransactionProgress<'client, T> {
/// may well indicate with some probability that the transaction will not make it into a block,
/// there is no guarantee that this is true. Thus, we prefer to "play it safe" here. Use the lower
/// level [`TransactionProgress::next_item()`] API if you'd like to handle these statuses yourself.
pub async fn wait_for_finalized_success(self) -> Result<TransactionEvents<T>, Error> {
pub async fn wait_for_finalized_success(
self,
) -> Result<TransactionEvents<T>, Error<E>> {
let evs = self.wait_for_finalized().await?.wait_for_success().await?;
Ok(evs)
}
}
impl<'client, T: Config> Stream for TransactionProgress<'client, T> {
type Item = Result<TransactionStatus<'client, T>, Error>;
impl<'client, T: Config, E: Decode> Stream for TransactionProgress<'client, T, E> {
type Item = Result<TransactionStatus<'client, T, E>, BasicError>;
fn poll_next(
mut self: std::pin::Pin<&mut Self>,
@@ -177,11 +186,11 @@ impl<'client, T: Config> Stream for TransactionProgress<'client, T> {
TransactionStatus::Broadcast(peers)
}
SubstrateTransactionStatus::InBlock(hash) => {
TransactionStatus::InBlock(TransactionInBlock {
block_hash: hash,
ext_hash: self.ext_hash,
client: self.client,
})
TransactionStatus::InBlock(TransactionInBlock::new(
hash,
self.ext_hash,
self.client,
))
}
SubstrateTransactionStatus::Retracted(hash) => {
TransactionStatus::Retracted(hash)
@@ -206,11 +215,11 @@ impl<'client, T: Config> Stream for TransactionProgress<'client, T> {
}
SubstrateTransactionStatus::Finalized(hash) => {
self.sub = None;
TransactionStatus::Finalized(TransactionInBlock {
block_hash: hash,
ext_hash: self.ext_hash,
client: self.client,
})
TransactionStatus::Finalized(TransactionInBlock::new(
hash,
self.ext_hash,
self.client,
))
}
}
})
@@ -265,7 +274,7 @@ impl<'client, T: Config> Stream for TransactionProgress<'client, T> {
/// or that finality gadget is lagging behind.
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub enum TransactionStatus<'client, T: Config> {
pub enum TransactionStatus<'client, T: Config, E: Decode> {
/// The transaction is part of the "future" queue.
Future,
/// The transaction is part of the "ready" queue.
@@ -273,7 +282,7 @@ pub enum TransactionStatus<'client, T: Config> {
/// The transaction has been broadcast to the given peers.
Broadcast(Vec<String>),
/// The transaction has been included in a block with given hash.
InBlock(TransactionInBlock<'client, T>),
InBlock(TransactionInBlock<'client, T, E>),
/// The block this transaction was included in has been retracted,
/// probably because it did not make it onto the blocks which were
/// finalized.
@@ -282,7 +291,7 @@ pub enum TransactionStatus<'client, T: Config> {
/// blocks, and so the subscription has ended.
FinalityTimeout(T::Hash),
/// The transaction has been finalized by a finality-gadget, e.g GRANDPA.
Finalized(TransactionInBlock<'client, T>),
Finalized(TransactionInBlock<'client, T, E>),
/// The transaction has been replaced in the pool by another transaction
/// that provides the same tags. (e.g. same (sender, nonce)).
Usurped(T::Hash),
@@ -292,10 +301,10 @@ pub enum TransactionStatus<'client, T: Config> {
Invalid,
}
impl<'client, T: Config> TransactionStatus<'client, T> {
impl<'client, T: Config, E: Decode> TransactionStatus<'client, T, E> {
/// A convenience method to return the `Finalized` details. Returns
/// [`None`] if the enum variant is not [`TransactionStatus::Finalized`].
pub fn as_finalized(&self) -> Option<&TransactionInBlock<'client, T>> {
pub fn as_finalized(&self) -> Option<&TransactionInBlock<'client, T, E>> {
match self {
Self::Finalized(val) => Some(val),
_ => None,
@@ -304,7 +313,7 @@ impl<'client, T: Config> TransactionStatus<'client, T> {
/// A convenience method to return the `InBlock` details. Returns
/// [`None`] if the enum variant is not [`TransactionStatus::InBlock`].
pub fn as_in_block(&self) -> Option<&TransactionInBlock<'client, T>> {
pub fn as_in_block(&self) -> Option<&TransactionInBlock<'client, T, E>> {
match self {
Self::InBlock(val) => Some(val),
_ => None,
@@ -315,13 +324,27 @@ impl<'client, T: Config> TransactionStatus<'client, T> {
/// This struct represents a transaction that has made it into a block.
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct TransactionInBlock<'client, T: Config> {
pub struct TransactionInBlock<'client, T: Config, E: Decode> {
block_hash: T::Hash,
ext_hash: T::Hash,
client: &'client Client<T>,
_error: PhantomDataSendSync<E>,
}
impl<'client, T: Config> TransactionInBlock<'client, T> {
impl<'client, T: Config, E: Decode> TransactionInBlock<'client, T, E> {
pub(crate) fn new(
block_hash: T::Hash,
ext_hash: T::Hash,
client: &'client Client<T>,
) -> Self {
Self {
block_hash,
ext_hash,
client,
_error: PhantomDataSendSync::new(),
}
}
/// Return the hash of the block that the transaction has made it into.
pub fn block_hash(&self) -> T::Hash {
self.block_hash
@@ -345,19 +368,14 @@ impl<'client, T: Config> TransactionInBlock<'client, T> {
///
/// **Note:** This has to download block details from the node and decode events
/// from them.
pub async fn wait_for_success(&self) -> Result<TransactionEvents<T>, Error> {
pub async fn wait_for_success(&self) -> Result<TransactionEvents<T>, Error<E>> {
let events = self.fetch_events().await?;
// Try to find any errors; return the first one we encounter.
for ev in events.as_slice() {
if &ev.pallet == "System" && &ev.variant == "ExtrinsicFailed" {
use codec::Decode;
let dispatch_error = sp_runtime::DispatchError::decode(&mut &*ev.data)?;
let runtime_error = crate::RuntimeError::from_dispatch(
self.client.metadata(),
dispatch_error,
)?;
return Err(runtime_error.into())
let dispatch_error = E::decode(&mut &*ev.data)?;
return Err(Error::Runtime(RuntimeError(dispatch_error)))
}
}
@@ -370,13 +388,13 @@ impl<'client, T: Config> TransactionInBlock<'client, T> {
///
/// **Note:** This has to download block details from the node and decode events
/// from them.
pub async fn fetch_events(&self) -> Result<TransactionEvents<T>, Error> {
pub async fn fetch_events(&self) -> Result<TransactionEvents<T>, BasicError> {
let block = self
.client
.rpc()
.block(Some(self.block_hash))
.await?
.ok_or(Error::Transaction(TransactionError::BlockHashNotFound))?;
.ok_or(BasicError::Transaction(TransactionError::BlockHashNotFound))?;
let extrinsic_idx = block.block.extrinsics
.iter()
@@ -386,7 +404,7 @@ impl<'client, T: Config> TransactionInBlock<'client, T> {
})
// If we successfully obtain the block hash we think contains our
// extrinsic, the extrinsic should be in there somewhere..
.ok_or(Error::Transaction(TransactionError::BlockHashNotFound))?;
.ok_or(BasicError::Transaction(TransactionError::BlockHashNotFound))?;
let raw_events = self
.client
@@ -446,10 +464,10 @@ impl<T: Config> TransactionEvents<T> {
/// Find all of the events matching the event type provided as a generic parameter. This
/// will return an error if a matching event is found but cannot be properly decoded.
pub fn find_events<E: crate::Event>(&self) -> Result<Vec<E>, Error> {
pub fn find_events<Ev: crate::Event>(&self) -> Result<Vec<Ev>, BasicError> {
self.events
.iter()
.filter_map(|e| e.as_event::<E>().map_err(Into::into).transpose())
.filter_map(|e| e.as_event::<Ev>().map_err(Into::into).transpose())
.collect()
}
@@ -458,18 +476,18 @@ impl<T: Config> TransactionEvents<T> {
///
/// Use [`TransactionEvents::find_events`], or iterate over [`TransactionEvents`] yourself
/// if you'd like to handle multiple events of the same type.
pub fn find_first_event<E: crate::Event>(&self) -> Result<Option<E>, Error> {
pub fn find_first_event<Ev: crate::Event>(&self) -> Result<Option<Ev>, BasicError> {
self.events
.iter()
.filter_map(|e| e.as_event::<E>().transpose())
.filter_map(|e| e.as_event::<Ev>().transpose())
.next()
.transpose()
.map_err(Into::into)
}
/// Find an event. Returns true if it was found.
pub fn has_event<E: crate::Event>(&self) -> Result<bool, Error> {
Ok(self.find_first_event::<E>()?.is_some())
pub fn has_event<Ev: crate::Event>(&self) -> Result<bool, BasicError> {
Ok(self.find_first_event::<Ev>()?.is_some())
}
}
+1 -1
View File
@@ -98,7 +98,7 @@ async fn run() {
r#"
#[subxt::subxt(
runtime_metadata_path = "{}",
generated_type_derives = "Debug, Eq, PartialEq"
generated_type_derives = "Eq, PartialEq"
)]
pub mod node_runtime {{
#[subxt(substitute_type = "sp_arithmetic::per_things::Perbill")]
File diff suppressed because one or more lines are too long
+7 -11
View File
@@ -19,6 +19,7 @@ use crate::{
balances,
runtime_types,
system,
DispatchError,
},
pair_signer,
test_context,
@@ -33,13 +34,11 @@ use subxt::{
DefaultConfig,
Error,
EventSubscription,
PalletError,
RuntimeError,
Signer,
};
#[async_std::test]
async fn tx_basic_transfer() -> Result<(), subxt::Error> {
async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = pair_signer(AccountKeyring::Bob.pair());
let bob_address = bob.account_id().clone().into();
@@ -111,7 +110,7 @@ async fn storage_total_issuance() {
}
#[async_std::test]
async fn storage_balance_lock() -> Result<(), subxt::Error> {
async fn storage_balance_lock() -> Result<(), subxt::Error<DispatchError>> {
let bob = pair_signer(AccountKeyring::Bob.pair());
let charlie = AccountKeyring::Charlie.to_account_id();
let cxt = test_context().await;
@@ -181,13 +180,10 @@ async fn transfer_error() {
.wait_for_finalized_success()
.await;
if let Err(Error::Runtime(RuntimeError::Module(error))) = res {
let error2 = PalletError {
pallet: "Balances".into(),
error: "InsufficientBalance".into(),
description: vec!["Balance too low to send value".to_string()],
};
assert_eq!(error, error2);
if let Err(Error::Runtime(err)) = res {
let details = err.inner().details().unwrap();
assert_eq!(details.pallet, "Balances");
assert_eq!(details.error, "InsufficientBalance");
} else {
panic!("expected a runtime module error");
}
+7 -3
View File
@@ -25,6 +25,7 @@ use crate::{
},
system,
DefaultAccountData,
DispatchError,
},
test_context,
NodeRuntimeSignedExtra,
@@ -67,7 +68,9 @@ impl ContractsTestContext {
self.cxt.api.tx().contracts()
}
async fn instantiate_with_code(&self) -> Result<(Hash, AccountId), Error> {
async fn instantiate_with_code(
&self,
) -> Result<(Hash, AccountId), Error<DispatchError>> {
log::info!("instantiate_with_code:");
const CONTRACT: &str = r#"
(module
@@ -118,7 +121,7 @@ impl ContractsTestContext {
code_hash: Hash,
data: Vec<u8>,
salt: Vec<u8>,
) -> Result<AccountId, Error> {
) -> Result<AccountId, Error<DispatchError>> {
// call instantiate extrinsic
let result = self
.contracts_tx()
@@ -147,7 +150,8 @@ impl ContractsTestContext {
&self,
contract: AccountId,
input_data: Vec<u8>,
) -> Result<TransactionProgress<'_, DefaultConfig>, Error> {
) -> Result<TransactionProgress<'_, DefaultConfig, DispatchError>, Error<DispatchError>>
{
log::info!("call: {:?}", contract);
let result = self
.contracts_tx()
+24 -21
View File
@@ -21,6 +21,7 @@ use crate::{
ValidatorPrefs,
},
staking,
DispatchError,
},
pair_signer,
test_context,
@@ -33,7 +34,6 @@ use sp_core::{
use sp_keyring::AccountKeyring;
use subxt::{
Error,
RuntimeError,
Signer,
};
@@ -67,7 +67,7 @@ async fn validate_with_controller_account() {
}
#[async_std::test]
async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
async fn validate_not_possible_for_stash_account() -> Result<(), Error<DispatchError>> {
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
let cxt = test_context().await;
let announce_validator = cxt
@@ -79,9 +79,10 @@ async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
.await?
.wait_for_finalized_success()
.await;
assert_matches!(announce_validator, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
assert_eq!(module_err.pallet, "Staking");
assert_eq!(module_err.error, "NotController");
assert_matches!(announce_validator, Err(Error::Runtime(err)) => {
let details = err.inner().details().unwrap();
assert_eq!(details.pallet, "Staking");
assert_eq!(details.error, "NotController");
});
Ok(())
}
@@ -105,7 +106,7 @@ async fn nominate_with_controller_account() {
}
#[async_std::test]
async fn nominate_not_possible_for_stash_account() -> Result<(), Error> {
async fn nominate_not_possible_for_stash_account() -> Result<(), Error<DispatchError>> {
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
let bob = pair_signer(AccountKeyring::Bob.pair());
let cxt = test_context().await;
@@ -120,15 +121,16 @@ async fn nominate_not_possible_for_stash_account() -> Result<(), Error> {
.wait_for_finalized_success()
.await;
assert_matches!(nomination, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
assert_eq!(module_err.pallet, "Staking");
assert_eq!(module_err.error, "NotController");
assert_matches!(nomination, Err(Error::Runtime(err)) => {
let details = err.inner().details().unwrap();
assert_eq!(details.pallet, "Staking");
assert_eq!(details.error, "NotController");
});
Ok(())
}
#[async_std::test]
async fn chill_works_for_controller_only() -> Result<(), Error> {
async fn chill_works_for_controller_only() -> Result<(), Error<DispatchError>> {
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
let bob_stash = pair_signer(get_from_seed("Bob//stash"));
let alice = pair_signer(AccountKeyring::Alice.pair());
@@ -163,9 +165,10 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
.wait_for_finalized_success()
.await;
assert_matches!(chill, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
assert_eq!(module_err.pallet, "Staking");
assert_eq!(module_err.error, "NotController");
assert_matches!(chill, Err(Error::Runtime(err)) => {
let details = err.inner().details().unwrap();
assert_eq!(details.pallet, "Staking");
assert_eq!(details.error, "NotController");
});
let is_chilled = cxt
@@ -184,7 +187,7 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
}
#[async_std::test]
async fn tx_bond() -> Result<(), Error> {
async fn tx_bond() -> Result<(), Error<DispatchError>> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let cxt = test_context().await;
@@ -218,16 +221,16 @@ async fn tx_bond() -> Result<(), Error> {
.wait_for_finalized_success()
.await;
assert_matches!(bond_again, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
assert_eq!(module_err.pallet, "Staking");
assert_eq!(module_err.error, "AlreadyBonded");
assert_matches!(bond_again, Err(Error::Runtime(err)) => {
let details = err.inner().details().unwrap();
assert_eq!(details.pallet, "Staking");
assert_eq!(details.error, "AlreadyBonded");
});
Ok(())
}
#[async_std::test]
async fn storage_history_depth() -> Result<(), Error> {
async fn storage_history_depth() -> Result<(), Error<DispatchError>> {
let cxt = test_context().await;
let history_depth = cxt.api.storage().staking().history_depth(None).await?;
assert_eq!(history_depth, 84);
@@ -235,7 +238,7 @@ async fn storage_history_depth() -> Result<(), Error> {
}
#[async_std::test]
async fn storage_current_era() -> Result<(), Error> {
async fn storage_current_era() -> Result<(), Error<DispatchError>> {
let cxt = test_context().await;
let _current_era = cxt
.api
@@ -248,7 +251,7 @@ async fn storage_current_era() -> Result<(), Error> {
}
#[async_std::test]
async fn storage_era_reward_points() -> Result<(), Error> {
async fn storage_era_reward_points() -> Result<(), Error<DispatchError>> {
let cxt = test_context().await;
let current_era_result = cxt
.api
+3 -2
View File
@@ -18,6 +18,7 @@ use crate::{
node_runtime::{
runtime_types,
sudo,
DispatchError,
},
pair_signer,
test_context,
@@ -28,7 +29,7 @@ type Call = runtime_types::node_runtime::Call;
type BalancesCall = runtime_types::pallet_balances::pallet::Call;
#[async_std::test]
async fn test_sudo() -> Result<(), subxt::Error> {
async fn test_sudo() -> Result<(), subxt::Error<DispatchError>> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id().into();
let cxt = test_context().await;
@@ -54,7 +55,7 @@ async fn test_sudo() -> Result<(), subxt::Error> {
}
#[async_std::test]
async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error> {
async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error<DispatchError>> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id().into();
let cxt = test_context().await;
+6 -3
View File
@@ -15,7 +15,10 @@
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
use crate::{
node_runtime::system,
node_runtime::{
system,
DispatchError,
},
pair_signer,
test_context,
};
@@ -24,7 +27,7 @@ use sp_keyring::AccountKeyring;
use subxt::Signer;
#[async_std::test]
async fn storage_account() -> Result<(), subxt::Error> {
async fn storage_account() -> Result<(), subxt::Error<DispatchError>> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let cxt = test_context().await;
@@ -40,7 +43,7 @@ async fn storage_account() -> Result<(), subxt::Error> {
}
#[async_std::test]
async fn tx_remark_with_event() -> Result<(), subxt::Error> {
async fn tx_remark_with_event() -> Result<(), subxt::Error<DispatchError>> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let cxt = test_context().await;