mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-22 02:08:00 +00:00
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:
@@ -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
@@ -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
@@ -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.
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
# Subxt-Core
|
||||
|
||||
This library provides a no-std compatible subset of functionality that `subxt` and `subxt-signer` rely on.
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
+5
-21
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
+6
-5
@@ -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(())
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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>;
|
||||
@@ -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,
|
||||
{
|
||||
Generated
+26
-2
@@ -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"
|
||||
|
||||
@@ -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()`.
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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 }
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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`],
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
)*
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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::{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user