Rework Subxt API to support offline and dynamic transactions (#593)

* WIP API changes

* debug impls

* Get main crate compiling with first round of changes

* Some tidy up

* Add WithExtrinsicParams, and have SubstrateConfig + PolkadotConfig, not DefaultConfig

* move transaction into extrinsic folder

* Add runtime updates back to OnlineClient

* rework to be 'client first' to fit better with storage + events

* add support for events to Client

* tidy dupe trait bound

* Wire storage into client, but need to remove static reliance

* various tidy up and start stripping codegen to remove bits we dont need now

* First pass updating calls and constants codegen

* WIP storage client updates

* First pass migrated runtime storage over to new format

* pass over codegen to generate StorageAddresses and throw other stuff out

* don't need a Call trait any more

* shuffle things around a bit

* Various proc_macro fixes to get 'cargo check' working

* organise what's exposed from subxt

* Get first example working; balance_transfer_with_params

* get balance_transfer example compiling

* get concurrent_storage_requests.rs example compiling

* get fetch_all_accounts example compiling

* get a bunch more of the examples compiling

* almost get final example working; type mismatch to look into

* wee tweaks

* move StorageAddress to separate file

* pass Defaultable/Iterable info to StorageAddress in codegen

* fix storage validation ne, and partial run through example code

* Remove static iteration and strip a generic param from everything

* fix doc tests in subxt crate

* update test utils and start fixing frame tests

* fix frame staking tests

* fix the rest of the test compile issues, Borrow on storage values

* cargo fmt

* remove extra logging during tests

* Appease clippy and no more need for into_iter on events

* cargo fmt

* fix dryRun tests by waiting for blocks

* wait for blocks instead of sleeping or other test hacks

* cargo fmt

* Fix doc links

* Traitify StorageAddress

* remove out-of-date doc comments

* optimise decoding storage a little

* cleanup tx stuff, trait for TxPayload, remove Err type param and decode at runtime

* clippy fixes

* fix doc links

* fix doc example

* constant address trait for consistency

* fix a typo and remove EncodeWithMetadata stuff

* Put EventDetails behind a proper interface and allow decoding into top level event, too

* fix docs

* tweak StorageAddress docs

* re-export StorageAddress at root for consistency

* fix clippy things

* Add support for dynamic values

* fix double encoding of storage map key after refactor

* clippy fix

* Fixes and add a dynamic usage example (needs new scale_value release)

* bump scale_value version

* cargo fmt

* Tweak event bits

* cargo fmt

* Add a test and bump scale-value to 0.4.0 to support this

* remove unnecessary vec from dynamic example

* Various typo/grammar fixes

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>

* Address PR nits

* Undo accidental rename in changelog

* Small PR nits/tidyups

* fix tests; codegen change against latest substrate

* tweak storage address util names

* move error decoding to DecodeError and expose

* impl some basic traits on the extrinsic param builder

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
James Wilson
2022-08-08 11:55:20 +01:00
committed by GitHub
parent 7a09ac6cd7
commit e48f0e3b1d
84 changed files with 23097 additions and 35863 deletions
+37 -111
View File
@@ -6,7 +6,6 @@
mod calls;
mod constants;
mod errors;
mod events;
mod storage;
@@ -110,33 +109,33 @@ impl RuntimeGenerator {
let mut type_substitutes = [
(
"bitvec::order::Lsb0",
parse_quote!(::subxt::bitvec::order::Lsb0),
parse_quote!(::subxt::ext::bitvec::order::Lsb0),
),
(
"bitvec::order::Msb0",
parse_quote!(::subxt::bitvec::order::Msb0),
parse_quote!(::subxt::ext::bitvec::order::Msb0),
),
(
"sp_core::crypto::AccountId32",
parse_quote!(::subxt::sp_core::crypto::AccountId32),
parse_quote!(::subxt::ext::sp_core::crypto::AccountId32),
),
(
"primitive_types::H256",
parse_quote!(::subxt::sp_core::H256),
parse_quote!(::subxt::ext::sp_core::H256),
),
(
"sp_runtime::multiaddress::MultiAddress",
parse_quote!(::subxt::sp_runtime::MultiAddress),
parse_quote!(::subxt::ext::sp_runtime::MultiAddress),
),
(
"frame_support::traits::misc::WrapperKeepOpaque",
parse_quote!(::subxt::WrapperKeepOpaque),
parse_quote!(::subxt::utils::WrapperKeepOpaque),
),
// BTreeMap and BTreeSet impose an `Ord` constraint on their key types. This
// can cause an issue with generated code that doesn't impl `Ord` by default.
// Decoding them to Vec by default (KeyedVec is just an alias for Vec with
// suitable type params) avoids these issues.
("BTreeMap", parse_quote!(::subxt::KeyedVec)),
("BTreeMap", parse_quote!(::subxt::utils::KeyedVec)),
("BTreeSet", parse_quote!(::std::vec::Vec)),
]
.iter()
@@ -256,9 +255,6 @@ impl RuntimeGenerator {
})
.collect();
let has_module_error_impl =
errors::generate_has_module_error_impl(&self.metadata, types_mod_ident);
quote! {
#[allow(dead_code, unused_imports, non_camel_case_types)]
pub mod #mod_ident {
@@ -271,128 +267,58 @@ impl RuntimeGenerator {
#( #modules )*
#types_mod
/// The default error type returned when there is a runtime issue.
/// The default error type returned when there is a runtime issue,
/// exposed here for ease of use.
pub type DispatchError = #types_mod_ident::sp_runtime::DispatchError;
// Impl HasModuleError on DispatchError so we can pluck out module error details.
#has_module_error_impl
pub struct RuntimeApi<T: ::subxt::Config, X> {
pub client: ::subxt::Client<T>,
marker: ::core::marker::PhantomData<X>,
pub fn constants() -> ConstantsApi {
ConstantsApi
}
impl<T: ::subxt::Config, X> Clone for RuntimeApi<T, X> {
fn clone(&self) -> Self {
Self { client: self.client.clone(), marker: ::core::marker::PhantomData }
}
pub fn storage() -> StorageApi {
StorageApi
}
impl<T, X> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X>
where
T: ::subxt::Config,
X: ::subxt::extrinsic::ExtrinsicParams<T>
{
fn from(client: ::subxt::Client<T>) -> Self {
Self { client, marker: ::core::marker::PhantomData }
}
pub fn tx() -> TransactionApi {
TransactionApi
}
impl<'a, T, X> RuntimeApi<T, X>
where
T: ::subxt::Config,
X: ::subxt::extrinsic::ExtrinsicParams<T>,
{
pub fn validate_metadata(&'a self) -> Result<(), ::subxt::MetadataError> {
let runtime_metadata_hash = {
let locked_metadata = self.client.metadata();
let metadata = locked_metadata.read();
metadata.metadata_hash(&PALLETS)
};
if runtime_metadata_hash != [ #(#metadata_hash,)* ] {
Err(::subxt::MetadataError::IncompatibleMetadata)
} else {
Ok(())
}
}
pub fn constants(&'a self) -> ConstantsApi<'a, T> {
ConstantsApi { client: &self.client }
}
pub fn storage(&'a self) -> StorageApi<'a, T> {
StorageApi { client: &self.client }
}
pub fn tx(&'a self) -> TransactionApi<'a, T, X> {
TransactionApi { client: &self.client, marker: ::core::marker::PhantomData }
}
pub fn events(&'a self) -> EventsApi<'a, T> {
EventsApi { client: &self.client }
}
}
pub struct EventsApi<'a, T: ::subxt::Config> {
client: &'a ::subxt::Client<T>,
}
impl <'a, T: ::subxt::Config> EventsApi<'a, T> {
pub async fn at(&self, block_hash: T::Hash) -> Result<::subxt::events::Events<T, Event>, ::subxt::BasicError> {
::subxt::events::at::<T, Event>(self.client, block_hash).await
}
pub async fn subscribe(&self) -> Result<::subxt::events::EventSubscription<'a, ::subxt::events::EventSub<T::Header>, T, Event>, ::subxt::BasicError> {
::subxt::events::subscribe::<T, Event>(self.client).await
}
pub async fn subscribe_finalized(&self) -> Result<::subxt::events::EventSubscription<'a, ::subxt::events::FinalizedEventSub<'a, T::Header>, T, Event>, ::subxt::BasicError> {
::subxt::events::subscribe_finalized::<T, Event>(self.client).await
}
}
pub struct ConstantsApi<'a, T: ::subxt::Config> {
client: &'a ::subxt::Client<T>,
}
impl<'a, T: ::subxt::Config> ConstantsApi<'a, T> {
pub struct ConstantsApi;
impl ConstantsApi {
#(
pub fn #pallets_with_constants(&self) -> #pallets_with_constants::constants::ConstantsApi<'a, T> {
#pallets_with_constants::constants::ConstantsApi::new(self.client)
pub fn #pallets_with_constants(&self) -> #pallets_with_constants::constants::ConstantsApi {
#pallets_with_constants::constants::ConstantsApi
}
)*
}
pub struct StorageApi<'a, T: ::subxt::Config> {
client: &'a ::subxt::Client<T>,
}
impl<'a, T> StorageApi<'a, T>
where
T: ::subxt::Config,
{
pub struct StorageApi;
impl StorageApi {
#(
pub fn #pallets_with_storage(&self) -> #pallets_with_storage::storage::StorageApi<'a, T> {
#pallets_with_storage::storage::StorageApi::new(self.client)
pub fn #pallets_with_storage(&self) -> #pallets_with_storage::storage::StorageApi {
#pallets_with_storage::storage::StorageApi
}
)*
}
pub struct TransactionApi<'a, T: ::subxt::Config, X> {
client: &'a ::subxt::Client<T>,
marker: ::core::marker::PhantomData<X>,
}
impl<'a, T, X> TransactionApi<'a, T, X>
where
T: ::subxt::Config,
X: ::subxt::extrinsic::ExtrinsicParams<T>,
{
pub struct TransactionApi;
impl TransactionApi {
#(
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, X> {
#pallets_with_calls::calls::TransactionApi::new(self.client)
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi {
#pallets_with_calls::calls::TransactionApi
}
)*
}
/// check whether the Client you are using is aligned with the statically generated codegen.
pub fn validate_codegen<T: ::subxt::Config, C: ::subxt::client::OfflineClientT<T>>(client: &C) -> Result<(), ::subxt::error::MetadataError> {
let runtime_metadata_hash = client.metadata().metadata_hash(&PALLETS);
if runtime_metadata_hash != [ #(#metadata_hash,)* ] {
Err(::subxt::error::MetadataError::IncompatibleMetadata)
} else {
Ok(())
}
}
}
}
}