subxt-core crate (#1466)

* start migrating, broken

* first iteration of updating

* fmt and clippy

* add Composite<u32> decoding via scale value patch

* bump scale type gen versions

* fix decoding with new scale decode

* compiling with changed deps

* core utils, condig, client, metadata

* core crate compiling

* signer crate no once lock

* add core to no-std-tests, change imports

* broken commit, start pulling everything together in subxt

* port more things to subxt

* events in core crate, extrinsics sadly much more difficult

* almost all examples pass again

* dynamic values fix in examples

* fix no std issue and fmt

* remove unused dependencies

* fix lightclient impl

* runtime version refactor

* formatting and addressing nits

* more comments addressed

* update wasm example and no-std-signer tests

* other nits and error impl on signer errors

* fix feature flag

* fix runtime version refactor

* fix doc links

* fix integration tests

* fix feature flag gated client state

* fix native feature in CI

* fix lightclient utils

* make imports more lean in subxt-core

* integrate changes from subxt-core imports into subxt

* other changes in subxt simplify imports more

* fix  and docs

* doc false for cli

* fix clippy

* remove events block hash in tests

* codegen no-std support in generated code

* export alloc crate for no-std codegen

* fix doc test

* implement James comments

* remove std traits, use core traits instead

* address nits

* remove unusued dep in no-std tests

* fix Box import in no_std

* sp-crypto-hashing instead of sp-core-hashing

* bump scale-typegen, add no std codegen tests

* fix some things

* replace unmaintained derivative with derive_where to remove non-canonical warnings

* fmt

* remove unused dep

* fix deps

* update artifacts to fix type ID mismatches

* bump to latest scale-typegen

---------

Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
Tadeo Hepperle
2024-03-27 09:55:08 +01:00
committed by GitHub
parent 92c1ba7f66
commit a0cb14aa4f
106 changed files with 24329 additions and 26882 deletions
+2 -2
View File
@@ -187,8 +187,8 @@ jobs:
- name: Cargo check subxt-signer
run: |
cargo check -p subxt-signer
cargo check -p subxt-signer --no-default-features --features sr25519,native
cargo check -p subxt-signer --no-default-features --features ecdsa,native
cargo check -p subxt-signer --no-default-features --features sr25519
cargo check -p subxt-signer --no-default-features --features ecdsa
# We can't enable web features here, so no cargo hack.
- name: Cargo check subxt-lightclient
Generated
+54 -13
View File
@@ -1270,6 +1270,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "derive-where"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
]
[[package]]
name = "derive_more"
version = "0.99.17"
@@ -2816,7 +2827,7 @@ checksum = "4e69bf016dc406eff7d53a7d3f7cf1c2e72c82b9088aac1118591e36dd2cd3e9"
dependencies = [
"bitcoin_hashes 0.13.0",
"rand",
"rand_core 0.5.1",
"rand_core 0.6.4",
"serde",
"unicode-normalization",
]
@@ -3637,9 +3648,9 @@ dependencies = [
[[package]]
name = "scale-info"
version = "2.11.0"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ef2175c2907e7c8bc0a9c3f86aeb5ec1f3b275300ad58a44d0c3ae379a5e52e"
checksum = "788745a868b0e751750388f4e6546eb921ef714a4317fa6954f7cde114eb2eb7"
dependencies = [
"bitvec",
"cfg-if",
@@ -3651,9 +3662,9 @@ dependencies = [
[[package]]
name = "scale-info-derive"
version = "2.10.0"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19"
checksum = "7dc2f4e8bc344b9fc3d5f74f72c2e55bfc38d28dc2ebc69c194a3df424e4d9ac"
dependencies = [
"proc-macro-crate 1.3.1",
"proc-macro2",
@@ -3673,22 +3684,23 @@ dependencies = [
[[package]]
name = "scale-typegen"
version = "0.2.0"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6108609f017741c78d35967c7afe4aeaa3999b848282581041428e10d23b63"
checksum = "0906a872e5733f2633457001962007b644d2e17e08e74340f79820e4951b653f"
dependencies = [
"proc-macro2",
"quote",
"scale-info",
"smallvec",
"syn 2.0.53",
"thiserror",
]
[[package]]
name = "scale-typegen-description"
version = "0.2.0"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479f0b8b0d75cce8d284ace5a9b7f5a12c523c94387c710835695e8b194a17bb"
checksum = "1601f61a2ec4f278cc6102204860893b90abd938ec7e9d02799748a97169b3c9"
dependencies = [
"anyhow",
"peekmore",
@@ -4613,10 +4625,8 @@ version = "0.35.0"
dependencies = [
"assert_matches",
"async-trait",
"base58",
"bitvec",
"blake2",
"derivative",
"derive-where",
"either",
"frame-metadata 16.0.0",
"futures",
@@ -4639,6 +4649,7 @@ dependencies = [
"sp-crypto-hashing",
"sp-keyring",
"sp-runtime",
"subxt-core",
"subxt-lightclient",
"subxt-macro",
"subxt-metadata",
@@ -4700,6 +4711,36 @@ dependencies = [
"tokio",
]
[[package]]
name = "subxt-core"
version = "0.35.0"
dependencies = [
"base58",
"bitvec",
"blake2",
"derive-where",
"derive_more",
"frame-metadata 16.0.0",
"hashbrown 0.14.3",
"hex",
"impl-serde",
"parity-scale-codec",
"primitive-types",
"scale-bits",
"scale-decode",
"scale-encode",
"scale-info",
"scale-value",
"serde",
"serde_json",
"sp-core",
"sp-crypto-hashing",
"sp-keyring",
"sp-runtime",
"subxt-metadata",
"tracing",
]
[[package]]
name = "subxt-lightclient"
version = "0.35.0"
@@ -4773,7 +4814,7 @@ dependencies = [
"sp-core",
"sp-crypto-hashing",
"sp-keyring",
"subxt",
"subxt-core",
"zeroize",
]
+5 -3
View File
@@ -2,6 +2,7 @@
members = [
"cli",
"codegen",
"core",
"lightclient",
"testing/substrate-runner",
"testing/test-runtime",
@@ -70,7 +71,7 @@ codec = { package = "parity-scale-codec", version = "3.6.9", default-features =
color-eyre = "0.6.3"
console_error_panic_hook = "0.1.7"
darling = "0.20.8"
derivative = "2.2.0"
derive-where = "1.2.7"
derive_more = "0.99.17"
either = { version = "1.10.0", default-features = false }
frame-metadata = { version = "16.0.0", default-features = false }
@@ -93,6 +94,8 @@ scale-value = { version = "0.14.1", default-features = false }
scale-bits = { version = "0.5.0", default-features = false }
scale-decode = { version = "0.11.1", default-features = false }
scale-encode = { version = "0.6.0", default-features = false }
scale-typegen = "0.4.2"
scale-typegen-description = "0.4.2"
serde = { version = "1.0.197", default-features = false, features = ["derive"] }
serde_json = { version = "1.0.114", default-features = false }
syn = { version = "2.0.15", features = ["full", "extra-traits"] }
@@ -106,8 +109,6 @@ url = "2.5.0"
wabt = "0.10.0"
wasm-bindgen-test = "0.3.24"
which = "5.0.0"
scale-typegen-description = "0.2.0"
scale-typegen = "0.2.0"
strip-ansi-escapes = "0.2.0"
# Light client support:
@@ -136,6 +137,7 @@ sp-keyring = "34.0.0"
# Subxt workspace crates:
subxt = { version = "0.35.0", path = "subxt", default-features = false }
subxt-core = { version = "0.35.0", path = "core", default-features = false }
subxt-macro = { version = "0.35.0", path = "macro" }
subxt-metadata = { version = "0.35.0", path = "metadata", default-features = false }
subxt-codegen = { version = "0.35.0", path = "codegen" }
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -46,7 +46,7 @@ pub struct Opts {
#[clap(long = "substitute-type", value_parser = substitute_type_parser)]
substitute_types: Vec<(String, String)>,
/// The `subxt` crate access path in the generated code.
/// Defaults to `::subxt`.
/// Defaults to `::subxt::ext::subxt_core`.
#[clap(long = "crate")]
crate_path: Option<String>,
/// Do not generate documentation for the runtime API code.
+1 -4
View File
@@ -154,10 +154,7 @@ fn mocked_offline_client(metadata: Metadata) -> OfflineClient<SubstrateConfig> {
H256::from_str("91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3")
.expect("Valid hash; qed");
let runtime_version = subxt::backend::RuntimeVersion {
spec_version: 9370,
transaction_version: 20,
};
let runtime_version = subxt::client::RuntimeVersion::new(9370, 20);
OfflineClient::<SubstrateConfig>::new(genesis_hash, runtime_version, metadata)
}
+10 -5
View File
@@ -6,6 +6,7 @@ use super::CodegenError;
use heck::{ToSnakeCase as _, ToUpperCamelCase as _};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use scale_typegen::typegen::ir::ToTokensWithSettings;
use scale_typegen::{typegen::ir::type_ir::CompositeIRKind, TypeGenerator};
use subxt_metadata::PalletMetadata;
@@ -16,7 +17,7 @@ use subxt_metadata::PalletMetadata;
///
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
/// - `pallet` - Pallet metadata from which the calls are generated.
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
/// - `crate_path` - The crate path under which the `subxt-core` crate is located, e.g. `::subxt::ext::subxt_core` when using subxt as a dependency.
pub fn generate_calls(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
@@ -41,9 +42,9 @@ pub fn generate_calls(
.iter()
.map(|(name, field)| {
// Note: fn_arg_type this is relative the type path of the type alias when prefixed with `types::`, e.g. `set_max_code_size::New`
let fn_arg_type = &field.type_path;
let fn_arg_type = field.type_path.to_token_stream(type_gen.settings());
let call_arg = if field.is_boxed {
quote! { #name: ::std::boxed::Box::new(#name) }
quote! { #name: #crate_path::alloc::boxed::Box::new(#name) }
} else {
quote! { #name }
};
@@ -71,7 +72,9 @@ pub fn generate_calls(
let docs = &var.composite.docs;
// this converts the composite into a full struct type. No Type Parameters needed here.
let struct_def = type_gen.upcast_composite(&var.composite);
let struct_def = type_gen
.upcast_composite(&var.composite)
.to_token_stream(type_gen.settings());
let alias_mod = var.type_alias_mod;
// The call structure's documentation was stripped above.
let call_struct = quote! {
@@ -105,7 +108,9 @@ pub fn generate_calls(
.into_iter()
.unzip();
let call_type = type_gen.resolve_type_path(call_ty)?;
let call_type = type_gen
.resolve_type_path(call_ty)?
.to_token_stream(type_gen.settings());
let call_ty = type_gen.resolve_type(call_ty)?;
let docs = type_gen.docs_from_scale_info(&call_ty.docs);
+5 -2
View File
@@ -5,6 +5,7 @@
use heck::ToSnakeCase as _;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use scale_typegen::typegen::ir::ToTokensWithSettings;
use scale_typegen::TypeGenerator;
use subxt_metadata::PalletMetadata;
@@ -31,7 +32,7 @@ use super::CodegenError;
///
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
/// - `pallet` - Pallet metadata from which the constants are generated.
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
/// - `crate_path` - The crate path under which the `subxt-core` crate is located, e.g. `::subxt::ext::subxt_core` when using subxt as a dependency.
pub fn generate_constants(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
@@ -55,7 +56,9 @@ pub fn generate_constants(
));
};
let return_ty = type_gen.resolve_type_path(constant.ty())?;
let return_ty = type_gen
.resolve_type_path(constant.ty())?
.to_token_stream(type_gen.settings());
let docs = constant.docs();
let docs = type_gen
.settings()
+4 -3
View File
@@ -3,12 +3,13 @@
// see LICENSE for license details.
use heck::ToSnakeCase as _;
use scale_typegen::typegen::ir::ToTokensWithSettings;
use scale_typegen::TypeGenerator;
use std::collections::HashSet;
use subxt_metadata::{CustomValueMetadata, Metadata};
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
use quote::quote;
/// Generate the custom values mod, if there are any custom values in the metadata. Else returns None.
pub fn generate_custom_values(
@@ -60,8 +61,8 @@ fn generate_custom_value_fn(
let return_ty = type_gen
.resolve_type_path(custom_value.type_id())
.expect("type is in metadata; qed")
.to_token_stream();
let decodable = quote!(#crate_path::custom_values::Yes);
.to_token_stream(type_gen.settings());
let decodable = quote!(#crate_path::utils::Yes);
(return_ty, decodable)
} else {
// if type registry does not contain the type, we can just return the Encoded scale bytes.
+4 -1
View File
@@ -8,6 +8,7 @@ use scale_typegen::TypeGenerator;
use subxt_metadata::PalletMetadata;
use super::CodegenError;
use scale_typegen::typegen::ir::ToTokensWithSettings;
/// Generate error type alias from the provided pallet metadata.
pub fn generate_error_type_alias(
@@ -18,7 +19,9 @@ pub fn generate_error_type_alias(
return Ok(quote!());
};
let error_type = type_gen.resolve_type_path(error_ty)?;
let error_type = type_gen
.resolve_type_path(error_ty)?
.to_token_stream(type_gen.settings());
let error_ty = type_gen.resolve_type(error_ty)?;
let docs = &error_ty.docs;
let docs = type_gen
+9 -5
View File
@@ -2,13 +2,13 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use super::CodegenError;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use scale_typegen::typegen::ir::ToTokensWithSettings;
use scale_typegen::TypeGenerator;
use subxt_metadata::PalletMetadata;
use super::CodegenError;
/// Generate events from the provided pallet metadata.
///
/// The function creates a new module named `events` under the pallet's module.
@@ -37,7 +37,7 @@ use super::CodegenError;
///
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
/// - `pallet` - Pallet metadata from which the events are generated.
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
/// - `crate_path` - The crate path under which the `subxt-core` crate is located, e.g. `::subxt::ext::subxt_core` when using subxt as a dependency.
pub fn generate_events(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
@@ -56,7 +56,9 @@ pub fn generate_events(
let event_struct_name = &var.composite.name;
let event_name = var.variant_name;
let alias_mod = var.type_alias_mod;
let struct_def = type_gen.upcast_composite(&var.composite);
let struct_def = type_gen
.upcast_composite(&var.composite)
.to_token_stream(type_gen.settings());
quote! {
#struct_def
#alias_mod
@@ -68,7 +70,9 @@ pub fn generate_events(
}
});
let event_type = type_gen.resolve_type_path(event_ty)?;
let event_type = type_gen
.resolve_type_path(event_ty)?
.to_token_stream(type_gen.settings());
let event_ty = type_gen.resolve_type(event_ty)?;
let docs = &event_ty.docs;
let docs = type_gen
+18 -7
View File
@@ -13,6 +13,7 @@ mod runtime_apis;
mod storage;
use scale_typegen::typegen::ir::type_ir::{CompositeFieldIR, CompositeIR, CompositeIRKind};
use scale_typegen::typegen::ir::ToTokensWithSettings;
use scale_typegen::typegen::type_params::TypeParameters;
use scale_typegen::typegen::type_path::TypePath;
use scale_typegen::TypeGenerator;
@@ -44,7 +45,7 @@ impl RuntimeGenerator {
///
/// Supported versions: v14 and v15.
pub fn new(mut metadata: Metadata) -> Self {
metadata.ensure_unique_type_paths();
scale_typegen::utils::ensure_unique_type_paths(metadata.types_mut());
RuntimeGenerator { metadata }
}
@@ -72,7 +73,9 @@ impl RuntimeGenerator {
subxt_type_gen_settings(derives, type_substitutes, &crate_path, should_gen_docs);
let type_gen = TypeGenerator::new(self.metadata.types(), &settings);
let types_mod = type_gen.generate_types_mod()?;
let types_mod = type_gen
.generate_types_mod()?
.to_token_stream(type_gen.settings());
let mod_ident = &item_mod_ir.ident;
let rust_items = item_mod_ir.rust_items();
@@ -121,7 +124,9 @@ impl RuntimeGenerator {
subxt_type_gen_settings(derives, type_substitutes, &crate_path, should_gen_docs);
let type_gen = TypeGenerator::new(self.metadata.types(), &settings);
let types_mod = type_gen.generate_types_mod()?;
let types_mod = type_gen
.generate_types_mod()?
.to_token_stream(type_gen.settings());
let types_mod_ident = type_gen.types_mod_ident();
let pallets_with_mod_names = self
.metadata
@@ -214,9 +219,15 @@ impl RuntimeGenerator {
// Fetch the paths of the outer enums.
// Substrate exposes those under `kitchensink_runtime`, while Polkadot under `polkadot_runtime`.
let call_path = type_gen.resolve_type_path(self.metadata.outer_enums().call_enum_ty())?;
let event_path = type_gen.resolve_type_path(self.metadata.outer_enums().event_enum_ty())?;
let error_path = type_gen.resolve_type_path(self.metadata.outer_enums().error_enum_ty())?;
let call_path = type_gen
.resolve_type_path(self.metadata.outer_enums().call_enum_ty())?
.to_token_stream(type_gen.settings());
let event_path = type_gen
.resolve_type_path(self.metadata.outer_enums().event_enum_ty())?
.to_token_stream(type_gen.settings());
let error_path = type_gen
.resolve_type_path(self.metadata.outer_enums().error_enum_ty())?
.to_token_stream(type_gen.settings());
let custom_values = generate_custom_values(&self.metadata, &type_gen, &crate_path);
@@ -399,7 +410,7 @@ pub fn generate_type_alias_mod(
.expect("composite name in snake_case should be a valid identifier");
let mut modify_field_to_be_type_alias = |field: &mut CompositeFieldIR, alias_name: Ident| {
let type_path = &field.type_path;
let type_path = field.type_path.to_token_stream(type_gen.settings());
aliases.push(quote!(pub type #alias_name = #type_path;));
let type_alias_path: syn::Path = parse_quote!(#alias_mod_name::#alias_name);
+3 -2
View File
@@ -7,6 +7,7 @@ use std::collections::HashSet;
use heck::ToSnakeCase as _;
use heck::ToUpperCamelCase as _;
use scale_typegen::typegen::ir::ToTokensWithSettings;
use scale_typegen::TypeGenerator;
use subxt_metadata::{Metadata, RuntimeApiMetadata};
@@ -77,7 +78,7 @@ fn generate_runtime_api(
// Generate alias for runtime type.
let ty = type_gen
.resolve_type_path(input.ty)
.expect("runtime api input type is in metadata; qed");
.expect("runtime api input type is in metadata; qed").to_token_stream(type_gen.settings());
let aliased_param = quote!( pub type #alias_name = #ty; );
// Structures are placed on the same level as the alias module.
@@ -96,7 +97,7 @@ fn generate_runtime_api(
let type_aliases = inputs.iter().map(|(_, _, _, aliased_param)| aliased_param);
let types_mod_ident = type_gen.types_mod_ident();
let output = type_gen.resolve_type_path(method.output_ty())?;
let output = type_gen.resolve_type_path(method.output_ty())?.to_token_stream(type_gen.settings());
let aliased_module = quote!(
pub mod #method_name {
use super::#types_mod_ident;
+17 -10
View File
@@ -13,6 +13,8 @@ use subxt_metadata::{
use super::CodegenError;
use scale_typegen::typegen::ir::ToTokensWithSettings;
/// Generate functions which create storage addresses from the provided pallet's metadata.
/// These addresses can be used to access and iterate over storage values.
///
@@ -20,7 +22,7 @@ use super::CodegenError;
///
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
/// - `pallet` - Pallet metadata from which the storage items are generated.
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
/// - `crate_path` - The crate path under which the `subxt-core` crate is located, e.g. `::subxt::ext::subxt_core` when using subxt as a dependency.
pub fn generate_storage(
type_gen: &TypeGenerator,
pallet: &PalletMetadata,
@@ -69,7 +71,8 @@ fn generate_storage_entry_fns(
let storage_entry_ty = storage_entry.entry_type().value_ty();
let storage_entry_value_ty = type_gen
.resolve_type_path(storage_entry_ty)
.expect("storage type is in metadata; qed");
.expect("storage type is in metadata; qed")
.to_token_stream(type_gen.settings());
let alias_name = format_ident!("{}", storage_entry.name().to_upper_camel_case());
let alias_module_name = format_ident!("{snake_case_name}");
@@ -89,7 +92,7 @@ fn generate_storage_entry_fns(
.expect("type is in metadata; qed");
let alias_name = format_ident!("Param{}", idx);
let alias_type = primitive_type_alias(&ty_path);
let alias_type = primitive_type_alias(&ty_path, type_gen.settings());
let alias_type_def = quote!( pub type #alias_name = #alias_type; );
let alias_type_path = quote!( types::#alias_module_name::#alias_name );
@@ -169,7 +172,7 @@ fn generate_storage_entry_fns(
.unwrap_or_default();
let is_defaultable_type = match storage_entry.modifier() {
StorageEntryModifier::Default => quote!(#crate_path::storage::address::Yes),
StorageEntryModifier::Default => quote!(#crate_path::utils::Yes),
StorageEntryModifier::Optional => quote!(()),
};
@@ -191,10 +194,10 @@ fn generate_storage_entry_fns(
(fn_name, false, true)
};
let is_fetchable_type = is_fetchable
.then_some(quote!(#crate_path::storage::address::Yes))
.then_some(quote!(#crate_path::utils::Yes))
.unwrap_or(quote!(()));
let is_iterable_type = is_iterable
.then_some(quote!(#crate_path::storage::address::Yes))
.then_some(quote!(#crate_path::utils::Yes))
.unwrap_or(quote!(()));
let (keys, keys_type) = match keys_slice.len() {
@@ -247,7 +250,7 @@ fn generate_storage_entry_fns(
arg_name,
alias_type_path,
..
}| quote!( #arg_name: impl ::std::borrow::Borrow<#alias_type_path> ),
}| quote!( #arg_name: impl ::core::borrow::Borrow<#alias_type_path> ),
);
quote!(
@@ -297,16 +300,20 @@ fn generate_storage_entry_fns(
))
}
fn primitive_type_alias(type_path: &TypePath) -> TokenStream {
fn primitive_type_alias(
type_path: &TypePath,
settings: &scale_typegen::TypeGeneratorSettings,
) -> TokenStream {
// Vec<T> is cast to [T]
if let Some(ty) = type_path.vec_type_param() {
let ty = ty.to_token_stream(settings);
return quote!([#ty]);
}
// String is cast to str
if type_path.is_string() {
return quote!(::core::primitive::str);
}
quote!(#type_path)
type_path.to_token_stream(settings)
}
#[cfg(test)]
@@ -424,7 +431,7 @@ mod tests {
let expected_storage_constructor = quote!(
fn #name_ident(
&self,
_0: impl ::std::borrow::Borrow<types::#name_ident::Param0>,
_0: impl ::core::borrow::Borrow<types::#name_ident::Param0>,
)
);
dbg!(&generated_str);
+10 -5
View File
@@ -25,6 +25,7 @@ use getrandom as _;
use api::RuntimeGenerator;
use proc_macro2::TokenStream as TokenStream2;
use scale_typegen::typegen::settings::AllocCratePath;
use scale_typegen::{
typegen::settings::substitutes::absolute_path, DerivesRegistry, TypeGeneratorSettings,
TypeSubstitutes, TypegenError,
@@ -77,7 +78,7 @@ pub struct CodegenBuilder {
impl Default for CodegenBuilder {
fn default() -> Self {
CodegenBuilder {
crate_path: syn::parse_quote!(::subxt),
crate_path: syn::parse_quote!(::subxt::ext::subxt_core),
use_default_derives: true,
use_default_substitutions: true,
generate_docs: true,
@@ -222,12 +223,12 @@ impl CodegenBuilder {
self.item_mod = item_mod;
}
/// Set the path to the `subxt` crate. By default, we expect it to be at `::subxt`.
/// Set the path to the `subxt` crate. By default, we expect it to be at `::subxt::ext::subxt_core`.
pub fn set_subxt_crate_path(&mut self, crate_path: syn::Path) {
self.crate_path = crate_path;
}
/// Generate an interface, assuming that the default path to the `subxt` crate is `::subxt`.
/// Generate an interface, assuming that the default path to the `subxt` crate is `::subxt::ext::subxt_core`.
/// If the `subxt` crate is not available as a top level dependency, use `generate` and provide
/// a valid path to the `subxt¦ crate.
pub fn generate(self, metadata: Metadata) -> Result<TokenStream2, CodegenError> {
@@ -295,7 +296,7 @@ impl CodegenBuilder {
/// The default [`scale_typegen::TypeGeneratorSettings`], subxt is using for generating code.
/// Useful for emulating subxt's code generation settings from e.g. subxt-explorer.
pub fn default_subxt_type_gen_settings() -> TypeGeneratorSettings {
let crate_path: syn::Path = parse_quote!(::subxt);
let crate_path: syn::Path = parse_quote!(::subxt::ext::subxt_core);
let derives = default_derives(&crate_path);
let substitutes = default_substitutes(&crate_path);
subxt_type_gen_settings(derives, substitutes, &crate_path, true)
@@ -316,6 +317,7 @@ fn subxt_type_gen_settings(
compact_as_type_path: Some(parse_quote!(#crate_path::ext::codec::CompactAs)),
compact_type_path: Some(parse_quote!(#crate_path::ext::codec::Compact)),
insert_codec_attributes: true,
alloc_crate_path: AllocCratePath::Custom(parse_quote!(#crate_path::alloc)),
}
}
@@ -387,7 +389,10 @@ fn default_substitutes(crate_path: &syn::Path) -> TypeSubstitutes {
parse_quote!(BTreeMap),
parse_quote!(#crate_path::utils::KeyedVec),
),
(parse_quote!(BTreeSet), parse_quote!(::std::vec::Vec)),
(
parse_quote!(BTreeSet),
parse_quote!(#crate_path::alloc::vec::Vec),
),
// The `UncheckedExtrinsic(pub Vec<u8>)` is part of the runtime API calls.
// The inner bytes represent the encoded extrinsic, however when deriving the
// `EncodeAsType` the bytes would be re-encoded. This leads to the bytes
+82
View File
@@ -0,0 +1,82 @@
[package]
name = "subxt-core"
version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
publish = true
license.workspace = true
readme = "README.md"
repository.workspace = true
documentation.workspace = true
homepage.workspace = true
description = "A no-std compatible subset of Subxt's functionality"
keywords = ["parity", "subxt", "extrinsic", "no-std"]
[features]
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"scale-value/std",
"scale-bits/std",
"scale-decode/std",
"scale-encode/std",
"frame-metadata/std",
"subxt-metadata/std",
"hex/std",
"serde/std",
"serde_json/std",
"tracing/std",
"impl-serde/std",
"primitive-types/std",
]
substrate-compat = ["sp-core", "sp-runtime"]
[dependencies]
codec = { package = "parity-scale-codec", workspace = true, default-features = false, features = ["derive"] }
scale-info = { workspace = true, default-features = false, features = ["bit-vec"] }
scale-value = { workspace = true, default-features = false }
scale-bits = { workspace = true, default-features = false }
scale-decode = { workspace = true, default-features = false, features = ["derive", "primitive-types"] }
scale-encode = { workspace = true, default-features = false, features = ["derive", "primitive-types", "bits"] }
frame-metadata = { workspace = true, default-features = false }
subxt-metadata = { workspace = true, default-features = false }
derive-where = { workspace = true }
derive_more = { workspace = true }
hex = { workspace = true, default-features = false, features = ["alloc"] }
serde = { workspace = true, default-features = false, features = ["derive"] }
serde_json = { workspace = true, default-features = false, features = ["raw_value", "alloc"] }
hashbrown = { workspace = true }
# For ss58 encoding AccountId32 to serialize them properly:
base58 = { workspace = true }
blake2 = { workspace = true }
# Provides some deserialization, types like U256/H256 and hashing impls like twox/blake256:
impl-serde = { workspace = true, default-features = false }
primitive-types = { workspace = true, default-features = false, features = ["codec", "serde_no_std", "scale-info"] }
sp-crypto-hashing = { workspace = true }
# Included if the "substrate-compat" feature is enabled.
sp-core = { workspace = true, optional = true }
sp-runtime = { workspace = true, optional = true }
tracing = { workspace = true, default-features = false }
[dev-dependencies]
bitvec = { workspace = true }
codec = { workspace = true, features = ["derive", "bit-vec"] }
sp-core = { workspace = true }
sp-keyring = { workspace = true }
sp-runtime = { workspace = true }
[package.metadata.docs.rs]
defalt-features = true
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.playground]
defalt-features = true
+3
View File
@@ -0,0 +1,3 @@
# Subxt-Core
This library provides a no-std compatible subset of functionality that `subxt` and `subxt-signer` rely on.
+19
View File
@@ -0,0 +1,19 @@
use scale_decode::DecodeAsFields;
/// Trait to uniquely identify the extrinsic's identity from the runtime metadata.
///
/// Generated API structures that represent an extrinsic implement this trait.
///
/// The trait is utilized to decode emitted extrinsics from a block, via obtaining the
/// form of the `Extrinsic` from the metadata.
pub trait StaticExtrinsic: DecodeAsFields {
/// Pallet name.
const PALLET: &'static str;
/// Call name.
const CALL: &'static str;
/// Returns true if the given pallet and call names match this extrinsic.
fn is_extrinsic(pallet: &str, call: &str) -> bool {
Self::PALLET == pallet && Self::CALL == call
}
}
+71
View File
@@ -0,0 +1,71 @@
use crate::{config::Config, metadata::Metadata};
use derive_where::derive_where;
/// Each client should be able to provide access to the following fields
/// - runtime version
/// - genesis hash
/// - metadata
#[derive_where(Clone, Debug)]
pub struct ClientState<C: Config> {
genesis_hash: C::Hash,
runtime_version: RuntimeVersion,
metadata: Metadata,
}
impl<C: Config> ClientState<C> {
pub fn new(genesis_hash: C::Hash, runtime_version: RuntimeVersion, metadata: Metadata) -> Self {
Self {
genesis_hash,
runtime_version,
metadata,
}
}
pub fn metadata(&self) -> Metadata {
self.metadata.clone()
}
pub fn runtime_version(&self) -> RuntimeVersion {
self.runtime_version
}
pub fn genesis_hash(&self) -> C::Hash {
self.genesis_hash
}
}
/// Runtime version information needed to submit transactions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RuntimeVersion {
spec_version: u32,
transaction_version: u32,
}
impl RuntimeVersion {
pub fn new(spec_version: u32, transaction_version: u32) -> Self {
RuntimeVersion {
spec_version,
transaction_version,
}
}
/// Version of the runtime specification. A full-node will not attempt to use its native
/// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
/// `spec_version` and `authoring_version` are the same between Wasm and native.
pub fn spec_version(&self) -> u32 {
self.spec_version
}
/// All existing dispatches are fully compatible when this number doesn't change. If this
/// number changes, then `spec_version` must change, also.
///
/// This number must change when an existing dispatchable (module ID, dispatch ID) is changed,
/// either through an alteration in its user-level semantics, a parameter
/// added/removed/changed, a dispatchable being removed, a module being removed, or a
/// dispatchable/module changing its index.
///
/// It need *not* change when a new module is added or when a dispatchable is added.
pub fn transaction_version(&self) -> u32 {
self.transaction_version
}
}
@@ -7,46 +7,9 @@
//! [`crate::config::DefaultExtrinsicParams`] provides a general-purpose
//! implementation of this that will work in many cases.
use crate::{client::OfflineClientT, Config};
use core::fmt::Debug;
use super::refine_params::RefineParams;
/// An error that can be emitted when trying to construct an instance of [`ExtrinsicParams`],
/// encode data from the instance, or match on signed extensions.
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ExtrinsicParamsError {
/// Cannot find a type id in the metadata. The context provides some additional
/// information about the source of the error (eg the signed extension name).
#[error("Cannot find type id '{type_id} in the metadata (context: {context})")]
MissingTypeId {
/// Type ID.
type_id: u32,
/// Some arbitrary context to help narrow the source of the error.
context: &'static str,
},
/// A signed extension in use on some chain was not provided.
#[error("The chain expects a signed extension with the name {0}, but we did not provide one")]
UnknownSignedExtension(String),
/// Some custom error.
#[error("Error constructing extrinsic parameters: {0}")]
Custom(CustomExtrinsicParamsError),
}
/// A custom error.
pub type CustomExtrinsicParamsError = Box<dyn std::error::Error + Send + Sync + 'static>;
impl From<std::convert::Infallible> for ExtrinsicParamsError {
fn from(value: std::convert::Infallible) -> Self {
match value {}
}
}
impl From<CustomExtrinsicParamsError> for ExtrinsicParamsError {
fn from(value: CustomExtrinsicParamsError) -> Self {
ExtrinsicParamsError::Custom(value)
}
}
use crate::{client::ClientState, error::ExtrinsicParamsError, Config};
use alloc::vec::Vec;
/// This trait allows you to configure the "signed extra" and
/// "additional" parameters that are a part of the transaction payload
@@ -58,10 +21,7 @@ pub trait ExtrinsicParams<T: Config>: ExtrinsicParamsEncoder + Sized + 'static {
type Params: RefineParams<T>;
/// Construct a new instance of our [`ExtrinsicParams`].
fn new<Client: OfflineClientT<T>>(
client: Client,
params: Self::Params,
) -> Result<Self, ExtrinsicParamsError>;
fn new(client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError>;
}
/// This trait is expected to be implemented for any [`ExtrinsicParams`], and
@@ -1,4 +1,4 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// Copyright 2019-2024 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
@@ -24,7 +24,7 @@ use scale_encode::EncodeAsType;
use serde::{de::DeserializeOwned, Serialize};
pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder};
pub use extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError};
pub use extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder};
pub use polkadot::{PolkadotConfig, PolkadotExtrinsicParams, PolkadotExtrinsicParamsBuilder};
pub use refine_params::{RefineParams, RefineParamsData};
pub use signed_extensions::SignedExtension;
@@ -77,7 +77,7 @@ pub trait BlockHash:
+ Encode
+ PartialEq
+ Eq
+ std::hash::Hash
+ core::hash::Hash
{
}
impl<T> BlockHash for T where
@@ -92,7 +92,7 @@ impl<T> BlockHash for T where
+ Encode
+ PartialEq
+ Eq
+ std::hash::Hash
+ core::hash::Hash
{
}
@@ -6,8 +6,8 @@
use super::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder};
use crate::config::SubstrateConfig;
pub use crate::utils::{AccountId32, MultiAddress, MultiSignature};
use crate::SubstrateConfig;
pub use primitive_types::{H256, U256};
/// Default set of commonly used types by Polkadot nodes.
@@ -14,7 +14,9 @@ pub struct RefineParamsData<T: Config> {
}
impl<T: Config> RefineParamsData<T> {
pub(crate) fn new(account_nonce: u64, block_number: u64, block_hash: T::Hash) -> Self {
#[doc(hidden)]
/// Creates a new [`RefineParamsData`] instance. Called from `subxt` when refining signed extensions.
pub fn new(account_nonce: u64, block_number: u64, block_hash: T::Hash) -> Self {
RefineParamsData {
account_nonce,
block_number,
@@ -7,19 +7,24 @@
//! [`AnyOf`] to configure the set of signed extensions which are known about
//! when interacting with a chain.
use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError};
use super::extrinsic_params::ExtrinsicParams;
use super::refine_params::RefineParamsData;
use super::RefineParams;
use crate::client::ClientState;
use crate::config::ExtrinsicParamsEncoder;
use crate::error::ExtrinsicParamsError;
use crate::utils::Era;
use crate::{client::OfflineClientT, Config};
use crate::Config;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::vec::Vec;
use codec::{Compact, Encode};
use core::fmt::Debug;
use derivative::Derivative;
use derive_where::derive_where;
use hashbrown::HashMap;
use scale_decode::DecodeAsType;
use scale_info::PortableRegistry;
use std::collections::HashMap;
/// A single [`SignedExtension`] has a unique name, but is otherwise the
/// same as [`ExtrinsicParams`] in describing how to encode the extra and
/// additional data.
@@ -41,11 +46,8 @@ pub struct CheckSpecVersion(u32);
impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
type Params = ();
fn new<Client: OfflineClientT<T>>(
client: Client,
_params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckSpecVersion(client.runtime_version().spec_version))
fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckSpecVersion(client.runtime_version().spec_version()))
}
}
@@ -68,10 +70,7 @@ pub struct CheckNonce(Compact<u64>);
impl<T: Config> ExtrinsicParams<T> for CheckNonce {
type Params = CheckNonceParams;
fn new<Client: OfflineClientT<T>>(
_client: Client,
params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
// If no nonce is set (nor by user nor refinement), use a nonce of 0.
let nonce = params.0.unwrap_or(0);
Ok(CheckNonce(Compact(nonce)))
@@ -109,11 +108,10 @@ pub struct CheckTxVersion(u32);
impl<T: Config> ExtrinsicParams<T> for CheckTxVersion {
type Params = ();
fn new<Client: OfflineClientT<T>>(
client: Client,
_params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckTxVersion(client.runtime_version().transaction_version))
fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckTxVersion(
client.runtime_version().transaction_version(),
))
}
}
@@ -136,10 +134,7 @@ pub struct CheckGenesis<T: Config>(T::Hash);
impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
type Params = ();
fn new<Client: OfflineClientT<T>>(
client: Client,
_params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckGenesis(client.genesis_hash()))
}
}
@@ -210,10 +205,7 @@ impl<T: Config> CheckMortalityParams<T> {
impl<T: Config> ExtrinsicParams<T> for CheckMortality<T> {
type Params = CheckMortalityParams<T>;
fn new<Client: OfflineClientT<T>>(
client: Client,
params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
fn new(client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
let check_mortality = if let Some(params) = params.0 {
CheckMortality {
era: params.era,
@@ -246,8 +238,8 @@ impl<T: Config> SignedExtension<T> for CheckMortality<T> {
}
/// The [`ChargeAssetTxPayment`] signed extension.
#[derive(Derivative, DecodeAsType)]
#[derivative(Clone(bound = "T::AssetId: Clone"), Debug(bound = "T::AssetId: Debug"))]
#[derive(DecodeAsType)]
#[derive_where(Clone, Debug; T::AssetId)]
#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")]
pub struct ChargeAssetTxPayment<T: Config> {
tip: Compact<u128>,
@@ -308,10 +300,7 @@ impl<T: Config> ChargeAssetTxPaymentParams<T> {
impl<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment<T> {
type Params = ChargeAssetTxPaymentParams<T>;
fn new<Client: OfflineClientT<T>>(
_client: Client,
params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(ChargeAssetTxPayment {
tip: Compact(params.tip),
asset_id: params.asset_id,
@@ -367,10 +356,7 @@ impl ChargeTransactionPaymentParams {
impl<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
type Params = ChargeTransactionPaymentParams;
fn new<Client: OfflineClientT<T>>(
_client: Client,
params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(ChargeTransactionPayment {
tip: Compact(params.tip),
})
@@ -397,7 +383,7 @@ impl<T: Config> SignedExtension<T> for ChargeTransactionPayment {
/// is a sensible default, and allows for a single configuration to work across multiple chains.
pub struct AnyOf<T, Params> {
params: Vec<Box<dyn ExtrinsicParamsEncoder>>,
_marker: std::marker::PhantomData<(T, Params)>,
_marker: core::marker::PhantomData<(T, Params)>,
}
macro_rules! impl_tuples {
@@ -412,8 +398,8 @@ macro_rules! impl_tuples {
{
type Params = ($($ident::Params,)+);
fn new<Client: OfflineClientT<T>>(
client: Client,
fn new(
client: &ClientState<T>,
params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
let metadata = client.metadata();
@@ -430,7 +416,7 @@ macro_rules! impl_tuples {
}
// Break and record as soon as we find a match:
if $ident::matches(e.identifier(), e.extra_ty(), types) {
let ext = $ident::new(client.clone(), params.$index)?;
let ext = $ident::new(client, params.$index)?;
let boxed_ext: Box<dyn ExtrinsicParamsEncoder> = Box::new(ext);
exts_by_index.insert(idx, boxed_ext);
break
@@ -453,7 +439,7 @@ macro_rules! impl_tuples {
Ok(AnyOf {
params,
_marker: std::marker::PhantomData
_marker: core::marker::PhantomData
})
}
}
@@ -5,6 +5,8 @@
//! Substrate specific configuration
use super::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder, Hasher, Header};
use alloc::format;
use alloc::vec::Vec;
use codec::{Decode, Encode};
use serde::{Deserialize, Serialize};
@@ -225,12 +227,69 @@ where
{
// At the time of writing, Smoldot gives back block numbers in numeric rather
// than hex format. So let's support deserializing from both here:
use crate::backend::legacy::rpc_methods::NumberOrHex;
let number_or_hex = NumberOrHex::deserialize(d)?;
let u256 = number_or_hex.into_u256();
TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed"))
}
/// A number type that can be serialized both as a number or a string that encodes a number in a
/// string.
///
/// We allow two representations of the block number as input. Either we deserialize to the type
/// that is specified in the block type or we attempt to parse given hex value.
///
/// The primary motivation for having this type is to avoid overflows when using big integers in
/// JavaScript (which we consider as an important RPC API consumer).
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum NumberOrHex {
/// The number represented directly.
Number(u64),
/// Hex representation of the number.
Hex(U256),
}
impl NumberOrHex {
/// Converts this number into an U256.
pub fn into_u256(self) -> U256 {
match self {
NumberOrHex::Number(n) => n.into(),
NumberOrHex::Hex(h) => h,
}
}
}
impl From<NumberOrHex> for U256 {
fn from(num_or_hex: NumberOrHex) -> U256 {
num_or_hex.into_u256()
}
}
macro_rules! into_number_or_hex {
($($t: ty)+) => {
$(
impl From<$t> for NumberOrHex {
fn from(x: $t) -> Self {
NumberOrHex::Number(x.into())
}
}
)+
}
}
into_number_or_hex!(u8 u16 u32 u64);
impl From<u128> for NumberOrHex {
fn from(n: u128) -> Self {
NumberOrHex::Hex(n.into())
}
}
impl From<U256> for NumberOrHex {
fn from(n: U256) -> Self {
NumberOrHex::Hex(n)
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -2,9 +2,11 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::{dynamic::DecodedValueThunk, metadata::DecodeWithMetadata};
use derivative::Derivative;
use std::borrow::Cow;
use crate::dynamic::DecodedValueThunk;
use crate::metadata::DecodeWithMetadata;
use alloc::borrow::Cow;
use alloc::string::String;
use derive_where::derive_where;
/// This represents a constant address. Anything implementing this trait
/// can be used to fetch constants.
@@ -27,26 +29,12 @@ pub trait ConstantAddress {
}
/// This represents the address of a constant.
#[derive(Derivative)]
#[derivative(
Clone(bound = ""),
Debug(bound = ""),
Eq(bound = ""),
Ord(bound = ""),
PartialEq(bound = "")
)]
#[derive_where(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Address<ReturnTy> {
pallet_name: Cow<'static, str>,
constant_name: Cow<'static, str>,
constant_hash: Option<[u8; 32]>,
_marker: std::marker::PhantomData<ReturnTy>,
}
// Manual implementation to work around https://github.com/mcarton/rust-derivative/issues/115.
impl<ReturnTy> PartialOrd for Address<ReturnTy> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
_marker: core::marker::PhantomData<ReturnTy>,
}
/// The type of address typically used to return dynamic constant values.
@@ -59,7 +47,7 @@ impl<ReturnTy> Address<ReturnTy> {
pallet_name: Cow::Owned(pallet_name.into()),
constant_name: Cow::Owned(constant_name.into()),
constant_hash: None,
_marker: std::marker::PhantomData,
_marker: core::marker::PhantomData,
}
}
@@ -75,7 +63,7 @@ impl<ReturnTy> Address<ReturnTy> {
pallet_name: Cow::Borrowed(pallet_name),
constant_name: Cow::Borrowed(constant_name),
constant_hash: Some(hash),
_marker: std::marker::PhantomData,
_marker: core::marker::PhantomData,
}
}
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Types associated with accessing constants.
mod constant_address;
pub use constant_address::{dynamic, Address, ConstantAddress, DynamicAddress};
use alloc::borrow::ToOwned;
use crate::{
error::MetadataError,
metadata::{DecodeWithMetadata, MetadataExt},
Error, Metadata,
};
/// Run validation logic against some constant address you'd like to access. Returns `Ok(())`
/// if the address is valid (or if it's not possible to check since the address has no validation hash).
/// Return an error if the address was not valid or something went wrong trying to validate it (ie
/// the pallet or constant in question do not exist at all).
pub fn validate_constant<Address: ConstantAddress>(
metadata: &subxt_metadata::Metadata,
address: &Address,
) -> Result<(), Error> {
if let Some(actual_hash) = address.validation_hash() {
let expected_hash = metadata
.pallet_by_name_err(address.pallet_name())?
.constant_hash(address.constant_name())
.ok_or_else(|| {
MetadataError::ConstantNameNotFound(address.constant_name().to_owned())
})?;
if actual_hash != expected_hash {
return Err(MetadataError::IncompatibleCodegen.into());
}
}
Ok(())
}
pub fn get_constant<Address: ConstantAddress>(
metadata: &Metadata,
address: &Address,
) -> Result<Address::Target, Error> {
// 1. Validate constant shape if hash given:
validate_constant(metadata, address)?;
// 2. Attempt to decode the constant into the type given:
let constant = metadata
.pallet_by_name_err(address.pallet_name())?
.constant_by_name(address.constant_name())
.ok_or_else(|| MetadataError::ConstantNameNotFound(address.constant_name().to_owned()))?;
let value = <Address::Target as DecodeWithMetadata>::decode_with_metadata(
&mut constant.value(),
constant.ty(),
metadata,
)?;
Ok(value)
}
@@ -1,8 +1,8 @@
use derivative::Derivative;
use std::marker::PhantomData;
use derive_where::derive_where;
use crate::dynamic::DecodedValueThunk;
use crate::metadata::DecodeWithMetadata;
use crate::utils::Yes;
/// This represents the address of a custom value in in the metadata.
/// Anything, that implements the [CustomValueAddress] trait can be used, to fetch
@@ -33,28 +33,12 @@ impl CustomValueAddress for str {
}
}
/// Used to signal whether a [`CustomValueAddress`] can be decoded.
pub struct Yes;
/// A static address to a custom value.
#[derive(Derivative)]
#[derivative(
Clone(bound = ""),
Debug(bound = ""),
Eq(bound = ""),
Ord(bound = ""),
PartialEq(bound = "")
)]
#[derive_where(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct StaticAddress<ReturnTy, IsDecodable> {
name: &'static str,
hash: Option<[u8; 32]>,
phantom: PhantomData<(ReturnTy, IsDecodable)>,
}
impl<ReturnTy, IsDecodable> PartialOrd for StaticAddress<ReturnTy, IsDecodable> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
phantom: core::marker::PhantomData<(ReturnTy, IsDecodable)>,
}
impl<ReturnTy, IsDecodable> StaticAddress<ReturnTy, IsDecodable> {
@@ -64,7 +48,7 @@ impl<ReturnTy, IsDecodable> StaticAddress<ReturnTy, IsDecodable> {
StaticAddress::<ReturnTy, IsDecodable> {
name,
hash: Some(hash),
phantom: PhantomData,
phantom: core::marker::PhantomData,
}
}
+156
View File
@@ -0,0 +1,156 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Types associated with accessing custom types
mod custom_value_address;
use crate::utils::Yes;
pub use custom_value_address::{CustomValueAddress, StaticAddress};
use crate::{
error::MetadataError,
metadata::{DecodeWithMetadata, MetadataExt},
Error, Metadata,
};
use alloc::vec::Vec;
/// Run the validation logic against some custom value address you'd like to access. Returns `Ok(())`
/// if the address is valid (or if it's not possible to check since the address has no validation hash).
/// Returns an error if the address was not valid (wrong name, type or raw bytes)
pub fn validate_custom_value<Address: CustomValueAddress + ?Sized>(
metadata: &Metadata,
address: &Address,
) -> Result<(), Error> {
if let Some(actual_hash) = address.validation_hash() {
let custom = metadata.custom();
let custom_value = custom
.get(address.name())
.ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().into()))?;
let expected_hash = custom_value.hash();
if actual_hash != expected_hash {
return Err(MetadataError::IncompatibleCodegen.into());
}
}
if metadata.custom().get(address.name()).is_none() {
return Err(MetadataError::IncompatibleCodegen.into());
}
Ok(())
}
/// Access a custom value by the address it is registered under. This can be just a [str] to get back a dynamic value,
/// or a static address from the generated static interface to get a value of a static type returned.
pub fn get_custom_value<Address: CustomValueAddress<IsDecodable = Yes> + ?Sized>(
metadata: &Metadata,
address: &Address,
) -> Result<Address::Target, Error> {
// 1. Validate custom value shape if hash given:
validate_custom_value(metadata, address)?;
// 2. Attempt to decode custom value:
let custom_value = metadata.custom_value_by_name_err(address.name())?;
let value = <Address::Target as DecodeWithMetadata>::decode_with_metadata(
&mut custom_value.bytes(),
custom_value.type_id(),
metadata,
)?;
Ok(value)
}
/// Access the bytes of a custom value by the address it is registered under.
pub fn get_custom_value_bytes<Address: CustomValueAddress + ?Sized>(
metadata: &Metadata,
address: &Address,
) -> Result<Vec<u8>, Error> {
// 1. Validate custom value shape if hash given:
validate_custom_value(metadata, address)?;
// 2. Return the underlying bytes:
let custom_value = metadata.custom_value_by_name_err(address.name())?;
Ok(custom_value.bytes().to_vec())
}
#[cfg(test)]
mod tests {
use alloc::collections::BTreeMap;
use codec::Encode;
use scale_decode::DecodeAsType;
use scale_info::form::PortableForm;
use scale_info::TypeInfo;
use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec;
use crate::custom_values::get_custom_value;
use crate::Metadata;
#[derive(Debug, Clone, PartialEq, Eq, Encode, TypeInfo, DecodeAsType)]
pub struct Person {
age: u16,
name: String,
}
fn mock_metadata() -> Metadata {
let person_ty = scale_info::MetaType::new::<Person>();
let unit = scale_info::MetaType::new::<()>();
let mut types = scale_info::Registry::new();
let person_ty_id = types.register_type(&person_ty);
let unit_id = types.register_type(&unit);
let types: scale_info::PortableRegistry = types.into();
let person = Person {
age: 42,
name: "Neo".into(),
};
let person_value_metadata: frame_metadata::v15::CustomValueMetadata<PortableForm> =
frame_metadata::v15::CustomValueMetadata {
ty: person_ty_id,
value: person.encode(),
};
let frame_metadata = frame_metadata::v15::RuntimeMetadataV15 {
types,
pallets: vec![],
extrinsic: frame_metadata::v15::ExtrinsicMetadata {
version: 0,
address_ty: unit_id,
call_ty: unit_id,
signature_ty: unit_id,
extra_ty: unit_id,
signed_extensions: vec![],
},
ty: unit_id,
apis: vec![],
outer_enums: frame_metadata::v15::OuterEnums {
call_enum_ty: unit_id,
event_enum_ty: unit_id,
error_enum_ty: unit_id,
},
custom: frame_metadata::v15::CustomMetadata {
map: BTreeMap::from_iter([("Mr. Robot".to_owned(), person_value_metadata)]),
},
};
let metadata: subxt_metadata::Metadata = frame_metadata.try_into().unwrap();
Metadata::new(metadata)
}
#[test]
fn test_decoding() {
let metadata = mock_metadata();
assert!(get_custom_value(&metadata, "Invalid Address").is_err());
let person_decoded_value_thunk = get_custom_value(&metadata, "Mr. Robot").unwrap();
let person: Person = person_decoded_value_thunk.as_type().unwrap();
assert_eq!(
person,
Person {
age: 42,
name: "Neo".into()
}
)
}
}
+4 -7
View File
@@ -5,12 +5,9 @@
//! This module provides the entry points to create dynamic
//! transactions, storage and constant lookups.
use crate::{
error::Error,
metadata::{DecodeWithMetadata, Metadata},
};
use crate::metadata::{DecodeWithMetadata, Metadata};
use alloc::vec::Vec;
use scale_decode::DecodeAsType;
pub use scale_value::{At, Value};
/// A [`scale_value::Value`] type endowed with contextual information
@@ -45,7 +42,7 @@ impl DecodeWithMetadata for DecodedValueThunk {
bytes: &mut &[u8],
type_id: u32,
metadata: &Metadata,
) -> Result<Self, Error> {
) -> Result<Self, scale_decode::Error> {
let mut v = Vec::with_capacity(bytes.len());
v.extend_from_slice(bytes);
*bytes = &[];
@@ -67,7 +64,7 @@ impl DecodedValueThunk {
&self.scale_bytes
}
/// Decode the SCALE encoded storage entry into a dynamic [`DecodedValue`] type.
pub fn to_value(&self) -> Result<DecodedValue, Error> {
pub fn to_value(&self) -> Result<DecodedValue, scale_decode::Error> {
let val = scale_value::scale::decode_as_type(
&mut &*self.scale_bytes,
&self.type_id,
+184
View File
@@ -0,0 +1,184 @@
use alloc::boxed::Box;
use alloc::string::String;
use derive_more::{Display, From};
use subxt_metadata::StorageHasher;
#[derive(Debug, Display, From)]
pub enum Error {
/// Codec error.
#[display(fmt = "Scale codec error: {_0}")]
Codec(codec::Error),
#[display(fmt = "Metadata Error: {_0}")]
Metadata(MetadataError),
#[display(fmt = "Storage Error: {_0}")]
StorageAddress(StorageAddressError),
/// Error decoding to a [`crate::dynamic::Value`].
#[display(fmt = "Error decoding into dynamic value: {_0}")]
Decode(scale_decode::Error),
/// Error encoding from a [`crate::dynamic::Value`].
#[display(fmt = "Error encoding from dynamic value: {_0}")]
Encode(scale_encode::Error),
/// Error constructing the appropriate extrinsic params.
#[display(fmt = "Extrinsic params error: {_0}")]
ExtrinsicParams(ExtrinsicParamsError),
}
impl From<scale_decode::visitor::DecodeError> for Error {
fn from(value: scale_decode::visitor::DecodeError) -> Self {
Error::Decode(value.into())
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
/// Something went wrong trying to access details in the metadata.
#[derive(Clone, Debug, PartialEq, Display)]
#[non_exhaustive]
pub enum MetadataError {
/// The DispatchError type isn't available in the metadata
#[display(fmt = "The DispatchError type isn't available")]
DispatchErrorNotFound,
/// Type not found in metadata.
#[display(fmt = "Type with ID {_0} not found")]
TypeNotFound(u32),
/// Pallet not found (index).
#[display(fmt = "Pallet with index {_0} not found")]
PalletIndexNotFound(u8),
/// Pallet not found (name).
#[display(fmt = "Pallet with name {_0} not found")]
PalletNameNotFound(String),
/// Variant not found.
#[display(fmt = "Variant with index {_0} not found")]
VariantIndexNotFound(u8),
/// Constant not found.
#[display(fmt = "Constant with name {_0} not found")]
ConstantNameNotFound(String),
/// Call not found.
#[display(fmt = "Call with name {_0} not found")]
CallNameNotFound(String),
/// Runtime trait not found.
#[display(fmt = "Runtime trait with name {_0} not found")]
RuntimeTraitNotFound(String),
/// Runtime method not found.
#[display(fmt = "Runtime method with name {_0} not found")]
RuntimeMethodNotFound(String),
/// Call type not found in metadata.
#[display(fmt = "Call type not found in pallet with index {_0}")]
CallTypeNotFoundInPallet(u8),
/// Event type not found in metadata.
#[display(fmt = "Event type not found in pallet with index {_0}")]
EventTypeNotFoundInPallet(u8),
/// Storage details not found in metadata.
#[display(fmt = "Storage details not found in pallet with name {_0}")]
StorageNotFoundInPallet(String),
/// Storage entry not found.
#[display(fmt = "Storage entry {_0} not found")]
StorageEntryNotFound(String),
/// The generated interface used is not compatible with the node.
#[display(fmt = "The generated code is not compatible with the node")]
IncompatibleCodegen,
/// Custom value not found.
#[display(fmt = "Custom value with name {_0} not found")]
CustomValueNameNotFound(String),
}
#[cfg(feature = "std")]
impl std::error::Error for MetadataError {}
/// Something went wrong trying to encode or decode a storage address.
#[derive(Clone, Debug, Display)]
#[non_exhaustive]
pub enum StorageAddressError {
/// Storage lookup does not have the expected number of keys.
#[display(fmt = "Storage lookup requires {expected} keys but more keys have been provided.")]
TooManyKeys {
/// The number of keys provided in the storage address.
expected: usize,
},
/// This storage entry in the metadata does not have the correct number of hashers to fields.
#[display(
fmt = "Storage entry in metadata does not have the correct number of hashers to fields"
)]
WrongNumberOfHashers {
/// The number of hashers in the metadata for this storage entry.
hashers: usize,
/// The number of fields in the metadata for this storage entry.
fields: usize,
},
/// We weren't given enough bytes to decode the storage address/key.
#[display(fmt = "Not enough remaining bytes to decode the storage address/key")]
NotEnoughBytes,
/// We have leftover bytes after decoding the storage address.
#[display(fmt = "We have leftover bytes after decoding the storage address")]
TooManyBytes,
/// The bytes of a storage address are not the expected address for decoding the storage keys of the address.
#[display(
fmt = "Storage address bytes are not the expected format. Addresses need to be at least 16 bytes (pallet ++ entry) and follow a structure given by the hashers defined in the metadata"
)]
UnexpectedAddressBytes,
/// An invalid hasher was used to reconstruct a value from a chunk of bytes that is part of a storage address. Hashers where the hash does not contain the original value are invalid for this purpose.
#[display(
fmt = "An invalid hasher was used to reconstruct a value with type ID {ty_id} from a hash formed by a {hasher:?} hasher. This is only possible for concat-style hashers or the identity hasher"
)]
HasherCannotReconstructKey {
/// Type id of the key's type.
ty_id: u32,
/// The invalid hasher that caused this error.
hasher: StorageHasher,
},
}
#[cfg(feature = "std")]
impl std::error::Error for StorageAddressError {}
/// An error that can be emitted when trying to construct an instance of [`crate::config::ExtrinsicParams`],
/// encode data from the instance, or match on signed extensions.
#[derive(Display, Debug)]
#[non_exhaustive]
pub enum ExtrinsicParamsError {
/// Cannot find a type id in the metadata. The context provides some additional
/// information about the source of the error (eg the signed extension name).
#[display(fmt = "Cannot find type id '{type_id} in the metadata (context: {context})")]
MissingTypeId {
/// Type ID.
type_id: u32,
/// Some arbitrary context to help narrow the source of the error.
context: &'static str,
},
/// A signed extension in use on some chain was not provided.
#[display(
fmt = "The chain expects a signed extension with the name {_0}, but we did not provide one"
)]
UnknownSignedExtension(String),
/// Some custom error.
#[display(fmt = "Error constructing extrinsic parameters: {_0}")]
Custom(Box<dyn CustomError>),
}
/// Anything implementing this trait can be used in [`ExtrinsicParamsError::Custom`].
#[cfg(feature = "std")]
pub trait CustomError: std::error::Error + Send + Sync + 'static {}
#[cfg(feature = "std")]
impl<T: std::error::Error + Send + Sync + 'static> CustomError for T {}
/// Anything implementing this trait can be used in [`ExtrinsicParamsError::Custom`].
#[cfg(not(feature = "std"))]
pub trait CustomError: core::fmt::Debug + core::fmt::Display + Send + Sync + 'static {}
#[cfg(not(feature = "std"))]
impl<T: core::fmt::Debug + core::fmt::Display + Send + Sync + 'static> CustomError for T {}
#[cfg(feature = "std")]
impl std::error::Error for ExtrinsicParamsError {}
impl From<core::convert::Infallible> for ExtrinsicParamsError {
fn from(value: core::convert::Infallible) -> Self {
match value {}
}
}
impl From<Box<dyn CustomError>> for ExtrinsicParamsError {
fn from(value: Box<dyn CustomError>) -> Self {
ExtrinsicParamsError::Custom(value)
}
}
@@ -1,42 +1,48 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use alloc::sync::Arc;
use alloc::vec::Vec;
use codec::{Compact, Decode, Encode};
use derive_where::derive_where;
use scale_decode::{DecodeAsFields, DecodeAsType};
use subxt_metadata::PalletMetadata;
//! A representation of a block of events.
use crate::{error::MetadataError, Config, Error, Metadata};
use super::{Phase, StaticEvent};
use crate::{
client::OnlineClientT,
error::{Error, MetadataError},
events::events_client::get_event_bytes,
metadata::types::PalletMetadata,
Config, Metadata,
};
use codec::{Compact, Decode};
use derivative::Derivative;
use scale_decode::DecodeAsType;
use std::sync::Arc;
/// Trait to uniquely identify the events's identity from the runtime metadata.
///
/// Generated API structures that represent an event implement this trait.
///
/// The trait is utilized to decode emitted events from a block, via obtaining the
/// form of the `Event` from the metadata.
pub trait StaticEvent: DecodeAsFields {
/// Pallet name.
const PALLET: &'static str;
/// Event name.
const EVENT: &'static str;
/// Returns true if the given pallet and event names match this event.
fn is_event(pallet: &str, event: &str) -> bool {
Self::PALLET == pallet && Self::EVENT == event
}
}
/// A collection of events obtained from a block, bundled with the necessary
/// information needed to decode and iterate over them.
#[derive(Derivative)]
#[derivative(Clone(bound = ""))]
#[derive_where(Clone)]
pub struct Events<T: Config> {
metadata: Metadata,
block_hash: T::Hash,
// Note; raw event bytes are prefixed with a Compact<u32> containing
// the number of events to be decoded. The start_idx reflects that, so
// that we can skip over those bytes when decoding them
event_bytes: Arc<[u8]>,
start_idx: usize,
num_events: u32,
marker: core::marker::PhantomData<T>,
}
// Ignore the Metadata when debug-logging events; it's big and distracting.
impl<T: Config> std::fmt::Debug for Events<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<T: Config> core::fmt::Debug for Events<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Events")
.field("block_hash", &self.block_hash)
.field("event_bytes", &self.event_bytes)
.field("start_idx", &self.start_idx)
.field("num_events", &self.num_events)
@@ -45,7 +51,8 @@ impl<T: Config> std::fmt::Debug for Events<T> {
}
impl<T: Config> Events<T> {
pub(crate) fn new(metadata: Metadata, block_hash: T::Hash, event_bytes: Vec<u8>) -> Self {
/// Create a new [`Events`] instance from the given bytes.
pub fn decode_from(metadata: Metadata, event_bytes: Vec<u8>) -> Self {
// event_bytes is a SCALE encoded vector of events. So, pluck the
// compact encoded length from the front, leaving the remaining bytes
// for our iterating to decode.
@@ -60,34 +67,13 @@ impl<T: Config> Events<T> {
Self {
metadata,
block_hash,
event_bytes: event_bytes.into(),
start_idx,
num_events,
marker: core::marker::PhantomData,
}
}
/// Obtain the events from a block hash given custom metadata and a client.
///
/// # Notes
///
/// - Prefer to use [`crate::events::EventsClient::at`] to obtain the events.
/// - Subxt may fail to decode things that aren't from a runtime using the
/// latest metadata version.
/// - The client may not be able to obtain the block at the given hash. Only
/// archive nodes keep hold of all past block information.
pub async fn new_from_client<Client>(
metadata: Metadata,
block_hash: T::Hash,
client: Client,
) -> Result<Self, Error>
where
Client: OnlineClientT<T>,
{
let event_bytes = get_event_bytes(client.backend(), block_hash).await?;
Ok(Events::new(metadata, block_hash, event_bytes))
}
/// The number of events.
pub fn len(&self) -> u32 {
self.num_events
@@ -99,11 +85,6 @@ impl<T: Config> Events<T> {
self.num_events == 0
}
/// Return the block hash that these events are from.
pub fn block_hash(&self) -> T::Hash {
self.block_hash
}
/// Iterate over all of the events, using metadata to dynamically
/// decode them as we go, and returning the raw bytes and other associated
/// details. If an error occurs, all subsequent iterations return `None`.
@@ -119,7 +100,7 @@ impl<T: Config> Events<T> {
let mut pos = self.start_idx;
let mut index = 0;
std::iter::from_fn(move || {
core::iter::from_fn(move || {
if event_bytes.len() <= pos || num_events == index {
None
} else {
@@ -172,6 +153,17 @@ impl<T: Config> Events<T> {
}
}
/// A phase of a block's execution.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Decode, Encode)]
pub enum Phase {
/// Applying an extrinsic.
ApplyExtrinsic(u32),
/// Finalizing the block.
Finalization,
/// Initializing the block.
Initialization,
}
/// The event details.
#[derive(Debug, Clone)]
pub struct EventDetails<T: Config> {
@@ -194,7 +186,7 @@ pub struct EventDetails<T: Config> {
}
impl<T: Config> EventDetails<T> {
// Attempt to dynamically decode a single event from our events input.
/// Attempt to dynamically decode a single event from our events input.
fn decode_from(
metadata: Metadata,
all_bytes: Arc<[u8]>,
@@ -284,12 +276,12 @@ impl<T: Config> EventDetails<T> {
/// The name of the pallet from whence the Event originated.
pub fn pallet_name(&self) -> &str {
self.event_metadata().pallet.name()
self.event_metadata().pallet().name()
}
/// The name of the event (ie the name of the variant that it corresponds to).
pub fn variant_name(&self) -> &str {
&self.event_metadata().variant.name
&self.event_metadata().variant().name
}
/// Fetch details from the metadata for this event.
@@ -378,15 +370,24 @@ impl<T: Config> EventDetails<T> {
/// Details for the given event plucked from the metadata.
pub struct EventMetadataDetails<'a> {
pub pallet: PalletMetadata<'a>,
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
pallet: PalletMetadata<'a>,
variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
}
impl<'a> EventMetadataDetails<'a> {
pub fn pallet(&self) -> PalletMetadata<'a> {
self.pallet
}
pub fn variant(&self) -> &'a scale_info::Variant<scale_info::form::PortableForm> {
self.variant
}
}
/// Event related test utilities used outside this module.
#[cfg(test)]
pub(crate) mod test_utils {
use super::*;
use crate::{Config, SubstrateConfig};
use crate::config::{Config, SubstrateConfig};
use codec::Encode;
use frame_metadata::{
v15::{
@@ -531,11 +532,7 @@ pub(crate) mod test_utils {
// Prepend compact encoded length to event bytes:
let mut all_event_bytes = Compact(num_events).encode();
all_event_bytes.extend(event_bytes);
Events::new(
metadata,
<SubstrateConfig as Config>::Hash::default(),
all_event_bytes,
)
Events::decode_from(metadata, all_event_bytes)
}
}
@@ -545,7 +542,8 @@ mod tests {
test_utils::{event_record, events, events_raw, AllEvents, EventRecord},
*,
};
use crate::SubstrateConfig;
use crate::config::SubstrateConfig;
use crate::events::Phase;
use codec::Encode;
use primitive_types::H256;
use scale_info::TypeInfo;
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2019-2024 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Subxt-core
//!
//! `#[no_std]` compatible core crate for subxt.
#![cfg_attr(not(feature = "std"), no_std)]
pub extern crate alloc;
pub mod blocks;
pub mod client;
pub mod config;
pub mod constants;
pub mod custom_values;
pub mod dynamic;
pub mod error;
pub mod events;
pub mod metadata;
pub mod runtime_api;
pub mod storage;
pub mod tx;
pub mod utils;
pub use config::Config;
pub use error::Error;
pub use metadata::Metadata;
#[macro_use]
mod macros;
pub mod ext {
pub use codec;
pub use scale_decode;
pub use scale_encode;
cfg_substrate_compat! {
pub use sp_runtime;
pub use sp_core;
}
}
+17
View File
@@ -0,0 +1,17 @@
macro_rules! cfg_feature {
($feature:literal, $($item:item)*) => {
$(
#[cfg(feature = $feature)]
#[cfg_attr(docsrs, doc(cfg(feature = $feature)))]
$item
)*
}
}
macro_rules! cfg_substrate_compat {
($($item:item)*) => {
crate::macros::cfg_feature!("substrate-compat", $($item)*);
};
}
pub(crate) use {cfg_feature, cfg_substrate_compat};
@@ -3,7 +3,8 @@
// see LICENSE for license details.
use super::Metadata;
use crate::error::Error;
use alloc::vec::Vec;
/// This trait is implemented for all types that also implement [`scale_decode::DecodeAsType`].
pub trait DecodeWithMetadata: Sized {
@@ -12,7 +13,7 @@ pub trait DecodeWithMetadata: Sized {
bytes: &mut &[u8],
type_id: u32,
metadata: &Metadata,
) -> Result<Self, Error>;
) -> Result<Self, scale_decode::Error>;
}
impl<T: scale_decode::DecodeAsType> DecodeWithMetadata for T {
@@ -20,7 +21,7 @@ impl<T: scale_decode::DecodeAsType> DecodeWithMetadata for T {
bytes: &mut &[u8],
type_id: u32,
metadata: &Metadata,
) -> Result<T, Error> {
) -> Result<T, scale_decode::Error> {
let val = T::decode_as_type(bytes, &type_id, metadata.types())?;
Ok(val)
}
@@ -34,7 +35,7 @@ pub trait EncodeWithMetadata {
type_id: u32,
metadata: &Metadata,
bytes: &mut Vec<u8>,
) -> Result<(), Error>;
) -> Result<(), scale_encode::Error>;
}
impl<T: scale_encode::EncodeAsType> EncodeWithMetadata for T {
@@ -44,7 +45,7 @@ impl<T: scale_encode::EncodeAsType> EncodeWithMetadata for T {
type_id: u32,
metadata: &Metadata,
bytes: &mut Vec<u8>,
) -> Result<(), Error> {
) -> Result<(), scale_encode::Error> {
self.encode_as_type_to(&type_id, metadata.types(), bytes)?;
Ok(())
}
+137
View File
@@ -0,0 +1,137 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::error::MetadataError;
use alloc::borrow::ToOwned;
use alloc::sync::Arc;
/// A cheaply clone-able representation of the runtime metadata received from a node.
#[derive(Clone, Debug)]
pub struct Metadata {
inner: Arc<subxt_metadata::Metadata>,
}
impl core::ops::Deref for Metadata {
type Target = subxt_metadata::Metadata;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl Metadata {
pub fn new(md: subxt_metadata::Metadata) -> Self {
Metadata {
inner: Arc::new(md),
}
}
/// Identical to `metadata.pallet_by_name()`, but returns an error if the pallet is not found.
pub fn pallet_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
self.pallet_by_name(name)
.ok_or_else(|| MetadataError::PalletNameNotFound(name.to_owned()))
}
/// Identical to `metadata.pallet_by_index()`, but returns an error if the pallet is not found.
pub fn pallet_by_index_err(
&self,
index: u8,
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
self.pallet_by_index(index)
.ok_or(MetadataError::PalletIndexNotFound(index))
}
/// Identical to `metadata.runtime_api_trait_by_name()`, but returns an error if the trait is not found.
pub fn runtime_api_trait_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::RuntimeApiMetadata, MetadataError> {
self.runtime_api_trait_by_name(name)
.ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned()))
}
}
impl From<subxt_metadata::Metadata> for Metadata {
fn from(md: subxt_metadata::Metadata) -> Self {
Metadata::new(md)
}
}
impl TryFrom<frame_metadata::RuntimeMetadataPrefixed> for Metadata {
type Error = subxt_metadata::TryFromError;
fn try_from(value: frame_metadata::RuntimeMetadataPrefixed) -> Result<Self, Self::Error> {
subxt_metadata::Metadata::try_from(value).map(Metadata::from)
}
}
impl codec::Decode for Metadata {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
subxt_metadata::Metadata::decode(input).map(Metadata::new)
}
}
/// Some extension methods on [`subxt_metadata::Metadata`] that return Errors instead of Options.
pub trait MetadataExt {
fn pallet_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::PalletMetadata, MetadataError>;
fn pallet_by_index_err(
&self,
index: u8,
) -> Result<subxt_metadata::PalletMetadata, MetadataError>;
fn runtime_api_trait_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::RuntimeApiMetadata, MetadataError>;
fn custom_value_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::CustomValueMetadata, MetadataError>;
}
impl MetadataExt for subxt_metadata::Metadata {
/// Identical to `metadata.pallet_by_name()`, but returns an error if the pallet is not found.
fn pallet_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
self.pallet_by_name(name)
.ok_or_else(|| MetadataError::PalletNameNotFound(name.to_owned()))
}
/// Identical to `metadata.pallet_by_index()`, but returns an error if the pallet is not found.
fn pallet_by_index_err(
&self,
index: u8,
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
self.pallet_by_index(index)
.ok_or(MetadataError::PalletIndexNotFound(index))
}
/// Identical to `metadata.runtime_api_trait_by_name()`, but returns an error if the trait is not found.
fn runtime_api_trait_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::RuntimeApiMetadata, MetadataError> {
self.runtime_api_trait_by_name(name)
.ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned()))
}
/// Identical to `metadata.runtime_api_trait_by_name()`, but returns an error if the trait is not found.
fn custom_value_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::CustomValueMetadata, MetadataError> {
self.custom()
.get(name)
.ok_or_else(|| MetadataError::CustomValueNameNotFound(name.to_owned()))
}
}
@@ -8,7 +8,7 @@ mod decode_encode_traits;
mod metadata_type;
pub use decode_encode_traits::{DecodeWithMetadata, EncodeWithMetadata};
pub use metadata_type::Metadata;
pub use metadata_type::{Metadata, MetadataExt};
// Expose metadata types under a sub module in case somebody needs to reference them:
pub use subxt_metadata as types;
@@ -2,15 +2,20 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use alloc::borrow::Cow;
use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec::Vec;
use core::marker::PhantomData;
use derivative::Derivative;
use derive_where::derive_where;
use scale_encode::EncodeAsFields;
use scale_value::Composite;
use std::borrow::Cow;
use crate::dynamic::DecodedValueThunk;
use crate::error::MetadataError;
use crate::{metadata::DecodeWithMetadata, Error, Metadata};
use crate::Error;
use crate::metadata::{DecodeWithMetadata, Metadata};
/// This represents a runtime API payload that can call into the runtime of node.
///
@@ -66,15 +71,7 @@ pub trait RuntimeApiPayload {
///
/// This can be created from static values (ie those generated
/// via the `subxt` macro) or dynamic values via [`dynamic`].
#[derive(Derivative)]
#[derivative(
Clone(bound = "ArgsData: Clone"),
Debug(bound = "ArgsData: std::fmt::Debug"),
Eq(bound = "ArgsData: std::cmp::Eq"),
Ord(bound = "ArgsData: std::cmp::Ord"),
PartialEq(bound = "ArgsData: std::cmp::PartialEq"),
PartialOrd(bound = "ArgsData: std::cmp::PartialOrd")
)]
#[derive_where(Clone, Debug, Eq, Ord, PartialEq, PartialOrd; ArgsData)]
pub struct Payload<ArgsData, ReturnTy> {
trait_name: Cow<'static, str>,
method_name: Cow<'static, str>,
@@ -150,7 +147,7 @@ impl<ReturnTy, ArgsData> Payload<ArgsData, ReturnTy> {
method_name: Cow::Borrowed(method_name),
args_data,
validation_hash: Some(hash),
_marker: std::marker::PhantomData,
_marker: core::marker::PhantomData,
}
}
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Types associated with accessing and working with storage items.
mod storage_address;
mod storage_key;
pub mod utils;
/// Types representing an address which describes where a storage
/// entry lives and how to properly decode it.
pub mod address {
pub use super::storage_address::{dynamic, Address, DynamicAddress, StorageAddress};
pub use super::storage_key::{StaticStorageKey, StorageHashers, StorageKey};
}
pub use storage_key::StorageKey;
// For consistency with other modules, also expose
// the basic address stuff at the root of the module.
pub use storage_address::{dynamic, Address, DynamicAddress, StorageAddress};
@@ -6,10 +6,13 @@ use crate::{
dynamic::DecodedValueThunk,
error::{Error, MetadataError},
metadata::{DecodeWithMetadata, Metadata},
utils::Yes,
};
use derivative::Derivative;
use derive_where::derive_where;
use std::borrow::Cow;
use alloc::borrow::{Cow, ToOwned};
use alloc::string::String;
use alloc::vec::Vec;
use super::{storage_key::StorageHashers, StorageKey};
@@ -48,27 +51,15 @@ pub trait StorageAddress {
}
}
/// Used to signal whether a [`StorageAddress`] can be iterated,
/// fetched and returned with a default value in the type system.
pub struct Yes;
/// A concrete storage address. This can be created from static values (ie those generated
/// via the `subxt` macro) or dynamic values via [`dynamic`].
#[derive(Derivative)]
#[derivative(
Clone(bound = "Keys: Clone"),
Debug(bound = "Keys: std::fmt::Debug"),
Eq(bound = "Keys: std::cmp::Eq"),
Ord(bound = "Keys: std::cmp::Ord"),
PartialEq(bound = "Keys: std::cmp::PartialEq"),
PartialOrd(bound = "Keys: std::cmp::PartialOrd")
)]
#[derive_where(Clone, Debug, Eq, Ord, PartialEq, PartialOrd; Keys)]
pub struct Address<Keys: StorageKey, ReturnTy, Fetchable, Defaultable, Iterable> {
pallet_name: Cow<'static, str>,
entry_name: Cow<'static, str>,
keys: Keys,
validation_hash: Option<[u8; 32]>,
_marker: std::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>,
_marker: core::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>,
}
/// A typical storage address constructed at runtime rather than via the `subxt` macro; this
@@ -83,7 +74,7 @@ impl<Keys: StorageKey> DynamicAddress<Keys> {
entry_name: Cow::Owned(entry_name.into()),
keys,
validation_hash: None,
_marker: std::marker::PhantomData,
_marker: core::marker::PhantomData,
}
}
}
@@ -108,7 +99,7 @@ where
entry_name: Cow::Borrowed(entry_name),
keys,
validation_hash: Some(hash),
_marker: std::marker::PhantomData,
_marker: core::marker::PhantomData,
}
}
}
@@ -127,9 +118,7 @@ where
}
}
/// Return bytes representing the root of this storage entry (ie a hash of
/// the pallet and entry name). Use [`crate::storage::StorageClient::address_bytes()`]
/// to obtain the bytes representing the entire address.
/// Return bytes representing the root of this storage entry (a hash of the pallet and entry name).
pub fn to_root_bytes(&self) -> Vec<u8> {
super::utils::storage_address_root_bytes(self)
}
@@ -1,21 +1,21 @@
use super::utils::hash_bytes;
use crate::{
error::{Error, MetadataError, StorageAddressError},
utils::{Encoded, Static},
};
use alloc::vec;
use alloc::vec::Vec;
use derive_where::derive_where;
use scale_decode::visitor::IgnoreVisitor;
use scale_encode::EncodeAsType;
use scale_info::{PortableRegistry, TypeDef};
use scale_value::Value;
use subxt_metadata::{StorageEntryType, StorageHasher};
use derivative::Derivative;
use super::utils::hash_bytes;
/// A collection of storage hashers paired with the type ids of the types they should hash.
/// Can be created for each storage entry in the metadata via [`StorageHashers::new()`].
#[derive(Debug)]
pub(crate) struct StorageHashers {
pub struct StorageHashers {
hashers_and_ty_ids: Vec<(StorageHasher, u32)>,
}
@@ -162,11 +162,10 @@ impl StorageKey for () {
/// A storage key for static encoded values.
/// The original value is only present at construction, but can be decoded from the contained bytes.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
#[derive_where(Clone, Debug, PartialOrd, PartialEq, Eq)]
pub struct StaticStorageKey<K: ?Sized> {
bytes: Static<Encoded>,
_marker: std::marker::PhantomData<K>,
_marker: core::marker::PhantomData<K>,
}
impl<K: codec::Encode + ?Sized> StaticStorageKey<K> {
@@ -174,7 +173,7 @@ impl<K: codec::Encode + ?Sized> StaticStorageKey<K> {
pub fn new(key: &K) -> Self {
StaticStorageKey {
bytes: Static(Encoded(key.encode())),
_marker: std::marker::PhantomData,
_marker: core::marker::PhantomData,
}
}
}
@@ -227,7 +226,7 @@ impl<K: ?Sized> StorageKey for StaticStorageKey<K> {
// Return the key bytes.
let key = StaticStorageKey {
bytes: Static(Encoded(key_bytes.to_vec())),
_marker: std::marker::PhantomData::<K>,
_marker: core::marker::PhantomData::<K>,
};
Ok(key)
}
@@ -362,6 +361,10 @@ mod tests {
use crate::utils::Era;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use super::{StaticStorageKey, StorageKey};
struct KeyBuilder {
+118
View File
@@ -0,0 +1,118 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! these utility methods complement the [`StorageAddress`] trait, but
//! aren't things that should ever be overridden, and so don't exist on
//! the trait itself.
use crate::error::MetadataError;
use crate::metadata::{DecodeWithMetadata, MetadataExt};
use alloc::vec::Vec;
use subxt_metadata::PalletMetadata;
use subxt_metadata::{StorageEntryMetadata, StorageHasher};
use super::StorageAddress;
use crate::{error::Error, metadata::Metadata};
use alloc::borrow::ToOwned;
/// Return the root of a given [`StorageAddress`]: hash the pallet name and entry name
/// and append those bytes to the output.
pub fn write_storage_address_root_bytes<Address: StorageAddress>(
addr: &Address,
out: &mut Vec<u8>,
) {
out.extend(sp_crypto_hashing::twox_128(addr.pallet_name().as_bytes()));
out.extend(sp_crypto_hashing::twox_128(addr.entry_name().as_bytes()));
}
/// Outputs the [`storage_address_root_bytes`] as well as any additional bytes that represent
/// a lookup in a storage map at that location.
pub fn storage_address_bytes<Address: StorageAddress>(
addr: &Address,
metadata: &Metadata,
) -> Result<Vec<u8>, Error> {
let mut bytes = Vec::new();
write_storage_address_root_bytes(addr, &mut bytes);
addr.append_entry_bytes(metadata, &mut bytes)?;
Ok(bytes)
}
/// Outputs a vector containing the bytes written by [`write_storage_address_root_bytes`].
pub fn storage_address_root_bytes<Address: StorageAddress>(addr: &Address) -> Vec<u8> {
let mut bytes = Vec::new();
write_storage_address_root_bytes(addr, &mut bytes);
bytes
}
/// Take some SCALE encoded bytes and a [`StorageHasher`] and hash the bytes accordingly.
pub fn hash_bytes(input: &[u8], hasher: StorageHasher, bytes: &mut Vec<u8>) {
match hasher {
StorageHasher::Identity => bytes.extend(input),
StorageHasher::Blake2_128 => bytes.extend(sp_crypto_hashing::blake2_128(input)),
StorageHasher::Blake2_128Concat => {
bytes.extend(sp_crypto_hashing::blake2_128(input));
bytes.extend(input);
}
StorageHasher::Blake2_256 => bytes.extend(sp_crypto_hashing::blake2_256(input)),
StorageHasher::Twox128 => bytes.extend(sp_crypto_hashing::twox_128(input)),
StorageHasher::Twox256 => bytes.extend(sp_crypto_hashing::twox_256(input)),
StorageHasher::Twox64Concat => {
bytes.extend(sp_crypto_hashing::twox_64(input));
bytes.extend(input);
}
}
}
/// Return details about the given storage entry.
pub fn lookup_entry_details<'a>(
pallet_name: &str,
entry_name: &str,
metadata: &'a subxt_metadata::Metadata,
) -> Result<(PalletMetadata<'a>, &'a StorageEntryMetadata), Error> {
let pallet_metadata = metadata.pallet_by_name_err(pallet_name)?;
let storage_metadata = pallet_metadata
.storage()
.ok_or_else(|| MetadataError::StorageNotFoundInPallet(pallet_name.to_owned()))?;
let storage_entry = storage_metadata
.entry_by_name(entry_name)
.ok_or_else(|| MetadataError::StorageEntryNotFound(entry_name.to_owned()))?;
Ok((pallet_metadata, storage_entry))
}
/// Validate a storage address against the metadata.
pub fn validate_storage_address<Address: StorageAddress>(
address: &Address,
pallet: PalletMetadata<'_>,
) -> Result<(), Error> {
if let Some(hash) = address.validation_hash() {
validate_storage(pallet, address.entry_name(), hash)?;
}
Ok(())
}
/// Validate a storage entry against the metadata.
fn validate_storage(
pallet: PalletMetadata<'_>,
storage_name: &str,
hash: [u8; 32],
) -> Result<(), Error> {
let Some(expected_hash) = pallet.storage_hash(storage_name) else {
return Err(MetadataError::IncompatibleCodegen.into());
};
if expected_hash != hash {
return Err(MetadataError::IncompatibleCodegen.into());
}
Ok(())
}
/// Given some bytes, a pallet and storage name, decode the response.
pub fn decode_storage_with_metadata<T: DecodeWithMetadata>(
bytes: &mut &[u8],
metadata: &Metadata,
storage_metadata: &StorageEntryMetadata,
) -> Result<T, Error> {
let return_ty = storage_metadata.entry_type().value_ty();
let val = T::decode_with_metadata(bytes, return_ty, metadata)?;
Ok(val)
}
@@ -5,16 +5,19 @@
//! This module contains the trait and types used to represent
//! transactions that can be submitted.
use crate::{
dynamic::Value,
error::{Error, MetadataError},
metadata::Metadata,
};
use crate::error::MetadataError;
use crate::metadata::Metadata;
use crate::Error;
use alloc::borrow::{Cow, ToOwned};
use alloc::string::String;
use alloc::vec::Vec;
use codec::Encode;
use derivative::Derivative;
use scale_encode::EncodeAsFields;
use scale_value::{Composite, ValueDef, Variant};
use std::borrow::Cow;
use scale_value::{Composite, Value, ValueDef, Variant};
pub mod signer;
pub use signer::Signer;
/// This represents a transaction payload that can be submitted
/// to a node.
@@ -49,15 +52,7 @@ pub struct ValidationDetails<'a> {
}
/// A transaction payload containing some generic `CallData`.
#[derive(Derivative)]
#[derivative(
Clone(bound = "CallData: Clone"),
Debug(bound = "CallData: std::fmt::Debug"),
Eq(bound = "CallData: std::cmp::Eq"),
Ord(bound = "CallData: std::cmp::Ord"),
PartialEq(bound = "CallData: std::cmp::PartialEq"),
PartialOrd(bound = "CallData: std::cmp::PartialOrd")
)]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Payload<CallData> {
pallet_name: Cow<'static, str>,
call_name: Cow<'static, str>,
@@ -160,7 +155,8 @@ impl<CallData: EncodeAsFields> TxPayload for Payload<CallData> {
.map(|f| scale_encode::Field::new(&f.ty.id, f.name.as_deref()));
self.call_data
.encode_as_fields_to(&mut fields, metadata.types(), out)?;
.encode_as_fields_to(&mut fields, metadata.types(), out)
.expect("The fields are valid types from the metadata, qed;");
Ok(())
}
@@ -6,7 +6,12 @@
//! This doesn't contain much functionality itself, but is easy to convert to/from an `sp_core::AccountId32`
//! for instance, to gain functionality without forcing a dependency on Substrate crates here.
use alloc::format;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use codec::{Decode, Encode};
use derive_more::Display;
use serde::{Deserialize, Serialize};
/// A 32-byte cryptographic identifier. This is a simplified version of Substrate's
@@ -100,19 +105,22 @@ impl AccountId32 {
}
/// An error obtained from trying to interpret an SS58 encoded string into an AccountId32
#[derive(thiserror::Error, Clone, Copy, Eq, PartialEq, Debug)]
#[derive(Clone, Copy, Eq, PartialEq, Debug, Display)]
#[allow(missing_docs)]
pub enum FromSs58Error {
#[error("Base 58 requirement is violated")]
#[display(fmt = "Base 58 requirement is violated")]
BadBase58,
#[error("Length is bad")]
#[display(fmt = "Length is bad")]
BadLength,
#[error("Invalid checksum")]
#[display(fmt = "Invalid checksum")]
InvalidChecksum,
#[error("Invalid SS58 prefix byte.")]
#[display(fmt = "Invalid SS58 prefix byte.")]
InvalidPrefix,
}
#[cfg(feature = "std")]
impl std::error::Error for FromSs58Error {}
// We do this just to get a checksum to help verify the validity of the address in to_ss58check
fn ss58hash(data: &[u8]) -> Vec<u8> {
use blake2::{Blake2b512, Digest};
@@ -142,13 +150,13 @@ impl<'de> Deserialize<'de> for AccountId32 {
}
}
impl std::fmt::Display for AccountId32 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
impl core::fmt::Display for AccountId32 {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.to_ss58check())
}
}
impl std::str::FromStr for AccountId32 {
impl core::str::FromStr for AccountId32 {
type Err = FromSs58Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
AccountId32::from_ss58check(s)
@@ -4,15 +4,16 @@
//! Generic `scale_bits` over `bitvec`-like `BitOrder` and `BitFormat` types.
use alloc::vec;
use alloc::vec::Vec;
use codec::{Compact, Input};
use core::marker::PhantomData;
use scale_bits::{
scale::format::{Format, OrderFormat, StoreFormat},
Bits,
};
use scale_decode::{IntoVisitor, TypeResolver};
use std::marker::PhantomData;
/// Associates `bitvec::store::BitStore` trait with corresponding, type-erased `scale_bits::StoreFormat` enum.
///
/// Used to decode bit sequences by providing `scale_bits::StoreFormat` using
@@ -145,7 +146,7 @@ impl<Store: BitStore, Order: BitOrder> codec::Encode for DecodedBits<Store, Orde
}
#[doc(hidden)]
pub struct DecodedBitsVisitor<S, O, R: TypeResolver>(std::marker::PhantomData<(S, O, R)>);
pub struct DecodedBitsVisitor<S, O, R: TypeResolver>(core::marker::PhantomData<(S, O, R)>);
impl<Store, Order, R: TypeResolver> scale_decode::Visitor for DecodedBitsVisitor<Store, Order, R> {
type Value<'scale, 'info> = DecodedBits<Store, Order>;
+83
View File
@@ -0,0 +1,83 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Miscellaneous utility helpers.
mod account_id;
pub mod bits;
mod era;
mod multi_address;
mod multi_signature;
mod static_type;
mod unchecked_extrinsic;
mod wrapper_opaque;
use alloc::borrow::ToOwned;
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use codec::{Compact, Decode, Encode};
use derive_where::derive_where;
pub use account_id::AccountId32;
pub use era::Era;
pub use multi_address::MultiAddress;
pub use multi_signature::MultiSignature;
pub use static_type::Static;
pub use unchecked_extrinsic::UncheckedExtrinsic;
pub use wrapper_opaque::WrapperKeepOpaque;
// Used in codegen
#[doc(hidden)]
pub use primitive_types::{H160, H256, H512};
/// Wraps an already encoded byte vector, prevents being encoded as a raw byte vector as part of
/// the transaction payload
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Encoded(pub Vec<u8>);
impl codec::Encode for Encoded {
fn encode(&self) -> Vec<u8> {
self.0.to_owned()
}
}
/// Decodes a compact encoded value from the beginning of the provided bytes,
/// returning the value and any remaining bytes.
pub fn strip_compact_prefix(bytes: &[u8]) -> Result<(u64, &[u8]), codec::Error> {
let cursor = &mut &*bytes;
let val = <Compact<u64>>::decode(cursor)?;
Ok((val.0, *cursor))
}
/// A version of [`core::marker::PhantomData`] that is also Send and Sync (which is fine
/// because regardless of the generic param, it is always possible to Send + Sync this
/// 0 size type).
#[derive(Encode, Decode, scale_info::TypeInfo)]
#[derive_where(Clone, PartialEq, Debug, Eq, Default, Hash)]
#[scale_info(skip_type_params(T))]
#[doc(hidden)]
pub struct PhantomDataSendSync<T>(core::marker::PhantomData<T>);
impl<T> PhantomDataSendSync<T> {
pub fn new() -> Self {
Self(core::marker::PhantomData)
}
}
unsafe impl<T> Send for PhantomDataSendSync<T> {}
unsafe impl<T> Sync for PhantomDataSendSync<T> {}
/// This represents a key-value collection and is SCALE compatible
/// with collections like BTreeMap. This has the same type params
/// as `BTreeMap` which allows us to easily swap the two during codegen.
pub type KeyedVec<K, V> = Vec<(K, V)>;
/// A unit marker struct signalling that some property is true
pub struct Yes;
/// A quick helper to encode some bytes to hex.
pub fn to_hex(bytes: impl AsRef<[u8]>) -> String {
format!("0x{}", hex::encode(bytes.as_ref()))
}
@@ -6,6 +6,7 @@
//! This doesn't contain much functionality itself, but is easy to convert to/from an `sp_runtime::MultiAddress`
//! for instance, to gain functionality without forcing a dependency on Substrate crates here.
use alloc::vec::Vec;
use codec::{Decode, Encode};
/// A multi-format address wrapper for on-chain accounts. This is a simplified version of Substrate's
@@ -6,6 +6,8 @@ use codec::{Decode, Encode};
use scale_decode::{visitor::DecodeAsTypeResult, IntoVisitor, TypeResolver, Visitor};
use scale_encode::EncodeAsType;
use alloc::vec::Vec;
/// If the type inside this implements [`Encode`], this will implement [`scale_encode::EncodeAsType`].
/// If the type inside this implements [`Decode`], this will implement [`scale_decode::DecodeAsType`].
///
@@ -29,7 +31,7 @@ impl<T: Encode> EncodeAsType for Static<T> {
}
}
pub struct StaticDecodeAsTypeVisitor<T, R>(std::marker::PhantomData<(T, R)>);
pub struct StaticDecodeAsTypeVisitor<T, R>(core::marker::PhantomData<(T, R)>);
impl<T: Decode, R: TypeResolver> Visitor for StaticDecodeAsTypeVisitor<T, R> {
type Value<'scale, 'info> = Static<T>;
@@ -53,7 +55,7 @@ impl<T: Decode, R: TypeResolver> Visitor for StaticDecodeAsTypeVisitor<T, R> {
impl<T: Decode> IntoVisitor for Static<T> {
type AnyVisitor<R: TypeResolver> = StaticDecodeAsTypeVisitor<T, R>;
fn into_visitor<R: TypeResolver>() -> StaticDecodeAsTypeVisitor<T, R> {
StaticDecodeAsTypeVisitor(std::marker::PhantomData)
StaticDecodeAsTypeVisitor(core::marker::PhantomData)
}
}
@@ -65,14 +67,14 @@ impl<T> From<T> for Static<T> {
}
// Static<T> is just a marker type and should be as transparent as possible:
impl<T> std::ops::Deref for Static<T> {
impl<T> core::ops::Deref for Static<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for Static<T> {
impl<T> core::ops::DerefMut for Static<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
@@ -9,12 +9,13 @@
//! runtime APIs. Deriving `EncodeAsType` would lead to the inner
//! bytes to be re-encoded (length prefixed).
use std::marker::PhantomData;
use core::marker::PhantomData;
use codec::{Decode, Encode};
use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType, IntoVisitor, TypeResolver, Visitor};
use super::{Encoded, Static};
use alloc::vec::Vec;
/// The unchecked extrinsic from substrate.
#[derive(Clone, Debug, Eq, PartialEq, Encode)]
@@ -115,6 +116,8 @@ impl<Address, Call, Signature, Extra> IntoVisitor
pub mod tests {
use super::*;
use alloc::vec;
#[test]
fn unchecked_extrinsic_encoding() {
// A tx is basically some bytes with a compact length prefix; ie an encoded vec:
@@ -4,10 +4,13 @@
use super::PhantomDataSendSync;
use codec::{Compact, Decode, DecodeAll, Encode};
use derivative::Derivative;
use derive_where::derive_where;
use scale_decode::{ext::scale_type_resolver::visitor, IntoVisitor, TypeResolver, Visitor};
use scale_encode::EncodeAsType;
use alloc::format;
use alloc::vec::Vec;
/// A wrapper for any type `T` which implement encode/decode in a way compatible with `Vec<u8>`.
/// [`WrapperKeepOpaque`] stores the type only in its opaque format, aka as a `Vec<u8>`. To
/// access the real type `T` [`Self::try_decode`] needs to be used.
@@ -18,15 +21,8 @@ use scale_encode::EncodeAsType;
// - However, the TypeInfo describes the type as a composite with first a compact encoded length and next the type itself.
// [`Encode`] and [`Decode`] impls will "just work" to take this into a `Vec<u8>`, but we need a custom [`EncodeAsType`]
// and [`Visitor`] implementation to encode and decode based on TypeInfo.
#[derive(Derivative, Encode, Decode)]
#[derivative(
Debug(bound = ""),
Clone(bound = ""),
PartialEq(bound = ""),
Eq(bound = ""),
Default(bound = ""),
Hash(bound = "")
)]
#[derive(Encode, Decode)]
#[derive_where(Debug, Clone, PartialEq, Eq, Default, Hash)]
pub struct WrapperKeepOpaque<T> {
data: Vec<u8>,
_phantom: PhantomDataSendSync<T>,
@@ -83,7 +79,7 @@ impl<T> EncodeAsType for WrapperKeepOpaque<T> {
use scale_encode::error::{Error, ErrorKind, Kind};
let visitor = visitor::new(out, |_, _| {
// Check that the target shape lines up: any other shape but the composite is wrong.
// Check that the target shape lines up: any other shape but composite is wrong.
Err(Error::new(ErrorKind::WrongShape {
actual: Kind::Struct,
expected_id: format!("{:?}", type_id),
@@ -100,7 +96,7 @@ impl<T> EncodeAsType for WrapperKeepOpaque<T> {
}
}
pub struct WrapperKeepOpaqueVisitor<T, R>(std::marker::PhantomData<(T, R)>);
pub struct WrapperKeepOpaqueVisitor<T, R>(core::marker::PhantomData<(T, R)>);
impl<T, R: TypeResolver> Visitor for WrapperKeepOpaqueVisitor<T, R> {
type Value<'scale, 'info> = WrapperKeepOpaque<T>;
type Error = scale_decode::Error;
@@ -143,7 +139,7 @@ impl<T, R: TypeResolver> Visitor for WrapperKeepOpaqueVisitor<T, R> {
impl<T> IntoVisitor for WrapperKeepOpaque<T> {
type AnyVisitor<R: TypeResolver> = WrapperKeepOpaqueVisitor<T, R>;
fn into_visitor<R: TypeResolver>() -> WrapperKeepOpaqueVisitor<T, R> {
WrapperKeepOpaqueVisitor(std::marker::PhantomData)
WrapperKeepOpaqueVisitor(core::marker::PhantomData)
}
}
@@ -151,6 +147,8 @@ impl<T> IntoVisitor for WrapperKeepOpaque<T> {
mod test {
use scale_decode::DecodeAsType;
use alloc::vec;
use super::*;
// Copied from https://github.com/paritytech/substrate/blob/master/frame/support/src/traits/misc.rs
@@ -188,7 +186,7 @@ mod test {
+ Encode
+ Decode
+ PartialEq
+ std::fmt::Debug
+ core::fmt::Debug
+ scale_info::TypeInfo
+ 'static,
{
+26 -2
View File
@@ -2502,8 +2502,6 @@ name = "subxt"
version = "0.35.0"
dependencies = [
"async-trait",
"base58",
"blake2",
"derivative",
"either",
"frame-metadata 16.0.0",
@@ -2551,6 +2549,32 @@ dependencies = [
"tokio",
]
[[package]]
name = "subxt-core"
version = "0.34.0"
dependencies = [
"base58",
"blake2",
"derivative",
"derive_more",
"frame-metadata 16.0.0",
"hashbrown 0.14.3",
"hex",
"impl-serde",
"parity-scale-codec",
"primitive-types",
"scale-bits",
"scale-decode",
"scale-encode",
"scale-info",
"scale-value",
"serde",
"serde_json",
"sp-core-hashing",
"subxt-metadata",
"tracing",
]
[[package]]
name = "subxt-lightclient"
version = "0.35.0"
+2 -2
View File
@@ -130,8 +130,8 @@ pub async fn extension_signature_for_extrinsic(
) -> Result<Vec<u8>, anyhow::Error> {
let genesis_hash = encode_then_hex(&api.genesis_hash());
// These numbers aren't SCALE encoded; their bytes are just converted to hex:
let spec_version = to_hex(&api.runtime_version().spec_version.to_be_bytes());
let transaction_version = to_hex(&api.runtime_version().transaction_version.to_be_bytes());
let spec_version = to_hex(&api.runtime_version().spec_version().to_be_bytes());
let transaction_version = to_hex(&api.runtime_version().transaction_version().to_be_bytes());
let nonce = to_hex(&account_nonce.to_be_bytes());
// If you construct a mortal transaction, then this block hash needs to correspond
// to the block number passed to `Era::mortal()`.
-39
View File
@@ -171,45 +171,6 @@ impl Metadata {
&OuterEnumHashes::empty(),
))
}
/// Ensure that every unique type we'll be generating or referring to also has a
/// unique path, so that types with matching paths don't end up overwriting each other
/// in the codegen. We ignore any types with generics; Subxt actually endeavours to
/// de-duplicate those into single types with a generic.
pub fn ensure_unique_type_paths(&mut self) {
let mut visited_path_counts = HashMap::<Vec<String>, usize>::new();
for ty in self.types.types.iter_mut() {
// Ignore types without a path (ie prelude types).
if ty.ty.path.namespace().is_empty() {
continue;
}
let has_valid_type_params = ty.ty.type_params.iter().any(|tp| tp.ty.is_some());
// Ignore types which have generic params that the type generation will use.
// Ordinarily we'd expect that any two types with identical paths must be parameterized
// in order to share the path. However scale-info doesn't understand all forms of generics
// properly I think (eg generics that have associated types that can differ), and so in
// those cases we need to fix the paths for Subxt to generate correct code.
if has_valid_type_params {
continue;
}
// Count how many times we've seen the same path already.
let visited_count = visited_path_counts
.entry(ty.ty.path.segments.clone())
.or_default();
*visited_count += 1;
// alter the type so that if it's been seen more than once, we append a number to
// its name to ensure that every unique type has a unique path, too.
if *visited_count > 1 {
if let Some(name) = ty.ty.path.segments.last_mut() {
*name = alloc::format!("{name}{visited_count}");
}
}
}
}
}
/// Metadata for a specific pallet.
+6 -7
View File
@@ -15,7 +15,7 @@ description = "Sign extrinsics to be submitted by Subxt"
keywords = ["parity", "subxt", "extrinsic", "signer"]
[features]
default = ["sr25519", "ecdsa", "subxt", "std", "native"]
default = ["sr25519", "ecdsa", "subxt", "std"]
std = ["regex/std", "sp-crypto-hashing/std", "pbkdf2/std", "sha2/std", "hmac/std", "bip39/std", "schnorrkel/std", "secp256k1/std", "sp-core/std"]
# Pick the signer implementation(s) you need by enabling the
@@ -27,15 +27,15 @@ ecdsa = ["secp256k1"]
# Make the keypair algorithms here compatible with Subxt's Signer trait,
# so that they can be used to sign transactions for compatible chains.
subxt = ["dep:subxt"]
subxt = ["dep:subxt-core"]
# The getrandom package is used via schnorrkel. We need to enable the JS
# feature on it if compiling for the web.
web = ["getrandom/js", "subxt?/web"]
native = ["subxt?/native"]
web = ["getrandom/js"]
[dependencies]
subxt = { workspace = true, optional = true }
subxt-core = { workspace = true, optional = true, default-features = false }
secrecy = { workspace = true }
regex = { workspace = true, features = ["unicode"] }
hex = { workspace = true }
cfg-if = { workspace = true }
@@ -49,15 +49,14 @@ zeroize = { workspace = true }
bip39 = { workspace = true }
schnorrkel = { workspace = true, optional = true }
secp256k1 = { workspace = true, optional = true, features = ["alloc", "recovery"] }
secrecy = { workspace = true }
# We only pull this in to enable the JS flag for schnorrkel to use.
getrandom = { workspace = true, optional = true }
[dev-dependencies]
sp-core = { workspace = true }
sp-keyring = { workspace = true }
sp-core = { workspace = true }
[package.metadata.cargo-machete]
ignored = ["getrandom"]
+3 -3
View File
@@ -272,9 +272,9 @@ pub mod dev {
mod subxt_compat {
use super::*;
use subxt::config::Config;
use subxt::tx::Signer as SignerT;
use subxt::utils::{AccountId32, MultiAddress, MultiSignature};
use subxt_core::config::Config;
use subxt_core::tx::Signer as SignerT;
use subxt_core::utils::{AccountId32, MultiAddress, MultiSignature};
impl From<Signature> for MultiSignature {
fn from(value: Signature) -> Self {
+5 -3
View File
@@ -258,9 +258,11 @@ pub mod dev {
mod subxt_compat {
use super::*;
use subxt::config::Config;
use subxt::tx::Signer as SignerT;
use subxt::utils::{AccountId32, MultiAddress, MultiSignature};
use subxt_core::{
tx::Signer as SignerT,
utils::{AccountId32, MultiAddress, MultiSignature},
Config,
};
impl From<Signature> for MultiSignature {
fn from(value: Signature) -> Self {
+3 -10
View File
@@ -53,7 +53,7 @@ jsonrpsee = [
# Enable this to pull in extra Substrate dependencies which make it possible to
# use the `sp_core::crypto::Pair` Signer implementation, as well as adding some
# `From` impls for types like `AccountId32`. Cannot be used with "web".
substrate-compat = ["sp-core", "sp-runtime"]
substrate-compat = ["subxt-core/substrate-compat"]
# Enable this to fetch and utilize the latest unstable metadata from a node.
# The unstable metadata is subject to breaking changes and the subxt might
@@ -68,6 +68,7 @@ unstable-light-client = ["subxt-lightclient"]
[dependencies]
async-trait = { workspace = true }
codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] }
derive-where = { workspace = true }
scale-info = { workspace = true, features = ["default"] }
scale-value = { workspace = true, features = ["default"] }
scale-bits = { workspace = true, features = ["default"] }
@@ -80,7 +81,6 @@ serde_json = { workspace = true, features = ["default", "raw_value"] }
thiserror = { workspace = true }
tracing = { workspace = true }
frame-metadata = { workspace = true }
derivative = { workspace = true }
either = { workspace = true }
instant = { workspace = true }
@@ -89,19 +89,12 @@ impl-serde = { workspace = true }
primitive-types = { workspace = true, features = ["codec", "scale-info", "serde"] }
sp-crypto-hashing = { workspace = true }
# For ss58 encoding AccountId32 to serialize them properly:
base58 = { workspace = true }
blake2 = { workspace = true }
# Included if the "jsonrpsee" feature is enabled.
jsonrpsee = { workspace = true, optional = true, features = ["jsonrpsee-types"] }
# Included if the "substrate-compat" feature is enabled.
sp-core = { workspace = true, optional = true }
sp-runtime = { workspace = true, optional = true }
# Other subxt crates we depend on.
subxt-macro = { workspace = true }
subxt-core = { workspace = true, features = ["std"] }
subxt-metadata = { workspace = true, features = ["std"] }
subxt-lightclient = { workspace = true, optional = true, default-features = false }
+1 -4
View File
@@ -16,10 +16,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
};
// 2. A runtime version (system_version constant on a Substrate node has these):
let runtime_version = subxt::backend::RuntimeVersion {
spec_version: 9370,
transaction_version: 20,
};
let runtime_version = subxt::client::RuntimeVersion::new(9370, 20);
// 3. Metadata (I'll load it from the downloaded metadata, but you can use
// `subxt metadata > file.scale` to download it):
+2 -5
View File
@@ -1,6 +1,6 @@
#![allow(missing_docs)]
use codec::Encode;
use subxt::client::OfflineClientT;
use subxt::client::ClientState;
use subxt::config::{
Config, ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError, RefineParams,
};
@@ -60,10 +60,7 @@ impl<T: Config> ExtrinsicParams<T> for CustomExtrinsicParams<T> {
type Params = CustomExtrinsicParamsBuilder;
// Gather together all of the params we will need to encode:
fn new<Client: OfflineClientT<T>>(
client: Client,
params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
fn new(client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(Self {
genesis_hash: client.genesis_hash(),
tip: params.tip,
@@ -2,7 +2,7 @@
use codec::Encode;
use scale_encode::EncodeAsType;
use scale_info::PortableRegistry;
use subxt::client::OfflineClientT;
use subxt::client::ClientState;
use subxt::config::signed_extensions;
use subxt::config::{
Config, DefaultExtrinsicParamsBuilder, ExtrinsicParams, ExtrinsicParamsEncoder,
@@ -60,10 +60,7 @@ impl<T: Config> signed_extensions::SignedExtension<T> for CustomSignedExtension
impl<T: Config> ExtrinsicParams<T> for CustomSignedExtension {
type Params = ();
fn new<Client: OfflineClientT<T>>(
_client: Client,
_params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
fn new(_client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(CustomSignedExtension)
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
#![allow(missing_docs)]
use subxt::{OnlineClient, PolkadotConfig};
use subxt::{dynamic::Value, OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -8,7 +8,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Build a dynamic storage query to iterate account information.
// With a dynamic query, we can just provide an empty vector as the keys to iterate over all entries.
let keys: Vec<scale_value::Value> = vec![];
let keys: Vec<Value> = vec![];
let storage_query = subxt::dynamic::storage("System", "Account", keys);
// Use that query to return an iterator over the results.
+6 -10
View File
@@ -181,20 +181,16 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for LegacyBackend<T> {
async fn current_runtime_version(&self) -> Result<RuntimeVersion, Error> {
let details = self.methods.state_get_runtime_version(None).await?;
Ok(RuntimeVersion {
spec_version: details.spec_version,
transaction_version: details.transaction_version,
})
Ok(RuntimeVersion::new(
details.spec_version,
details.transaction_version,
))
}
async fn stream_runtime_version(&self) -> Result<StreamOfResults<RuntimeVersion>, Error> {
let sub = self.methods.state_subscribe_runtime_version().await?;
let sub = sub.map(|r| {
r.map(|v| RuntimeVersion {
spec_version: v.spec_version,
transaction_version: v.transaction_version,
})
});
let sub =
sub.map(|r| r.map(|v| RuntimeVersion::new(v.spec_version, v.transaction_version)));
Ok(StreamOf(Box::pin(sub)))
}
+2 -3
View File
@@ -8,15 +8,14 @@ use crate::backend::rpc::{rpc_params, RpcClient, RpcSubscription};
use crate::metadata::Metadata;
use crate::{Config, Error};
use codec::Decode;
use derivative::Derivative;
use derive_where::derive_where;
use primitive_types::U256;
use serde::{Deserialize, Serialize};
/// An interface to call the legacy RPC methods. This interface is instantiated with
/// some `T: Config` trait which determines some of the types that the RPC methods will
/// take or hand back.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
#[derive_where(Clone, Debug)]
pub struct LegacyRpcMethods<T> {
client: RpcClient,
_marker: std::marker::PhantomData<T>,
+2 -20
View File
@@ -10,6 +10,8 @@ pub mod legacy;
pub mod rpc;
pub mod unstable;
use subxt_core::client::RuntimeVersion;
use crate::error::Error;
use crate::metadata::Metadata;
use crate::Config;
@@ -277,26 +279,6 @@ impl<T> StreamOf<T> {
/// A stream of [`Result<Item, Error>`].
pub type StreamOfResults<T> = StreamOf<Result<T, Error>>;
/// Runtime version information needed to submit transactions.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RuntimeVersion {
/// Version of the runtime specification. A full-node will not attempt to use its native
/// runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
/// `spec_version` and `authoring_version` are the same between Wasm and native.
pub spec_version: u32,
/// All existing dispatches are fully compatible when this number doesn't change. If this
/// number changes, then `spec_version` must change, also.
///
/// This number must change when an existing dispatchable (module ID, dispatch ID) is changed,
/// either through an alteration in its user-level semantics, a parameter
/// added/removed/changed, a dispatchable being removed, a module being removed, or a
/// dispatchable/module changing its index.
///
/// It need *not* change when a new module is added or when a dispatchable is added.
pub transaction_version: u32,
}
/// The status of the transaction.
///
/// If the status is [`TransactionStatus::InFinalizedBlock`], [`TransactionStatus::Error`],
+2 -5
View File
@@ -361,7 +361,6 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for UnstableBackend<T> {
for finalized_block in ev.finalized_block_hashes {
runtimes.remove(&finalized_block.hash());
}
ev.finalized_block_runtime
}
FollowEvent::NewBlock(ev) => {
@@ -417,10 +416,8 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for UnstableBackend<T> {
RuntimeEvent::Valid(ev) => ev,
};
std::future::ready(Some(Ok(RuntimeVersion {
spec_version: runtime_details.spec.spec_version,
transaction_version: runtime_details.spec.transaction_version,
})))
let runtime_version = RuntimeVersion::new(runtime_details.spec.spec_version, runtime_details.spec.transaction_version);
std::future::ready(Some(Ok(runtime_version)))
});
Ok(StreamOf(Box::pin(runtime_stream)))
+2 -3
View File
@@ -9,7 +9,7 @@
use crate::backend::rpc::{rpc_params, RpcClient, RpcSubscription};
use crate::config::BlockHash;
use crate::{Config, Error};
use derivative::Derivative;
use derive_where::derive_where;
use futures::{Stream, StreamExt};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
@@ -18,8 +18,7 @@ use std::task::Poll;
/// An interface to call the unstable RPC methods. This interface is instantiated with
/// some `T: Config` trait which determines some of the types that the RPC methods will
/// take or hand back.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
#[derive_where(Clone, Debug)]
pub struct UnstableRpcMethods<T> {
client: RpcClient,
_marker: std::marker::PhantomData<T>,
+2 -3
View File
@@ -10,7 +10,7 @@ use crate::{
error::{BlockError, Error},
utils::PhantomDataSendSync,
};
use derivative::Derivative;
use derive_where::derive_where;
use futures::StreamExt;
use std::future::Future;
@@ -18,8 +18,7 @@ type BlockStream<T> = StreamOfResults<T>;
type BlockStreamRes<T> = Result<BlockStream<T>, Error>;
/// A client for working with blocks.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct BlocksClient<T, Client> {
client: Client,
_marker: PhantomDataSendSync<T>,
+17 -39
View File
@@ -16,31 +16,14 @@ use crate::config::signed_extensions::{
ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce,
};
use crate::config::SignedExtension;
use crate::dynamic::DecodedValue;
use crate::dynamic::Value;
use crate::utils::strip_compact_prefix;
use codec::Decode;
use derivative::Derivative;
use scale_decode::{DecodeAsFields, DecodeAsType};
use derive_where::derive_where;
use scale_decode::DecodeAsType;
use std::sync::Arc;
/// Trait to uniquely identify the extrinsic's identity from the runtime metadata.
///
/// Generated API structures that represent an extrinsic implement this trait.
///
/// The trait is utilized to decode emitted extrinsics from a block, via obtaining the
/// form of the `Extrinsic` from the metadata.
pub trait StaticExtrinsic: DecodeAsFields {
/// Pallet name.
const PALLET: &'static str;
/// Call name.
const CALL: &'static str;
/// Returns true if the given pallet and call names match this extrinsic.
fn is_extrinsic(pallet: &str, call: &str) -> bool {
Self::PALLET == pallet && Self::CALL == call
}
}
pub use subxt_core::blocks::StaticExtrinsic;
/// The body of a block.
pub struct Extrinsics<T: Config, C> {
@@ -524,8 +507,7 @@ impl ExtrinsicPartTypeIds {
}
/// The events associated with a given extrinsic.
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
#[derive_where(Debug)]
pub struct ExtrinsicEvents<T: Config> {
// The hash of the extrinsic (handy to expose here because
// this type is returned from TxProgress things in the most
@@ -547,11 +529,6 @@ impl<T: Config> ExtrinsicEvents<T> {
}
}
/// Return the hash of the block that the extrinsic is in.
pub fn block_hash(&self) -> T::Hash {
self.events.block_hash()
}
/// The index of the extrinsic that these events are produced from.
pub fn extrinsic_index(&self) -> u32 {
self.idx
@@ -572,11 +549,14 @@ impl<T: Config> ExtrinsicEvents<T> {
/// This works in the same way that [`events::Events::iter()`] does, with the
/// exception that it filters out events not related to the submitted extrinsic.
pub fn iter(&self) -> impl Iterator<Item = Result<events::EventDetails<T>, Error>> + '_ {
self.events.iter().filter(|ev| {
ev.as_ref()
.map(|ev| ev.phase() == events::Phase::ApplyExtrinsic(self.idx))
.unwrap_or(true) // Keep any errors.
})
self.events
.iter()
.filter(|ev| {
ev.as_ref()
.map(|ev| ev.phase() == events::Phase::ApplyExtrinsic(self.idx))
.unwrap_or(true) // Keep any errors.
})
.map(|e| e.map_err(Error::from))
}
/// Find all of the transaction events matching the event type provided as a generic parameter.
@@ -742,7 +722,7 @@ impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> {
}
/// Signed Extension as a [`scale_value::Value`]
pub fn value(&self) -> Result<DecodedValue, Error> {
pub fn value(&self) -> Result<Value<u32>, Error> {
let value = scale_value::scale::decode_as_type(
&mut &self.bytes[..],
&self.ty_id,
@@ -770,7 +750,7 @@ impl<'a, T: Config> ExtrinsicSignedExtension<'a, T> {
#[cfg(test)]
mod tests {
use super::*;
use crate::{backend::RuntimeVersion, OfflineClient, PolkadotConfig};
use crate::{OfflineClient, PolkadotConfig};
use assert_matches::assert_matches;
use codec::{Decode, Encode};
use frame_metadata::v15::{CustomMetadata, OuterEnums};
@@ -781,6 +761,7 @@ mod tests {
use primitive_types::H256;
use scale_info::{meta_type, TypeInfo};
use scale_value::Value;
use subxt_core::client::RuntimeVersion;
// Extrinsic needs to contain at least the generic type parameter "Call"
// for the metadata to be valid.
@@ -902,10 +883,7 @@ mod tests {
/// Build an offline client to work with the test metadata.
fn client(metadata: Metadata) -> OfflineClient<PolkadotConfig> {
// Create the encoded extrinsic bytes.
let rt_version = RuntimeVersion {
spec_version: 1,
transaction_version: 4,
};
let rt_version = RuntimeVersion::new(1, 4);
let block_hash = H256::random();
OfflineClient::new(block_hash, rt_version, metadata)
}
+1
View File
@@ -15,3 +15,4 @@ pub use offline_client::{OfflineClient, OfflineClientT};
pub use online_client::{
ClientRuntimeUpdater, OnlineClient, OnlineClientT, RuntimeUpdaterStream, Update, UpgradeError,
};
pub use subxt_core::client::{ClientState, RuntimeVersion};
+28 -21
View File
@@ -4,13 +4,13 @@
use crate::custom_values::CustomValuesClient;
use crate::{
backend::RuntimeVersion, blocks::BlocksClient, constants::ConstantsClient,
events::EventsClient, runtime_api::RuntimeApiClient, storage::StorageClient, tx::TxClient,
Config, Metadata,
blocks::BlocksClient, constants::ConstantsClient, events::EventsClient,
runtime_api::RuntimeApiClient, storage::StorageClient, tx::TxClient, Config, Metadata,
};
use derivative::Derivative;
use derive_where::derive_where;
use std::sync::Arc;
use subxt_core::client::{ClientState, RuntimeVersion};
/// A trait representing a client that can perform
/// offline-only actions.
@@ -21,6 +21,10 @@ pub trait OfflineClientT<T: Config>: Clone + Send + Sync + 'static {
fn genesis_hash(&self) -> T::Hash;
/// Return the provided [`RuntimeVersion`].
fn runtime_version(&self) -> RuntimeVersion;
/// Return the [subxt_core::client::ClientState] (metadata, runtime version and genesis hash).
fn client_state(&self) -> ClientState<T> {
ClientState::new(self.genesis_hash(), self.runtime_version(), self.metadata())
}
/// Work with transactions.
fn tx(&self) -> TxClient<T, Self> {
@@ -60,18 +64,9 @@ pub trait OfflineClientT<T: Config>: Clone + Send + Sync + 'static {
/// A client that is capable of performing offline-only operations.
/// Can be constructed as long as you can populate the required fields.
#[derive(Derivative)]
#[derivative(Debug(bound = ""), Clone(bound = ""))]
#[derive_where(Debug, Clone)]
pub struct OfflineClient<T: Config> {
inner: Arc<Inner<T>>,
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""), Clone(bound = ""))]
struct Inner<T: Config> {
genesis_hash: T::Hash,
runtime_version: RuntimeVersion,
metadata: Metadata,
inner: Arc<ClientState<T>>,
}
impl<T: Config> OfflineClient<T> {
@@ -83,27 +78,36 @@ impl<T: Config> OfflineClient<T> {
metadata: impl Into<Metadata>,
) -> OfflineClient<T> {
OfflineClient {
inner: Arc::new(Inner {
inner: Arc::new(ClientState::new(
genesis_hash,
runtime_version,
metadata: metadata.into(),
}),
metadata.into(),
)),
}
}
/// Return the genesis hash.
pub fn genesis_hash(&self) -> T::Hash {
self.inner.genesis_hash
self.inner.genesis_hash()
}
/// Return the runtime version.
pub fn runtime_version(&self) -> RuntimeVersion {
self.inner.runtime_version.clone()
self.inner.runtime_version()
}
/// Return the [`Metadata`] used in this client.
pub fn metadata(&self) -> Metadata {
self.inner.metadata.clone()
self.inner.metadata()
}
/// Return the [subxt_core::client::ClientState] (metadata, runtime version and genesis hash).
pub fn client_state(&self) -> ClientState<T> {
ClientState::new(
self.inner.genesis_hash(),
self.inner.runtime_version(),
self.inner.metadata(),
)
}
// Just a copy of the most important trait methods so that people
@@ -145,6 +149,9 @@ impl<T: Config> OfflineClientT<T> for OfflineClient<T> {
fn metadata(&self) -> Metadata {
self.metadata()
}
fn client_state(&self) -> ClientState<T> {
self.client_state()
}
}
// For ergonomics; cloning a client is deliberately fairly cheap (via Arc),
+24 -13
View File
@@ -5,9 +5,7 @@
use super::{OfflineClient, OfflineClientT};
use crate::custom_values::CustomValuesClient;
use crate::{
backend::{
legacy::LegacyBackend, rpc::RpcClient, Backend, BackendExt, RuntimeVersion, StreamOfResults,
},
backend::{legacy::LegacyBackend, rpc::RpcClient, Backend, BackendExt, StreamOfResults},
blocks::{BlockRef, BlocksClient},
constants::ConstantsClient,
error::Error,
@@ -17,9 +15,10 @@ use crate::{
tx::TxClient,
Config, Metadata,
};
use derivative::Derivative;
use derive_where::derive_where;
use futures::future;
use std::sync::{Arc, RwLock};
use subxt_core::client::{ClientState, RuntimeVersion};
/// A trait representing a client that can perform
/// online actions.
@@ -30,15 +29,13 @@ pub trait OnlineClientT<T: Config>: OfflineClientT<T> {
/// A client that can be used to perform API calls (that is, either those
/// requiring an [`OfflineClientT`] or those requiring an [`OnlineClientT`]).
#[derive(Derivative)]
#[derivative(Clone(bound = ""))]
#[derive_where(Clone)]
pub struct OnlineClient<T: Config> {
inner: Arc<RwLock<Inner<T>>>,
backend: Arc<dyn Backend<T>>,
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
#[derive_where(Debug)]
struct Inner<T: Config> {
genesis_hash: T::Hash,
runtime_version: RuntimeVersion,
@@ -231,7 +228,7 @@ impl<T: Config> OnlineClient<T> {
/// let mut update_stream = updater.runtime_updates().await.unwrap();
///
/// while let Some(Ok(update)) = update_stream.next().await {
/// let version = update.runtime_version().spec_version;
/// let version = update.runtime_version().spec_version();
///
/// match updater.apply_update(update) {
/// Ok(()) => {
@@ -286,7 +283,17 @@ impl<T: Config> OnlineClient<T> {
/// Return the runtime version.
pub fn runtime_version(&self) -> RuntimeVersion {
let inner = self.inner.read().expect("shouldn't be poisoned");
inner.runtime_version.clone()
inner.runtime_version
}
/// Return the [subxt_core::client::ClientState] (metadata, runtime version and genesis hash).
pub fn client_state(&self) -> ClientState<T> {
let inner = self.inner.read().expect("shouldn't be poisoned");
ClientState::new(
inner.genesis_hash,
inner.runtime_version,
inner.metadata.clone(),
)
}
/// Change the [`RuntimeVersion`] used in this client.
@@ -310,7 +317,7 @@ impl<T: Config> OnlineClient<T> {
let inner = self.inner.read().expect("shouldn't be poisoned");
OfflineClient::new(
inner.genesis_hash,
inner.runtime_version.clone(),
inner.runtime_version,
inner.metadata.clone(),
)
}
@@ -364,6 +371,10 @@ impl<T: Config> OfflineClientT<T> for OnlineClient<T> {
fn runtime_version(&self) -> RuntimeVersion {
self.runtime_version()
}
fn client_state(&self) -> ClientState<T> {
self.client_state()
}
}
impl<T: Config> OnlineClientT<T> for OnlineClient<T> {
@@ -525,7 +536,7 @@ async fn wait_runtime_upgrade_in_finalized_block<T: Config>(
let scale_val = match chunk.to_value() {
Ok(v) => v,
Err(e) => return Some(Err(e)),
Err(e) => return Some(Err(e.into())),
};
let Some(Ok(spec_version)) = scale_val
@@ -540,7 +551,7 @@ async fn wait_runtime_upgrade_in_finalized_block<T: Config>(
// We are waiting for the chain to have the same spec version
// as sent out via the runtime subscription.
if spec_version == runtime_version.spec_version {
if spec_version == runtime_version.spec_version() {
break block_ref;
}
};
+6 -40
View File
@@ -3,17 +3,11 @@
// see LICENSE for license details.
use super::ConstantAddress;
use crate::{
client::OfflineClientT,
error::{Error, MetadataError},
metadata::DecodeWithMetadata,
Config,
};
use derivative::Derivative;
use crate::{client::OfflineClientT, error::Error, Config};
use derive_where::derive_where;
/// A client for accessing constants.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct ConstantsClient<T, Client> {
client: Client,
_marker: std::marker::PhantomData<T>,
@@ -35,20 +29,8 @@ impl<T: Config, Client: OfflineClientT<T>> ConstantsClient<T, Client> {
/// Return an error if the address was not valid or something went wrong trying to validate it (ie
/// the pallet or constant in question do not exist at all).
pub fn validate<Address: ConstantAddress>(&self, address: &Address) -> Result<(), Error> {
if let Some(actual_hash) = address.validation_hash() {
let expected_hash = self
.client
.metadata()
.pallet_by_name_err(address.pallet_name())?
.constant_hash(address.constant_name())
.ok_or_else(|| {
MetadataError::ConstantNameNotFound(address.constant_name().to_owned())
})?;
if actual_hash != expected_hash {
return Err(MetadataError::IncompatibleCodegen.into());
}
}
Ok(())
let metadata = self.client.metadata();
subxt_core::constants::validate_constant(&metadata, address).map_err(Error::from)
}
/// Access the constant at the address given, returning the type defined by this address.
@@ -59,22 +41,6 @@ impl<T: Config, Client: OfflineClientT<T>> ConstantsClient<T, Client> {
address: &Address,
) -> Result<Address::Target, Error> {
let metadata = self.client.metadata();
// 1. Validate constant shape if hash given:
self.validate(address)?;
// 2. Attempt to decode the constant into the type given:
let constant = metadata
.pallet_by_name_err(address.pallet_name())?
.constant_by_name(address.constant_name())
.ok_or_else(|| {
MetadataError::ConstantNameNotFound(address.constant_name().to_owned())
})?;
let value = <Address::Target as DecodeWithMetadata>::decode_with_metadata(
&mut constant.value(),
constant.ty(),
&metadata,
)?;
Ok(value)
subxt_core::constants::get_constant(&metadata, address).map_err(Error::from)
}
}
+1 -2
View File
@@ -4,8 +4,7 @@
//! Types associated with accessing constants.
mod constant_address;
mod constants_client;
pub use constant_address::{dynamic, Address, ConstantAddress, DynamicAddress};
pub use constants_client::ConstantsClient;
pub use subxt_core::constants::{dynamic, Address, ConstantAddress, DynamicAddress};
+12 -53
View File
@@ -1,13 +1,14 @@
use crate::client::OfflineClientT;
use crate::custom_values::custom_value_address::{CustomValueAddress, Yes};
use crate::error::MetadataError;
use crate::metadata::DecodeWithMetadata;
use crate::{Config, Error};
use derivative::Derivative;
use derive_where::derive_where;
use subxt_core::custom_values::{
get_custom_value, get_custom_value_bytes, validate_custom_value, CustomValueAddress,
};
use subxt_core::utils::Yes;
/// A client for accessing custom values stored in the metadata.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct CustomValuesClient<T, Client> {
client: Client,
_marker: std::marker::PhantomData<T>,
@@ -30,22 +31,7 @@ impl<T: Config, Client: OfflineClientT<T>> CustomValuesClient<T, Client> {
&self,
address: &Address,
) -> Result<Address::Target, Error> {
// 1. Validate custom value shape if hash given:
self.validate(address)?;
// 2. Attempt to decode custom value:
let metadata = self.client.metadata();
let custom = metadata.custom();
let custom_value = custom
.get(address.name())
.ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().to_string()))?;
let value = <Address::Target as DecodeWithMetadata>::decode_with_metadata(
&mut custom_value.bytes(),
custom_value.type_id(),
&metadata,
)?;
Ok(value)
get_custom_value(&self.client.metadata(), address).map_err(Into::into)
}
/// Access the bytes of a custom value by the address it is registered under.
@@ -53,17 +39,7 @@ impl<T: Config, Client: OfflineClientT<T>> CustomValuesClient<T, Client> {
&self,
address: &Address,
) -> Result<Vec<u8>, Error> {
// 1. Validate custom value shape if hash given:
self.validate(address)?;
// 2. Return the underlying bytes:
let metadata = self.client.metadata();
let custom = metadata.custom();
let custom_value = custom
.get(address.name())
.ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().to_string()))?;
Ok(custom_value.bytes().to_vec())
get_custom_value_bytes(&self.client.metadata(), address).map_err(Into::into)
}
/// Run the validation logic against some custom value address you'd like to access. Returns `Ok(())`
@@ -73,27 +49,12 @@ impl<T: Config, Client: OfflineClientT<T>> CustomValuesClient<T, Client> {
&self,
address: &Address,
) -> Result<(), Error> {
let metadata = self.client.metadata();
if let Some(actual_hash) = address.validation_hash() {
let custom = metadata.custom();
let custom_value = custom
.get(address.name())
.ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().into()))?;
let expected_hash = custom_value.hash();
if actual_hash != expected_hash {
return Err(MetadataError::IncompatibleCodegen.into());
}
}
if metadata.custom().get(address.name()).is_none() {
return Err(MetadataError::IncompatibleCodegen.into());
}
Ok(())
validate_custom_value(&self.client.metadata(), address).map_err(Into::into)
}
}
#[cfg(test)]
mod tests {
use crate::backend::RuntimeVersion;
use crate::custom_values::CustomValuesClient;
use crate::{Metadata, OfflineClient, SubstrateConfig};
use codec::Encode;
@@ -101,6 +62,7 @@ mod tests {
use scale_info::form::PortableForm;
use scale_info::TypeInfo;
use std::collections::BTreeMap;
use subxt_core::client::RuntimeVersion;
#[derive(Debug, Clone, PartialEq, Eq, Encode, TypeInfo, DecodeAsType)]
pub struct Person {
@@ -158,10 +120,7 @@ mod tests {
fn test_decoding() {
let client = OfflineClient::<SubstrateConfig>::new(
Default::default(),
RuntimeVersion {
spec_version: 0,
transaction_version: 0,
},
RuntimeVersion::new(0, 0),
mock_metadata(),
);
let custom_value_client = CustomValuesClient::new(client);
+1 -2
View File
@@ -4,8 +4,7 @@
//! Types associated with accessing custom types
mod custom_value_address;
mod custom_values_client;
pub use custom_value_address::{CustomValueAddress, StaticAddress, Yes};
pub use custom_values_client::CustomValuesClient;
pub use subxt_core::custom_values::{CustomValueAddress, StaticAddress};
+14 -90
View File
@@ -14,13 +14,12 @@ crate::macros::cfg_unstable_light_client! {
pub use dispatch_error::{
ArithmeticError, DispatchError, ModuleError, TokenError, TransactionalError,
};
use subxt_metadata::StorageHasher;
// Re-expose the errors we use from other crates here:
pub use crate::config::ExtrinsicParamsError;
pub use crate::metadata::Metadata;
pub use scale_decode::Error as DecodeError;
pub use scale_encode::Error as EncodeError;
pub use subxt_core::error::{ExtrinsicParamsError, MetadataError, StorageAddressError};
pub use subxt_metadata::TryFromError as MetadataTryFromError;
/// The underlying error enum, generic over the type held by the `Runtime`
@@ -81,6 +80,19 @@ pub enum Error {
Other(String),
}
impl From<subxt_core::Error> for Error {
fn from(value: subxt_core::Error) -> Self {
match value {
subxt_core::Error::Codec(e) => Error::Codec(e),
subxt_core::Error::Metadata(e) => Error::Metadata(e),
subxt_core::Error::StorageAddress(e) => Error::StorageAddress(e),
subxt_core::Error::Decode(e) => Error::Decode(e),
subxt_core::Error::Encode(e) => Error::Encode(e),
subxt_core::Error::ExtrinsicParams(e) => Error::ExtrinsicParams(e),
}
}
}
impl<'a> From<&'a str> for Error {
fn from(error: &'a str) -> Self {
Error::Other(error.into())
@@ -189,91 +201,3 @@ pub enum TransactionError {
#[error("The transaction was dropped: {0}")]
Dropped(String),
}
/// Something went wrong trying to encode a storage address.
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum StorageAddressError {
/// Storage lookup does not have the expected number of keys.
#[error("Storage lookup requires {expected} keys but more keys have been provided.")]
TooManyKeys {
/// The number of keys provided in the storage address.
expected: usize,
},
/// This storage entry in the metadata does not have the correct number of hashers to fields.
#[error("Storage entry in metadata does not have the correct number of hashers to fields")]
WrongNumberOfHashers {
/// The number of hashers in the metadata for this storage entry.
hashers: usize,
/// The number of fields in the metadata for this storage entry.
fields: usize,
},
/// We weren't given enough bytes to decode the storage address/key.
#[error("Not enough remaining bytes to decode the storage address/key")]
NotEnoughBytes,
/// We have leftover bytes after decoding the storage address.
#[error("We have leftover bytes after decoding the storage address")]
TooManyBytes,
/// The bytes of a storage address are not the expected address for decoding the storage keys of the address.
#[error("Storage address bytes are not the expected format. Addresses need to be at least 16 bytes (pallet ++ entry) and follow a structure given by the hashers defined in the metadata")]
UnexpectedAddressBytes,
/// An invalid hasher was used to reconstruct a value from a chunk of bytes that is part of a storage address. Hashers where the hash does not contain the original value are invalid for this purpose.
#[error("An invalid hasher was used to reconstruct a value with type ID {ty_id} from a hash formed by a {hasher:?} hasher. This is only possible for concat-style hashers or the identity hasher")]
HasherCannotReconstructKey {
/// Type id of the key's type.
ty_id: u32,
/// The invalid hasher that caused this error.
hasher: StorageHasher,
},
}
/// Something went wrong trying to access details in the metadata.
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum MetadataError {
/// The DispatchError type isn't available in the metadata
#[error("The DispatchError type isn't available")]
DispatchErrorNotFound,
/// Type not found in metadata.
#[error("Type with ID {0} not found")]
TypeNotFound(u32),
/// Pallet not found (index).
#[error("Pallet with index {0} not found")]
PalletIndexNotFound(u8),
/// Pallet not found (name).
#[error("Pallet with name {0} not found")]
PalletNameNotFound(String),
/// Variant not found.
#[error("Variant with index {0} not found")]
VariantIndexNotFound(u8),
/// Constant not found.
#[error("Constant with name {0} not found")]
ConstantNameNotFound(String),
/// Call not found.
#[error("Call with name {0} not found")]
CallNameNotFound(String),
/// Runtime trait not found.
#[error("Runtime trait with name {0} not found")]
RuntimeTraitNotFound(String),
/// Runtime method not found.
#[error("Runtime method with name {0} not found")]
RuntimeMethodNotFound(String),
/// Call type not found in metadata.
#[error("Call type not found in pallet with index {0}")]
CallTypeNotFoundInPallet(u8),
/// Event type not found in metadata.
#[error("Event type not found in pallet with index {0}")]
EventTypeNotFoundInPallet(u8),
/// Storage details not found in metadata.
#[error("Storage details not found in pallet with name {0}")]
StorageNotFoundInPallet(String),
/// Storage entry not found.
#[error("Storage entry {0} not found")]
StorageEntryNotFound(String),
/// The generated interface used is not compatible with the node.
#[error("The generated code is not compatible with the node")]
IncompatibleCodegen,
/// Custom value not found.
#[error("Custom value with name {0} not found")]
CustomValueNameNotFound(String),
}
+3 -8
View File
@@ -4,12 +4,11 @@
use crate::backend::{Backend, BackendExt, BlockRef};
use crate::{client::OnlineClientT, error::Error, events::Events, Config};
use derivative::Derivative;
use derive_where::derive_where;
use std::future::Future;
/// A client for working with events.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct EventsClient<T, Client> {
client: Client,
_marker: std::marker::PhantomData<T>,
@@ -65,11 +64,7 @@ where
};
let event_bytes = get_event_bytes(client.backend(), block_ref.hash()).await?;
Ok(Events::new(
client.metadata(),
block_ref.hash(),
event_bytes,
))
Ok(Events::decode_from(client.metadata(), event_bytes))
}
}
}
+16 -32
View File
@@ -5,40 +5,24 @@
//! This module exposes the types and such necessary for working with events.
//! The two main entry points into events are [`crate::OnlineClient::events()`]
//! and calls like [crate::tx::TxProgress::wait_for_finalized_success()].
use crate::client::OnlineClientT;
use crate::Error;
use subxt_core::{Config, Metadata};
mod events_client;
mod events_type;
use codec::{Decode, Encode};
pub use events_client::EventsClient;
pub use events_type::{EventDetails, Events};
use scale_decode::DecodeAsFields;
pub use subxt_core::events::{EventDetails, Events, Phase, StaticEvent};
/// Trait to uniquely identify the events's identity from the runtime metadata.
///
/// Generated API structures that represent an event implement this trait.
///
/// The trait is utilized to decode emitted events from a block, via obtaining the
/// form of the `Event` from the metadata.
pub trait StaticEvent: DecodeAsFields {
/// Pallet name.
const PALLET: &'static str;
/// Event name.
const EVENT: &'static str;
/// Returns true if the given pallet and event names match this event.
fn is_event(pallet: &str, event: &str) -> bool {
Self::PALLET == pallet && Self::EVENT == event
}
}
/// A phase of a block's execution.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Decode, Encode)]
pub enum Phase {
/// Applying an extrinsic.
ApplyExtrinsic(u32),
/// Finalizing the block.
Finalization,
/// Initializing the block.
Initialization,
/// Creates a new [`Events`] instance by fetching the corresponding bytes at `block_hash` from the client.
pub async fn new_events_from_client<T, C>(
metadata: Metadata,
block_hash: T::Hash,
client: C,
) -> Result<Events<T>, Error>
where
T: Config,
C: OnlineClientT<T>,
{
let event_bytes = events_client::get_event_bytes(client.backend(), block_hash).await?;
Ok(Events::<T>::decode_from(metadata, event_bytes))
}
+35 -9
View File
@@ -45,18 +45,44 @@ pub use getrandom as _;
pub mod backend;
pub mod blocks;
pub mod client;
pub mod config;
pub mod constants;
pub mod custom_values;
pub mod dynamic;
pub mod error;
pub mod events;
pub mod metadata;
pub mod runtime_api;
pub mod storage;
pub mod tx;
pub mod utils;
/// This module provides a [`Config`] type, which is used to define various
/// types that are important in order to speak to a particular chain.
/// [`SubstrateConfig`] provides a default set of these types suitable for the
/// default Substrate node implementation, and [`PolkadotConfig`] for a
/// Polkadot node.
pub mod config {
pub use subxt_core::config::{
polkadot, signed_extensions, substrate, BlockHash, Config, DefaultExtrinsicParams,
DefaultExtrinsicParamsBuilder, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header,
PolkadotConfig, PolkadotExtrinsicParams, RefineParams, RefineParamsData, SignedExtension,
SubstrateConfig, SubstrateExtrinsicParams,
};
pub use subxt_core::error::ExtrinsicParamsError;
}
/// Types representing the metadata obtained from a node.
pub mod metadata {
pub use subxt_core::metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata, MetadataExt};
// Expose metadata types under a sub module in case somebody needs to reference them:
pub use subxt_metadata as types;
}
/// Submit dynamic transactions.
pub mod dynamic {
pub use subxt_core::dynamic::{
constant, runtime_api_call, storage, tx, At, DecodedValue, DecodedValueThunk, Value,
};
}
// Internal helper macros
#[macro_use]
mod macros;
@@ -84,10 +110,10 @@ pub mod ext {
pub use scale_decode;
pub use scale_encode;
pub use scale_value;
pub use subxt_core;
cfg_substrate_compat! {
pub use sp_runtime;
pub use sp_core;
pub use subxt_core::ext::{sp_runtime, sp_core};
}
}
@@ -127,15 +153,15 @@ pub mod ext {
///
/// ## `crate = "..."`
///
/// Use this attribute to specify a custom path to the `subxt` crate:
/// Use this attribute to specify a custom path to the `subxt_core` crate:
///
/// ```rust
/// # pub extern crate subxt;
/// # pub mod path { pub mod to { pub use subxt; } }
/// # pub extern crate subxt_core;
/// # pub mod path { pub mod to { pub use subxt_core; } }
/// # fn main() {}
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale",
/// crate = "crate::path::to::subxt"
/// crate = "crate::path::to::subxt_core"
/// )]
/// mod polkadot {}
/// ```
+1 -1
View File
@@ -56,7 +56,7 @@ macro_rules! cfg_jsonrpsee_web {
macro_rules! cfg_reconnecting_rpc_client {
($($item:item)*) => {
$(
#[cfg(all(feature = "unstable-reconnecting-rpc-client"))]
#[cfg(feature = "unstable-reconnecting-rpc-client")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-reconnecting-rpc-client")))]
$item
)*
-74
View File
@@ -1,74 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::error::MetadataError;
use std::sync::Arc;
/// A cheaply clone-able representation of the runtime metadata received from a node.
#[derive(Clone, Debug)]
pub struct Metadata {
inner: Arc<subxt_metadata::Metadata>,
}
impl std::ops::Deref for Metadata {
type Target = subxt_metadata::Metadata;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl Metadata {
pub(crate) fn new(md: subxt_metadata::Metadata) -> Self {
Metadata {
inner: Arc::new(md),
}
}
/// Identical to `metadata.pallet_by_name()`, but returns an error if the pallet is not found.
pub fn pallet_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
self.pallet_by_name(name)
.ok_or_else(|| MetadataError::PalletNameNotFound(name.to_owned()))
}
/// Identical to `metadata.pallet_by_index()`, but returns an error if the pallet is not found.
pub fn pallet_by_index_err(
&self,
index: u8,
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
self.pallet_by_index(index)
.ok_or(MetadataError::PalletIndexNotFound(index))
}
/// Identical to `metadata.runtime_api_trait_by_name()`, but returns an error if the trait is not found.
pub fn runtime_api_trait_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::RuntimeApiMetadata, MetadataError> {
self.runtime_api_trait_by_name(name)
.ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned()))
}
}
impl From<subxt_metadata::Metadata> for Metadata {
fn from(md: subxt_metadata::Metadata) -> Self {
Metadata::new(md)
}
}
impl TryFrom<frame_metadata::RuntimeMetadataPrefixed> for Metadata {
type Error = subxt_metadata::TryFromError;
fn try_from(value: frame_metadata::RuntimeMetadataPrefixed) -> Result<Self, Self::Error> {
subxt_metadata::Metadata::try_from(value).map(Metadata::from)
}
}
impl codec::Decode for Metadata {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
subxt_metadata::Metadata::decode(input).map(Metadata::new)
}
}
+1 -2
View File
@@ -5,9 +5,8 @@
//! Types associated with executing runtime API calls.
mod runtime_client;
mod runtime_payload;
mod runtime_types;
pub use runtime_client::RuntimeApiClient;
pub use runtime_payload::{dynamic, DynamicRuntimeApiPayload, Payload, RuntimeApiPayload};
pub use runtime_types::RuntimeApi;
pub use subxt_core::runtime_api::{dynamic, DynamicRuntimeApiPayload, Payload, RuntimeApiPayload};
+2 -3
View File
@@ -5,12 +5,11 @@
use super::runtime_types::RuntimeApi;
use crate::{backend::BlockRef, client::OnlineClientT, error::Error, Config};
use derivative::Derivative;
use derive_where::derive_where;
use std::{future::Future, marker::PhantomData};
/// Execute runtime API calls.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct RuntimeApiClient<T, Client> {
client: Client,
_marker: PhantomData<T>,
+2 -3
View File
@@ -10,14 +10,13 @@ use crate::{
Config,
};
use codec::Decode;
use derivative::Derivative;
use derive_where::derive_where;
use std::{future::Future, marker::PhantomData};
use super::RuntimeApiPayload;
/// Execute runtime API calls.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct RuntimeApi<T: Config, Client> {
client: Client,
block_ref: BlockRef<T::Hash>,
+5 -7
View File
@@ -4,11 +4,8 @@
//! Types associated with accessing and working with storage items.
mod storage_address;
mod storage_client;
mod storage_key;
mod storage_type;
mod utils;
pub use storage_client::StorageClient;
@@ -17,12 +14,13 @@ pub use storage_type::{Storage, StorageKeyValuePair};
/// Types representing an address which describes where a storage
/// entry lives and how to properly decode it.
pub mod address {
pub use super::storage_address::{dynamic, Address, DynamicAddress, StorageAddress, Yes};
pub use super::storage_key::{StaticStorageKey, StorageKey};
pub use subxt_core::storage::address::{
dynamic, Address, DynamicAddress, StaticStorageKey, StorageAddress, StorageKey,
};
}
pub use storage_key::StorageKey;
pub use subxt_core::storage::StorageKey;
// For consistency with other modules, also expose
// the basic address stuff at the root of the module.
pub use storage_address::{dynamic, Address, DynamicAddress, StorageAddress};
pub use address::{dynamic, Address, DynamicAddress, StorageAddress};
+6 -6
View File
@@ -4,7 +4,7 @@
use super::{
storage_type::{validate_storage_address, Storage},
utils, StorageAddress,
StorageAddress,
};
use crate::{
backend::BlockRef,
@@ -12,12 +12,11 @@ use crate::{
error::Error,
Config,
};
use derivative::Derivative;
use derive_where::derive_where;
use std::{future::Future, marker::PhantomData};
/// Query the runtime storage.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct StorageClient<T, Client> {
client: Client,
_marker: PhantomData<T>,
@@ -51,7 +50,7 @@ where
/// Convert some storage address into the raw bytes that would be submitted to the node in order
/// to retrieve the entries at the root of the associated address.
pub fn address_root_bytes<Address: StorageAddress>(&self, address: &Address) -> Vec<u8> {
utils::storage_address_root_bytes(address)
subxt_core::storage::utils::storage_address_root_bytes(address)
}
/// Convert some storage address into the raw bytes that would be submitted to the node in order
@@ -63,7 +62,8 @@ where
&self,
address: &Address,
) -> Result<Vec<u8>, Error> {
utils::storage_address_bytes(address, &self.client.metadata())
subxt_core::storage::utils::storage_address_bytes(address, &self.client.metadata())
.map_err(Into::into)
}
}
+8 -8
View File
@@ -2,9 +2,8 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use super::storage_address::{StorageAddress, Yes};
use super::storage_key::StorageHashers;
use super::StorageKey;
use subxt_core::storage::address::{StorageAddress, StorageHashers, StorageKey};
use subxt_core::utils::Yes;
use crate::{
backend::{BackendExt, BlockRef},
@@ -14,7 +13,7 @@ use crate::{
Config,
};
use codec::Decode;
use derivative::Derivative;
use derive_where::derive_where;
use futures::StreamExt;
use std::{future::Future, marker::PhantomData};
@@ -25,8 +24,7 @@ use subxt_metadata::{PalletMetadata, StorageEntryMetadata, StorageEntryType};
pub use crate::backend::StreamOfResults;
/// Query the runtime storage.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct Storage<T: Config, Client> {
client: Client,
block_ref: BlockRef<T::Hash>,
@@ -136,7 +134,8 @@ where
validate_storage_address(address, pallet)?;
// Look up the return type ID to enable DecodeWithMetadata:
let lookup_bytes = super::utils::storage_address_bytes(address, &metadata)?;
let lookup_bytes =
subxt_core::storage::utils::storage_address_bytes(address, &metadata)?;
if let Some(data) = client.fetch_raw(lookup_bytes).await? {
let val =
decode_storage_with_metadata::<Address::Target>(&mut &*data, &metadata, entry)?;
@@ -237,7 +236,8 @@ where
let hashers = StorageHashers::new(entry, metadata.types())?;
// The address bytes of this entry:
let address_bytes = super::utils::storage_address_bytes(&address, &metadata)?;
let address_bytes =
subxt_core::storage::utils::storage_address_bytes(&address, &metadata)?;
let s = client
.backend()
.storage_fetch_descendant_values(address_bytes, block_ref.hash())
+4 -3
View File
@@ -11,15 +11,16 @@
use crate::macros::cfg_substrate_compat;
mod signer;
mod tx_client;
mod tx_payload;
mod tx_progress;
pub use subxt_core::tx as tx_payload;
pub use subxt_core::tx::signer;
// The PairSigner impl currently relies on Substrate bits and pieces, so make it an optional
// feature if we want to avoid needing sp_core and sp_runtime.
cfg_substrate_compat! {
pub use self::signer::PairSigner;
pub use signer::PairSigner;
}
pub use self::{
+3 -4
View File
@@ -16,12 +16,11 @@ use crate::{
utils::{Encoded, PhantomDataSendSync},
};
use codec::{Compact, Decode, Encode};
use derivative::Derivative;
use derive_where::derive_where;
use sp_crypto_hashing::blake2_256;
/// A client for working with transactions.
#[derive(Derivative)]
#[derivative(Clone(bound = "Client: Clone"))]
#[derive_where(Clone; Client)]
pub struct TxClient<T: Config, Client> {
client: Client,
_marker: PhantomDataSendSync<T>,
@@ -126,7 +125,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
// 3. Construct our custom additional/extra params.
let additional_and_extra_params =
<T::ExtrinsicParams as ExtrinsicParams<T>>::new(self.client.clone(), params)?;
<T::ExtrinsicParams as ExtrinsicParams<T>>::new(&self.client.client_state(), params)?;
// Return these details, ready to construct a signed extrinsic from.
Ok(PartialExtrinsic {
+11 -7
View File
@@ -6,7 +6,6 @@
use std::task::Poll;
use crate::utils::strip_compact_prefix;
use crate::{
backend::{BlockRef, StreamOfResults, TransactionStatus as BackendTxStatus},
client::OnlineClientT,
@@ -14,8 +13,9 @@ use crate::{
events::EventsClient,
Config,
};
use derivative::Derivative;
use derive_where::derive_where;
use futures::{Stream, StreamExt};
use subxt_core::utils::strip_compact_prefix;
/// This struct represents a subscription to the progress of some transaction.
pub struct TxProgress<T: Config, C> {
@@ -167,8 +167,7 @@ impl<T: Config, C: Clone> Stream for TxProgress<T, C> {
}
/// Possible transaction statuses returned from our [`TxProgress::next()`] call.
#[derive(Derivative)]
#[derivative(Debug(bound = "C: std::fmt::Debug"))]
#[derive_where(Debug; C)]
pub enum TxStatus<T: Config, C> {
/// Transaction is part of the future queue.
Validated,
@@ -221,8 +220,7 @@ impl<T: Config, C> TxStatus<T, C> {
}
/// This struct represents a transaction that has made it into a block.
#[derive(Derivative)]
#[derivative(Debug(bound = "C: std::fmt::Debug"))]
#[derive_where(Debug; C)]
pub struct TxInBlock<T: Config, C> {
block_ref: BlockRef<T::Hash>,
ext_hash: T::Hash,
@@ -321,6 +319,8 @@ impl<T: Config, C: OnlineClientT<T>> TxInBlock<T, C> {
#[cfg(test)]
mod test {
use subxt_core::client::RuntimeVersion;
use crate::{
backend::{StreamOfResults, TransactionStatus},
client::{OfflineClientT, OnlineClientT},
@@ -345,7 +345,11 @@ mod test {
unimplemented!("just a mock impl to satisfy trait bounds")
}
fn runtime_version(&self) -> crate::backend::RuntimeVersion {
fn runtime_version(&self) -> RuntimeVersion {
unimplemented!("just a mock impl to satisfy trait bounds")
}
fn client_state(&self) -> subxt_core::client::ClientState<SubstrateConfig> {
unimplemented!("just a mock impl to satisfy trait bounds")
}
}
+6 -73
View File
@@ -4,58 +4,21 @@
//! Miscellaneous utility helpers.
mod account_id;
pub mod bits;
mod era;
mod multi_address;
mod multi_signature;
mod static_type;
mod unchecked_extrinsic;
mod wrapper_opaque;
use crate::error::RpcError;
use crate::macros::cfg_jsonrpsee;
use crate::Error;
use codec::{Compact, Decode, Encode};
use derivative::Derivative;
use crate::{error::RpcError, Error};
use url::Url;
pub use account_id::AccountId32;
pub use era::Era;
pub use multi_address::MultiAddress;
pub use multi_signature::MultiSignature;
pub use static_type::Static;
pub use unchecked_extrinsic::UncheckedExtrinsic;
pub use wrapper_opaque::WrapperKeepOpaque;
pub use subxt_core::utils::{
bits, strip_compact_prefix, to_hex, AccountId32, Encoded, Era, KeyedVec, MultiAddress,
MultiSignature, PhantomDataSendSync, Static, UncheckedExtrinsic, WrapperKeepOpaque, Yes, H160,
H256, H512,
};
cfg_jsonrpsee! {
mod fetch_chain_spec;
pub use fetch_chain_spec::{fetch_chainspec_from_rpc_node, FetchChainspecError};
}
// Used in codegen
#[doc(hidden)]
pub use primitive_types::{H160, H256, H512};
/// Wraps an already encoded byte vector, prevents being encoded as a raw byte vector as part of
/// the transaction payload
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Encoded(pub Vec<u8>);
impl codec::Encode for Encoded {
fn encode(&self) -> Vec<u8> {
self.0.to_owned()
}
}
/// Decodes a compact encoded value from the beginning of the provided bytes,
/// returning the value and any remaining bytes.
pub(crate) fn strip_compact_prefix(bytes: &[u8]) -> Result<(u64, &[u8]), codec::Error> {
let cursor = &mut &*bytes;
let val = <Compact<u64>>::decode(cursor)?;
Ok((val.0, *cursor))
}
/// A URL is considered secure if it uses a secure scheme ("https" or "wss") or is referring to localhost.
///
/// Returns an error if the the string could not be parsed into a URL.
@@ -80,33 +43,3 @@ pub fn validate_url_is_secure(url: &str) -> Result<(), Error> {
Ok(())
}
}
/// A version of [`std::marker::PhantomData`] that is also Send and Sync (which is fine
/// because regardless of the generic param, it is always possible to Send + Sync this
/// 0 size type).
#[derive(Derivative, Encode, Decode, scale_info::TypeInfo)]
#[derivative(
Clone(bound = ""),
PartialEq(bound = ""),
Debug(bound = ""),
Eq(bound = ""),
Default(bound = ""),
Hash(bound = "")
)]
#[scale_info(skip_type_params(T))]
#[doc(hidden)]
pub struct PhantomDataSendSync<T>(core::marker::PhantomData<T>);
impl<T> PhantomDataSendSync<T> {
pub(crate) fn new() -> Self {
Self(core::marker::PhantomData)
}
}
unsafe impl<T> Send for PhantomDataSendSync<T> {}
unsafe impl<T> Sync for PhantomDataSendSync<T> {}
/// This represents a key-value collection and is SCALE compatible
/// with collections like BTreeMap. This has the same type params
/// as `BTreeMap` which allows us to easily swap the two during codegen.
pub type KeyedVec<K, V> = Vec<(K, V)>;
@@ -51,8 +51,8 @@ async fn chainhead_unstable_follow() {
FollowEvent::Initialized(init) => {
assert_eq!(init.finalized_block_hashes, vec![finalized_block_hash]);
if let Some(RuntimeEvent::Valid(RuntimeVersionEvent { spec })) = init.finalized_block_runtime {
assert_eq!(spec.spec_version, runtime_version.spec_version);
assert_eq!(spec.transaction_version, runtime_version.transaction_version);
assert_eq!(spec.spec_version, runtime_version.spec_version());
assert_eq!(spec.transaction_version, runtime_version.transaction_version());
} else {
panic!("runtime details not provided with init event, got {:?}", init.finalized_block_runtime);
}
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More