Skip to content

Commit

Permalink
Use RPC call to get account nonce (#476)
Browse files Browse the repository at this point in the history
* remove code related to getting nonce from storage and use RPC call instead

* cargo fmt

* move nonce fetching into Rpc, since it's just an RPC call

* clippy
  • Loading branch information
jsdw authored Mar 14, 2022
1 parent 798a1d0 commit 8b19549
Show file tree
Hide file tree
Showing 10 changed files with 460 additions and 1,086 deletions.
9 changes: 4 additions & 5 deletions codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub fn generate_calls(
pub fn #fn_name(
&self,
#( #call_fn_args, )*
) -> ::subxt::SubmittableExtrinsic<'a, T, X, A, #call_struct_name, DispatchError, root_mod::Event> {
) -> ::subxt::SubmittableExtrinsic<'a, T, X, #call_struct_name, DispatchError, root_mod::Event> {
let call = #call_struct_name { #( #call_args, )* };
::subxt::SubmittableExtrinsic::new(self.client, call)
}
Expand All @@ -108,16 +108,15 @@ pub fn generate_calls(

#( #call_structs )*

pub struct TransactionApi<'a, T: ::subxt::Config, X, A> {
pub struct TransactionApi<'a, T: ::subxt::Config, X> {
client: &'a ::subxt::Client<T>,
marker: ::core::marker::PhantomData<(X, A)>,
marker: ::core::marker::PhantomData<X>,
}

impl<'a, T, X, A> TransactionApi<'a, T, X, A>
impl<'a, T, X> TransactionApi<'a, T, X>
where
T: ::subxt::Config,
X: ::subxt::SignedExtra<T>,
A: ::subxt::AccountData,
{
pub fn new(client: &'a ::subxt::Client<T>) -> Self {
Self { client, marker: ::core::marker::PhantomData }
Expand Down
105 changes: 10 additions & 95 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ use crate::{
use codec::Decode;
use frame_metadata::{
v14::RuntimeMetadataV14,
PalletMetadata,
RuntimeMetadata,
RuntimeMetadataPrefixed,
StorageEntryType,
};
use heck::ToSnakeCase as _;
use proc_macro2::TokenStream as TokenStream2;
Expand All @@ -61,7 +59,6 @@ use quote::{
format_ident,
quote,
};
use scale_info::form::PortableForm;
use std::{
collections::HashMap,
fs,
Expand Down Expand Up @@ -268,16 +265,6 @@ impl RuntimeGenerator {
let has_module_error_impl =
errors::generate_has_module_error_impl(&self.metadata, types_mod_ident);

let default_account_data_ident = format_ident!("DefaultAccountData");
let default_account_data_impl = generate_default_account_data_impl(
&pallets_with_mod_names,
&default_account_data_ident,
&type_gen,
);
let type_parameter_default_impl = default_account_data_impl
.as_ref()
.map(|_| quote!( = #default_account_data_ident ));

quote! {
#[allow(dead_code, unused_imports, non_camel_case_types)]
pub mod #mod_ident {
Expand All @@ -293,29 +280,25 @@ impl RuntimeGenerator {
// Impl HasModuleError on DispatchError so we can pluck out module error details.
#has_module_error_impl

#default_account_data_impl

pub struct RuntimeApi<T: ::subxt::Config, X, A #type_parameter_default_impl> {
pub struct RuntimeApi<T: ::subxt::Config, X> {
pub client: ::subxt::Client<T>,
marker: ::core::marker::PhantomData<(X, A)>,
marker: ::core::marker::PhantomData<X>,
}

impl<T, X, A> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X, A>
impl<T, X> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X>
where
T: ::subxt::Config,
X: ::subxt::SignedExtra<T>,
A: ::subxt::AccountData,
X: ::subxt::SignedExtra<T>
{
fn from(client: ::subxt::Client<T>) -> Self {
Self { client, marker: ::core::marker::PhantomData }
}
}

impl<'a, T, X, A> RuntimeApi<T, X, A>
impl<'a, T, X> RuntimeApi<T, X>
where
T: ::subxt::Config,
X: ::subxt::SignedExtra<T>,
A: ::subxt::AccountData,
{
pub fn constants(&'a self) -> ConstantsApi {
ConstantsApi
Expand All @@ -325,7 +308,7 @@ impl RuntimeGenerator {
StorageApi { client: &self.client }
}

pub fn tx(&'a self) -> TransactionApi<'a, T, X, A> {
pub fn tx(&'a self) -> TransactionApi<'a, T, X> {
TransactionApi { client: &self.client, marker: ::core::marker::PhantomData }
}

Expand Down Expand Up @@ -377,19 +360,18 @@ impl RuntimeGenerator {
)*
}

pub struct TransactionApi<'a, T: ::subxt::Config, X, A> {
pub struct TransactionApi<'a, T: ::subxt::Config, X> {
client: &'a ::subxt::Client<T>,
marker: ::core::marker::PhantomData<(X, A)>,
marker: ::core::marker::PhantomData<X>,
}

impl<'a, T, X, A> TransactionApi<'a, T, X, A>
impl<'a, T, X> TransactionApi<'a, T, X>
where
T: ::subxt::Config,
X: ::subxt::SignedExtra<T>,
A: ::subxt::AccountData,
{
#(
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, X, A> {
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, X> {
#pallets_with_calls::calls::TransactionApi::new(self.client)
}
)*
Expand All @@ -399,73 +381,6 @@ impl RuntimeGenerator {
}
}

/// Most chains require a valid account nonce as part of the extrinsic, so the default behaviour of
/// the client is to fetch the nonce for the current account.
///
/// The account index (aka nonce) is commonly stored in the `System` pallet's `Account` storage item.
/// This function attempts to find that storage item, and if it is present will implement the
/// `subxt::AccountData` trait for it. This allows the client to construct the appropriate
/// storage key from the account id, and then retrieve the `nonce` from the resulting storage item.
fn generate_default_account_data_impl(
pallets_with_mod_names: &[(&PalletMetadata<PortableForm>, syn::Ident)],
default_impl_name: &syn::Ident,
type_gen: &TypeGenerator,
) -> Option<TokenStream2> {
let storage = pallets_with_mod_names
.iter()
.find(|(pallet, _)| pallet.name == "System")
.and_then(|(pallet, _)| pallet.storage.as_ref())?;
let storage_entry = storage
.entries
.iter()
.find(|entry| entry.name == "Account")?;

// resolve the concrete types for `AccountId` (to build the key) and `Index` to extract the
// account index (nonce) value from the result.
let (account_id_ty, account_nonce_ty) =
if let StorageEntryType::Map { key, value, .. } = &storage_entry.ty {
let account_id_ty = type_gen.resolve_type_path(key.id(), &[]);
let account_data_ty = type_gen.resolve_type(value.id());
let nonce_field = if let scale_info::TypeDef::Composite(composite) =
account_data_ty.type_def()
{
composite
.fields()
.iter()
.find(|f| f.name() == Some(&"nonce".to_string()))?
} else {
abort_call_site!("Expected a `nonce` field in the account info struct")
};
let account_nonce_ty = type_gen.resolve_type_path(nonce_field.ty().id(), &[]);
(account_id_ty, account_nonce_ty)
} else {
abort_call_site!("System::Account should be a `StorageEntryType::Map`")
};

// this path to the storage entry depends on storage codegen.
// AccountOwned contains the same data as Account does, but without references.
let storage_entry_path = quote!(self::system::storage::AccountOwned);

Some(quote! {
/// The default storage entry from which to fetch an account nonce, required for
/// constructing a transaction.
pub enum #default_impl_name {}

impl ::subxt::AccountData for #default_impl_name {
type StorageEntry = #storage_entry_path;
type AccountId = #account_id_ty;
type Index = #account_nonce_ty;

fn storage_entry(account_id: Self::AccountId) -> Self::StorageEntry {
#storage_entry_path(account_id)
}
fn nonce(result: &<Self::StorageEntry as ::subxt::StorageEntry>::Value) -> Self::Index {
result.nonce
}
}
})
}

pub fn generate_structs_from_variants<'a, F>(
type_gen: &'a TypeGenerator,
type_id: u32,
Expand Down
31 changes: 0 additions & 31 deletions codegen/src/api/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ fn generate_storage_entry_fns(
storage_entry: &StorageEntryMetadata<PortableForm>,
) -> (TokenStream2, TokenStream2) {
let entry_struct_ident = format_ident!("{}", storage_entry.name);
let is_account_wrapper = pallet.name == "System" && storage_entry.name == "Account";
let wrapper_struct_ident = format_ident!("{}Owned", storage_entry.name);
let (fields, entry_struct, constructor, key_impl, should_ref) = match storage_entry.ty
{
StorageEntryType::Plain(_) => {
Expand Down Expand Up @@ -183,18 +181,6 @@ fn generate_storage_entry_fns(

let ty_path = type_gen.resolve_type_path(key.id(), &[]);
let fields = vec![(format_ident!("_0"), ty_path.clone())];
// `::system::storage::Account` was utilized as associated type `StorageEntry`
// for `::subxt::AccountData` implementation by the generated `DefaultAccountData`.
// Due to changes in the storage API, `::system::storage::Account` cannot be
// used without specifying a lifetime. To satisfy `::subxt::AccountData`
// implementation, a non-reference wrapper `AccountOwned` is generated.
let wrapper_struct = if is_account_wrapper {
quote!(
pub struct #wrapper_struct_ident ( pub #ty_path );
)
} else {
quote!()
};

// `ty_path` can be `std::vec::Vec<T>`. In such cases, the entry struct
// should contain a slice reference.
Expand All @@ -204,7 +190,6 @@ fn generate_storage_entry_fns(
};
let entry_struct = quote! {
pub struct #entry_struct_ident #lifetime_param( pub #lifetime_ref #ty_slice );
#wrapper_struct
};
let constructor = quote!( #entry_struct_ident(_0) );
let hasher = hashers.get(0).unwrap_or_else(|| {
Expand Down Expand Up @@ -256,27 +241,11 @@ fn generate_storage_entry_fns(
}
);

// The wrapper account must implement the same trait as the counter-part Account,
// with the same pallet and storage. This continues the implementation of the wrapper
// generated with the Account.
let wrapper_entry_impl = if is_account_wrapper {
quote!(
impl ::subxt::StorageEntry for #wrapper_struct_ident {
#storage_entry_impl
}
)
} else {
quote!()
};

let storage_entry_type = quote! {
#entry_struct

impl ::subxt::StorageEntry for #entry_struct_ident #anon_lifetime {
#storage_entry_impl
}

#wrapper_entry_impl
};

let client_iter_fn = if matches!(storage_entry.ty, StorageEntryType::Map { .. }) {
Expand Down
36 changes: 7 additions & 29 deletions subxt/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ use crate::{
},
storage::StorageClient,
transaction::TransactionProgress,
AccountData,
Call,
Config,
Metadata,
Expand Down Expand Up @@ -170,23 +169,6 @@ impl<T: Config> Client<T> {
StorageClient::new(&self.rpc, &self.metadata, self.iter_page_size)
}

/// Fetch the current nonce for the given account id.
pub async fn fetch_nonce<A: AccountData>(
&self,
account: &T::AccountId,
) -> Result<T::Index, BasicError>
where
<A as AccountData>::AccountId: From<<T as Config>::AccountId>,
<A as AccountData>::Index: Into<<T as Config>::Index>,
{
let account_storage_entry = A::storage_entry(account.clone().into());
let account_data = self
.storage()
.fetch_or_default(&account_storage_entry, None)
.await?;
Ok(A::nonce(&account_data).into())
}

/// Convert the client to a runtime api wrapper for custom runtime access.
///
/// The `subxt` proc macro will provide methods to submit extrinsics and read storage specific
Expand All @@ -197,17 +179,16 @@ impl<T: Config> Client<T> {
}

/// A constructed call ready to be signed and submitted.
pub struct SubmittableExtrinsic<'client, T: Config, X, A, C, E: Decode, Evs: Decode> {
pub struct SubmittableExtrinsic<'client, T: Config, X, C, E: Decode, Evs: Decode> {
client: &'client Client<T>,
call: C,
marker: std::marker::PhantomData<(X, A, E, Evs)>,
marker: std::marker::PhantomData<(X, E, Evs)>,
}

impl<'client, T, X, A, C, E, Evs> SubmittableExtrinsic<'client, T, X, A, C, E, Evs>
impl<'client, T, X, C, E, Evs> SubmittableExtrinsic<'client, T, X, C, E, Evs>
where
T: Config,
X: SignedExtra<T>,
A: AccountData,
C: Call + Send + Sync,
E: Decode + HasModuleError,
Evs: Decode,
Expand All @@ -232,8 +213,6 @@ where
where
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
<A as AccountData>::AccountId: From<<T as Config>::AccountId>,
<A as AccountData>::Index: Into<<T as Config>::Index>,
{
// Sign the call data to create our extrinsic.
let extrinsic = self.create_signed(signer, Default::default()).await?;
Expand Down Expand Up @@ -262,8 +241,6 @@ where
where
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
<A as AccountData>::AccountId: From<<T as Config>::AccountId>,
<A as AccountData>::Index: Into<<T as Config>::Index>,
{
let extrinsic = self.create_signed(signer, Default::default()).await?;
self.client.rpc().submit_extrinsic(extrinsic).await
Expand All @@ -278,13 +255,14 @@ where
where
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
<A as AccountData>::AccountId: From<<T as Config>::AccountId>,
<A as AccountData>::Index: Into<<T as Config>::Index>,
{
let account_nonce = if let Some(nonce) = signer.nonce() {
nonce
} else {
self.client.fetch_nonce::<A>(signer.account_id()).await?
self.client
.rpc()
.system_account_next_index(signer.account_id())
.await?
};
let call = self
.client
Expand Down
Loading

0 comments on commit 8b19549

Please sign in to comment.