Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use RPC call to get account nonce #476

Merged
merged 4 commits into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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