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 3 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
102 changes: 10 additions & 92 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,16 +268,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 +283,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 +311,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 +363,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 +384,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
29 changes: 8 additions & 21 deletions subxt/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ use sp_runtime::traits::{
pub trait Config: 'static {
/// Account index (aka nonce) type. This stores the number of previous
/// transactions associated with a sender account.
type Index: Parameter + Member + Default + AtLeast32Bit + Copy + scale_info::TypeInfo;
type Index: Parameter
+ Member
+ serde::de::DeserializeOwned
+ Default
+ AtLeast32Bit
+ Copy
+ scale_info::TypeInfo;

/// The block number type used by the runtime.
type BlockNumber: Parameter
Expand All @@ -65,7 +71,7 @@ pub trait Config: 'static {
type Hashing: Hash<Output = Self::Hash>;

/// The user account identifier type for the runtime.
type AccountId: Parameter + Member;
type AccountId: Parameter + Member + serde::Serialize;

/// The address type. This instead of `<frame_system::Trait::Lookup as StaticLookup>::Source`.
type Address: Codec + Clone + PartialEq;
Expand Down Expand Up @@ -103,22 +109,3 @@ impl Config for DefaultConfig {
type Signature = sp_runtime::MultiSignature;
type Extrinsic = sp_runtime::OpaqueExtrinsic;
}

/// Trait to fetch data about an account.
pub trait AccountData {
/// The runtime storage entry from which the account data can be fetched.
/// Usually generated by the `subxt` macro.
type StorageEntry: StorageEntry;

/// The type of the account id to fetch the account data for.
type AccountId;

/// The type of the account nonce returned from storage.
type Index;

/// Create a new storage entry key from the account id.
fn storage_entry(account_id: Self::AccountId) -> Self::StorageEntry;

/// Get the nonce from the storage entry value.
fn nonce(result: &<Self::StorageEntry as StorageEntry>::Value) -> Self::Index;
}
1 change: 0 additions & 1 deletion subxt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ pub use crate::{
SubmittableExtrinsic,
},
config::{
AccountData,
Config,
DefaultConfig,
},
Expand Down
Loading