From a262766739d123c3c780659ffdb0125aeaf16426 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 26 Jan 2024 15:34:58 +0100 Subject: [PATCH 01/37] no-std tests and porting of subxt-metadata --- .github/workflows/rust.yml | 27 + Cargo.lock | 50 +- Cargo.toml | 11 +- metadata/Cargo.toml | 14 +- metadata/src/from_into/mod.rs | 19 +- metadata/src/from_into/v14.rs | 31 +- metadata/src/from_into/v15.rs | 4 +- metadata/src/lib.rs | 9 +- metadata/src/prelude.rs | 59 +++ metadata/src/utils/ordered_map.rs | 16 +- metadata/src/utils/retain.rs | 4 +- metadata/src/utils/validation.rs | 4 +- .../src/utils/validation/outer_enum_hashes.rs | 2 +- metadata/src/utils/variant_index.rs | 9 +- subxt/Cargo.toml | 18 +- testing/no-std-tests/.gitignore | 1 + testing/no-std-tests/Cargo.lock | 489 ++++++++++++++++++ testing/no-std-tests/Cargo.toml | 22 + testing/no-std-tests/rust-toolchain | 1 + testing/no-std-tests/src/main.rs | 49 ++ 20 files changed, 760 insertions(+), 79 deletions(-) create mode 100644 metadata/src/prelude.rs create mode 100644 testing/no-std-tests/.gitignore create mode 100644 testing/no-std-tests/Cargo.lock create mode 100644 testing/no-std-tests/Cargo.toml create mode 100644 testing/no-std-tests/rust-toolchain create mode 100644 testing/no-std-tests/src/main.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 20c5479d4e4..ec3c3e78a8b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -358,3 +358,30 @@ jobs: wasm-pack test --headless --firefox wasm-pack test --headless --chrome working-directory: signer/wasm-tests + + no-std-tests: + name: "Test (no_std)" + runs-on: ubuntu-latest + needs: [machete, docs] + timeout-minutes: 30 + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + # Note: needs nighly toolchain because otherwise we cannot define custom lang-items. + - name: Install Rust nightly toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + + - name: Rust Cache + uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 + + # Note: in `no_std` no real tests are possible, we just run a binary with some tests in it which panic upon failure. + - name: Run tests + uses: actions-rs/cargo@v1.0.3 + run: | + cargo run + working-directory: testing/no-std-tests diff --git a/Cargo.lock b/Cargo.lock index 74538d8ced0..92fad4e73d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -793,9 +793,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ "android-tzdata", "iana-time-zone", @@ -805,9 +805,9 @@ dependencies = [ [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -816,15 +816,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -1891,9 +1891,13 @@ dependencies = [ [[package]] name = "half" -version = "1.8.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "hash-db" @@ -2865,18 +2869,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -3182,7 +3186,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.4", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] @@ -3197,9 +3201,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -4573,12 +4577,14 @@ version = "0.34.0" dependencies = [ "assert_matches", "bitvec", + "cfg-if", "criterion", + "derive_more", "frame-metadata 16.0.0", + "hashbrown 0.14.3", "parity-scale-codec", "scale-info", "sp-core-hashing", - "thiserror", ] [[package]] @@ -5312,9 +5318,9 @@ checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "wasmi" -version = "0.31.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc1e384a36ca532d070a315925887247f3c7e23567e23e0ac9b1c5d6b8bf76" +checksum = "77a8281d1d660cdf54c76a3efa9ddd0c270cada1383a995db3ccb43d166456c7" dependencies = [ "smallvec", "spin", @@ -5325,9 +5331,9 @@ dependencies = [ [[package]] name = "wasmi_arena" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" [[package]] name = "wasmi_core" diff --git a/Cargo.toml b/Cargo.toml index 1a5a07ade5b..033e3b1907e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ members = [ # We exclude any crates that would depend on non mutually # exclusive feature flags and thus can't compile with the # workspace: -exclude = ["testing/wasm-rpc-tests", "testing/wasm-lightclient-tests", "signer/wasm-tests", "examples/wasm-example", "examples/parachain-example"] +exclude = ["testing/no-std-tests", "testing/wasm-rpc-tests", "testing/wasm-lightclient-tests", "signer/wasm-tests", "examples/wasm-example", "examples/parachain-example"] resolver = "2" [workspace.package] @@ -57,16 +57,19 @@ base58 = { version = "0.2.0" } bitvec = { version = "1", default-features = false } blake2 = { version = "0.10.6", default-features = false } clap = { version = "4.4.18", features = ["derive", "cargo"] } +cfg-if = "1.0.0" criterion = "0.4" -codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false } color-eyre = "0.6.1" console_error_panic_hook = "0.1.7" darling = "0.20.3" derivative = "2.2.0" +derive_more = "0.99.17" either = "1.9.0" -frame-metadata = { version = "16.0.0", default-features = false, features = ["current", "std"] } +frame-metadata = { version = "16.0.0", default-features = false } futures = { version = "0.3.30", default-features = false, features = ["std"] } getrandom = { version = "0.2", default-features = false } +hashbrown = "0.14.3" hex = "0.4.3" heck = "0.4.1" impl-serde = { version = "0.4.0" } @@ -78,7 +81,7 @@ proc-macro-error = "1.0.4" proc-macro2 = "1.0.78" quote = "1.0.35" regex = "1.10.3" -scale-info = "2.10.0" +scale-info = { version = "2.10.0", default-features = false } scale-value = "0.13.0" scale-bits = "0.4.0" scale-decode = "0.10.0" diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index 6e3669278d7..e1391650b6e 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -13,12 +13,18 @@ documentation.workspace = true homepage.workspace = true description = "Command line utilities for checking metadata compatibility between nodes." +[features] +default = ["std"] +std = ["scale-info/std", "frame-metadata/std"] + [dependencies] -codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] } -frame-metadata = { workspace = true } -scale-info = { workspace = true } +scale-info = { workspace = true, default-features = false } +frame-metadata = { workspace = true, default-features = false, features = ["current", "decode"] } +codec = { package = "parity-scale-codec", workspace = true, default-features = false, features = ["derive"] } sp-core-hashing = { workspace = true } -thiserror = { workspace = true } +hashbrown = { workspace = true } +derive_more = { workspace = true } +cfg-if = { workspace = true } [dev-dependencies] bitvec = { workspace = true, features = ["alloc"] } diff --git a/metadata/src/from_into/mod.rs b/metadata/src/from_into/mod.rs index 8170bc6133e..33074a2db05 100644 --- a/metadata/src/from_into/mod.rs +++ b/metadata/src/from_into/mod.rs @@ -2,31 +2,38 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. +use crate::prelude::*; +use derive_more::Display; +use string::String; + mod v14; mod v15; /// An error emitted if something goes wrong converting [`frame_metadata`] /// types into [`crate::Metadata`]. -#[derive(Debug, thiserror::Error, PartialEq, Eq)] +#[derive(Debug, Display, PartialEq, Eq)] #[non_exhaustive] pub enum TryFromError { /// Type missing from type registry - #[error("Type id {0} is expected but not found in the type registry")] + #[display(fmt = "Type id {_0} is expected but not found in the type registry")] TypeNotFound(u32), /// Type was not a variant/enum type - #[error("Type {0} was not a variant/enum type, but is expected to be one")] + #[display(fmt = "Type {_0} was not a variant/enum type, but is expected to be one")] VariantExpected(u32), /// An unsupported metadata version was provided. - #[error("Cannot convert v{0} metadata into Metadata type")] + #[display(fmt = "Cannot convert v{_0} metadata into Metadata type")] UnsupportedMetadataVersion(u32), /// Type name missing from type registry - #[error("Type name {0} is expected but not found in the type registry")] + #[display(fmt = "Type name {_0} is expected but not found in the type registry")] TypeNameNotFound(String), /// Invalid type path. - #[error("Type has an invalid path {0}")] + #[display(fmt = "Type has an invalid path {_0}")] InvalidTypePath(String), } +#[cfg(feature = "std")] +impl std::error::Error for TryFromError {} + impl From for frame_metadata::RuntimeMetadataPrefixed { fn from(value: crate::Metadata) -> Self { let m: frame_metadata::v15::RuntimeMetadataV15 = value.into(); diff --git a/metadata/src/from_into/v14.rs b/metadata/src/from_into/v14.rs index 7df5e953475..a324aa89534 100644 --- a/metadata/src/from_into/v14.rs +++ b/metadata/src/from_into/v14.rs @@ -2,12 +2,16 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use std::collections::HashMap; - use super::TryFromError; +use crate::prelude::*; use crate::Metadata; +use borrow::ToOwned; +use fmt::Write; use frame_metadata::{v14, v15}; +use hashbrown::HashMap; use scale_info::TypeDef; +use string::String; +use vec::Vec; impl TryFrom for Metadata { type Error = TryFromError; @@ -31,27 +35,27 @@ fn v15_to_v14(mut metadata: v15::RuntimeMetadataV15) -> v14::RuntimeMetadataV14 let extrinsic_type = scale_info::Type { path: scale_info::Path { segments: vec![ - "primitives".to_string(), - "runtime".to_string(), - "generic".to_string(), - "UncheckedExtrinsic".to_string(), + "primitives".to_owned(), + "runtime".to_owned(), + "generic".to_owned(), + "UncheckedExtrinsic".to_owned(), ], }, type_params: vec![ scale_info::TypeParameter:: { - name: "Address".to_string(), + name: "Address".to_owned(), ty: Some(metadata.extrinsic.address_ty), }, scale_info::TypeParameter:: { - name: "Call".to_string(), + name: "Call".to_owned(), ty: Some(metadata.extrinsic.call_ty), }, scale_info::TypeParameter:: { - name: "Signature".to_string(), + name: "Signature".to_owned(), ty: Some(metadata.extrinsic.signature_ty), }, scale_info::TypeParameter:: { - name: "Extra".to_string(), + name: "Extra".to_owned(), ty: Some(metadata.extrinsic.extra_ty), }, ], @@ -342,7 +346,7 @@ fn generate_outer_enums( let Some(last) = call_path.last_mut() else { return Err(TryFromError::InvalidTypePath("RuntimeCall".into())); }; - *last = "RuntimeError".to_string(); + *last = "RuntimeError".to_owned(); generate_outer_error_enum_type(metadata, call_path) }; @@ -368,7 +372,10 @@ fn generate_outer_error_enum_type( return None; }; - let path = format!("{}Error", pallet.name); + // Note: using the `format!` macro like in `let path = format!("{}Error", pallet.name);` + // leads to linker errors about extern function `_Unwind_Resume` not being defined. + let mut path = String::new(); + write!(path, "{}Error", pallet.name).expect("Cannot panic, qed;"); let ty = error.ty.id.into(); Some(scale_info::Variant { diff --git a/metadata/src/from_into/v15.rs b/metadata/src/from_into/v15.rs index f210089dcea..5021da1b8d1 100644 --- a/metadata/src/from_into/v15.rs +++ b/metadata/src/from_into/v15.rs @@ -3,6 +3,7 @@ // see LICENSE for license details. use super::TryFromError; +use crate::prelude::*; use crate::utils::variant_index::VariantIndex; use crate::{ utils::ordered_map::OrderedMap, ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata, @@ -10,9 +11,10 @@ use crate::{ RuntimeApiMethodParamMetadata, SignedExtensionMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata, }; +use borrow::ToOwned; use frame_metadata::v15; +use hashbrown::HashMap; use scale_info::form::PortableForm; -use std::collections::HashMap; // Converting from V15 metadata into our Subxt repr. mod from_v15 { diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 4ebc68a44ec..ad2503235aa 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -14,16 +14,21 @@ //! 2. Obtaining [`frame_metadata::RuntimeMetadataPrefixed`], and then //! using `.try_into()` to convert it into [`Metadata`]. +#![cfg_attr(not(feature = "std"), no_std)] #![deny(missing_docs)] mod from_into; +mod prelude; mod utils; +use hashbrown::HashMap; +use prelude::*; use scale_info::{form::PortableForm, PortableRegistry, Variant}; -use std::collections::HashMap; -use std::sync::Arc; +use string::String; +use sync::Arc; use utils::variant_index::VariantIndex; use utils::{ordered_map::OrderedMap, validation::outer_enum_hashes::OuterEnumHashes}; +use vec::Vec; type ArcStr = Arc; diff --git a/metadata/src/prelude.rs b/metadata/src/prelude.rs new file mode 100644 index 00000000000..007bd44665a --- /dev/null +++ b/metadata/src/prelude.rs @@ -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. + +#[cfg(not(feature = "std"))] +extern crate alloc; + +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "std")] { + #[allow(unused)] + pub use std::{ + any, + borrow, + boxed, + cmp, + collections, + fmt, + format, + hash, + marker, + mem, + num, + ops, + string, + sync, + time, + vec, + rc, + iter, + }; + } else { + #[allow(unused)] + pub use alloc::{ + borrow, + boxed, + collections, + format, + string, + sync, + vec, + rc + }; + #[allow(unused)] + pub use core::{ + any, + cmp, + fmt, + hash, + marker, + mem, + num, + ops, + time, + iter, + }; + } +} diff --git a/metadata/src/utils/ordered_map.rs b/metadata/src/utils/ordered_map.rs index c9a9a40f468..8df2ade3a34 100644 --- a/metadata/src/utils/ordered_map.rs +++ b/metadata/src/utils/ordered_map.rs @@ -2,7 +2,9 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use std::collections::HashMap; +use crate::prelude::*; +use hashbrown::HashMap; +use vec::Vec; /// A minimal ordered map to let one search for /// things by key or get the values in insert order. @@ -23,7 +25,7 @@ impl Default for OrderedMap { impl OrderedMap where - K: PartialEq + Eq + std::hash::Hash, + K: PartialEq + Eq + hash::Hash, { /// Create a new, empty [`OrderedMap`]. pub fn new() -> Self { @@ -47,8 +49,8 @@ where where F: FnMut(&V) -> bool, { - let values = std::mem::take(&mut self.values); - let map = std::mem::take(&mut self.map); + let values = mem::take(&mut self.values); + let map = mem::take(&mut self.map); // Filter the values, storing a map from old to new positions: let mut new_values = Vec::new(); @@ -78,8 +80,8 @@ where /// Get an item by its key. pub fn get_by_key(&self, key: &Q) -> Option<&V> where - K: std::borrow::Borrow, - Q: std::hash::Hash + Eq + ?Sized, + K: borrow::Borrow, + Q: hash::Hash + Eq + ?Sized, { self.map.get(key).and_then(|&v| self.values.get(v)) } @@ -107,7 +109,7 @@ where impl FromIterator<(K, V)> for OrderedMap where - K: PartialEq + Eq + std::hash::Hash, + K: PartialEq + Eq + hash::Hash, { fn from_iter>(iter: T) -> Self { let mut map = OrderedMap::new(); diff --git a/metadata/src/utils/retain.rs b/metadata/src/utils/retain.rs index d33315c2aa8..5e26bd2e83a 100644 --- a/metadata/src/utils/retain.rs +++ b/metadata/src/utils/retain.rs @@ -4,12 +4,14 @@ //! Utility functions to generate a subset of the metadata. +use crate::prelude::*; use crate::{ ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner, StorageEntryType, }; +use collections::BTreeMap; +use hashbrown::HashSet; use scale_info::TypeDef; -use std::collections::{BTreeMap, HashSet}; /// Collect all type IDs needed to represent the provided pallet. fn collect_pallet_types(pallet: &PalletMetadataInner, type_ids: &mut HashSet) { diff --git a/metadata/src/utils/validation.rs b/metadata/src/utils/validation.rs index 81c50b45938..e36d05810d9 100644 --- a/metadata/src/utils/validation.rs +++ b/metadata/src/utils/validation.rs @@ -4,13 +4,15 @@ //! Utility functions for metadata validation. +use crate::prelude::*; use crate::{ CustomMetadata, CustomValueMetadata, ExtrinsicMetadata, Metadata, PalletMetadata, RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata, StorageEntryType, }; +use hashbrown::HashMap; use outer_enum_hashes::OuterEnumHashes; use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefVariant, Variant}; -use std::collections::HashMap; +use vec::Vec; pub mod outer_enum_hashes; diff --git a/metadata/src/utils/validation/outer_enum_hashes.rs b/metadata/src/utils/validation/outer_enum_hashes.rs index 33bd4f52c19..5e50fcb40ef 100644 --- a/metadata/src/utils/validation/outer_enum_hashes.rs +++ b/metadata/src/utils/validation/outer_enum_hashes.rs @@ -1,6 +1,6 @@ //! Hash representations of the `frame_metadata::v15::OuterEnums`. -use std::collections::HashMap; +use hashbrown::HashMap; use scale_info::{PortableRegistry, TypeDef}; diff --git a/metadata/src/utils/variant_index.rs b/metadata/src/utils/variant_index.rs index bcc8a789025..31aed098b64 100644 --- a/metadata/src/utils/variant_index.rs +++ b/metadata/src/utils/variant_index.rs @@ -2,8 +2,11 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. +use crate::prelude::*; +use borrow::ToOwned; +use hashbrown::HashMap; use scale_info::{form::PortableForm, PortableRegistry, TypeDef, Variant}; -use std::collections::HashMap; +use string::String; /// Given some type ID and type registry, build a couple of /// indexes to look up variants by index or name. If the ID provided @@ -62,8 +65,8 @@ impl VariantIndex { types: &'a PortableRegistry, ) -> Option<&'a Variant> where - String: std::borrow::Borrow, - K: std::hash::Hash + Eq + ?Sized, + String: borrow::Borrow, + K: hash::Hash + Eq + ?Sized, { let pos = *self.by_name.get(name)?; let variants = Self::get(variant_id, types)?; diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index 0686a8c897a..6f6b664a0f9 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -24,23 +24,11 @@ default = ["jsonrpsee", "native"] # Enable this for native (ie non web/wasm builds). # Exactly 1 of "web" and "native" is expected. -native = [ - "jsonrpsee?/async-client", - "jsonrpsee?/client-ws-transport-native-tls", - "subxt-lightclient?/native", - "tokio-util" -] +native = ["jsonrpsee?/async-client", "jsonrpsee?/client-ws-transport-native-tls", "subxt-lightclient?/native", "tokio-util"] # Enable this for web/wasm builds. # Exactly 1 of "web" and "native" is expected. -web = [ - "jsonrpsee?/async-wasm-client", - "jsonrpsee?/client-web-transport", - "getrandom/js", - "subxt-lightclient?/web", - "subxt-macro/web", - "instant/wasm-bindgen" -] +web = ["jsonrpsee?/async-wasm-client", "jsonrpsee?/client-web-transport", "getrandom/js", "subxt-lightclient?/web", "subxt-macro/web", "instant/wasm-bindgen"] # Enable this to use jsonrpsee (allowing for example `OnlineClient::from_url`). jsonrpsee = ["dep:jsonrpsee"] @@ -97,7 +85,7 @@ sp-runtime = { workspace = true, optional = true } # Other subxt crates we depend on. subxt-macro = { workspace = true } -subxt-metadata = { workspace = true } +subxt-metadata = { workspace = true, features = ["std"] } subxt-lightclient = { workspace = true, optional = true, default-features = false } # Light client support: diff --git a/testing/no-std-tests/.gitignore b/testing/no-std-tests/.gitignore new file mode 100644 index 00000000000..c41cc9e35e3 --- /dev/null +++ b/testing/no-std-tests/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock new file mode 100644 index 00000000000..e1d9e0d46e0 --- /dev/null +++ b/testing/no-std-tests/Cargo.lock @@ -0,0 +1,489 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "frame-metadata" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libc_alloc" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a835c038b748123287f9fb1743d565e7c635879997f43c345a18a026690364e" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parity-scale-codec" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +dependencies = [ + "arrayvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +dependencies = [ + "proc-macro-crate 2.0.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "scale-info" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +dependencies = [ + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sp-core-hashing" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0f4990add7b2cefdeca883c0efa99bb4d912cb2196120e1500c0cc099553b0" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest", + "sha2", + "sha3", + "twox-hash", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subxt-core-no-std-tests" +version = "0.0.0" +dependencies = [ + "libc", + "libc_alloc", + "parity-scale-codec", + "subxt-metadata", +] + +[[package]] +name = "subxt-metadata" +version = "0.34.0" +dependencies = [ + "cfg-if", + "derive_more", + "frame-metadata", + "hashbrown", + "parity-scale-codec", + "scale-info", + "sp-core-hashing", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/testing/no-std-tests/Cargo.toml b/testing/no-std-tests/Cargo.toml new file mode 100644 index 00000000000..29a4bca2423 --- /dev/null +++ b/testing/no-std-tests/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "subxt-core-no-std-tests" +edition = "2021" +publish = false +version = "0.0.0" +resolver = "2" + +[dependencies] +subxt-metadata = { path = "../../metadata", default-features = false } +codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] } +libc = { version = "0.2", default-features = false } +libc_alloc = { version = "1.0.6" } + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +# this shouldn't be needed, it's in workspace.exclude, but still +# I get the complaint unless I add it... +[workspace] diff --git a/testing/no-std-tests/rust-toolchain b/testing/no-std-tests/rust-toolchain new file mode 100644 index 00000000000..07ade694b1a --- /dev/null +++ b/testing/no-std-tests/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs new file mode 100644 index 00000000000..0caff7a433b --- /dev/null +++ b/testing/no-std-tests/src/main.rs @@ -0,0 +1,49 @@ +// 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. + +#![allow(internal_features)] +#![feature(lang_items, start)] +#![feature(alloc_error_handler)] +#![no_std] + +#[start] +fn start(_argc: isize, _argv: *const *const u8) -> isize { + run_tests(); + 0 +} + +#[lang = "eh_personality"] +#[no_mangle] +pub extern "C" fn rust_eh_personality() {} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + unsafe { + libc::abort(); + } +} + +use libc_alloc::LibcAlloc; + +#[global_allocator] +static ALLOCATOR: LibcAlloc = LibcAlloc; + +////////////////////////////////////////////////////////////////////////////// + +extern crate alloc; + +// Note: Panics in this function will lead to `Aborted (core dumped)` and a non-zero exit status => suitable for CI tests. +fn run_tests() { + subxt_metadata_test(); +} + +/// Makes sure, subxt-metadata works in a no-std-context: +fn subxt_metadata_test() { + use codec::Decode; + let bytes: alloc::vec::Vec = alloc::vec![0, 1, 2, 3, 4]; + subxt_metadata::Metadata::decode(&mut &bytes[..]).expect_err("invalid byte sequence"); + + const METADATA: &[u8] = include_bytes!("../../../artifacts/polkadot_metadata_small.scale"); + subxt_metadata::Metadata::decode(&mut &METADATA[..]).expect("should be valid metadata"); +} From 55cfb232b17eb36e860525d9bca2c073ce83544e Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 26 Jan 2024 15:52:33 +0100 Subject: [PATCH 02/37] update pipeline --- .github/workflows/rust.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ec3c3e78a8b..e7efea4cb56 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -382,6 +382,7 @@ jobs: # Note: in `no_std` no real tests are possible, we just run a binary with some tests in it which panic upon failure. - name: Run tests uses: actions-rs/cargo@v1.0.3 - run: | - cargo run - working-directory: testing/no-std-tests + with: + command: cargo + args: run + working-directory: testing/no-std-tests From 1b4e37105110e0078df49d753ca4f97970c15d6e Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 26 Jan 2024 16:45:38 +0100 Subject: [PATCH 03/37] fix generate custom metadata test --- testing/generate-custom-metadata/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testing/generate-custom-metadata/Cargo.toml b/testing/generate-custom-metadata/Cargo.toml index 9ebff6bb133..14da285a13b 100644 --- a/testing/generate-custom-metadata/Cargo.toml +++ b/testing/generate-custom-metadata/Cargo.toml @@ -12,6 +12,6 @@ homepage.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -scale-info = { workspace = true, features = ["bit-vec"] } -frame-metadata = { workspace = true } -codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] } +scale-info = { workspace = true, features = ["std", "bit-vec"] } +frame-metadata = { workspace = true, features = ["decode", "current"] } +codec = { package = "parity-scale-codec", workspace = true, features = ["std", "derive", "bit-vec"] } From d679e95edd8f98648dfa9a73fe8c41661a9cba81 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 26 Jan 2024 17:52:57 +0100 Subject: [PATCH 04/37] fix cargo run command --- .github/workflows/rust.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e7efea4cb56..97222acd695 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -383,6 +383,5 @@ jobs: - name: Run tests uses: actions-rs/cargo@v1.0.3 with: - command: cargo - args: run + command: run working-directory: testing/no-std-tests From 795481531723028ff557f504a023683aac485a78 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Mon, 29 Jan 2024 11:46:34 +0100 Subject: [PATCH 05/37] adjust pipeline --- .github/workflows/rust.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 97222acd695..b8e54652a67 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -380,8 +380,7 @@ jobs: uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 # Note: in `no_std` no real tests are possible, we just run a binary with some tests in it which panic upon failure. - - name: Run tests - uses: actions-rs/cargo@v1.0.3 - with: - command: run - working-directory: testing/no-std-tests + - name: Run no_std tests + run: | + cargo run + working-directory: testing/no-std-tests From a3ec38f8b683a41ccb85de6d74ebd8be3a5bf12e Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Mon, 29 Jan 2024 15:08:42 +0100 Subject: [PATCH 06/37] init of subxt-core crate --- Cargo.lock | 30 ++ Cargo.toml | 9 +- core/Cargo.toml | 68 +++ core/README.md | 3 + core/src/client/mod.rs | 51 ++ core/src/config/default_extrinsic_params.rs | 144 ++++++ core/src/config/extrinsic_params.rs | 94 ++++ core/src/config/mod.rs | 154 ++++++ core/src/config/polkadot.rs | 33 ++ core/src/config/signed_extensions.rs | 500 ++++++++++++++++++++ core/src/config/substrate.rs | 342 +++++++++++++ core/src/dynamic.rs | 83 ++++ core/src/lib.rs | 25 + core/src/macros.rs | 17 + core/src/metadata/decode_encode_traits.rs | 52 ++ core/src/metadata/metadata_type.rs | 127 +++++ core/src/metadata/mod.rs | 14 + core/src/prelude.rs | 59 +++ core/src/tx/mod.rs | 2 + core/src/tx/signer.rs | 100 ++++ core/src/tx/tx_payload.rs | 192 ++++++++ core/src/utils/account_id.rs | 225 +++++++++ core/src/utils/bits.rs | 268 +++++++++++ core/src/utils/era.rs | 107 +++++ core/src/utils/mod.rs | 99 ++++ core/src/utils/multi_address.rs | 74 +++ core/src/utils/multi_signature.rs | 58 +++ core/src/utils/static_type.rs | 80 ++++ core/src/utils/unchecked_extrinsic.rs | 136 ++++++ core/src/utils/wrapper_opaque.rs | 245 ++++++++++ testing/no-std-tests/Cargo.lock | 500 +++++++++++++++++++- testing/no-std-tests/Cargo.toml | 1 + testing/no-std-tests/src/main.rs | 5 + testing/no-std-tests/tree.txt | 215 +++++++++ 34 files changed, 4107 insertions(+), 5 deletions(-) create mode 100644 core/Cargo.toml create mode 100644 core/README.md create mode 100644 core/src/client/mod.rs create mode 100644 core/src/config/default_extrinsic_params.rs create mode 100644 core/src/config/extrinsic_params.rs create mode 100644 core/src/config/mod.rs create mode 100644 core/src/config/polkadot.rs create mode 100644 core/src/config/signed_extensions.rs create mode 100644 core/src/config/substrate.rs create mode 100644 core/src/dynamic.rs create mode 100644 core/src/lib.rs create mode 100644 core/src/macros.rs create mode 100644 core/src/metadata/decode_encode_traits.rs create mode 100644 core/src/metadata/metadata_type.rs create mode 100644 core/src/metadata/mod.rs create mode 100644 core/src/prelude.rs create mode 100644 core/src/tx/mod.rs create mode 100644 core/src/tx/signer.rs create mode 100644 core/src/tx/tx_payload.rs create mode 100644 core/src/utils/account_id.rs create mode 100644 core/src/utils/bits.rs create mode 100644 core/src/utils/era.rs create mode 100644 core/src/utils/mod.rs create mode 100644 core/src/utils/multi_address.rs create mode 100644 core/src/utils/multi_signature.rs create mode 100644 core/src/utils/static_type.rs create mode 100644 core/src/utils/unchecked_extrinsic.rs create mode 100644 core/src/utils/wrapper_opaque.rs create mode 100644 testing/no-std-tests/tree.txt diff --git a/Cargo.lock b/Cargo.lock index 1cb864285b0..85bcecf80ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4533,6 +4533,36 @@ dependencies = [ "tokio", ] +[[package]] +name = "subxt-core" +version = "0.34.0" +dependencies = [ + "base58", + "bitvec", + "blake2", + "cfg-if", + "derivative", + "derive_more", + "frame-metadata 16.0.0", + "hex", + "impl-serde", + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-decode", + "scale-encode", + "scale-info", + "scale-value", + "serde", + "serde_json", + "sp-core", + "sp-core-hashing", + "sp-keyring", + "sp-runtime", + "subxt-metadata", + "url", +] + [[package]] name = "subxt-lightclient" version = "0.34.0" diff --git a/Cargo.toml b/Cargo.toml index 41241ebe0c2..9608f9176fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "cli", "codegen", + "core", "lightclient", "testing/substrate-runner", "testing/test-runtime", @@ -82,10 +83,10 @@ proc-macro2 = "1.0.78" quote = "1.0.35" regex = "1.10.3" scale-info = { version = "2.10.0", default-features = false } -scale-value = "0.13.0" -scale-bits = "0.4.0" -scale-decode = "0.10.0" -scale-encode = "0.5.0" +scale-value = { version = "0.13.0", default-features = false } +scale-bits = { version = "0.4.0", default-features = false } +scale-decode = { version = "0.10.0", default-features = false } +scale-encode = { version = "0.5.0", default-features = false } serde = { version = "1.0.196" } serde_json = { version = "1.0.113" } syn = { version = "2.0.15", features = ["full", "extra-traits"] } diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 00000000000..98947ffc654 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,68 @@ +[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 = "Sign extrinsics to be submitted by Subxt" +keywords = ["parity", "subxt", "extrinsic", "no-std"] + +[features] +# default = ["std"] +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 } +derivative = { workspace = true, features = ["use_core"] } +derive_more = { workspace = true } +hex = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true, features = ["raw_value"] } + +# 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 } +primitive-types = { workspace = true, default-features = false, features = ["codec", "serde_no_std", "scale-info"] } +sp-core-hashing = { workspace = true } + +# For parsing urls to disallow insecure schemes +url = { workspace = true } + +# Included if the "substrate-compat" feature is enabled. +sp-core = { workspace = true, optional = true } +sp-runtime = { workspace = true, optional = true } +cfg-if = { workspace = true } + +[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 diff --git a/core/README.md b/core/README.md new file mode 100644 index 00000000000..745b0fea121 --- /dev/null +++ b/core/README.md @@ -0,0 +1,3 @@ +# Subxt-Core + +This library provides core functionality using in `subxt` and `subxt-signer`. It should be no-std compatible. \ No newline at end of file diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs new file mode 100644 index 00000000000..919154b836a --- /dev/null +++ b/core/src/client/mod.rs @@ -0,0 +1,51 @@ +use derivative::Derivative; + +use crate::{config::Config, metadata::Metadata}; + +/// the base for a client should be: +/// - runtime version +/// - genesis hash +/// - metadata + +#[derive(Derivative)] +#[derivative(Debug(bound = ""))] +pub struct MinimalClient { + pub genesis_hash: C::Hash, + pub runtime_version: RuntimeVersion, + pub metadata: Metadata, + marker: core::marker::PhantomData, +} + +impl MinimalClient { + 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 { + /// 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, +} diff --git a/core/src/config/default_extrinsic_params.rs b/core/src/config/default_extrinsic_params.rs new file mode 100644 index 00000000000..dce83853bf2 --- /dev/null +++ b/core/src/config/default_extrinsic_params.rs @@ -0,0 +1,144 @@ +// 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 super::{signed_extensions, ExtrinsicParams}; +use super::{Config, Header}; + +/// The default [`super::ExtrinsicParams`] implementation understands common signed extensions +/// and how to apply them to a given chain. +pub type DefaultExtrinsicParams = signed_extensions::AnyOf< + T, + ( + signed_extensions::CheckSpecVersion, + signed_extensions::CheckTxVersion, + signed_extensions::CheckNonce, + signed_extensions::CheckGenesis, + signed_extensions::CheckMortality, + signed_extensions::ChargeAssetTxPayment, + signed_extensions::ChargeTransactionPayment, + ), +>; + +/// A builder that outputs the set of [`super::ExtrinsicParams::OtherParams`] required for +/// [`DefaultExtrinsicParams`]. This may expose methods that aren't applicable to the current +/// chain; such values will simply be ignored if so. +pub struct DefaultExtrinsicParamsBuilder { + /// `None` means the tx will be immortal. + mortality: Option>, + /// `None` means we'll use the native token. + tip_of_asset_id: Option, + tip: u128, + tip_of: u128, +} + +struct Mortality { + /// Block hash that mortality starts from + checkpoint_hash: Hash, + /// Block number that mortality starts from (must + // point to the same block as the hash above) + checkpoint_number: u64, + /// How many blocks the tx is mortal for + period: u64, +} + +impl Default for DefaultExtrinsicParamsBuilder { + fn default() -> Self { + Self { + mortality: None, + tip: 0, + tip_of: 0, + tip_of_asset_id: None, + } + } +} + +impl DefaultExtrinsicParamsBuilder { + /// Configure new extrinsic params. We default to providing no tip + /// and using an immortal transaction unless otherwise configured + pub fn new() -> Self { + Default::default() + } + + /// Make the transaction mortal, given a block header that it should be mortal from, + /// and the number of blocks (roughly; it'll be rounded to a power of two) that it will + /// be mortal for. + pub fn mortal(mut self, from_block: &T::Header, for_n_blocks: u64) -> Self { + self.mortality = Some(Mortality { + checkpoint_hash: from_block.hash(), + checkpoint_number: from_block.number().into(), + period: for_n_blocks, + }); + self + } + + /// Make the transaction mortal, given a block number and block hash (which must both point to + /// the same block) that it should be mortal from, and the number of blocks (roughly; it'll be + /// rounded to a power of two) that it will be mortal for. + /// + /// Prefer to use [`DefaultExtrinsicParamsBuilder::mortal()`], which ensures that the block hash + /// and number align. + pub fn mortal_unchecked( + mut self, + from_block_number: u64, + from_block_hash: T::Hash, + for_n_blocks: u64, + ) -> Self { + self.mortality = Some(Mortality { + checkpoint_hash: from_block_hash, + checkpoint_number: from_block_number, + period: for_n_blocks, + }); + self + } + + /// Provide a tip to the block author in the chain's native token. + pub fn tip(mut self, tip: u128) -> Self { + self.tip = tip; + self.tip_of = tip; + self.tip_of_asset_id = None; + self + } + + /// Provide a tip to the block author using the token denominated by the `asset_id` provided. This + /// is not applicable on chains which don't use the `ChargeAssetTxPayment` signed extension; in this + /// case, no tip will be given. + pub fn tip_of(mut self, tip: u128, asset_id: T::AssetId) -> Self { + self.tip = 0; + self.tip_of = tip; + self.tip_of_asset_id = Some(asset_id); + self + } + + /// Build the extrinsic parameters. + pub fn build(self) -> as ExtrinsicParams>::OtherParams { + let check_mortality_params = if let Some(mortality) = self.mortality { + signed_extensions::CheckMortalityParams::mortal( + mortality.period, + mortality.checkpoint_number, + mortality.checkpoint_hash, + ) + } else { + signed_extensions::CheckMortalityParams::immortal() + }; + + let charge_asset_tx_params = if let Some(asset_id) = self.tip_of_asset_id { + signed_extensions::ChargeAssetTxPaymentParams::tip_of(self.tip, asset_id) + } else { + signed_extensions::ChargeAssetTxPaymentParams::tip(self.tip) + }; + + let charge_transaction_params = + signed_extensions::ChargeTransactionPaymentParams::tip(self.tip); + + ( + (), + (), + (), + (), + check_mortality_params, + charge_asset_tx_params, + charge_transaction_params, + ) + } +} diff --git a/core/src/config/extrinsic_params.rs b/core/src/config/extrinsic_params.rs new file mode 100644 index 00000000000..79d7837e5bc --- /dev/null +++ b/core/src/config/extrinsic_params.rs @@ -0,0 +1,94 @@ +// 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. + +//! This module contains a trait which controls the parameters that must +//! be provided in order to successfully construct an extrinsic. +//! [`crate::config::DefaultExtrinsicParams`] provides a general-purpose +//! implementation of this that will work in many cases. + +use crate::client::MinimalClient; + +use super::Config; +use crate::prelude::*; +use core::fmt::Debug; +use string::String; +use vec::Vec; + +use derive_more::Display; + +/// 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(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}")] + #[cfg(feature = "std")] + Custom(CustomExtrinsicParamsError), +} + +/// A custom error. +#[cfg(feature = "std")] +pub type CustomExtrinsicParamsError = Box; + +impl From for ExtrinsicParamsError { + fn from(value: core::convert::Infallible) -> Self { + match value {} + } +} + +#[cfg(feature = "std")] +impl From for ExtrinsicParamsError { + fn from(value: CustomExtrinsicParamsError) -> Self { + ExtrinsicParamsError::Custom(value) + } +} + +/// This trait allows you to configure the "signed extra" and +/// "additional" parameters that are a part of the transaction payload +/// or the signer payload respectively. +pub trait ExtrinsicParams: ExtrinsicParamsEncoder + Sized + 'static { + /// These parameters can be provided to the constructor along with + /// some default parameters that `subxt` understands, in order to + /// help construct your [`ExtrinsicParams`] object. + type OtherParams; + + /// Construct a new instance of our [`ExtrinsicParams`]. + fn new( + nonce: u64, + client: &MinimalClient, + other_params: Self::OtherParams, + ) -> Result; +} + +/// This trait is expected to be implemented for any [`ExtrinsicParams`], and +/// defines how to encode the "additional" and "extra" params. Both functions +/// are optional and will encode nothing by default. +pub trait ExtrinsicParamsEncoder: 'static { + /// This is expected to SCALE encode the "signed extra" parameters + /// to some buffer that has been provided. These are the parameters + /// which are sent along with the transaction, as well as taken into + /// account when signing the transaction. + fn encode_extra_to(&self, _v: &mut Vec) {} + + /// This is expected to SCALE encode the "additional" parameters + /// to some buffer that has been provided. These parameters are _not_ + /// sent along with the transaction, but are taken into account when + /// signing it, meaning the client and node must agree on their values. + fn encode_additional_to(&self, _v: &mut Vec) {} +} diff --git a/core/src/config/mod.rs b/core/src/config/mod.rs new file mode 100644 index 00000000000..f14849df6ec --- /dev/null +++ b/core/src/config/mod.rs @@ -0,0 +1,154 @@ +// 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. + +//! 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. + +mod default_extrinsic_params; +mod extrinsic_params; + +pub mod polkadot; +pub mod signed_extensions; +pub mod substrate; + +use crate::macros::cfg_substrate_compat; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use scale_decode::DecodeAsType; +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 polkadot::{PolkadotConfig, PolkadotExtrinsicParams, PolkadotExtrinsicParamsBuilder}; +pub use signed_extensions::SignedExtension; +pub use substrate::{SubstrateConfig, SubstrateExtrinsicParams, SubstrateExtrinsicParamsBuilder}; + +/// Runtime types. +// Note: the `Send + Sync + 'static` bound isn't strictly required, but currently deriving +// TypeInfo automatically applies a 'static bound to all generic types (including this one), +// And we want the compiler to infer `Send` and `Sync` OK for things which have `T: Config` +// rather than having to `unsafe impl` them ourselves. +pub trait Config: Sized + Send + Sync + 'static { + /// The output of the `Hasher` function. + type Hash: BlockHash; + + /// The account ID type. + type AccountId: Debug + Clone + Encode; + + /// The address type. + type Address: Debug + Encode + From; + + /// The signature type. + type Signature: Debug + Encode; + + /// The hashing system (algorithm) being used in the runtime (e.g. Blake2). + type Hasher: Debug + Hasher; + + /// The block header. + type Header: Debug + Header + Sync + Send + DeserializeOwned; + + /// This type defines the extrinsic extra and additional parameters. + type ExtrinsicParams: ExtrinsicParams; + + /// This is used to identify an asset in the `ChargeAssetTxPayment` signed extension. + type AssetId: Debug + Clone + Encode + DecodeAsType + EncodeAsType; +} + +/// given some [`Config`], this return the other params needed for its `ExtrinsicParams`. +pub type OtherParamsFor = <::ExtrinsicParams as ExtrinsicParams>::OtherParams; + +/// Block hashes must conform to a bunch of things to be used in Subxt. +pub trait BlockHash: + Debug + + Copy + + Send + + Sync + + Decode + + AsRef<[u8]> + + Serialize + + DeserializeOwned + + Encode + + PartialEq + + Eq + + core::hash::Hash +{ +} +impl BlockHash for T where + T: Debug + + Copy + + Send + + Sync + + Decode + + AsRef<[u8]> + + Serialize + + DeserializeOwned + + Encode + + PartialEq + + Eq + + core::hash::Hash +{ +} + +/// This represents the hasher used by a node to hash things like block headers +/// and extrinsics. +pub trait Hasher { + /// The type given back from the hash operation + type Output; + + /// Hash some bytes to the given output type. + fn hash(s: &[u8]) -> Self::Output; + + /// Hash some SCALE encodable type to the given output type. + fn hash_of(s: &S) -> Self::Output { + let out = s.encode(); + Self::hash(&out) + } +} + +/// This represents the block header type used by a node. +pub trait Header: Sized + Encode + Decode { + /// The block number type for this header. + type Number: Into; + /// The hasher used to hash this header. + type Hasher: Hasher; + + /// Return the block number of this header. + fn number(&self) -> Self::Number; + + /// Hash this header. + fn hash(&self) -> ::Output { + Self::Hasher::hash_of(self) + } +} + +cfg_substrate_compat! { + /// implement subxt's Hasher and Header traits for some substrate structs + mod substrate_impls { + use super::*; + + impl Header for T + where + ::Number: Into, + { + type Number = T::Number; + type Hasher = T::Hashing; + + fn number(&self) -> Self::Number { + *self.number() + } + } + + impl Hasher for T { + type Output = T::Output; + + fn hash(s: &[u8]) -> Self::Output { + ::hash(s) + } + } + } +} diff --git a/core/src/config/polkadot.rs b/core/src/config/polkadot.rs new file mode 100644 index 00000000000..04c3d70f27c --- /dev/null +++ b/core/src/config/polkadot.rs @@ -0,0 +1,33 @@ +// 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. + +//! Polkadot specific configuration + +use super::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder}; + +use super::SubstrateConfig; +pub use crate::utils::{AccountId32, MultiAddress, MultiSignature}; +pub use primitive_types::{H256, U256}; + +/// Default set of commonly used types by Polkadot nodes. +pub enum PolkadotConfig {} + +impl Config for PolkadotConfig { + type Hash = ::Hash; + type AccountId = ::AccountId; + type Address = MultiAddress; + type Signature = ::Signature; + type Hasher = ::Hasher; + type Header = ::Header; + type ExtrinsicParams = PolkadotExtrinsicParams; + type AssetId = u32; +} + +/// A struct representing the signed extra and additional parameters required +/// to construct a transaction for a polkadot node. +pub type PolkadotExtrinsicParams = DefaultExtrinsicParams; + +/// A builder which leads to [`PolkadotExtrinsicParams`] being constructed. +/// This is what you provide to methods like `sign_and_submit()`. +pub type PolkadotExtrinsicParamsBuilder = DefaultExtrinsicParamsBuilder; diff --git a/core/src/config/signed_extensions.rs b/core/src/config/signed_extensions.rs new file mode 100644 index 00000000000..9d05c4cb536 --- /dev/null +++ b/core/src/config/signed_extensions.rs @@ -0,0 +1,500 @@ +// 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. + +//! This module contains implementations for common signed extensions, each +//! of which implements [`SignedExtension`], and can be used in conjunction with +//! [`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::Config; +use crate::client::MinimalClient; +use crate::prelude::*; +use crate::utils::Era; +use borrow::ToOwned; +use boxed::Box; +use codec::{Compact, Encode}; +use collections::BTreeMap; +use core::fmt::Debug; +use derivative::Derivative; +use scale_decode::DecodeAsType; +use scale_info::PortableRegistry; +use vec::Vec; + +/// A single [`SignedExtension`] has a unique name, but is otherwise the +/// same as [`ExtrinsicParams`] in describing how to encode the extra and +/// additional data. +pub trait SignedExtension: ExtrinsicParams { + /// The type representing the `extra` bytes of a signed extension. + /// Decoding from this type should be symmetrical to the respective + /// `ExtrinsicParamsEncoder::encode_extra_to()` implementation of this signed extension. + type Decoded: DecodeAsType; + + /// This should return true if the signed extension matches the details given. + /// Often, this will involve just checking that the identifier given matches that of the + /// extension in question. + fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool; +} + +/// The [`CheckSpecVersion`] signed extension. +pub struct CheckSpecVersion(u32); + +impl ExtrinsicParams for CheckSpecVersion { + type OtherParams = (); + + fn new( + _nonce: u64, + client: &MinimalClient, + _other_params: Self::OtherParams, + ) -> Result { + Ok(CheckSpecVersion(client.runtime_version().spec_version)) + } +} + +impl ExtrinsicParamsEncoder for CheckSpecVersion { + fn encode_additional_to(&self, v: &mut Vec) { + self.0.encode_to(v); + } +} + +impl SignedExtension for CheckSpecVersion { + type Decoded = (); + fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { + identifier == "CheckSpecVersion" + } +} + +/// The [`CheckNonce`] signed extension. +pub struct CheckNonce(Compact); + +impl ExtrinsicParams for CheckNonce { + type OtherParams = (); + + fn new( + nonce: u64, + _client: &MinimalClient, + _other_params: Self::OtherParams, + ) -> Result { + Ok(CheckNonce(Compact(nonce))) + } +} + +impl ExtrinsicParamsEncoder for CheckNonce { + fn encode_extra_to(&self, v: &mut Vec) { + self.0.encode_to(v); + } +} + +impl SignedExtension for CheckNonce { + type Decoded = u64; + fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { + identifier == "CheckNonce" + } +} + +/// The [`CheckTxVersion`] signed extension. +pub struct CheckTxVersion(u32); + +impl ExtrinsicParams for CheckTxVersion { + type OtherParams = (); + + fn new( + _nonce: u64, + client: &MinimalClient, + _other_params: Self::OtherParams, + ) -> Result { + Ok(CheckTxVersion(client.runtime_version().transaction_version)) + } +} + +impl ExtrinsicParamsEncoder for CheckTxVersion { + fn encode_additional_to(&self, v: &mut Vec) { + self.0.encode_to(v); + } +} + +impl SignedExtension for CheckTxVersion { + type Decoded = (); + fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { + identifier == "CheckTxVersion" + } +} + +/// The [`CheckGenesis`] signed extension. +pub struct CheckGenesis(T::Hash); + +impl ExtrinsicParams for CheckGenesis { + type OtherParams = (); + + fn new( + _nonce: u64, + client: &MinimalClient, + _other_params: Self::OtherParams, + ) -> Result { + Ok(CheckGenesis(client.genesis_hash())) + } +} + +impl ExtrinsicParamsEncoder for CheckGenesis { + fn encode_additional_to(&self, v: &mut Vec) { + self.0.encode_to(v); + } +} + +impl SignedExtension for CheckGenesis { + type Decoded = (); + fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { + identifier == "CheckGenesis" + } +} + +/// The [`CheckMortality`] signed extension. +pub struct CheckMortality { + era: Era, + checkpoint: T::Hash, +} + +/// Parameters to configure the [`CheckMortality`] signed extension. +pub struct CheckMortalityParams { + era: Era, + checkpoint: Option, +} + +impl Default for CheckMortalityParams { + fn default() -> Self { + Self { + era: Default::default(), + checkpoint: Default::default(), + } + } +} + +impl CheckMortalityParams { + /// Configure a mortal transaction. The `period` is (roughly) how many + /// blocks the transaction will be valid for. The `block_number` and + /// `block_hash` should both point to the same block, and are the block that + /// the transaction is mortal from. + pub fn mortal(period: u64, block_number: u64, block_hash: T::Hash) -> Self { + CheckMortalityParams { + era: Era::mortal(period, block_number), + checkpoint: Some(block_hash), + } + } + /// An immortal transaction. + pub fn immortal() -> Self { + CheckMortalityParams { + era: Era::Immortal, + checkpoint: None, + } + } +} + +impl ExtrinsicParams for CheckMortality { + type OtherParams = CheckMortalityParams; + + fn new( + _nonce: u64, + client: &MinimalClient, + other_params: Self::OtherParams, + ) -> Result { + Ok(CheckMortality { + era: other_params.era, + checkpoint: other_params.checkpoint.unwrap_or(client.genesis_hash()), + }) + } +} + +impl ExtrinsicParamsEncoder for CheckMortality { + fn encode_extra_to(&self, v: &mut Vec) { + self.era.encode_to(v); + } + fn encode_additional_to(&self, v: &mut Vec) { + self.checkpoint.encode_to(v); + } +} + +impl SignedExtension for CheckMortality { + type Decoded = Era; + fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { + identifier == "CheckMortality" + } +} + +/// The [`ChargeAssetTxPayment`] signed extension. +#[derive(Derivative, DecodeAsType)] +#[derivative(Clone(bound = "T::AssetId: Clone"), Debug(bound = "T::AssetId: Debug"))] +#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")] +pub struct ChargeAssetTxPayment { + tip: Compact, + asset_id: Option, +} + +impl ChargeAssetTxPayment { + /// Tip to the extrinsic author in the native chain token. + pub fn tip(&self) -> u128 { + self.tip.0 + } + + /// Tip to the extrinsic author using the asset ID given. + pub fn asset_id(&self) -> Option<&T::AssetId> { + self.asset_id.as_ref() + } +} + +/// Parameters to configure the [`ChargeAssetTxPayment`] signed extension. +pub struct ChargeAssetTxPaymentParams { + tip: u128, + asset_id: Option, +} + +impl Default for ChargeAssetTxPaymentParams { + fn default() -> Self { + ChargeAssetTxPaymentParams { + tip: Default::default(), + asset_id: Default::default(), + } + } +} + +impl ChargeAssetTxPaymentParams { + /// Don't provide a tip to the extrinsic author. + pub fn no_tip() -> Self { + ChargeAssetTxPaymentParams { + tip: 0, + asset_id: None, + } + } + /// Tip the extrinsic author in the native chain token. + pub fn tip(tip: u128) -> Self { + ChargeAssetTxPaymentParams { + tip, + asset_id: None, + } + } + /// Tip the extrinsic author using the asset ID given. + pub fn tip_of(tip: u128, asset_id: T::AssetId) -> Self { + ChargeAssetTxPaymentParams { + tip, + asset_id: Some(asset_id), + } + } +} + +impl ExtrinsicParams for ChargeAssetTxPayment { + type OtherParams = ChargeAssetTxPaymentParams; + + fn new( + _nonce: u64, + _client: &MinimalClient, + other_params: Self::OtherParams, + ) -> Result { + Ok(ChargeAssetTxPayment { + tip: Compact(other_params.tip), + asset_id: other_params.asset_id, + }) + } +} + +impl ExtrinsicParamsEncoder for ChargeAssetTxPayment { + fn encode_extra_to(&self, v: &mut Vec) { + (self.tip, &self.asset_id).encode_to(v); + } +} + +impl SignedExtension for ChargeAssetTxPayment { + type Decoded = Self; + fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { + identifier == "ChargeAssetTxPayment" + } +} + +/// The [`ChargeTransactionPayment`] signed extension. +#[derive(Clone, Debug, DecodeAsType)] +pub struct ChargeTransactionPayment { + tip: Compact, +} + +impl ChargeTransactionPayment { + /// Tip to the extrinsic author in the native chain token. + pub fn tip(&self) -> u128 { + self.tip.0 + } +} + +/// Parameters to configure the [`ChargeTransactionPayment`] signed extension. +#[derive(Default)] +pub struct ChargeTransactionPaymentParams { + tip: u128, +} + +impl ChargeTransactionPaymentParams { + /// Don't provide a tip to the extrinsic author. + pub fn no_tip() -> Self { + ChargeTransactionPaymentParams { tip: 0 } + } + /// Tip the extrinsic author in the native chain token. + pub fn tip(tip: u128) -> Self { + ChargeTransactionPaymentParams { tip } + } +} + +impl ExtrinsicParams for ChargeTransactionPayment { + type OtherParams = ChargeTransactionPaymentParams; + + fn new( + _nonce: u64, + _client: &MinimalClient, + other_params: Self::OtherParams, + ) -> Result { + Ok(ChargeTransactionPayment { + tip: Compact(other_params.tip), + }) + } +} + +impl ExtrinsicParamsEncoder for ChargeTransactionPayment { + fn encode_extra_to(&self, v: &mut Vec) { + self.tip.encode_to(v); + } +} + +impl SignedExtension for ChargeTransactionPayment { + type Decoded = Self; + fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { + identifier == "ChargeTransactionPayment" + } +} + +/// This accepts a tuple of [`SignedExtension`]s, and will dynamically make use of whichever +/// ones are actually required for the chain in the correct order, ignoring the rest. This +/// is a sensible default, and allows for a single configuration to work across multiple chains. +pub struct AnyOf { + params: Vec>, + _marker: core::marker::PhantomData<(T, Params)>, +} + +macro_rules! impl_tuples { + ($($ident:ident $index:tt),+) => { + // We do some magic when the tuple is wrapped in AnyOf. We + // look at the metadata, and use this to select and make use of only the extensions + // that we actually need for the chain we're dealing with. + impl ExtrinsicParams for AnyOf + where + T: Config, + $($ident: SignedExtension,)+ + { + type OtherParams = ($($ident::OtherParams,)+); + + fn new( + nonce: u64, + client: &MinimalClient, + other_params: Self::OtherParams, + ) -> Result { + let metadata = client.metadata(); + let types = metadata.types(); + + // For each signed extension in the tuple, find the matching index in the metadata, if + // there is one, and add it to a map with that index as the key. + let mut exts_by_index = BTreeMap::new(); + $({ + for (idx, e) in metadata.extrinsic().signed_extensions().iter().enumerate() { + // Skip over any exts that have a match already: + if exts_by_index.contains_key(&idx) { + continue + } + // Break and record as soon as we find a match: + if $ident::matches(e.identifier(), e.extra_ty(), types) { + let ext = $ident::new(nonce, client, other_params.$index)?; + let boxed_ext: Box = Box::new(ext); + exts_by_index.insert(idx, boxed_ext); + break + } + } + })+ + + // Next, turn these into an ordered vec, erroring if we haven't matched on any exts yet. + let mut params = Vec::new(); + for (idx, e) in metadata.extrinsic().signed_extensions().iter().enumerate() { + let Some(ext) = exts_by_index.remove(&idx) else { + if is_type_empty(e.extra_ty(), types) { + continue + } else { + return Err(ExtrinsicParamsError::UnknownSignedExtension(e.identifier().to_owned())); + } + }; + params.push(ext); + } + + Ok(AnyOf { + params, + _marker: core::marker::PhantomData + }) + } + } + + impl ExtrinsicParamsEncoder for AnyOf + where + T: Config, + $($ident: SignedExtension,)+ + { + fn encode_extra_to(&self, v: &mut Vec) { + for ext in &self.params { + ext.encode_extra_to(v); + } + } + fn encode_additional_to(&self, v: &mut Vec) { + for ext in &self.params { + ext.encode_additional_to(v); + } + } + } + } +} + +#[rustfmt::skip] +const _: () = { + impl_tuples!(A 0); + impl_tuples!(A 0, B 1); + impl_tuples!(A 0, B 1, C 2); + impl_tuples!(A 0, B 1, C 2, D 3); + impl_tuples!(A 0, B 1, C 2, D 3, E 4); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19); + impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19, V 20); +}; + +/// Checks to see whether the type being given is empty, ie would require +/// 0 bytes to encode. +fn is_type_empty(type_id: u32, types: &scale_info::PortableRegistry) -> bool { + let Some(ty) = types.resolve(type_id) else { + // Can't resolve; type may not be empty. Not expected to hit this. + return false; + }; + + use scale_info::TypeDef; + match &ty.type_def { + TypeDef::Composite(c) => c.fields.iter().all(|f| is_type_empty(f.ty.id, types)), + TypeDef::Array(a) => a.len == 0 || is_type_empty(a.type_param.id, types), + TypeDef::Tuple(t) => t.fields.iter().all(|f| is_type_empty(f.id, types)), + // Explicitly list these in case any additions are made in the future. + TypeDef::BitSequence(_) + | TypeDef::Variant(_) + | TypeDef::Sequence(_) + | TypeDef::Compact(_) + | TypeDef::Primitive(_) => false, + } +} diff --git a/core/src/config/substrate.rs b/core/src/config/substrate.rs new file mode 100644 index 00000000000..b8e930c8827 --- /dev/null +++ b/core/src/config/substrate.rs @@ -0,0 +1,342 @@ +// 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. + +//! Substrate specific configuration + +use super::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder, Hasher, Header}; +use crate::prelude::*; +pub use crate::utils::{AccountId32, MultiAddress, MultiSignature}; +use codec::{Decode, Encode}; +pub use primitive_types::{H256, U256}; +use serde::{Deserialize, Serialize}; +use string::String; +use vec::Vec; + +/// Default set of commonly used types by Substrate runtimes. +// Note: We only use this at the type level, so it should be impossible to +// create an instance of it. +pub enum SubstrateConfig {} + +impl Config for SubstrateConfig { + type Hash = H256; + type AccountId = AccountId32; + type Address = MultiAddress; + type Signature = MultiSignature; + type Hasher = BlakeTwo256; + type Header = SubstrateHeader; + type ExtrinsicParams = SubstrateExtrinsicParams; + type AssetId = u32; +} + +/// A struct representing the signed extra and additional parameters required +/// to construct a transaction for the default substrate node. +pub type SubstrateExtrinsicParams = DefaultExtrinsicParams; + +/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed. +/// This is what you provide to methods like `sign_and_submit()`. +pub type SubstrateExtrinsicParamsBuilder = DefaultExtrinsicParamsBuilder; + +/// A type that can hash values using the blaks2_256 algorithm. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode)] +pub struct BlakeTwo256; + +impl Hasher for BlakeTwo256 { + type Output = H256; + fn hash(s: &[u8]) -> Self::Output { + sp_core_hashing::blake2_256(s).into() + } +} + +/// A generic Substrate header type, adapted from `sp_runtime::generic::Header`. +/// The block number and hasher can be configured to adapt this for other nodes. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SubstrateHeader + TryFrom, H: Hasher> { + /// The parent hash. + pub parent_hash: H::Output, + /// The block number. + #[serde( + serialize_with = "serialize_number", + deserialize_with = "deserialize_number" + )] + #[codec(compact)] + pub number: N, + /// The state trie merkle root + pub state_root: H::Output, + /// The merkle root of the extrinsics. + pub extrinsics_root: H::Output, + /// A chain-specific digest of data useful for light clients or referencing auxiliary data. + pub digest: Digest, +} + +impl Header for SubstrateHeader +where + N: Copy + Into + Into + TryFrom + Encode, + H: Hasher + Encode, + SubstrateHeader: Encode + Decode, +{ + type Number = N; + type Hasher = H; + fn number(&self) -> Self::Number { + self.number + } +} + +/// Generic header digest. From `sp_runtime::generic::digest`. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)] +pub struct Digest { + /// A list of digest items. + pub logs: Vec, +} + +/// Digest item that is able to encode/decode 'system' digest items and +/// provide opaque access to other items. From `sp_runtime::generic::digest`. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum DigestItem { + /// A pre-runtime digest. + /// + /// These are messages from the consensus engine to the runtime, although + /// the consensus engine can (and should) read them itself to avoid + /// code and state duplication. It is erroneous for a runtime to produce + /// these, but this is not (yet) checked. + /// + /// NOTE: the runtime is not allowed to panic or fail in an `on_initialize` + /// call if an expected `PreRuntime` digest is not present. It is the + /// responsibility of a external block verifier to check this. Runtime API calls + /// will initialize the block without pre-runtime digests, so initialization + /// cannot fail when they are missing. + PreRuntime(ConsensusEngineId, Vec), + + /// A message from the runtime to the consensus engine. This should *never* + /// be generated by the native code of any consensus engine, but this is not + /// checked (yet). + Consensus(ConsensusEngineId, Vec), + + /// Put a Seal on it. This is only used by native code, and is never seen + /// by runtimes. + Seal(ConsensusEngineId, Vec), + + /// Some other thing. Unsupported and experimental. + Other(Vec), + + /// An indication for the light clients that the runtime execution + /// environment is updated. + /// + /// Currently this is triggered when: + /// 1. Runtime code blob is changed or + /// 2. `heap_pages` value is changed. + RuntimeEnvironmentUpdated, +} + +// From sp_runtime::generic, DigestItem enum indexes are encoded using this: +#[repr(u32)] +#[derive(Encode, Decode)] +enum DigestItemType { + Other = 0u32, + Consensus = 4u32, + Seal = 5u32, + PreRuntime = 6u32, + RuntimeEnvironmentUpdated = 8u32, +} +impl Encode for DigestItem { + fn encode(&self) -> Vec { + let mut v = Vec::new(); + + match self { + Self::Consensus(val, data) => { + DigestItemType::Consensus.encode_to(&mut v); + (val, data).encode_to(&mut v); + } + Self::Seal(val, sig) => { + DigestItemType::Seal.encode_to(&mut v); + (val, sig).encode_to(&mut v); + } + Self::PreRuntime(val, data) => { + DigestItemType::PreRuntime.encode_to(&mut v); + (val, data).encode_to(&mut v); + } + Self::Other(val) => { + DigestItemType::Other.encode_to(&mut v); + val.encode_to(&mut v); + } + Self::RuntimeEnvironmentUpdated => { + DigestItemType::RuntimeEnvironmentUpdated.encode_to(&mut v); + } + } + + v + } +} +impl Decode for DigestItem { + fn decode(input: &mut I) -> Result { + let item_type: DigestItemType = Decode::decode(input)?; + match item_type { + DigestItemType::PreRuntime => { + let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; + Ok(Self::PreRuntime(vals.0, vals.1)) + } + DigestItemType::Consensus => { + let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; + Ok(Self::Consensus(vals.0, vals.1)) + } + DigestItemType::Seal => { + let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; + Ok(Self::Seal(vals.0, vals.1)) + } + DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)), + DigestItemType::RuntimeEnvironmentUpdated => Ok(Self::RuntimeEnvironmentUpdated), + } + } +} + +/// Consensus engine unique ID. From `sp_runtime::ConsensusEngineId`. +pub type ConsensusEngineId = [u8; 4]; + +impl serde::Serialize for DigestItem { + fn serialize(&self, seq: S) -> Result + where + S: serde::Serializer, + { + self.using_encoded(|bytes| impl_serde::serialize::serialize(bytes, seq)) + } +} + +impl<'a> serde::Deserialize<'a> for DigestItem { + fn deserialize(de: D) -> Result + where + D: serde::Deserializer<'a>, + { + let r = impl_serde::serialize::deserialize(de)?; + Decode::decode(&mut &r[..]) + .map_err(|e| serde::de::Error::custom(format!("Decode error: {e}"))) + } +} + +fn serialize_number>(val: &T, s: S) -> Result +where + S: serde::Serializer, +{ + let u256: U256 = (*val).into(); + serde::Serialize::serialize(&u256, s) +} + +fn deserialize_number<'a, D, T: TryFrom>(d: D) -> Result +where + D: serde::Deserializer<'a>, +{ + // At the time of writing, Smoldot gives back block numbers in numeric rather + // than hex format. So let's support deserializing from both here: + 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 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 for NumberOrHex { + fn from(n: u128) -> Self { + NumberOrHex::Hex(n.into()) + } +} + +impl From for NumberOrHex { + fn from(n: U256) -> Self { + NumberOrHex::Hex(n) + } +} + +/// A quick helper to encode some bytes to hex. +fn to_hex(bytes: impl AsRef<[u8]>) -> String { + format!("0x{}", hex::encode(bytes.as_ref())) +} + +#[cfg(test)] +mod test { + use super::*; + + // Smoldot returns numeric block numbers in the header at the time of writing; + // ensure we can deserialize them properly. + #[test] + fn can_deserialize_numeric_block_number() { + let numeric_block_number_json = r#" + { + "digest": { + "logs": [] + }, + "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "number": 4, + "parentHash": "0xcb2690b2c85ceab55be03fc7f7f5f3857e7efeb7a020600ebd4331e10be2f7a5", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + "#; + + let header: SubstrateHeader = + serde_json::from_str(numeric_block_number_json).expect("valid block header"); + assert_eq!(header.number(), 4); + } + + // Substrate returns hex block numbers; ensure we can also deserialize those OK. + #[test] + fn can_deserialize_hex_block_number() { + let numeric_block_number_json = r#" + { + "digest": { + "logs": [] + }, + "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "number": "0x04", + "parentHash": "0xcb2690b2c85ceab55be03fc7f7f5f3857e7efeb7a020600ebd4331e10be2f7a5", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + "#; + + let header: SubstrateHeader = + serde_json::from_str(numeric_block_number_json).expect("valid block header"); + assert_eq!(header.number(), 4); + } +} diff --git a/core/src/dynamic.rs b/core/src/dynamic.rs new file mode 100644 index 00000000000..e0260b1920d --- /dev/null +++ b/core/src/dynamic.rs @@ -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. + +// //! This module provides the entry points to create dynamic +// //! transactions, storage and constant lookups. + +// use crate::metadata::{DecodeWithMetadata, Metadata}; +// use scale_decode::DecodeAsType; + +// pub use scale_value::{At, Value}; + +// /// A [`scale_value::Value`] type endowed with contextual information +// /// regarding what type was used to decode each part of it. This implements +// /// [`crate::metadata::DecodeWithMetadata`], and is used as a return type +// /// for dynamic requests. +// pub type DecodedValue = scale_value::Value; + +// // Submit dynamic transactions. +// pub use crate::tx::dynamic as tx; + +// // Lookup constants dynamically. +// pub use crate::constants::dynamic as constant; + +// // Lookup storage values dynamically. +// pub use crate::storage::dynamic as storage; + +// // Execute runtime API function call dynamically. +// pub use crate::runtime_api::dynamic as runtime_api_call; + +// /// This is the result of making a dynamic request to a node. From this, +// /// we can return the raw SCALE bytes that we were handed back, or we can +// /// complete the decoding of the bytes into a [`DecodedValue`] type. +// pub struct DecodedValueThunk { +// type_id: u32, +// metadata: Metadata, +// scale_bytes: Vec, +// } + +// impl DecodeWithMetadata for DecodedValueThunk { +// fn decode_with_metadata( +// bytes: &mut &[u8], +// type_id: u32, +// metadata: &Metadata, +// ) -> Result { +// let mut v = Vec::with_capacity(bytes.len()); +// v.extend_from_slice(bytes); +// *bytes = &[]; +// Ok(DecodedValueThunk { +// type_id, +// metadata: metadata.clone(), +// scale_bytes: v, +// }) +// } +// } + +// impl DecodedValueThunk { +// /// Return the SCALE encoded bytes handed back from the node. +// pub fn into_encoded(self) -> Vec { +// self.scale_bytes +// } +// /// Return the SCALE encoded bytes handed back from the node without taking ownership of them. +// pub fn encoded(&self) -> &[u8] { +// &self.scale_bytes +// } +// /// Decode the SCALE encoded storage entry into a dynamic [`DecodedValue`] type. +// pub fn to_value(&self) -> Result { +// let val = DecodedValue::decode_as_type( +// &mut &*self.scale_bytes, +// self.type_id, +// self.metadata.types(), +// )?; +// Ok(val) +// } +// /// decode the `DecodedValueThunk` into a concrete type. +// pub fn as_type(&self) -> Result { +// T::decode_as_type( +// &mut &self.scale_bytes[..], +// self.type_id, +// self.metadata.types(), +// ) +// } +// } diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 00000000000..fe618ae1678 --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,25 @@ +// 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. + +//! # Subxt-core +//! +//! `#[no_std]` compatible core crate for subxt. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod client; +pub mod config; +pub mod dynamic; +pub mod metadata; +pub mod prelude; +pub mod tx; +pub mod utils; + +pub use config::{ + BlockHash, Config, ExtrinsicParams, ExtrinsicParamsEncoder, PolkadotConfig, + PolkadotExtrinsicParams, SubstrateConfig, SubstrateExtrinsicParams, +}; + +#[macro_use] +mod macros; diff --git a/core/src/macros.rs b/core/src/macros.rs new file mode 100644 index 00000000000..1a61ee93547 --- /dev/null +++ b/core/src/macros.rs @@ -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}; diff --git a/core/src/metadata/decode_encode_traits.rs b/core/src/metadata/decode_encode_traits.rs new file mode 100644 index 00000000000..4b9089a0015 --- /dev/null +++ b/core/src/metadata/decode_encode_traits.rs @@ -0,0 +1,52 @@ +// 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 super::Metadata; +use crate::prelude::*; +use vec::Vec; + +/// This trait is implemented for all types that also implement [`scale_decode::DecodeAsType`]. +pub trait DecodeWithMetadata: Sized { + /// Given some metadata and a type ID, attempt to SCALE decode the provided bytes into `Self`. + fn decode_with_metadata( + bytes: &mut &[u8], + type_id: u32, + metadata: &Metadata, + ) -> Result; +} + +impl DecodeWithMetadata for T { + fn decode_with_metadata( + bytes: &mut &[u8], + type_id: u32, + metadata: &Metadata, + ) -> Result { + let val = T::decode_as_type(bytes, type_id, metadata.types())?; + Ok(val) + } +} + +/// This trait is implemented for all types that also implement [`scale_encode::EncodeAsType`]. +pub trait EncodeWithMetadata { + /// SCALE encode this type to bytes, possibly with the help of metadata. + fn encode_with_metadata( + &self, + type_id: u32, + metadata: &Metadata, + bytes: &mut Vec, + ) -> Result<(), scale_encode::Error>; +} + +impl EncodeWithMetadata for T { + /// SCALE encode this type to bytes, possibly with the help of metadata. + fn encode_with_metadata( + &self, + type_id: u32, + metadata: &Metadata, + bytes: &mut Vec, + ) -> Result<(), scale_encode::Error> { + self.encode_as_type_to(type_id, metadata.types(), bytes)?; + Ok(()) + } +} diff --git a/core/src/metadata/metadata_type.rs b/core/src/metadata/metadata_type.rs new file mode 100644 index 00000000000..24b052c2ad8 --- /dev/null +++ b/core/src/metadata/metadata_type.rs @@ -0,0 +1,127 @@ +// 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::prelude::*; +use borrow::ToOwned; +use derive_more::Display; +use string::String; +use sync::Arc; + +/// A cheaply clone-able representation of the runtime metadata received from a node. +#[derive(Clone, Debug)] +pub struct Metadata { + inner: Arc, +} + +impl core::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 { + 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 { + 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 { + self.runtime_api_trait_by_name(name) + .ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned())) + } +} + +impl From for Metadata { + fn from(md: subxt_metadata::Metadata) -> Self { + Metadata::new(md) + } +} + +impl TryFrom for Metadata { + type Error = subxt_metadata::TryFromError; + fn try_from(value: frame_metadata::RuntimeMetadataPrefixed) -> Result { + subxt_metadata::Metadata::try_from(value).map(Metadata::from) + } +} + +impl codec::Decode for Metadata { + fn decode(input: &mut I) -> Result { + subxt_metadata::Metadata::decode(input).map(Metadata::new) + } +} + +/// 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), +} diff --git a/core/src/metadata/mod.rs b/core/src/metadata/mod.rs new file mode 100644 index 00000000000..a6ef00ae6f6 --- /dev/null +++ b/core/src/metadata/mod.rs @@ -0,0 +1,14 @@ +// 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 representing the metadata obtained from a node. + +mod decode_encode_traits; +mod metadata_type; + +pub use decode_encode_traits::{DecodeWithMetadata, EncodeWithMetadata}; +pub use metadata_type::Metadata; + +// Expose metadata types under a sub module in case somebody needs to reference them: +pub use subxt_metadata as types; diff --git a/core/src/prelude.rs b/core/src/prelude.rs new file mode 100644 index 00000000000..007bd44665a --- /dev/null +++ b/core/src/prelude.rs @@ -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. + +#[cfg(not(feature = "std"))] +extern crate alloc; + +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "std")] { + #[allow(unused)] + pub use std::{ + any, + borrow, + boxed, + cmp, + collections, + fmt, + format, + hash, + marker, + mem, + num, + ops, + string, + sync, + time, + vec, + rc, + iter, + }; + } else { + #[allow(unused)] + pub use alloc::{ + borrow, + boxed, + collections, + format, + string, + sync, + vec, + rc + }; + #[allow(unused)] + pub use core::{ + any, + cmp, + fmt, + hash, + marker, + mem, + num, + ops, + time, + iter, + }; + } +} diff --git a/core/src/tx/mod.rs b/core/src/tx/mod.rs new file mode 100644 index 00000000000..293cbd33c17 --- /dev/null +++ b/core/src/tx/mod.rs @@ -0,0 +1,2 @@ +pub mod signer; +pub mod tx_payload; diff --git a/core/src/tx/signer.rs b/core/src/tx/signer.rs new file mode 100644 index 00000000000..444aa464772 --- /dev/null +++ b/core/src/tx/signer.rs @@ -0,0 +1,100 @@ +// 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. + +//! A library to **sub**mit e**xt**rinsics to a +//! [substrate](https://github.com/paritytech/substrate) node via RPC. + +use crate::macros::cfg_substrate_compat; +use crate::Config; + +/// Signing transactions requires a [`Signer`]. This is responsible for +/// providing the "from" account that the transaction is being signed by, +/// as well as actually signing a SCALE encoded payload. +pub trait Signer { + /// Return the "from" account ID. + fn account_id(&self) -> T::AccountId; + + /// Return the "from" address. + fn address(&self) -> T::Address; + + /// Takes a signer payload for an extrinsic, and returns a signature based on it. + /// + /// Some signers may fail, for instance because the hardware on which the keys are located has + /// refused the operation. + fn sign(&self, signer_payload: &[u8]) -> T::Signature; +} + +cfg_substrate_compat! { + pub use pair_signer::PairSigner; +} + +// A signer suitable for substrate based chains. This provides compatibility with Substrate +// packages like sp_keyring and such, and so relies on sp_core and sp_runtime to be included. +#[cfg(feature = "substrate-compat")] +mod pair_signer { + use super::Signer; + use crate::Config; + use sp_core::Pair as PairT; + use sp_runtime::{ + traits::{IdentifyAccount, Verify}, + AccountId32 as SpAccountId32, MultiSignature as SpMultiSignature, + }; + + /// A [`Signer`] implementation that can be constructed from an [`sp_core::Pair`]. + #[derive(Clone, Debug)] + pub struct PairSigner { + account_id: T::AccountId, + signer: Pair, + } + + impl PairSigner + where + T: Config, + Pair: PairT, + // We go via an `sp_runtime::MultiSignature`. We can probably generalise this + // by implementing some of these traits on our built-in MultiSignature and then + // requiring them on all T::Signatures, to avoid any go-between. + ::Signer: From, + T::AccountId: From, + { + /// Creates a new [`Signer`] from an [`sp_core::Pair`]. + pub fn new(signer: Pair) -> Self { + let account_id = + ::Signer::from(signer.public()).into_account(); + Self { + account_id: account_id.into(), + signer, + } + } + + /// Returns the [`sp_core::Pair`] implementation used to construct this. + pub fn signer(&self) -> &Pair { + &self.signer + } + + /// Return the account ID. + pub fn account_id(&self) -> &T::AccountId { + &self.account_id + } + } + + impl Signer for PairSigner + where + T: Config, + Pair: PairT, + Pair::Signature: Into, + { + fn account_id(&self) -> T::AccountId { + self.account_id.clone() + } + + fn address(&self) -> T::Address { + self.account_id.clone().into() + } + + fn sign(&self, signer_payload: &[u8]) -> T::Signature { + self.signer.sign(signer_payload).into() + } + } +} diff --git a/core/src/tx/tx_payload.rs b/core/src/tx/tx_payload.rs new file mode 100644 index 00000000000..6b65d8e4343 --- /dev/null +++ b/core/src/tx/tx_payload.rs @@ -0,0 +1,192 @@ +// // 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. + +// //! 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 codec::Encode; +// use scale_encode::EncodeAsFields; +// use scale_value::{Composite, ValueDef, Variant}; +// use core::{borrow::Cow, sync::Arc}; + +// /// This represents a transaction payload that can be submitted +// /// to a node. +// pub trait TxPayload { +// /// Encode call data to the provided output. +// fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error>; + +// /// Encode call data and return the output. This is a convenience +// /// wrapper around [`TxPayload::encode_call_data_to`]. +// fn encode_call_data(&self, metadata: &Metadata) -> Result, Error> { +// let mut v = Vec::new(); +// self.encode_call_data_to(metadata, &mut v)?; +// Ok(v) +// } + +// /// Returns the details needed to validate the call, which +// /// include a statically generated hash, the pallet name, +// /// and the call name. +// fn validation_details(&self) -> Option> { +// None +// } +// } + +// pub struct ValidationDetails<'a> { +// /// The pallet name. +// pub pallet_name: &'a str, +// /// The call name. +// pub call_name: &'a str, +// /// A hash (this is generated at compile time in our codegen) +// /// to compare against the runtime code. +// pub hash: [u8; 32], +// } + +// /// A transaction payload containing some generic `CallData`. +// #[derive(Clone, Debug)] +// pub struct Payload { +// pallet_name: Cow<'static, str>, +// call_name: Cow<'static, str>, +// call_data: CallData, +// validation_hash: Option<[u8; 32]>, +// } + +// /// A boxed transaction payload. +// // Dev Note: Arc used to enable easy cloning (given that we can't have dyn Clone). +// pub type BoxedPayload = Payload>; + +// /// The type of a payload typically used for dynamic transaction payloads. +// pub type DynamicPayload = Payload>; + +// impl Payload { +// /// Create a new [`Payload`]. +// pub fn new( +// pallet_name: impl Into, +// call_name: impl Into, +// call_data: CallData, +// ) -> Self { +// Payload { +// pallet_name: Cow::Owned(pallet_name.into()), +// call_name: Cow::Owned(call_name.into()), +// call_data, +// validation_hash: None, +// } +// } + +// /// Create a new [`Payload`] using static strings for the pallet and call name. +// /// This is only expected to be used from codegen. +// #[doc(hidden)] +// pub fn new_static( +// pallet_name: &'static str, +// call_name: &'static str, +// call_data: CallData, +// validation_hash: [u8; 32], +// ) -> Self { +// Payload { +// pallet_name: Cow::Borrowed(pallet_name), +// call_name: Cow::Borrowed(call_name), +// call_data, +// validation_hash: Some(validation_hash), +// } +// } + +// /// Box the payload. +// pub fn boxed(self) -> BoxedPayload +// where +// CallData: EncodeAsFields + Send + Sync + 'static, +// { +// BoxedPayload { +// pallet_name: self.pallet_name, +// call_name: self.call_name, +// call_data: Arc::new(self.call_data), +// validation_hash: self.validation_hash, +// } +// } + +// /// Do not validate this call prior to submitting it. +// pub fn unvalidated(self) -> Self { +// Self { +// validation_hash: None, +// ..self +// } +// } + +// /// Returns the call data. +// pub fn call_data(&self) -> &CallData { +// &self.call_data +// } + +// /// Returns the pallet name. +// pub fn pallet_name(&self) -> &str { +// &self.pallet_name +// } + +// /// Returns the call name. +// pub fn call_name(&self) -> &str { +// &self.call_name +// } +// } + +// impl Payload> { +// /// Convert the dynamic `Composite` payload into a [`Value`]. +// /// This is useful if you want to use this as an argument for a +// /// larger dynamic call that wants to use this as a nested call. +// pub fn into_value(self) -> Value<()> { +// let call = Value { +// context: (), +// value: ValueDef::Variant(Variant { +// name: self.call_name.into_owned(), +// values: self.call_data, +// }), +// }; + +// Value::unnamed_variant(self.pallet_name, [call]) +// } +// } + +// impl TxPayload for Payload { +// fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error> { +// let pallet = metadata.pallet_by_name_err(&self.pallet_name)?; +// let call = pallet +// .call_variant_by_name(&self.call_name) +// .ok_or_else(|| MetadataError::CallNameNotFound((*self.call_name).to_owned()))?; + +// let pallet_index = pallet.index(); +// let call_index = call.index; + +// pallet_index.encode_to(out); +// call_index.encode_to(out); + +// let mut fields = call +// .fields +// .iter() +// .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)?; +// Ok(()) +// } + +// fn validation_details(&self) -> Option> { +// self.validation_hash.map(|hash| ValidationDetails { +// pallet_name: &self.pallet_name, +// call_name: &self.call_name, +// hash, +// }) +// } +// } + +// /// Construct a transaction at runtime; essentially an alias to [`Payload::new()`] +// /// which provides a [`Composite`] value for the call data. +// pub fn dynamic( +// pallet_name: impl Into, +// call_name: impl Into, +// call_data: impl Into>, +// ) -> DynamicPayload { +// Payload::new(pallet_name, call_name, call_data.into()) +// } diff --git a/core/src/utils/account_id.rs b/core/src/utils/account_id.rs new file mode 100644 index 00000000000..7a7ef7949fe --- /dev/null +++ b/core/src/utils/account_id.rs @@ -0,0 +1,225 @@ +// 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. + +//! The "default" Substrate/Polkadot AccountId. This is used in codegen, as well as signing related bits. +//! 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 crate::prelude::*; +use codec::{Decode, Encode}; +use derive_more::Display; +use serde::{Deserialize, Serialize}; +use string::String; +use vec::Vec; + +/// A 32-byte cryptographic identifier. This is a simplified version of Substrate's +/// `sp_core::crypto::AccountId32`. To obtain more functionality, convert this into +/// that type. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + scale_encode::EncodeAsType, + scale_decode::DecodeAsType, + scale_info::TypeInfo, +)] +pub struct AccountId32(pub [u8; 32]); + +impl AsRef<[u8]> for AccountId32 { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsRef<[u8; 32]> for AccountId32 { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl From<[u8; 32]> for AccountId32 { + fn from(x: [u8; 32]) -> Self { + AccountId32(x) + } +} + +impl AccountId32 { + // Return the ss58-check string for this key. Adapted from `sp_core::crypto`. We need this to + // serialize our account appropriately but otherwise don't care. + fn to_ss58check(&self) -> String { + // For serializing to a string to obtain the account nonce, we use the default substrate + // prefix (since we have no way to otherwise pick one). It doesn't really matter, since when + // it's deserialized back in system_accountNextIndex, we ignore this (so long as it's valid). + const SUBSTRATE_SS58_PREFIX: u8 = 42; + // prefix <= 63 just take up one byte at the start: + let mut v = vec![SUBSTRATE_SS58_PREFIX]; + // then push the account ID bytes. + v.extend(self.0); + // then push a 2 byte checksum of what we have so far. + let r = ss58hash(&v); + v.extend(&r[0..2]); + // then encode to base58. + use base58::ToBase58; + v.to_base58() + } + + // This isn't strictly needed, but to give our AccountId32 a little more usefulness, we also + // implement the logic needed to decode an AccountId32 from an SS58 encoded string. This is exposed + // via a `FromStr` impl. + fn from_ss58check(s: &str) -> Result { + const CHECKSUM_LEN: usize = 2; + let body_len = 32; + + use base58::FromBase58; + let data = s.from_base58().map_err(|_| FromSs58Error::BadBase58)?; + if data.len() < 2 { + return Err(FromSs58Error::BadLength); + } + let prefix_len = match data[0] { + 0..=63 => 1, + 64..=127 => 2, + _ => return Err(FromSs58Error::InvalidPrefix), + }; + if data.len() != prefix_len + body_len + CHECKSUM_LEN { + return Err(FromSs58Error::BadLength); + } + let hash = ss58hash(&data[0..body_len + prefix_len]); + let checksum = &hash[0..CHECKSUM_LEN]; + if data[body_len + prefix_len..body_len + prefix_len + CHECKSUM_LEN] != *checksum { + // Invalid checksum. + return Err(FromSs58Error::InvalidChecksum); + } + + let result = data[prefix_len..body_len + prefix_len] + .try_into() + .map_err(|_| FromSs58Error::BadLength)?; + Ok(AccountId32(result)) + } +} + +/// An error obtained from trying to interpret an SS58 encoded string into an AccountId32 +#[derive(Display, Clone, Copy, Eq, PartialEq, Debug)] +#[allow(missing_docs)] +pub enum FromSs58Error { + #[display(fmt = "Base 58 requirement is violated")] + BadBase58, + #[display(fmt = "Length is bad")] + BadLength, + #[display(fmt = "Invalid checksum")] + InvalidChecksum, + #[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 { + use blake2::{Blake2b512, Digest}; + const PREFIX: &[u8] = b"SS58PRE"; + let mut ctx = Blake2b512::new(); + ctx.update(PREFIX); + ctx.update(data); + ctx.finalize().to_vec() +} + +impl Serialize for AccountId32 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_ss58check()) + } +} + +impl<'de> Deserialize<'de> for AccountId32 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + AccountId32::from_ss58check(&String::deserialize(deserializer)?) + .map_err(|e| serde::de::Error::custom(format!("{e:?}"))) + } +} + +impl core::fmt::Display for AccountId32 { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{}", self.to_ss58check()) + } +} + +impl core::str::FromStr for AccountId32 { + type Err = FromSs58Error; + fn from_str(s: &str) -> Result { + AccountId32::from_ss58check(s) + } +} + +// Improve compat with the substrate version if we're using those crates: +#[cfg(feature = "substrate-compat")] +mod substrate_impls { + use super::*; + + impl From for AccountId32 { + fn from(value: sp_runtime::AccountId32) -> Self { + Self(value.into()) + } + } + impl From for AccountId32 { + fn from(value: sp_core::sr25519::Public) -> Self { + let acc: sp_runtime::AccountId32 = value.into(); + acc.into() + } + } + impl From for AccountId32 { + fn from(value: sp_core::ed25519::Public) -> Self { + let acc: sp_runtime::AccountId32 = value.into(); + acc.into() + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + use sp_core::crypto::Ss58Codec; + use sp_keyring::AccountKeyring; + + #[test] + fn ss58_is_compatible_with_substrate_impl() { + let keyrings = vec![ + AccountKeyring::Alice, + AccountKeyring::Bob, + AccountKeyring::Charlie, + ]; + + for keyring in keyrings { + let substrate_account = keyring.to_account_id(); + // Avoid "From" impl hidden behind "substrate-compat" feature so that this test + // can work either way: + let local_account = AccountId32(substrate_account.clone().into()); + + // Both should encode to ss58 the same way: + let substrate_ss58 = substrate_account.to_ss58check(); + assert_eq!(substrate_ss58, local_account.to_ss58check()); + + // Both should decode from ss58 back to the same: + assert_eq!( + sp_core::crypto::AccountId32::from_ss58check(&substrate_ss58).unwrap(), + substrate_account + ); + assert_eq!( + AccountId32::from_ss58check(&substrate_ss58).unwrap(), + local_account + ); + } + } +} diff --git a/core/src/utils/bits.rs b/core/src/utils/bits.rs new file mode 100644 index 00000000000..5774cd9fcf8 --- /dev/null +++ b/core/src/utils/bits.rs @@ -0,0 +1,268 @@ +// 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. + +//! Generic `scale_bits` over `bitvec`-like `BitOrder` and `BitFormat` types. + +use crate::prelude::*; +use codec::{Compact, Input}; +use core::marker::PhantomData; +use scale_bits::{ + scale::format::{Format, OrderFormat, StoreFormat}, + Bits, +}; +use scale_decode::IntoVisitor; +use vec::Vec; + +/// Associates `bitvec::store::BitStore` trait with corresponding, type-erased `scale_bits::StoreFormat` enum. +/// +/// Used to decode bit sequences by providing `scale_bits::StoreFormat` using +/// `bitvec`-like type type parameters. +pub trait BitStore { + /// Corresponding `scale_bits::StoreFormat` value. + const FORMAT: StoreFormat; + /// Number of bits that the backing store types holds. + const BITS: u32; +} +macro_rules! impl_store { + ($ty:ident, $wrapped:ty) => { + impl BitStore for $wrapped { + const FORMAT: StoreFormat = StoreFormat::$ty; + const BITS: u32 = <$wrapped>::BITS; + } + }; +} +impl_store!(U8, u8); +impl_store!(U16, u16); +impl_store!(U32, u32); +impl_store!(U64, u64); + +/// Associates `bitvec::order::BitOrder` trait with corresponding, type-erased `scale_bits::OrderFormat` enum. +/// +/// Used to decode bit sequences in runtime by providing `scale_bits::OrderFormat` using +/// `bitvec`-like type type parameters. +pub trait BitOrder { + /// Corresponding `scale_bits::OrderFormat` value. + const FORMAT: OrderFormat; +} +macro_rules! impl_order { + ($ty:ident) => { + #[doc = concat!("Type-level value that corresponds to `scale_bits::OrderFormat::", stringify!($ty), "` at run-time")] + #[doc = concat!(" and `bitvec::order::BitOrder::", stringify!($ty), "` at the type level.")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum $ty {} + impl BitOrder for $ty { + const FORMAT: OrderFormat = OrderFormat::$ty; + } + }; +} +impl_order!(Lsb0); +impl_order!(Msb0); + +/// Constructs a run-time format parameters based on the corresponding type-level parameters. +fn bit_format() -> Format { + Format { + order: Order::FORMAT, + store: Store::FORMAT, + } +} + +/// `scale_bits::Bits` generic over the bit store (`u8`/`u16`/`u32`/`u64`) and bit order (LSB, MSB) +/// used for SCALE encoding/decoding. Uses `scale_bits::Bits`-default `u8` and LSB format underneath. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DecodedBits { + bits: Bits, + _marker: PhantomData<(Store, Order)>, +} + +impl DecodedBits { + /// Extracts the underlying `scale_bits::Bits` value. + pub fn into_bits(self) -> Bits { + self.bits + } + + /// References the underlying `scale_bits::Bits` value. + pub fn as_bits(&self) -> &Bits { + &self.bits + } +} + +impl core::iter::FromIterator for DecodedBits { + fn from_iter>(iter: T) -> Self { + DecodedBits { + bits: Bits::from_iter(iter), + _marker: PhantomData, + } + } +} + +impl codec::Decode for DecodedBits { + fn decode(input: &mut I) -> Result { + /// Equivalent of `BitSlice::MAX_BITS` on 32bit machine. + const ARCH32BIT_BITSLICE_MAX_BITS: u32 = 0x1fff_ffff; + + let Compact(bits) = >::decode(input)?; + // Otherwise it is impossible to store it on 32bit machine. + if bits > ARCH32BIT_BITSLICE_MAX_BITS { + return Err("Attempt to decode a BitVec with too many bits".into()); + } + // NOTE: Replace with `bits.div_ceil(Store::BITS)` if `int_roundings` is stabilised + let elements = (bits / Store::BITS) + u32::from(bits % Store::BITS != 0); + let bytes_in_elem = Store::BITS.saturating_div(u8::BITS); + let bytes_needed = (elements * bytes_in_elem) as usize; + + // NOTE: We could reduce allocations if it would be possible to directly + // decode from an `Input` type using a custom format (rather than default ) + // for the `Bits` type. + let mut storage = codec::Encode::encode(&Compact(bits)); + let prefix_len = storage.len(); + storage.reserve_exact(bytes_needed); + storage.extend(vec![0; bytes_needed]); + input.read(&mut storage[prefix_len..])?; + + let decoder = scale_bits::decode_using_format_from(&storage, bit_format::())?; + let bits = decoder.collect::, _>>()?; + let bits = Bits::from_iter(bits); + + Ok(DecodedBits { + bits, + _marker: PhantomData, + }) + } +} + +impl codec::Encode for DecodedBits { + fn size_hint(&self) -> usize { + self.bits.size_hint() + } + + fn encoded_size(&self) -> usize { + self.bits.encoded_size() + } + + fn encode(&self) -> Vec { + scale_bits::encode_using_format(self.bits.iter(), bit_format::()) + } +} + +#[doc(hidden)] +pub struct DecodedBitsVisitor(core::marker::PhantomData<(S, O)>); +impl scale_decode::Visitor for DecodedBitsVisitor { + type Value<'scale, 'info> = DecodedBits; + type Error = scale_decode::Error; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + type_id: scale_decode::visitor::TypeId, + types: &'info scale_info::PortableRegistry, + ) -> scale_decode::visitor::DecodeAsTypeResult< + Self, + Result, Self::Error>, + > { + let res = scale_decode::visitor::decode_with_visitor( + input, + type_id.0, + types, + Bits::into_visitor(), + ) + .map(|bits| DecodedBits { + bits, + _marker: PhantomData, + }); + scale_decode::visitor::DecodeAsTypeResult::Decoded(res) + } +} +impl scale_decode::IntoVisitor for DecodedBits { + type Visitor = DecodedBitsVisitor; + fn into_visitor() -> Self::Visitor { + DecodedBitsVisitor(PhantomData) + } +} + +impl scale_encode::EncodeAsType for DecodedBits { + fn encode_as_type_to( + &self, + type_id: u32, + types: &scale_info::PortableRegistry, + out: &mut Vec, + ) -> Result<(), scale_encode::Error> { + self.bits.encode_as_type_to(type_id, types, out) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use core::fmt::Debug; + + use bitvec::vec::BitVec; + use codec::Decode as _; + + // NOTE: We don't use `bitvec::order` types in our implementation, since we + // don't want to depend on `bitvec`. Rather than reimplementing the unsafe + // trait on our types here for testing purposes, we simply convert and + // delegate to `bitvec`'s own types. + trait ToBitVec { + type Order: bitvec::order::BitOrder; + } + impl ToBitVec for Lsb0 { + type Order = bitvec::order::Lsb0; + } + impl ToBitVec for Msb0 { + type Order = bitvec::order::Msb0; + } + + fn scales_like_bitvec_and_roundtrips< + 'a, + Store: BitStore + bitvec::store::BitStore + PartialEq, + Order: BitOrder + ToBitVec + Debug + PartialEq, + >( + input: impl IntoIterator, + ) where + BitVec::Order>: codec::Encode + codec::Decode, + { + let input: Vec<_> = input.into_iter().copied().collect(); + + let decoded_bits = DecodedBits::::from_iter(input.clone()); + let bitvec = BitVec::::Order>::from_iter(input); + + let decoded_bits_encoded = codec::Encode::encode(&decoded_bits); + let bitvec_encoded = codec::Encode::encode(&bitvec); + assert_eq!(decoded_bits_encoded, bitvec_encoded); + + let decoded_bits_decoded = + DecodedBits::::decode(&mut &decoded_bits_encoded[..]) + .expect("SCALE-encoding DecodedBits to roundtrip"); + let bitvec_decoded = + BitVec::::Order>::decode(&mut &bitvec_encoded[..]) + .expect("SCALE-encoding BitVec to roundtrip"); + assert_eq!(decoded_bits, decoded_bits_decoded); + assert_eq!(bitvec, bitvec_decoded); + } + + #[test] + fn decoded_bitvec_scales_and_roundtrips() { + let test_cases = [ + vec![], + vec![true], + vec![false], + vec![true, false, true], + vec![true, false, true, false, false, false, false, false, true], + [vec![true; 5], vec![false; 5], vec![true; 1], vec![false; 3]].concat(), + [vec![true; 9], vec![false; 9], vec![true; 9], vec![false; 9]].concat(), + ]; + + for test_case in &test_cases { + scales_like_bitvec_and_roundtrips::(test_case); + scales_like_bitvec_and_roundtrips::(test_case); + scales_like_bitvec_and_roundtrips::(test_case); + scales_like_bitvec_and_roundtrips::(test_case); + scales_like_bitvec_and_roundtrips::(test_case); + scales_like_bitvec_and_roundtrips::(test_case); + scales_like_bitvec_and_roundtrips::(test_case); + scales_like_bitvec_and_roundtrips::(test_case); + } + } +} diff --git a/core/src/utils/era.rs b/core/src/utils/era.rs new file mode 100644 index 00000000000..c98ebe58f68 --- /dev/null +++ b/core/src/utils/era.rs @@ -0,0 +1,107 @@ +// 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 scale_decode::DecodeAsType; +use scale_encode::EncodeAsType; + +// Dev note: This and related bits taken from `sp_runtime::generic::Era` +/// An era to describe the longevity of a transaction. +#[derive( + PartialEq, + Default, + Eq, + Clone, + Copy, + Debug, + serde::Serialize, + serde::Deserialize, + DecodeAsType, + EncodeAsType, + scale_info::TypeInfo, +)] +pub enum Era { + /// The transaction is valid forever. The genesis hash must be present in the signed content. + #[default] + Immortal, + + /// The transaction will expire. Use [`Era::mortal`] to construct this with correct values. + /// + /// When used on `FRAME`-based runtimes, `period` cannot exceed `BlockHashCount` parameter + /// of `system` module. + Mortal { + /// The number of blocks that the tx will be valid for after the checkpoint block + /// hash found in the signer payload. + period: u64, + /// The phase in the period that this transaction's lifetime begins (and, importantly, + /// implies which block hash is included in the signature material). If the `period` is + /// greater than 1 << 12, then it will be a factor of the times greater than 1<<12 that + /// `period` is. + phase: u64, + }, +} + +// E.g. with period == 4: +// 0 10 20 30 40 +// 0123456789012345678901234567890123456789012 +// |...| +// authored -/ \- expiry +// phase = 1 +// n = Q(current - phase, period) + phase +impl Era { + /// Create a new era based on a period (which should be a power of two between 4 and 65536 + /// inclusive) and a block number on which it should start (or, for long periods, be shortly + /// after the start). + /// + /// If using `Era` in the context of `FRAME` runtime, make sure that `period` + /// does not exceed `BlockHashCount` parameter passed to `system` module, since that + /// prunes old blocks and renders transactions immediately invalid. + pub fn mortal(period: u64, current: u64) -> Self { + let period = period + .checked_next_power_of_two() + .unwrap_or(1 << 16) + .clamp(4, 1 << 16); + let phase = current % period; + let quantize_factor = (period >> 12).max(1); + let quantized_phase = phase / quantize_factor * quantize_factor; + + Self::Mortal { + period, + phase: quantized_phase, + } + } +} + +// Both copied from `sp_runtime::generic::Era`; this is the wire interface and so +// it's really the most important bit here. +impl codec::Encode for Era { + fn encode_to(&self, output: &mut T) { + match self { + Self::Immortal => output.push_byte(0), + Self::Mortal { period, phase } => { + let quantize_factor = (*period >> 12).max(1); + let encoded = (period.trailing_zeros() - 1).clamp(1, 15) as u16 + | ((phase / quantize_factor) << 4) as u16; + encoded.encode_to(output); + } + } + } +} +impl codec::Decode for Era { + fn decode(input: &mut I) -> Result { + let first = input.read_byte()?; + if first == 0 { + Ok(Self::Immortal) + } else { + let encoded = first as u64 + ((input.read_byte()? as u64) << 8); + let period = 2 << (encoded % (1 << 4)); + let quantize_factor = (period >> 12).max(1); + let phase = (encoded >> 4) * quantize_factor; + if period >= 4 && phase < period { + Ok(Self::Mortal { period, phase }) + } else { + Err("Invalid period and phase".into()) + } + } + } +} diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs new file mode 100644 index 00000000000..f8e74fafbb3 --- /dev/null +++ b/core/src/utils/mod.rs @@ -0,0 +1,99 @@ +// 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 codec::{Compact, Decode, Encode}; +use derivative::Derivative; +use url::Url; + +pub use account_id::AccountId32; +use borrow::ToOwned; +pub use era::Era; +pub use multi_address::MultiAddress; +pub use multi_signature::MultiSignature; +pub use static_type::Static; +pub use unchecked_extrinsic::UncheckedExtrinsic; +use vec::Vec; +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)] +pub struct Encoded(pub Vec); + +impl codec::Encode for Encoded { + fn encode(&self) -> Vec { + 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 = >::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. +pub fn url_is_secure(url: &str) -> Result { + let url = Url::parse(url)?; + + let secure_scheme = url.scheme() == "https" || url.scheme() == "wss"; + let is_localhost = url.host().is_some_and(|e| match e { + url::Host::Domain(e) => e == "localhost", + url::Host::Ipv4(e) => e.is_loopback(), + url::Host::Ipv6(e) => e.is_loopback(), + }); + + Ok(secure_scheme || is_localhost) +} + +use crate::prelude::*; + +/// 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(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(core::marker::PhantomData); + +impl PhantomDataSendSync { + pub(crate) fn new() -> Self { + Self(core::marker::PhantomData) + } +} + +unsafe impl Send for PhantomDataSendSync {} +unsafe impl Sync for PhantomDataSendSync {} + +/// 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 = Vec<(K, V)>; diff --git a/core/src/utils/multi_address.rs b/core/src/utils/multi_address.rs new file mode 100644 index 00000000000..c742c349371 --- /dev/null +++ b/core/src/utils/multi_address.rs @@ -0,0 +1,74 @@ +// 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. + +//! The "default" Substrate/Polkadot Address type. This is used in codegen, as well as signing related bits. +//! 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 crate::prelude::*; +use codec::{Decode, Encode}; +use vec::Vec; + +/// A multi-format address wrapper for on-chain accounts. This is a simplified version of Substrate's +/// `sp_runtime::MultiAddress`. To obtain more functionality, convert this into that type (this conversion +/// functionality is provided via `From` impls if the `substrate-compat` feature is enabled). +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + scale_encode::EncodeAsType, + scale_decode::DecodeAsType, + scale_info::TypeInfo, +)] +pub enum MultiAddress { + /// It's an account ID (pubkey). + Id(AccountId), + /// It's an account index. + Index(#[codec(compact)] AccountIndex), + /// It's some arbitrary raw bytes. + Raw(Vec), + /// It's a 32 byte representation. + Address32([u8; 32]), + /// Its a 20 byte representation. + Address20([u8; 20]), +} + +impl From for MultiAddress { + fn from(a: AccountId) -> Self { + Self::Id(a) + } +} + +// Improve compat with the substrate version if we're using those crates: +#[cfg(feature = "substrate-compat")] +mod substrate_impls { + use super::{super::AccountId32, *}; + + impl From for MultiAddress { + fn from(value: sp_runtime::AccountId32) -> Self { + let val: AccountId32 = value.into(); + val.into() + } + } + + impl From> for MultiAddress + where + Id: Into, + { + fn from(value: sp_runtime::MultiAddress) -> Self { + match value { + sp_runtime::MultiAddress::Id(v) => Self::Id(v.into()), + sp_runtime::MultiAddress::Index(v) => Self::Index(v), + sp_runtime::MultiAddress::Raw(v) => Self::Raw(v), + sp_runtime::MultiAddress::Address32(v) => Self::Address32(v), + sp_runtime::MultiAddress::Address20(v) => Self::Address20(v), + } + } + } +} diff --git a/core/src/utils/multi_signature.rs b/core/src/utils/multi_signature.rs new file mode 100644 index 00000000000..4ed9ea4bd54 --- /dev/null +++ b/core/src/utils/multi_signature.rs @@ -0,0 +1,58 @@ +// 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. + +//! The "default" Substrate/Polkadot Signature type. This is used in codegen, as well as signing related bits. +//! This doesn't contain much functionality itself, but is easy to convert to/from an `sp_runtime::MultiSignature` +//! for instance, to gain functionality without forcing a dependency on Substrate crates here. + +use codec::{Decode, Encode}; + +/// Signature container that can store known signature types. This is a simplified version of +/// `sp_runtime::MultiSignature`. To obtain more functionality, convert this into that type. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, scale_info::TypeInfo)] +pub enum MultiSignature { + /// An Ed25519 signature. + Ed25519([u8; 64]), + /// An Sr25519 signature. + Sr25519([u8; 64]), + /// An ECDSA/SECP256k1 signature (a 512-bit value, plus 8 bits for recovery ID). + Ecdsa([u8; 65]), +} + +// Improve compat with the substrate version if we're using those crates: +#[cfg(feature = "substrate-compat")] +mod substrate_impls { + use super::*; + + impl From for MultiSignature { + fn from(value: sp_runtime::MultiSignature) -> Self { + match value { + sp_runtime::MultiSignature::Ed25519(s) => Self::Ed25519(s.0), + sp_runtime::MultiSignature::Sr25519(s) => Self::Sr25519(s.0), + sp_runtime::MultiSignature::Ecdsa(s) => Self::Ecdsa(s.0), + } + } + } + + impl From for MultiSignature { + fn from(value: sp_core::ed25519::Signature) -> Self { + let sig: sp_runtime::MultiSignature = value.into(); + sig.into() + } + } + + impl From for MultiSignature { + fn from(value: sp_core::sr25519::Signature) -> Self { + let sig: sp_runtime::MultiSignature = value.into(); + sig.into() + } + } + + impl From for MultiSignature { + fn from(value: sp_core::ecdsa::Signature) -> Self { + let sig: sp_runtime::MultiSignature = value.into(); + sig.into() + } + } +} diff --git a/core/src/utils/static_type.rs b/core/src/utils/static_type.rs new file mode 100644 index 00000000000..9484706b850 --- /dev/null +++ b/core/src/utils/static_type.rs @@ -0,0 +1,80 @@ +// 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::prelude::*; +use codec::{Decode, Encode}; +use scale_decode::{visitor::DecodeAsTypeResult, IntoVisitor, Visitor}; +use scale_encode::EncodeAsType; +use 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`]. +/// +/// In either direction, we ignore any type information and just attempt to encode/decode statically +/// via the [`Encode`] and [`Decode`] implementations. This can be useful as an adapter for types which +/// do not implement [`scale_encode::EncodeAsType`] and [`scale_decode::DecodeAsType`] themselves, but +/// it's best to avoid using it where possible as it will not take into account any type information, +/// and is thus more likely to encode or decode incorrectly. +#[derive(Debug, Encode, Decode, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] +pub struct Static(pub T); + +impl EncodeAsType for Static { + fn encode_as_type_to( + &self, + _type_id: u32, + _types: &scale_decode::PortableRegistry, + out: &mut Vec, + ) -> Result<(), scale_encode::Error> { + self.0.encode_to(out); + Ok(()) + } +} + +pub struct StaticDecodeAsTypeVisitor(core::marker::PhantomData); + +impl Visitor for StaticDecodeAsTypeVisitor { + type Value<'scale, 'info> = Static; + type Error = scale_decode::Error; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + _type_id: scale_decode::visitor::TypeId, + _types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + use scale_decode::{visitor::DecodeError, Error}; + let decoded = T::decode(input) + .map(Static) + .map_err(|e| Error::new(DecodeError::CodecError(e).into())); + DecodeAsTypeResult::Decoded(decoded) + } +} + +impl IntoVisitor for Static { + type Visitor = StaticDecodeAsTypeVisitor; + fn into_visitor() -> Self::Visitor { + StaticDecodeAsTypeVisitor(core::marker::PhantomData) + } +} + +// Make it easy to convert types into Static where required. +impl From for Static { + fn from(value: T) -> Self { + Static(value) + } +} + +// Static is just a marker type and should be as transparent as possible: +impl core::ops::Deref for Static { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::ops::DerefMut for Static { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/core/src/utils/unchecked_extrinsic.rs b/core/src/utils/unchecked_extrinsic.rs new file mode 100644 index 00000000000..89744ac12a8 --- /dev/null +++ b/core/src/utils/unchecked_extrinsic.rs @@ -0,0 +1,136 @@ +// 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. + +//! The "default" Substrate/Polkadot UncheckedExtrinsic. +//! This is used in codegen for runtime API calls. +//! +//! The inner bytes represent the encoded extrinsic expected by the +//! runtime APIs. Deriving `EncodeAsType` would lead to the inner +//! bytes to be re-encoded (length prefixed). + +use super::{Encoded, Static}; +use crate::prelude::*; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType, IntoVisitor, Visitor}; +use vec::Vec; + +/// The unchecked extrinsic from substrate. +#[derive(Clone, Debug, Eq, PartialEq, Encode)] +pub struct UncheckedExtrinsic( + Static, + #[codec(skip)] PhantomData<(Address, Call, Signature, Extra)>, +); + +impl UncheckedExtrinsic { + /// Construct a new [`UncheckedExtrinsic`]. + pub fn new(bytes: Vec) -> Self { + Self(Static(Encoded(bytes)), PhantomData) + } + + /// Get the bytes of the encoded extrinsic. + pub fn bytes(&self) -> &[u8] { + self.0 .0 .0.as_slice() + } +} + +impl Decode + for UncheckedExtrinsic +{ + fn decode(input: &mut I) -> Result { + // The bytes for an UncheckedExtrinsic are first a compact + // encoded length, and then the bytes following. This is the + // same encoding as a Vec, so easiest ATM is just to decode + // into that, and then encode the vec bytes to get our extrinsic + // bytes, which we save into an `Encoded` to preserve as-is. + let xt_vec: Vec = Decode::decode(input)?; + Ok(UncheckedExtrinsic::new(xt_vec)) + } +} + +impl scale_encode::EncodeAsType + for UncheckedExtrinsic +{ + fn encode_as_type_to( + &self, + type_id: u32, + types: &scale_info::PortableRegistry, + out: &mut Vec, + ) -> Result<(), scale_encode::Error> { + self.0.encode_as_type_to(type_id, types, out) + } +} + +impl From> + for UncheckedExtrinsic +{ + fn from(bytes: Vec) -> Self { + UncheckedExtrinsic::new(bytes) + } +} + +impl From> + for Vec +{ + fn from(bytes: UncheckedExtrinsic) -> Self { + bytes.0 .0 .0 + } +} + +pub struct UncheckedExtrinsicDecodeAsTypeVisitor( + PhantomData<(Address, Call, Signature, Extra)>, +); + +impl Visitor + for UncheckedExtrinsicDecodeAsTypeVisitor +{ + type Value<'scale, 'info> = UncheckedExtrinsic; + type Error = scale_decode::Error; + + fn unchecked_decode_as_type<'scale, 'info>( + self, + input: &mut &'scale [u8], + type_id: scale_decode::visitor::TypeId, + types: &'info scale_info::PortableRegistry, + ) -> DecodeAsTypeResult, Self::Error>> { + DecodeAsTypeResult::Decoded(Self::Value::decode_as_type(input, type_id.0, types)) + } +} + +impl IntoVisitor + for UncheckedExtrinsic +{ + type Visitor = UncheckedExtrinsicDecodeAsTypeVisitor; + + fn into_visitor() -> Self::Visitor { + UncheckedExtrinsicDecodeAsTypeVisitor(PhantomData) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + #[test] + fn unchecked_extrinsic_encoding() { + // A tx is basically some bytes with a compact length prefix; ie an encoded vec: + let tx_bytes = vec![1u8, 2, 3].encode(); + + let unchecked_extrinsic = UncheckedExtrinsic::<(), (), (), ()>::new(tx_bytes.clone()); + let encoded_tx_bytes = unchecked_extrinsic.encode(); + + // The encoded representation must not alter the provided bytes. + assert_eq!(tx_bytes, encoded_tx_bytes); + + // However, for decoding we expect to be able to read the extrinsic from the wire + // which would be length prefixed. + let decoded_tx = UncheckedExtrinsic::<(), (), (), ()>::decode(&mut &tx_bytes[..]).unwrap(); + let decoded_tx_bytes = decoded_tx.bytes(); + let encoded_tx_bytes = decoded_tx.encode(); + + assert_eq!(decoded_tx_bytes, encoded_tx_bytes); + // Ensure we can decode the tx and fetch only the tx bytes. + assert_eq!(vec![1, 2, 3], encoded_tx_bytes); + } +} diff --git a/core/src/utils/wrapper_opaque.rs b/core/src/utils/wrapper_opaque.rs new file mode 100644 index 00000000000..8ca9ca1758b --- /dev/null +++ b/core/src/utils/wrapper_opaque.rs @@ -0,0 +1,245 @@ +// 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 super::PhantomDataSendSync; +use crate::prelude::*; +use codec::{Compact, Decode, DecodeAll, Encode}; +use derivative::Derivative; +use scale_decode::{IntoVisitor, Visitor}; +use scale_encode::EncodeAsType; +use vec::Vec; + +/// A wrapper for any type `T` which implement encode/decode in a way compatible with `Vec`. +/// [`WrapperKeepOpaque`] stores the type only in its opaque format, aka as a `Vec`. To +/// access the real type `T` [`Self::try_decode`] needs to be used. +// Dev notes: +// +// - This is adapted from [here](https://github.com/paritytech/substrate/blob/master/frame/support/src/traits/misc.rs). +// - The encoded bytes will be a compact encoded length followed by that number of bytes. +// - 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`, 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 = "") +)] +pub struct WrapperKeepOpaque { + data: Vec, + _phantom: PhantomDataSendSync, +} + +impl WrapperKeepOpaque { + /// Try to decode the wrapped type from the inner `data`. + /// + /// Returns `None` if the decoding failed. + pub fn try_decode(&self) -> Option + where + T: Decode, + { + T::decode_all(&mut &self.data[..]).ok() + } + + /// Returns the length of the encoded `T`. + pub fn encoded_len(&self) -> usize { + self.data.len() + } + + /// Returns the encoded data. + pub fn encoded(&self) -> &[u8] { + &self.data + } + + /// Create from the given encoded `data`. + pub fn from_encoded(data: Vec) -> Self { + Self { + data, + _phantom: PhantomDataSendSync::new(), + } + } + + /// Create from some raw value by encoding it. + pub fn from_value(value: T) -> Self + where + T: Encode, + { + Self { + data: value.encode(), + _phantom: PhantomDataSendSync::new(), + } + } +} + +impl EncodeAsType for WrapperKeepOpaque { + fn encode_as_type_to( + &self, + type_id: u32, + types: &scale_info::PortableRegistry, + out: &mut Vec, + ) -> Result<(), scale_encode::Error> { + use scale_encode::error::{Error, ErrorKind, Kind}; + + let Some(ty) = types.resolve(type_id) else { + return Err(Error::new(ErrorKind::TypeNotFound(type_id))); + }; + + // Do a basic check that the target shape lines up. + let scale_info::TypeDef::Composite(_) = &ty.type_def else { + return Err(Error::new(ErrorKind::WrongShape { + actual: Kind::Struct, + expected: type_id, + })); + }; + + // Check that the name also lines up. + if ty.path.ident().as_deref() != Some("WrapperKeepOpaque") { + return Err(Error::new(ErrorKind::WrongShape { + actual: Kind::Struct, + expected: type_id, + })); + } + + // Just blat the bytes out. + self.data.encode_to(out); + Ok(()) + } +} + +pub struct WrapperKeepOpaqueVisitor(core::marker::PhantomData); +impl Visitor for WrapperKeepOpaqueVisitor { + type Value<'scale, 'info> = WrapperKeepOpaque; + type Error = scale_decode::Error; + + fn visit_composite<'scale, 'info>( + self, + value: &mut scale_decode::visitor::types::Composite<'scale, 'info>, + _type_id: scale_decode::visitor::TypeId, + ) -> Result, Self::Error> { + use scale_decode::error::{Error, ErrorKind}; + + if value.path().ident().as_deref() != Some("WrapperKeepOpaque") { + return Err(Error::custom_str( + "Type to decode is not 'WrapperTypeKeepOpaque'", + )); + } + if value.remaining() != 2 { + return Err(Error::new(ErrorKind::WrongLength { + actual_len: value.remaining(), + expected_len: 2, + })); + } + + // The field to decode is a compact len followed by bytes. Decode the length, then grab the bytes. + let Compact(len) = value + .decode_item(Compact::::into_visitor()) + .expect("length checked")?; + let field = value.next().expect("length checked")?; + + // Sanity check that the compact length we decoded lines up with the number of bytes encoded in the next field. + if field.bytes().len() != len as usize { + return Err(Error::custom_str("WrapperTypeKeepOpaque compact encoded length doesn't line up with encoded byte len")); + } + + Ok(WrapperKeepOpaque { + data: field.bytes().to_vec(), + _phantom: PhantomDataSendSync::new(), + }) + } +} + +impl IntoVisitor for WrapperKeepOpaque { + type Visitor = WrapperKeepOpaqueVisitor; + fn into_visitor() -> Self::Visitor { + WrapperKeepOpaqueVisitor(core::marker::PhantomData) + } +} + +#[cfg(test)] +mod test { + use scale_decode::DecodeAsType; + + use super::*; + + // Copied from https://github.com/paritytech/substrate/blob/master/frame/support/src/traits/misc.rs + // and used for tests to check that we can work with the expected TypeInfo without needing to import + // the frame_support crate, which has quite a lot of dependencies. + impl scale_info::TypeInfo for WrapperKeepOpaque { + type Identity = Self; + fn type_info() -> scale_info::Type { + use scale_info::{build::Fields, meta_type, Path, Type, TypeParameter}; + + Type::builder() + .path(Path::new("WrapperKeepOpaque", module_path!())) + .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) + .composite( + Fields::unnamed() + .field(|f| f.compact::()) + .field(|f| f.ty::().type_name("T")), + ) + } + } + + /// Given a type definition, return type ID and registry representing it. + fn make_type() -> (u32, scale_info::PortableRegistry) { + let m = scale_info::MetaType::new::(); + let mut types = scale_info::Registry::new(); + let id = types.register_type(&m); + let portable_registry: scale_info::PortableRegistry = types.into(); + (id.id, portable_registry) + } + + fn roundtrips_like_scale_codec(t: T) + where + T: EncodeAsType + + DecodeAsType + + Encode + + Decode + + PartialEq + + core::fmt::Debug + + scale_info::TypeInfo + + 'static, + { + let (type_id, types) = make_type::(); + + let scale_codec_encoded = t.encode(); + let encode_as_type_encoded = t.encode_as_type(type_id, &types).unwrap(); + + assert_eq!( + scale_codec_encoded, encode_as_type_encoded, + "encoded bytes should match" + ); + + let decode_as_type_bytes = &mut &*scale_codec_encoded; + let decoded_as_type = T::decode_as_type(decode_as_type_bytes, type_id, &types) + .expect("decode-as-type decodes"); + + let decode_scale_codec_bytes = &mut &*scale_codec_encoded; + let decoded_scale_codec = T::decode(decode_scale_codec_bytes).expect("scale-codec decodes"); + + assert!( + decode_as_type_bytes.is_empty(), + "no bytes should remain in decode-as-type impl" + ); + assert!( + decode_scale_codec_bytes.is_empty(), + "no bytes should remain in codec-decode impl" + ); + + assert_eq!( + decoded_as_type, decoded_scale_codec, + "decoded values should match" + ); + } + + #[test] + fn wrapper_keep_opaque_roundtrips_ok() { + roundtrips_like_scale_codec(WrapperKeepOpaque::from_value(123u64)); + roundtrips_like_scale_codec(WrapperKeepOpaque::from_value(true)); + roundtrips_like_scale_codec(WrapperKeepOpaque::from_value(vec![1u8, 2, 3, 4])); + } +} diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index e1d9e0d46e0..ceff2e1339d 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -32,6 +32,33 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "blake2b_simd" version = "1.0.2" @@ -91,6 +118,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -101,6 +134,52 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -122,14 +201,59 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "frame-metadata" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "frame-metadata" version = "16.0.0" @@ -139,8 +263,15 @@ dependencies = [ "cfg-if", "parity-scale-codec", "scale-info", + "serde", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "generic-array" version = "0.14.7" @@ -151,6 +282,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -161,6 +303,46 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -182,6 +364,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + [[package]] name = "keccak" version = "0.1.5" @@ -222,9 +410,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec", + "bitvec", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", + "serde", ] [[package]] @@ -239,6 +429,31 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -277,6 +492,48 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustc_version" version = "0.4.0" @@ -286,16 +543,91 @@ dependencies = [ "semver", ] +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "scale-bits" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "036575c29af9b6e4866ffb7fa055dbf623fe7a9cc159b33786de6013a6969d89" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "scale-decode" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7caaf753f8ed1ab4752c6afb20174f03598c664724e0e32628e161c21000ff76" +dependencies = [ + "derive_more", + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-decode-derive", + "scale-info", + "smallvec", +] + +[[package]] +name = "scale-decode-derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3475108a1b62c7efd1b5c65974f30109a598b2f45f23c9ae030acb9686966db" +dependencies = [ + "darling", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-encode" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d70cb4b29360105483fac1ed567ff95d65224a14dd275b6303ed0a654c78de5" +dependencies = [ + "derive_more", + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-encode-derive", + "scale-info", + "smallvec", +] + +[[package]] +name = "scale-encode-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995491f110efdc6bea96d6a746140e32bfceb4ea47510750a5467295a4707a25" +dependencies = [ + "darling", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "scale-info" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ + "bitvec", "cfg-if", "derive_more", "parity-scale-codec", "scale-info-derive", + "serde", ] [[package]] @@ -310,12 +642,59 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "scale-value" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58223c7691bf0bd46b43c9aea6f0472d1067f378d574180232358d7c6e0a8089" +dependencies = [ + "derive_more", + "either", + "frame-metadata 15.1.0", + "parity-scale-codec", + "scale-bits", + "scale-decode", + "scale-encode", + "scale-info", +] + [[package]] name = "semver" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.10.8" @@ -337,6 +716,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + [[package]] name = "sp-core-hashing" version = "15.0.0" @@ -357,6 +742,44 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "subxt-core" +version = "0.34.0" +dependencies = [ + "base58", + "blake2", + "cfg-if", + "derivative", + "derive_more", + "frame-metadata 16.0.0", + "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", + "url", +] + [[package]] name = "subxt-core-no-std-tests" version = "0.0.0" @@ -364,6 +787,7 @@ dependencies = [ "libc", "libc_alloc", "parity-scale-codec", + "subxt-core", "subxt-metadata", ] @@ -373,7 +797,7 @@ version = "0.34.0" dependencies = [ "cfg-if", "derive_more", - "frame-metadata", + "frame-metadata 16.0.0", "hashbrown", "parity-scale-codec", "scale-info", @@ -402,6 +826,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml_datetime" version = "0.6.3" @@ -447,18 +892,62 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winnow" version = "0.5.34" @@ -468,6 +957,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/testing/no-std-tests/Cargo.toml b/testing/no-std-tests/Cargo.toml index 29a4bca2423..5e6f14c0744 100644 --- a/testing/no-std-tests/Cargo.toml +++ b/testing/no-std-tests/Cargo.toml @@ -7,6 +7,7 @@ resolver = "2" [dependencies] subxt-metadata = { path = "../../metadata", default-features = false } +subxt-core = { path = "../../core", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] } libc = { version = "0.2", default-features = false } libc_alloc = { version = "1.0.6" } diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 0caff7a433b..1638514801a 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -47,3 +47,8 @@ fn subxt_metadata_test() { const METADATA: &[u8] = include_bytes!("../../../artifacts/polkadot_metadata_small.scale"); subxt_metadata::Metadata::decode(&mut &METADATA[..]).expect("should be valid metadata"); } + +fn subxt_core_test() { + let url = "wss://mysite.com"; + assert!(subxt_core::utils::url_is_secure(url).unwrap()); +} diff --git a/testing/no-std-tests/tree.txt b/testing/no-std-tests/tree.txt new file mode 100644 index 00000000000..613fae578be --- /dev/null +++ b/testing/no-std-tests/tree.txt @@ -0,0 +1,215 @@ +subxt-core-no-std-tests v0.0.0 (/home/tadeo/code/subxt2/testing/no-std-tests) +├── libc v0.2.152 +├── libc_alloc v1.0.6 +├── parity-scale-codec v3.6.9 +│ ├── arrayvec v0.7.4 +│ ├── byte-slice-cast v1.2.2 +│ ├── impl-trait-for-tuples v0.2.2 (proc-macro) +│ │ ├── proc-macro2 v1.0.78 +│ │ │ └── unicode-ident v1.0.12 +│ │ ├── quote v1.0.35 +│ │ │ └── proc-macro2 v1.0.78 (*) +│ │ └── syn v1.0.109 +│ │ ├── proc-macro2 v1.0.78 (*) +│ │ ├── quote v1.0.35 (*) +│ │ └── unicode-ident v1.0.12 +│ ├── parity-scale-codec-derive v3.6.9 (proc-macro) +│ │ ├── proc-macro-crate v2.0.1 +│ │ │ ├── toml_datetime v0.6.3 +│ │ │ └── toml_edit v0.20.2 +│ │ │ ├── indexmap v2.1.0 +│ │ │ │ ├── equivalent v1.0.1 +│ │ │ │ └── hashbrown v0.14.3 +│ │ │ ├── toml_datetime v0.6.3 +│ │ │ └── winnow v0.5.34 +│ │ ├── proc-macro2 v1.0.78 (*) +│ │ ├── quote v1.0.35 (*) +│ │ └── syn v1.0.109 (*) +│ └── serde v1.0.196 +│ └── serde_derive v1.0.196 (proc-macro) +│ ├── proc-macro2 v1.0.78 (*) +│ ├── quote v1.0.35 (*) +│ └── syn v2.0.48 +│ ├── proc-macro2 v1.0.78 (*) +│ ├── quote v1.0.35 (*) +│ └── unicode-ident v1.0.12 +├── subxt-core v0.34.0 (/home/tadeo/code/subxt2/core) +│ ├── base58 v0.2.0 +│ ├── blake2 v0.10.6 +│ │ └── digest v0.10.7 +│ │ ├── block-buffer v0.10.4 +│ │ │ └── generic-array v0.14.7 +│ │ │ └── typenum v1.17.0 +│ │ │ [build-dependencies] +│ │ │ └── version_check v0.9.4 +│ │ ├── crypto-common v0.1.6 +│ │ │ ├── generic-array v0.14.7 (*) +│ │ │ └── typenum v1.17.0 +│ │ └── subtle v2.5.0 +│ ├── cfg-if v1.0.0 +│ ├── derivative v2.2.0 (proc-macro) +│ │ ├── proc-macro2 v1.0.78 (*) +│ │ ├── quote v1.0.35 (*) +│ │ └── syn v1.0.109 (*) +│ ├── derive_more v0.99.17 (proc-macro) +│ │ ├── convert_case v0.4.0 +│ │ ├── proc-macro2 v1.0.78 (*) +│ │ ├── quote v1.0.35 (*) +│ │ └── syn v1.0.109 (*) +│ │ [build-dependencies] +│ │ └── rustc_version v0.4.0 +│ │ └── semver v1.0.21 +│ ├── frame-metadata v16.0.0 +│ │ ├── cfg-if v1.0.0 +│ │ ├── parity-scale-codec v3.6.9 (*) +│ │ ├── scale-info v2.10.0 +│ │ │ ├── bitvec v1.0.1 +│ │ │ │ ├── funty v2.0.0 +│ │ │ │ ├── radium v0.7.0 +│ │ │ │ ├── tap v1.0.1 +│ │ │ │ └── wyz v0.5.1 +│ │ │ │ └── tap v1.0.1 +│ │ │ ├── cfg-if v1.0.0 +│ │ │ ├── derive_more v0.99.17 (proc-macro) (*) +│ │ │ ├── parity-scale-codec v3.6.9 (*) +│ │ │ ├── scale-info-derive v2.10.0 (proc-macro) +│ │ │ │ ├── proc-macro-crate v1.3.1 +│ │ │ │ │ ├── once_cell v1.19.0 +│ │ │ │ │ └── toml_edit v0.19.15 +│ │ │ │ │ ├── indexmap v2.1.0 (*) +│ │ │ │ │ ├── toml_datetime v0.6.3 +│ │ │ │ │ └── winnow v0.5.34 +│ │ │ │ ├── proc-macro2 v1.0.78 (*) +│ │ │ │ ├── quote v1.0.35 (*) +│ │ │ │ └── syn v1.0.109 (*) +│ │ │ └── serde v1.0.196 (*) +│ │ └── serde v1.0.196 (*) +│ ├── hex v0.4.3 +│ ├── impl-serde v0.4.0 +│ │ └── serde v1.0.196 (*) +│ ├── parity-scale-codec v3.6.9 (*) +│ ├── primitive-types v0.12.2 +│ │ ├── fixed-hash v0.8.0 +│ │ │ ├── byteorder v1.5.0 +│ │ │ ├── rand v0.8.5 +│ │ │ │ ├── libc v0.2.152 +│ │ │ │ ├── rand_chacha v0.3.1 +│ │ │ │ │ ├── ppv-lite86 v0.2.17 +│ │ │ │ │ └── rand_core v0.6.4 +│ │ │ │ │ └── getrandom v0.2.12 +│ │ │ │ │ ├── cfg-if v1.0.0 +│ │ │ │ │ └── libc v0.2.152 +│ │ │ │ └── rand_core v0.6.4 (*) +│ │ │ ├── rustc-hex v2.1.0 +│ │ │ └── static_assertions v1.1.0 +│ │ ├── impl-codec v0.6.0 +│ │ │ └── parity-scale-codec v3.6.9 (*) +│ │ ├── impl-serde v0.4.0 (*) +│ │ ├── scale-info v2.10.0 (*) +│ │ └── uint v0.9.5 +│ │ ├── byteorder v1.5.0 +│ │ ├── crunchy v0.2.2 +│ │ ├── hex v0.4.3 +│ │ └── static_assertions v1.1.0 +│ ├── scale-bits v0.4.0 +│ │ ├── parity-scale-codec v3.6.9 (*) +│ │ ├── scale-info v2.10.0 (*) +│ │ └── serde v1.0.196 (*) +│ ├── scale-decode v0.10.0 +│ │ ├── derive_more v0.99.17 (proc-macro) (*) +│ │ ├── parity-scale-codec v3.6.9 (*) +│ │ ├── primitive-types v0.12.2 (*) +│ │ ├── scale-bits v0.4.0 (*) +│ │ ├── scale-decode-derive v0.10.0 (proc-macro) +│ │ │ ├── darling v0.14.4 +│ │ │ │ ├── darling_core v0.14.4 +│ │ │ │ │ ├── fnv v1.0.7 +│ │ │ │ │ ├── ident_case v1.0.1 +│ │ │ │ │ ├── proc-macro2 v1.0.78 (*) +│ │ │ │ │ ├── quote v1.0.35 (*) +│ │ │ │ │ ├── strsim v0.10.0 +│ │ │ │ │ └── syn v1.0.109 (*) +│ │ │ │ └── darling_macro v0.14.4 (proc-macro) +│ │ │ │ ├── darling_core v0.14.4 (*) +│ │ │ │ ├── quote v1.0.35 (*) +│ │ │ │ └── syn v1.0.109 (*) +│ │ │ ├── proc-macro-crate v1.3.1 (*) +│ │ │ ├── proc-macro2 v1.0.78 (*) +│ │ │ ├── quote v1.0.35 (*) +│ │ │ └── syn v1.0.109 (*) +│ │ ├── scale-info v2.10.0 (*) +│ │ └── smallvec v1.13.1 +│ ├── scale-encode v0.5.0 +│ │ ├── derive_more v0.99.17 (proc-macro) (*) +│ │ ├── parity-scale-codec v3.6.9 (*) +│ │ ├── primitive-types v0.12.2 (*) +│ │ ├── scale-bits v0.4.0 (*) +│ │ ├── scale-encode-derive v0.5.0 (proc-macro) +│ │ │ ├── darling v0.14.4 (*) +│ │ │ ├── proc-macro-crate v1.3.1 (*) +│ │ │ ├── proc-macro2 v1.0.78 (*) +│ │ │ ├── quote v1.0.35 (*) +│ │ │ └── syn v1.0.109 (*) +│ │ ├── scale-info v2.10.0 (*) +│ │ └── smallvec v1.13.1 +│ ├── scale-info v2.10.0 (*) +│ ├── scale-value v0.13.0 +│ │ ├── derive_more v0.99.17 (proc-macro) (*) +│ │ ├── either v1.9.0 +│ │ ├── frame-metadata v15.1.0 +│ │ │ ├── cfg-if v1.0.0 +│ │ │ ├── parity-scale-codec v3.6.9 (*) +│ │ │ └── scale-info v2.10.0 (*) +│ │ ├── parity-scale-codec v3.6.9 (*) +│ │ ├── scale-bits v0.4.0 (*) +│ │ ├── scale-decode v0.10.0 (*) +│ │ ├── scale-encode v0.5.0 (*) +│ │ └── scale-info v2.10.0 (*) +│ ├── serde v1.0.196 (*) +│ ├── serde_json v1.0.113 +│ │ ├── itoa v1.0.10 +│ │ ├── ryu v1.0.16 +│ │ └── serde v1.0.196 (*) +│ ├── sp-core-hashing v15.0.0 +│ │ ├── blake2b_simd v1.0.2 +│ │ │ ├── arrayref v0.3.7 +│ │ │ ├── arrayvec v0.7.4 +│ │ │ └── constant_time_eq v0.3.0 +│ │ ├── byteorder v1.5.0 +│ │ ├── digest v0.10.7 (*) +│ │ ├── sha2 v0.10.8 +│ │ │ ├── cfg-if v1.0.0 +│ │ │ ├── cpufeatures v0.2.12 +│ │ │ └── digest v0.10.7 (*) +│ │ ├── sha3 v0.10.8 +│ │ │ ├── digest v0.10.7 (*) +│ │ │ └── keccak v0.1.5 +│ │ └── twox-hash v1.6.3 +│ │ ├── cfg-if v1.0.0 +│ │ ├── digest v0.10.7 (*) +│ │ └── static_assertions v1.1.0 +│ ├── subxt-metadata v0.34.0 (/home/tadeo/code/subxt2/metadata) +│ │ ├── cfg-if v1.0.0 +│ │ ├── derive_more v0.99.17 (proc-macro) (*) +│ │ ├── frame-metadata v16.0.0 (*) +│ │ ├── hashbrown v0.14.3 +│ │ │ ├── ahash v0.8.7 +│ │ │ │ ├── cfg-if v1.0.0 +│ │ │ │ ├── once_cell v1.19.0 +│ │ │ │ └── zerocopy v0.7.32 +│ │ │ │ [build-dependencies] +│ │ │ │ └── version_check v0.9.4 +│ │ │ └── allocator-api2 v0.2.16 +│ │ ├── parity-scale-codec v3.6.9 (*) +│ │ ├── scale-info v2.10.0 (*) +│ │ └── sp-core-hashing v15.0.0 (*) +│ └── url v2.5.0 +│ ├── form_urlencoded v1.2.1 +│ │ └── percent-encoding v2.3.1 +│ ├── idna v0.5.0 +│ │ ├── unicode-bidi v0.3.15 +│ │ └── unicode-normalization v0.1.22 +│ │ └── tinyvec v1.6.0 +│ │ └── tinyvec_macros v0.1.1 +│ └── percent-encoding v2.3.1 +└── subxt-metadata v0.34.0 (/home/tadeo/code/subxt2/metadata) (*) From 4512ad7fc0ca548a3c4d46a61f2c725fc0e6de70 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Tue, 30 Jan 2024 10:18:08 +0100 Subject: [PATCH 07/37] subxt-core compiles to no std again --- Cargo.lock | 1 - Cargo.toml | 5 +- core/Cargo.toml | 3 - core/src/client/mod.rs | 4 +- core/src/config/extrinsic_params.rs | 4 +- core/src/config/signed_extensions.rs | 18 ++-- core/src/utils/mod.rs | 17 ---- testing/no-std-tests/Cargo.lock | 133 --------------------------- testing/no-std-tests/src/main.rs | 4 +- 9 files changed, 18 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85bcecf80ff..d7fa206e6de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4560,7 +4560,6 @@ dependencies = [ "sp-keyring", "sp-runtime", "subxt-metadata", - "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9608f9176fd..cf2eda7e784 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ impl-serde = { version = "0.4.0" } indoc = "2" jsonrpsee = { version = "0.21" } pretty_assertions = "1.4.0" -primitive-types = { version = "0.12.2", default-features = false, features = ["codec", "scale-info", "serde"] } +primitive-types = { version = "0.12.2", default-features = false } proc-macro-error = "1.0.4" proc-macro2 = "1.0.78" quote = "1.0.35" @@ -131,7 +131,8 @@ sp-keyring = "31.0.0" # Subxt workspace crates: subxt = { version = "0.34.0", path = "subxt", default-features = false } subxt-macro = { version = "0.34.0", path = "macro" } -subxt-metadata = { version = "0.34.0", path = "metadata" } +subxt-core = { version = "0.34.0", path = "core", default-features = false } +subxt-metadata = { version = "0.34.0", path = "metadata", default-features = false } subxt-codegen = { version = "0.34.0", path = "codegen" } subxt-signer = { version = "0.34.0", path = "signer" } subxt-lightclient = { version = "0.34.0", path = "lightclient", default-features = false } diff --git a/core/Cargo.toml b/core/Cargo.toml index 98947ffc654..edc0307f299 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -44,9 +44,6 @@ impl-serde = { workspace = true } primitive-types = { workspace = true, default-features = false, features = ["codec", "serde_no_std", "scale-info"] } sp-core-hashing = { workspace = true } -# For parsing urls to disallow insecure schemes -url = { workspace = true } - # Included if the "substrate-compat" feature is enabled. sp-core = { workspace = true, optional = true } sp-runtime = { workspace = true, optional = true } diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 919154b836a..d768cbbd9d1 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -9,14 +9,14 @@ use crate::{config::Config, metadata::Metadata}; #[derive(Derivative)] #[derivative(Debug(bound = ""))] -pub struct MinimalClient { +pub struct ClientBase { pub genesis_hash: C::Hash, pub runtime_version: RuntimeVersion, pub metadata: Metadata, marker: core::marker::PhantomData, } -impl MinimalClient { +impl ClientBase { pub fn metadata(&self) -> Metadata { self.metadata.clone() } diff --git a/core/src/config/extrinsic_params.rs b/core/src/config/extrinsic_params.rs index 79d7837e5bc..9e6396d1b1f 100644 --- a/core/src/config/extrinsic_params.rs +++ b/core/src/config/extrinsic_params.rs @@ -7,7 +7,7 @@ //! [`crate::config::DefaultExtrinsicParams`] provides a general-purpose //! implementation of this that will work in many cases. -use crate::client::MinimalClient; +use crate::client::ClientBase; use super::Config; use crate::prelude::*; @@ -71,7 +71,7 @@ pub trait ExtrinsicParams: ExtrinsicParamsEncoder + Sized + 'static { /// Construct a new instance of our [`ExtrinsicParams`]. fn new( nonce: u64, - client: &MinimalClient, + client: &ClientBase, other_params: Self::OtherParams, ) -> Result; } diff --git a/core/src/config/signed_extensions.rs b/core/src/config/signed_extensions.rs index 9d05c4cb536..2bf6a1bc519 100644 --- a/core/src/config/signed_extensions.rs +++ b/core/src/config/signed_extensions.rs @@ -9,7 +9,7 @@ use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; use super::Config; -use crate::client::MinimalClient; +use crate::client::ClientBase; use crate::prelude::*; use crate::utils::Era; use borrow::ToOwned; @@ -45,7 +45,7 @@ impl ExtrinsicParams for CheckSpecVersion { fn new( _nonce: u64, - client: &MinimalClient, + client: &ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CheckSpecVersion(client.runtime_version().spec_version)) @@ -73,7 +73,7 @@ impl ExtrinsicParams for CheckNonce { fn new( nonce: u64, - _client: &MinimalClient, + _client: &ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CheckNonce(Compact(nonce))) @@ -101,7 +101,7 @@ impl ExtrinsicParams for CheckTxVersion { fn new( _nonce: u64, - client: &MinimalClient, + client: &ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CheckTxVersion(client.runtime_version().transaction_version)) @@ -129,7 +129,7 @@ impl ExtrinsicParams for CheckGenesis { fn new( _nonce: u64, - client: &MinimalClient, + client: &ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CheckGenesis(client.genesis_hash())) @@ -195,7 +195,7 @@ impl ExtrinsicParams for CheckMortality { fn new( _nonce: u64, - client: &MinimalClient, + client: &ClientBase, other_params: Self::OtherParams, ) -> Result { Ok(CheckMortality { @@ -286,7 +286,7 @@ impl ExtrinsicParams for ChargeAssetTxPayment { fn new( _nonce: u64, - _client: &MinimalClient, + _client: &ClientBase, other_params: Self::OtherParams, ) -> Result { Ok(ChargeAssetTxPayment { @@ -344,7 +344,7 @@ impl ExtrinsicParams for ChargeTransactionPayment { fn new( _nonce: u64, - _client: &MinimalClient, + _client: &ClientBase, other_params: Self::OtherParams, ) -> Result { Ok(ChargeTransactionPayment { @@ -388,7 +388,7 @@ macro_rules! impl_tuples { fn new( nonce: u64, - client: &MinimalClient, + client: &ClientBase, other_params: Self::OtherParams, ) -> Result { let metadata = client.metadata(); diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index f8e74fafbb3..c04aafcdb0a 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -15,7 +15,6 @@ mod wrapper_opaque; use codec::{Compact, Decode, Encode}; use derivative::Derivative; -use url::Url; pub use account_id::AccountId32; use borrow::ToOwned; @@ -50,22 +49,6 @@ pub(crate) fn strip_compact_prefix(bytes: &[u8]) -> Result<(u64, &[u8]), codec:: 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. -pub fn url_is_secure(url: &str) -> Result { - let url = Url::parse(url)?; - - let secure_scheme = url.scheme() == "https" || url.scheme() == "wss"; - let is_localhost = url.host().is_some_and(|e| match e { - url::Host::Domain(e) => e == "localhost", - url::Host::Ipv4(e) => e.is_loopback(), - url::Host::Ipv6(e) => e.is_loopback(), - }); - - Ok(secure_scheme || is_localhost) -} - use crate::prelude::*; /// A version of [`core::marker::PhantomData`] that is also Send and Sync (which is fine diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index ceff2e1339d..9adef2beda3 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -222,9 +222,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ - "byteorder", - "rand", - "rustc-hex", "static_assertions", ] @@ -234,15 +231,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - [[package]] name = "frame-metadata" version = "15.1.0" @@ -263,7 +251,6 @@ dependencies = [ "cfg-if", "parity-scale-codec", "scale-info", - "serde", ] [[package]] @@ -282,17 +269,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "hashbrown" version = "0.14.3" @@ -315,16 +291,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "impl-codec" version = "0.6.0" @@ -410,11 +376,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec", - "bitvec", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", - "serde", ] [[package]] @@ -429,18 +393,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "primitive-types" version = "0.12.2" @@ -498,42 +450,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - [[package]] name = "rustc_version" version = "0.4.0" @@ -627,7 +543,6 @@ dependencies = [ "derive_more", "parity-scale-codec", "scale-info-derive", - "serde", ] [[package]] @@ -777,7 +692,6 @@ dependencies = [ "serde_json", "sp-core-hashing", "subxt-metadata", - "url", ] [[package]] @@ -832,21 +746,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "toml_datetime" version = "0.6.3" @@ -904,50 +803,18 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "winnow" version = "0.5.34" diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 1638514801a..2ca5d22f5ea 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -49,6 +49,6 @@ fn subxt_metadata_test() { } fn subxt_core_test() { - let url = "wss://mysite.com"; - assert!(subxt_core::utils::url_is_secure(url).unwrap()); + // let er + // assert!(subxt_core::utils::url_is_secure(url).unwrap()); } From 2e66b48adf94f6e154dca178a036b63853965f33 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 1 Feb 2024 10:29:46 +0100 Subject: [PATCH 08/37] subxt compiles with new dependency features --- core/src/utils/mod.rs | 13 ++++++------- subxt/Cargo.toml | 12 ++++++------ testing/no-std-tests/src/main.rs | 3 +-- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index c04aafcdb0a..006864dd640 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -4,6 +4,12 @@ //! Miscellaneous utility helpers. +use crate::prelude::*; +use borrow::ToOwned; +use codec::{Compact, Decode, Encode}; +use derivative::Derivative; +use vec::Vec; + mod account_id; pub mod bits; mod era; @@ -13,17 +19,12 @@ mod static_type; mod unchecked_extrinsic; mod wrapper_opaque; -use codec::{Compact, Decode, Encode}; -use derivative::Derivative; - pub use account_id::AccountId32; -use borrow::ToOwned; pub use era::Era; pub use multi_address::MultiAddress; pub use multi_signature::MultiSignature; pub use static_type::Static; pub use unchecked_extrinsic::UncheckedExtrinsic; -use vec::Vec; pub use wrapper_opaque::WrapperKeepOpaque; // Used in codegen @@ -49,8 +50,6 @@ pub(crate) fn strip_compact_prefix(bytes: &[u8]) -> Result<(u64, &[u8]), codec:: Ok((val.0, *cursor)) } -use crate::prelude::*; - /// 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). diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index 6f6b664a0f9..b53cf79f0bf 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -51,11 +51,11 @@ unstable-light-client = ["subxt-lightclient", "tokio-stream"] [dependencies] async-trait = { workspace = true } codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] } -scale-info = { workspace = true } -scale-value = { workspace = true } -scale-bits = { workspace = true } -scale-decode = { workspace = true } -scale-encode = { workspace = true } +scale-info = { workspace = true, features = ["default"] } +scale-value = { workspace = true, features = ["default"] } +scale-bits = { workspace = true, features = ["default"] } +scale-decode = { workspace = true, features = ["default"] } +scale-encode = { workspace = true, features = ["default"] } futures = { workspace = true } hex = { workspace = true } serde = { workspace = true, features = ["derive"] } @@ -69,7 +69,7 @@ instant = { workspace = true } # Provides some deserialization, types like U256/H256 and hashing impls like twox/blake256: impl-serde = { workspace = true } -primitive-types = { workspace = true } +primitive-types = { workspace = true, features = ["codec", "scale-info", "serde"] } sp-core-hashing = { workspace = true } # For ss58 encoding AccountId32 to serialize them properly: diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 2ca5d22f5ea..65f425028f5 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -49,6 +49,5 @@ fn subxt_metadata_test() { } fn subxt_core_test() { - // let er - // assert!(subxt_core::utils::url_is_secure(url).unwrap()); + let era = subxt_core::utils::era::Era::Immortal; } From 7b2cfdc7bf17212858858b52e88a03f0b58973d6 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 1 Feb 2024 10:42:19 +0100 Subject: [PATCH 09/37] subxt: use utils from core --- Cargo.lock | 1 + core/Cargo.toml | 4 +- core/src/utils/mod.rs | 4 +- subxt/Cargo.toml | 1 + subxt/src/utils.rs | 36 ++++ subxt/src/utils/account_id.rs | 218 -------------------- subxt/src/utils/bits.rs | 266 ------------------------- subxt/src/utils/era.rs | 107 ---------- subxt/src/utils/mod.rs | 106 ---------- subxt/src/utils/multi_address.rs | 72 ------- subxt/src/utils/multi_signature.rs | 58 ------ subxt/src/utils/static_type.rs | 78 -------- subxt/src/utils/unchecked_extrinsic.rs | 136 ------------- subxt/src/utils/wrapper_opaque.rs | 243 ---------------------- 14 files changed, 42 insertions(+), 1288 deletions(-) create mode 100644 subxt/src/utils.rs delete mode 100644 subxt/src/utils/account_id.rs delete mode 100644 subxt/src/utils/bits.rs delete mode 100644 subxt/src/utils/era.rs delete mode 100644 subxt/src/utils/mod.rs delete mode 100644 subxt/src/utils/multi_address.rs delete mode 100644 subxt/src/utils/multi_signature.rs delete mode 100644 subxt/src/utils/static_type.rs delete mode 100644 subxt/src/utils/unchecked_extrinsic.rs delete mode 100644 subxt/src/utils/wrapper_opaque.rs diff --git a/Cargo.lock b/Cargo.lock index d7fa206e6de..f6cdbcf0e08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4471,6 +4471,7 @@ dependencies = [ "sp-core-hashing", "sp-keyring", "sp-runtime", + "subxt-core", "subxt-lightclient", "subxt-macro", "subxt-metadata", diff --git a/core/Cargo.toml b/core/Cargo.toml index edc0307f299..a184871dec7 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,8 +15,8 @@ description = "Sign extrinsics to be submitted by Subxt" keywords = ["parity", "subxt", "extrinsic", "no-std"] [features] -# default = ["std"] -std = [] +default = ["std"] +std = ["scale-info/std", "scale-value/std", "scale-bits/std", "scale-decode/std", "scale-encode/std", "frame-metadata/std", "subxt-metadata/std"] substrate-compat = ["sp-core", "sp-runtime"] [dependencies] diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index 006864dd640..067c43f3f25 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -44,7 +44,7 @@ impl codec::Encode for Encoded { /// 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> { +pub fn strip_compact_prefix(bytes: &[u8]) -> Result<(u64, &[u8]), codec::Error> { let cursor = &mut &*bytes; let val = >::decode(cursor)?; Ok((val.0, *cursor)) @@ -67,7 +67,7 @@ pub(crate) fn strip_compact_prefix(bytes: &[u8]) -> Result<(u64, &[u8]), codec:: pub struct PhantomDataSendSync(core::marker::PhantomData); impl PhantomDataSendSync { - pub(crate) fn new() -> Self { + pub fn new() -> Self { Self(core::marker::PhantomData) } } diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index b53cf79f0bf..8ef6cd7ba77 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -56,6 +56,7 @@ scale-value = { workspace = true, features = ["default"] } scale-bits = { workspace = true, features = ["default"] } scale-decode = { workspace = true, features = ["default"] } scale-encode = { workspace = true, features = ["default"] } +subxt-core = { workspace = true, features = ["std"] } futures = { workspace = true } hex = { workspace = true } serde = { workspace = true, features = ["derive"] } diff --git a/subxt/src/utils.rs b/subxt/src/utils.rs new file mode 100644 index 00000000000..b02acebba06 --- /dev/null +++ b/subxt/src/utils.rs @@ -0,0 +1,36 @@ +// 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. + +//! Utility functions used in subxt. Reexports all elements from [`subxt_core::utils`]; + +pub use subxt_core::utils::*; + +use url::Url; + +use crate::{error::RpcError, Error}; + +/// 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. +pub fn url_is_secure(url: &str) -> Result { + let url = Url::parse(url).map_err(|e| Error::Rpc(RpcError::ClientError(Box::new(e))))?; + + let secure_scheme = url.scheme() == "https" || url.scheme() == "wss"; + let is_localhost = url.host().is_some_and(|e| match e { + url::Host::Domain(e) => e == "localhost", + url::Host::Ipv4(e) => e.is_loopback(), + url::Host::Ipv6(e) => e.is_loopback(), + }); + + Ok(secure_scheme || is_localhost) +} + +/// Validates, that the given Url is secure ("https" or "wss" scheme) or is referring to localhost. +pub fn validate_url_is_secure(url: &str) -> Result<(), Error> { + if !url_is_secure(url)? { + Err(Error::Rpc(crate::error::RpcError::InsecureUrl(url.into()))) + } else { + Ok(()) + } +} diff --git a/subxt/src/utils/account_id.rs b/subxt/src/utils/account_id.rs deleted file mode 100644 index faa3dbbd4db..00000000000 --- a/subxt/src/utils/account_id.rs +++ /dev/null @@ -1,218 +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. - -//! The "default" Substrate/Polkadot AccountId. This is used in codegen, as well as signing related bits. -//! 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 codec::{Decode, Encode}; -use serde::{Deserialize, Serialize}; - -/// A 32-byte cryptographic identifier. This is a simplified version of Substrate's -/// `sp_core::crypto::AccountId32`. To obtain more functionality, convert this into -/// that type. -#[derive( - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - Debug, - scale_encode::EncodeAsType, - scale_decode::DecodeAsType, - scale_info::TypeInfo, -)] -pub struct AccountId32(pub [u8; 32]); - -impl AsRef<[u8]> for AccountId32 { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl AsRef<[u8; 32]> for AccountId32 { - fn as_ref(&self) -> &[u8; 32] { - &self.0 - } -} - -impl From<[u8; 32]> for AccountId32 { - fn from(x: [u8; 32]) -> Self { - AccountId32(x) - } -} - -impl AccountId32 { - // Return the ss58-check string for this key. Adapted from `sp_core::crypto`. We need this to - // serialize our account appropriately but otherwise don't care. - fn to_ss58check(&self) -> String { - // For serializing to a string to obtain the account nonce, we use the default substrate - // prefix (since we have no way to otherwise pick one). It doesn't really matter, since when - // it's deserialized back in system_accountNextIndex, we ignore this (so long as it's valid). - const SUBSTRATE_SS58_PREFIX: u8 = 42; - // prefix <= 63 just take up one byte at the start: - let mut v = vec![SUBSTRATE_SS58_PREFIX]; - // then push the account ID bytes. - v.extend(self.0); - // then push a 2 byte checksum of what we have so far. - let r = ss58hash(&v); - v.extend(&r[0..2]); - // then encode to base58. - use base58::ToBase58; - v.to_base58() - } - - // This isn't strictly needed, but to give our AccountId32 a little more usefulness, we also - // implement the logic needed to decode an AccountId32 from an SS58 encoded string. This is exposed - // via a `FromStr` impl. - fn from_ss58check(s: &str) -> Result { - const CHECKSUM_LEN: usize = 2; - let body_len = 32; - - use base58::FromBase58; - let data = s.from_base58().map_err(|_| FromSs58Error::BadBase58)?; - if data.len() < 2 { - return Err(FromSs58Error::BadLength); - } - let prefix_len = match data[0] { - 0..=63 => 1, - 64..=127 => 2, - _ => return Err(FromSs58Error::InvalidPrefix), - }; - if data.len() != prefix_len + body_len + CHECKSUM_LEN { - return Err(FromSs58Error::BadLength); - } - let hash = ss58hash(&data[0..body_len + prefix_len]); - let checksum = &hash[0..CHECKSUM_LEN]; - if data[body_len + prefix_len..body_len + prefix_len + CHECKSUM_LEN] != *checksum { - // Invalid checksum. - return Err(FromSs58Error::InvalidChecksum); - } - - let result = data[prefix_len..body_len + prefix_len] - .try_into() - .map_err(|_| FromSs58Error::BadLength)?; - Ok(AccountId32(result)) - } -} - -/// An error obtained from trying to interpret an SS58 encoded string into an AccountId32 -#[derive(thiserror::Error, Clone, Copy, Eq, PartialEq, Debug)] -#[allow(missing_docs)] -pub enum FromSs58Error { - #[error("Base 58 requirement is violated")] - BadBase58, - #[error("Length is bad")] - BadLength, - #[error("Invalid checksum")] - InvalidChecksum, - #[error("Invalid SS58 prefix byte.")] - InvalidPrefix, -} - -// We do this just to get a checksum to help verify the validity of the address in to_ss58check -fn ss58hash(data: &[u8]) -> Vec { - use blake2::{Blake2b512, Digest}; - const PREFIX: &[u8] = b"SS58PRE"; - let mut ctx = Blake2b512::new(); - ctx.update(PREFIX); - ctx.update(data); - ctx.finalize().to_vec() -} - -impl Serialize for AccountId32 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_ss58check()) - } -} - -impl<'de> Deserialize<'de> for AccountId32 { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - AccountId32::from_ss58check(&String::deserialize(deserializer)?) - .map_err(|e| serde::de::Error::custom(format!("{e:?}"))) - } -} - -impl std::fmt::Display for AccountId32 { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.to_ss58check()) - } -} - -impl std::str::FromStr for AccountId32 { - type Err = FromSs58Error; - fn from_str(s: &str) -> Result { - AccountId32::from_ss58check(s) - } -} - -// Improve compat with the substrate version if we're using those crates: -#[cfg(feature = "substrate-compat")] -mod substrate_impls { - use super::*; - - impl From for AccountId32 { - fn from(value: sp_runtime::AccountId32) -> Self { - Self(value.into()) - } - } - impl From for AccountId32 { - fn from(value: sp_core::sr25519::Public) -> Self { - let acc: sp_runtime::AccountId32 = value.into(); - acc.into() - } - } - impl From for AccountId32 { - fn from(value: sp_core::ed25519::Public) -> Self { - let acc: sp_runtime::AccountId32 = value.into(); - acc.into() - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - use sp_core::crypto::Ss58Codec; - use sp_keyring::AccountKeyring; - - #[test] - fn ss58_is_compatible_with_substrate_impl() { - let keyrings = vec![ - AccountKeyring::Alice, - AccountKeyring::Bob, - AccountKeyring::Charlie, - ]; - - for keyring in keyrings { - let substrate_account = keyring.to_account_id(); - // Avoid "From" impl hidden behind "substrate-compat" feature so that this test - // can work either way: - let local_account = AccountId32(substrate_account.clone().into()); - - // Both should encode to ss58 the same way: - let substrate_ss58 = substrate_account.to_ss58check(); - assert_eq!(substrate_ss58, local_account.to_ss58check()); - - // Both should decode from ss58 back to the same: - assert_eq!( - sp_core::crypto::AccountId32::from_ss58check(&substrate_ss58).unwrap(), - substrate_account - ); - assert_eq!( - AccountId32::from_ss58check(&substrate_ss58).unwrap(), - local_account - ); - } - } -} diff --git a/subxt/src/utils/bits.rs b/subxt/src/utils/bits.rs deleted file mode 100644 index ed830a0deaa..00000000000 --- a/subxt/src/utils/bits.rs +++ /dev/null @@ -1,266 +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. - -//! Generic `scale_bits` over `bitvec`-like `BitOrder` and `BitFormat` types. - -use codec::{Compact, Input}; -use scale_bits::{ - scale::format::{Format, OrderFormat, StoreFormat}, - Bits, -}; -use scale_decode::IntoVisitor; -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 -/// `bitvec`-like type type parameters. -pub trait BitStore { - /// Corresponding `scale_bits::StoreFormat` value. - const FORMAT: StoreFormat; - /// Number of bits that the backing store types holds. - const BITS: u32; -} -macro_rules! impl_store { - ($ty:ident, $wrapped:ty) => { - impl BitStore for $wrapped { - const FORMAT: StoreFormat = StoreFormat::$ty; - const BITS: u32 = <$wrapped>::BITS; - } - }; -} -impl_store!(U8, u8); -impl_store!(U16, u16); -impl_store!(U32, u32); -impl_store!(U64, u64); - -/// Associates `bitvec::order::BitOrder` trait with corresponding, type-erased `scale_bits::OrderFormat` enum. -/// -/// Used to decode bit sequences in runtime by providing `scale_bits::OrderFormat` using -/// `bitvec`-like type type parameters. -pub trait BitOrder { - /// Corresponding `scale_bits::OrderFormat` value. - const FORMAT: OrderFormat; -} -macro_rules! impl_order { - ($ty:ident) => { - #[doc = concat!("Type-level value that corresponds to `scale_bits::OrderFormat::", stringify!($ty), "` at run-time")] - #[doc = concat!(" and `bitvec::order::BitOrder::", stringify!($ty), "` at the type level.")] - #[derive(Clone, Debug, PartialEq, Eq)] - pub enum $ty {} - impl BitOrder for $ty { - const FORMAT: OrderFormat = OrderFormat::$ty; - } - }; -} -impl_order!(Lsb0); -impl_order!(Msb0); - -/// Constructs a run-time format parameters based on the corresponding type-level parameters. -fn bit_format() -> Format { - Format { - order: Order::FORMAT, - store: Store::FORMAT, - } -} - -/// `scale_bits::Bits` generic over the bit store (`u8`/`u16`/`u32`/`u64`) and bit order (LSB, MSB) -/// used for SCALE encoding/decoding. Uses `scale_bits::Bits`-default `u8` and LSB format underneath. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DecodedBits { - bits: Bits, - _marker: PhantomData<(Store, Order)>, -} - -impl DecodedBits { - /// Extracts the underlying `scale_bits::Bits` value. - pub fn into_bits(self) -> Bits { - self.bits - } - - /// References the underlying `scale_bits::Bits` value. - pub fn as_bits(&self) -> &Bits { - &self.bits - } -} - -impl core::iter::FromIterator for DecodedBits { - fn from_iter>(iter: T) -> Self { - DecodedBits { - bits: Bits::from_iter(iter), - _marker: PhantomData, - } - } -} - -impl codec::Decode for DecodedBits { - fn decode(input: &mut I) -> Result { - /// Equivalent of `BitSlice::MAX_BITS` on 32bit machine. - const ARCH32BIT_BITSLICE_MAX_BITS: u32 = 0x1fff_ffff; - - let Compact(bits) = >::decode(input)?; - // Otherwise it is impossible to store it on 32bit machine. - if bits > ARCH32BIT_BITSLICE_MAX_BITS { - return Err("Attempt to decode a BitVec with too many bits".into()); - } - // NOTE: Replace with `bits.div_ceil(Store::BITS)` if `int_roundings` is stabilised - let elements = (bits / Store::BITS) + u32::from(bits % Store::BITS != 0); - let bytes_in_elem = Store::BITS.saturating_div(u8::BITS); - let bytes_needed = (elements * bytes_in_elem) as usize; - - // NOTE: We could reduce allocations if it would be possible to directly - // decode from an `Input` type using a custom format (rather than default ) - // for the `Bits` type. - let mut storage = codec::Encode::encode(&Compact(bits)); - let prefix_len = storage.len(); - storage.reserve_exact(bytes_needed); - storage.extend(vec![0; bytes_needed]); - input.read(&mut storage[prefix_len..])?; - - let decoder = scale_bits::decode_using_format_from(&storage, bit_format::())?; - let bits = decoder.collect::, _>>()?; - let bits = Bits::from_iter(bits); - - Ok(DecodedBits { - bits, - _marker: PhantomData, - }) - } -} - -impl codec::Encode for DecodedBits { - fn size_hint(&self) -> usize { - self.bits.size_hint() - } - - fn encoded_size(&self) -> usize { - self.bits.encoded_size() - } - - fn encode(&self) -> Vec { - scale_bits::encode_using_format(self.bits.iter(), bit_format::()) - } -} - -#[doc(hidden)] -pub struct DecodedBitsVisitor(std::marker::PhantomData<(S, O)>); -impl scale_decode::Visitor for DecodedBitsVisitor { - type Value<'scale, 'info> = DecodedBits; - type Error = scale_decode::Error; - - fn unchecked_decode_as_type<'scale, 'info>( - self, - input: &mut &'scale [u8], - type_id: scale_decode::visitor::TypeId, - types: &'info scale_info::PortableRegistry, - ) -> scale_decode::visitor::DecodeAsTypeResult< - Self, - Result, Self::Error>, - > { - let res = scale_decode::visitor::decode_with_visitor( - input, - type_id.0, - types, - Bits::into_visitor(), - ) - .map(|bits| DecodedBits { - bits, - _marker: PhantomData, - }); - scale_decode::visitor::DecodeAsTypeResult::Decoded(res) - } -} -impl scale_decode::IntoVisitor for DecodedBits { - type Visitor = DecodedBitsVisitor; - fn into_visitor() -> Self::Visitor { - DecodedBitsVisitor(PhantomData) - } -} - -impl scale_encode::EncodeAsType for DecodedBits { - fn encode_as_type_to( - &self, - type_id: u32, - types: &scale_info::PortableRegistry, - out: &mut Vec, - ) -> Result<(), scale_encode::Error> { - self.bits.encode_as_type_to(type_id, types, out) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use core::fmt::Debug; - - use bitvec::vec::BitVec; - use codec::Decode as _; - - // NOTE: We don't use `bitvec::order` types in our implementation, since we - // don't want to depend on `bitvec`. Rather than reimplementing the unsafe - // trait on our types here for testing purposes, we simply convert and - // delegate to `bitvec`'s own types. - trait ToBitVec { - type Order: bitvec::order::BitOrder; - } - impl ToBitVec for Lsb0 { - type Order = bitvec::order::Lsb0; - } - impl ToBitVec for Msb0 { - type Order = bitvec::order::Msb0; - } - - fn scales_like_bitvec_and_roundtrips< - 'a, - Store: BitStore + bitvec::store::BitStore + PartialEq, - Order: BitOrder + ToBitVec + Debug + PartialEq, - >( - input: impl IntoIterator, - ) where - BitVec::Order>: codec::Encode + codec::Decode, - { - let input: Vec<_> = input.into_iter().copied().collect(); - - let decoded_bits = DecodedBits::::from_iter(input.clone()); - let bitvec = BitVec::::Order>::from_iter(input); - - let decoded_bits_encoded = codec::Encode::encode(&decoded_bits); - let bitvec_encoded = codec::Encode::encode(&bitvec); - assert_eq!(decoded_bits_encoded, bitvec_encoded); - - let decoded_bits_decoded = - DecodedBits::::decode(&mut &decoded_bits_encoded[..]) - .expect("SCALE-encoding DecodedBits to roundtrip"); - let bitvec_decoded = - BitVec::::Order>::decode(&mut &bitvec_encoded[..]) - .expect("SCALE-encoding BitVec to roundtrip"); - assert_eq!(decoded_bits, decoded_bits_decoded); - assert_eq!(bitvec, bitvec_decoded); - } - - #[test] - fn decoded_bitvec_scales_and_roundtrips() { - let test_cases = [ - vec![], - vec![true], - vec![false], - vec![true, false, true], - vec![true, false, true, false, false, false, false, false, true], - [vec![true; 5], vec![false; 5], vec![true; 1], vec![false; 3]].concat(), - [vec![true; 9], vec![false; 9], vec![true; 9], vec![false; 9]].concat(), - ]; - - for test_case in &test_cases { - scales_like_bitvec_and_roundtrips::(test_case); - scales_like_bitvec_and_roundtrips::(test_case); - scales_like_bitvec_and_roundtrips::(test_case); - scales_like_bitvec_and_roundtrips::(test_case); - scales_like_bitvec_and_roundtrips::(test_case); - scales_like_bitvec_and_roundtrips::(test_case); - scales_like_bitvec_and_roundtrips::(test_case); - scales_like_bitvec_and_roundtrips::(test_case); - } - } -} diff --git a/subxt/src/utils/era.rs b/subxt/src/utils/era.rs deleted file mode 100644 index c98ebe58f68..00000000000 --- a/subxt/src/utils/era.rs +++ /dev/null @@ -1,107 +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 scale_decode::DecodeAsType; -use scale_encode::EncodeAsType; - -// Dev note: This and related bits taken from `sp_runtime::generic::Era` -/// An era to describe the longevity of a transaction. -#[derive( - PartialEq, - Default, - Eq, - Clone, - Copy, - Debug, - serde::Serialize, - serde::Deserialize, - DecodeAsType, - EncodeAsType, - scale_info::TypeInfo, -)] -pub enum Era { - /// The transaction is valid forever. The genesis hash must be present in the signed content. - #[default] - Immortal, - - /// The transaction will expire. Use [`Era::mortal`] to construct this with correct values. - /// - /// When used on `FRAME`-based runtimes, `period` cannot exceed `BlockHashCount` parameter - /// of `system` module. - Mortal { - /// The number of blocks that the tx will be valid for after the checkpoint block - /// hash found in the signer payload. - period: u64, - /// The phase in the period that this transaction's lifetime begins (and, importantly, - /// implies which block hash is included in the signature material). If the `period` is - /// greater than 1 << 12, then it will be a factor of the times greater than 1<<12 that - /// `period` is. - phase: u64, - }, -} - -// E.g. with period == 4: -// 0 10 20 30 40 -// 0123456789012345678901234567890123456789012 -// |...| -// authored -/ \- expiry -// phase = 1 -// n = Q(current - phase, period) + phase -impl Era { - /// Create a new era based on a period (which should be a power of two between 4 and 65536 - /// inclusive) and a block number on which it should start (or, for long periods, be shortly - /// after the start). - /// - /// If using `Era` in the context of `FRAME` runtime, make sure that `period` - /// does not exceed `BlockHashCount` parameter passed to `system` module, since that - /// prunes old blocks and renders transactions immediately invalid. - pub fn mortal(period: u64, current: u64) -> Self { - let period = period - .checked_next_power_of_two() - .unwrap_or(1 << 16) - .clamp(4, 1 << 16); - let phase = current % period; - let quantize_factor = (period >> 12).max(1); - let quantized_phase = phase / quantize_factor * quantize_factor; - - Self::Mortal { - period, - phase: quantized_phase, - } - } -} - -// Both copied from `sp_runtime::generic::Era`; this is the wire interface and so -// it's really the most important bit here. -impl codec::Encode for Era { - fn encode_to(&self, output: &mut T) { - match self { - Self::Immortal => output.push_byte(0), - Self::Mortal { period, phase } => { - let quantize_factor = (*period >> 12).max(1); - let encoded = (period.trailing_zeros() - 1).clamp(1, 15) as u16 - | ((phase / quantize_factor) << 4) as u16; - encoded.encode_to(output); - } - } - } -} -impl codec::Decode for Era { - fn decode(input: &mut I) -> Result { - let first = input.read_byte()?; - if first == 0 { - Ok(Self::Immortal) - } else { - let encoded = first as u64 + ((input.read_byte()? as u64) << 8); - let period = 2 << (encoded % (1 << 4)); - let quantize_factor = (period >> 12).max(1); - let phase = (encoded >> 4) * quantize_factor; - if period >= 4 && phase < period { - Ok(Self::Mortal { period, phase }) - } else { - Err("Invalid period and phase".into()) - } - } - } -} diff --git a/subxt/src/utils/mod.rs b/subxt/src/utils/mod.rs deleted file mode 100644 index dc9320a303e..00000000000 --- a/subxt/src/utils/mod.rs +++ /dev/null @@ -1,106 +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. - -//! 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::Error; -use codec::{Compact, Decode, Encode}; -use derivative::Derivative; -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; - -// 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); - -impl codec::Encode for Encoded { - fn encode(&self) -> Vec { - 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 = >::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. -pub fn url_is_secure(url: &str) -> Result { - let url = Url::parse(url).map_err(|e| Error::Rpc(RpcError::ClientError(Box::new(e))))?; - - let secure_scheme = url.scheme() == "https" || url.scheme() == "wss"; - let is_localhost = url.host().is_some_and(|e| match e { - url::Host::Domain(e) => e == "localhost", - url::Host::Ipv4(e) => e.is_loopback(), - url::Host::Ipv6(e) => e.is_loopback(), - }); - - Ok(secure_scheme || is_localhost) -} - -/// Validates, that the given Url is secure ("https" or "wss" scheme) or is referring to localhost. -pub fn validate_url_is_secure(url: &str) -> Result<(), Error> { - if !url_is_secure(url)? { - Err(Error::Rpc(crate::error::RpcError::InsecureUrl(url.into()))) - } else { - 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(core::marker::PhantomData); - -impl PhantomDataSendSync { - pub(crate) fn new() -> Self { - Self(core::marker::PhantomData) - } -} - -unsafe impl Send for PhantomDataSendSync {} -unsafe impl Sync for PhantomDataSendSync {} - -/// 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 = Vec<(K, V)>; diff --git a/subxt/src/utils/multi_address.rs b/subxt/src/utils/multi_address.rs deleted file mode 100644 index 9b1e556fa9e..00000000000 --- a/subxt/src/utils/multi_address.rs +++ /dev/null @@ -1,72 +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. - -//! The "default" Substrate/Polkadot Address type. This is used in codegen, as well as signing related bits. -//! 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 codec::{Decode, Encode}; - -/// A multi-format address wrapper for on-chain accounts. This is a simplified version of Substrate's -/// `sp_runtime::MultiAddress`. To obtain more functionality, convert this into that type (this conversion -/// functionality is provided via `From` impls if the `substrate-compat` feature is enabled). -#[derive( - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - Debug, - scale_encode::EncodeAsType, - scale_decode::DecodeAsType, - scale_info::TypeInfo, -)] -pub enum MultiAddress { - /// It's an account ID (pubkey). - Id(AccountId), - /// It's an account index. - Index(#[codec(compact)] AccountIndex), - /// It's some arbitrary raw bytes. - Raw(Vec), - /// It's a 32 byte representation. - Address32([u8; 32]), - /// Its a 20 byte representation. - Address20([u8; 20]), -} - -impl From for MultiAddress { - fn from(a: AccountId) -> Self { - Self::Id(a) - } -} - -// Improve compat with the substrate version if we're using those crates: -#[cfg(feature = "substrate-compat")] -mod substrate_impls { - use super::{super::AccountId32, *}; - - impl From for MultiAddress { - fn from(value: sp_runtime::AccountId32) -> Self { - let val: AccountId32 = value.into(); - val.into() - } - } - - impl From> for MultiAddress - where - Id: Into, - { - fn from(value: sp_runtime::MultiAddress) -> Self { - match value { - sp_runtime::MultiAddress::Id(v) => Self::Id(v.into()), - sp_runtime::MultiAddress::Index(v) => Self::Index(v), - sp_runtime::MultiAddress::Raw(v) => Self::Raw(v), - sp_runtime::MultiAddress::Address32(v) => Self::Address32(v), - sp_runtime::MultiAddress::Address20(v) => Self::Address20(v), - } - } - } -} diff --git a/subxt/src/utils/multi_signature.rs b/subxt/src/utils/multi_signature.rs deleted file mode 100644 index 4ed9ea4bd54..00000000000 --- a/subxt/src/utils/multi_signature.rs +++ /dev/null @@ -1,58 +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. - -//! The "default" Substrate/Polkadot Signature type. This is used in codegen, as well as signing related bits. -//! This doesn't contain much functionality itself, but is easy to convert to/from an `sp_runtime::MultiSignature` -//! for instance, to gain functionality without forcing a dependency on Substrate crates here. - -use codec::{Decode, Encode}; - -/// Signature container that can store known signature types. This is a simplified version of -/// `sp_runtime::MultiSignature`. To obtain more functionality, convert this into that type. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, scale_info::TypeInfo)] -pub enum MultiSignature { - /// An Ed25519 signature. - Ed25519([u8; 64]), - /// An Sr25519 signature. - Sr25519([u8; 64]), - /// An ECDSA/SECP256k1 signature (a 512-bit value, plus 8 bits for recovery ID). - Ecdsa([u8; 65]), -} - -// Improve compat with the substrate version if we're using those crates: -#[cfg(feature = "substrate-compat")] -mod substrate_impls { - use super::*; - - impl From for MultiSignature { - fn from(value: sp_runtime::MultiSignature) -> Self { - match value { - sp_runtime::MultiSignature::Ed25519(s) => Self::Ed25519(s.0), - sp_runtime::MultiSignature::Sr25519(s) => Self::Sr25519(s.0), - sp_runtime::MultiSignature::Ecdsa(s) => Self::Ecdsa(s.0), - } - } - } - - impl From for MultiSignature { - fn from(value: sp_core::ed25519::Signature) -> Self { - let sig: sp_runtime::MultiSignature = value.into(); - sig.into() - } - } - - impl From for MultiSignature { - fn from(value: sp_core::sr25519::Signature) -> Self { - let sig: sp_runtime::MultiSignature = value.into(); - sig.into() - } - } - - impl From for MultiSignature { - fn from(value: sp_core::ecdsa::Signature) -> Self { - let sig: sp_runtime::MultiSignature = value.into(); - sig.into() - } - } -} diff --git a/subxt/src/utils/static_type.rs b/subxt/src/utils/static_type.rs deleted file mode 100644 index 2d13e61eba4..00000000000 --- a/subxt/src/utils/static_type.rs +++ /dev/null @@ -1,78 +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 codec::{Decode, Encode}; -use scale_decode::{visitor::DecodeAsTypeResult, IntoVisitor, Visitor}; -use scale_encode::EncodeAsType; - -/// 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`]. -/// -/// In either direction, we ignore any type information and just attempt to encode/decode statically -/// via the [`Encode`] and [`Decode`] implementations. This can be useful as an adapter for types which -/// do not implement [`scale_encode::EncodeAsType`] and [`scale_decode::DecodeAsType`] themselves, but -/// it's best to avoid using it where possible as it will not take into account any type information, -/// and is thus more likely to encode or decode incorrectly. -#[derive(Debug, Encode, Decode, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] -pub struct Static(pub T); - -impl EncodeAsType for Static { - fn encode_as_type_to( - &self, - _type_id: u32, - _types: &scale_decode::PortableRegistry, - out: &mut Vec, - ) -> Result<(), scale_encode::Error> { - self.0.encode_to(out); - Ok(()) - } -} - -pub struct StaticDecodeAsTypeVisitor(std::marker::PhantomData); - -impl Visitor for StaticDecodeAsTypeVisitor { - type Value<'scale, 'info> = Static; - type Error = scale_decode::Error; - - fn unchecked_decode_as_type<'scale, 'info>( - self, - input: &mut &'scale [u8], - _type_id: scale_decode::visitor::TypeId, - _types: &'info scale_info::PortableRegistry, - ) -> DecodeAsTypeResult, Self::Error>> { - use scale_decode::{visitor::DecodeError, Error}; - let decoded = T::decode(input) - .map(Static) - .map_err(|e| Error::new(DecodeError::CodecError(e).into())); - DecodeAsTypeResult::Decoded(decoded) - } -} - -impl IntoVisitor for Static { - type Visitor = StaticDecodeAsTypeVisitor; - fn into_visitor() -> Self::Visitor { - StaticDecodeAsTypeVisitor(std::marker::PhantomData) - } -} - -// Make it easy to convert types into Static where required. -impl From for Static { - fn from(value: T) -> Self { - Static(value) - } -} - -// Static is just a marker type and should be as transparent as possible: -impl std::ops::Deref for Static { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::DerefMut for Static { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} diff --git a/subxt/src/utils/unchecked_extrinsic.rs b/subxt/src/utils/unchecked_extrinsic.rs deleted file mode 100644 index 882b490bedb..00000000000 --- a/subxt/src/utils/unchecked_extrinsic.rs +++ /dev/null @@ -1,136 +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. - -//! The "default" Substrate/Polkadot UncheckedExtrinsic. -//! This is used in codegen for runtime API calls. -//! -//! The inner bytes represent the encoded extrinsic expected by the -//! runtime APIs. Deriving `EncodeAsType` would lead to the inner -//! bytes to be re-encoded (length prefixed). - -use std::marker::PhantomData; - -use codec::{Decode, Encode}; -use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType, IntoVisitor, Visitor}; - -use super::{Encoded, Static}; - -/// The unchecked extrinsic from substrate. -#[derive(Clone, Debug, Eq, PartialEq, Encode)] -pub struct UncheckedExtrinsic( - Static, - #[codec(skip)] PhantomData<(Address, Call, Signature, Extra)>, -); - -impl UncheckedExtrinsic { - /// Construct a new [`UncheckedExtrinsic`]. - pub fn new(bytes: Vec) -> Self { - Self(Static(Encoded(bytes)), PhantomData) - } - - /// Get the bytes of the encoded extrinsic. - pub fn bytes(&self) -> &[u8] { - self.0 .0 .0.as_slice() - } -} - -impl Decode - for UncheckedExtrinsic -{ - fn decode(input: &mut I) -> Result { - // The bytes for an UncheckedExtrinsic are first a compact - // encoded length, and then the bytes following. This is the - // same encoding as a Vec, so easiest ATM is just to decode - // into that, and then encode the vec bytes to get our extrinsic - // bytes, which we save into an `Encoded` to preserve as-is. - let xt_vec: Vec = Decode::decode(input)?; - Ok(UncheckedExtrinsic::new(xt_vec)) - } -} - -impl scale_encode::EncodeAsType - for UncheckedExtrinsic -{ - fn encode_as_type_to( - &self, - type_id: u32, - types: &scale_info::PortableRegistry, - out: &mut Vec, - ) -> Result<(), scale_encode::Error> { - self.0.encode_as_type_to(type_id, types, out) - } -} - -impl From> - for UncheckedExtrinsic -{ - fn from(bytes: Vec) -> Self { - UncheckedExtrinsic::new(bytes) - } -} - -impl From> - for Vec -{ - fn from(bytes: UncheckedExtrinsic) -> Self { - bytes.0 .0 .0 - } -} - -pub struct UncheckedExtrinsicDecodeAsTypeVisitor( - PhantomData<(Address, Call, Signature, Extra)>, -); - -impl Visitor - for UncheckedExtrinsicDecodeAsTypeVisitor -{ - type Value<'scale, 'info> = UncheckedExtrinsic; - type Error = scale_decode::Error; - - fn unchecked_decode_as_type<'scale, 'info>( - self, - input: &mut &'scale [u8], - type_id: scale_decode::visitor::TypeId, - types: &'info scale_info::PortableRegistry, - ) -> DecodeAsTypeResult, Self::Error>> { - DecodeAsTypeResult::Decoded(Self::Value::decode_as_type(input, type_id.0, types)) - } -} - -impl IntoVisitor - for UncheckedExtrinsic -{ - type Visitor = UncheckedExtrinsicDecodeAsTypeVisitor; - - fn into_visitor() -> Self::Visitor { - UncheckedExtrinsicDecodeAsTypeVisitor(PhantomData) - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - - #[test] - fn unchecked_extrinsic_encoding() { - // A tx is basically some bytes with a compact length prefix; ie an encoded vec: - let tx_bytes = vec![1u8, 2, 3].encode(); - - let unchecked_extrinsic = UncheckedExtrinsic::<(), (), (), ()>::new(tx_bytes.clone()); - let encoded_tx_bytes = unchecked_extrinsic.encode(); - - // The encoded representation must not alter the provided bytes. - assert_eq!(tx_bytes, encoded_tx_bytes); - - // However, for decoding we expect to be able to read the extrinsic from the wire - // which would be length prefixed. - let decoded_tx = UncheckedExtrinsic::<(), (), (), ()>::decode(&mut &tx_bytes[..]).unwrap(); - let decoded_tx_bytes = decoded_tx.bytes(); - let encoded_tx_bytes = decoded_tx.encode(); - - assert_eq!(decoded_tx_bytes, encoded_tx_bytes); - // Ensure we can decode the tx and fetch only the tx bytes. - assert_eq!(vec![1, 2, 3], encoded_tx_bytes); - } -} diff --git a/subxt/src/utils/wrapper_opaque.rs b/subxt/src/utils/wrapper_opaque.rs deleted file mode 100644 index 9257405715d..00000000000 --- a/subxt/src/utils/wrapper_opaque.rs +++ /dev/null @@ -1,243 +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 super::PhantomDataSendSync; -use codec::{Compact, Decode, DecodeAll, Encode}; -use derivative::Derivative; -use scale_decode::{IntoVisitor, Visitor}; -use scale_encode::EncodeAsType; - -/// A wrapper for any type `T` which implement encode/decode in a way compatible with `Vec`. -/// [`WrapperKeepOpaque`] stores the type only in its opaque format, aka as a `Vec`. To -/// access the real type `T` [`Self::try_decode`] needs to be used. -// Dev notes: -// -// - This is adapted from [here](https://github.com/paritytech/substrate/blob/master/frame/support/src/traits/misc.rs). -// - The encoded bytes will be a compact encoded length followed by that number of bytes. -// - 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`, 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 = "") -)] -pub struct WrapperKeepOpaque { - data: Vec, - _phantom: PhantomDataSendSync, -} - -impl WrapperKeepOpaque { - /// Try to decode the wrapped type from the inner `data`. - /// - /// Returns `None` if the decoding failed. - pub fn try_decode(&self) -> Option - where - T: Decode, - { - T::decode_all(&mut &self.data[..]).ok() - } - - /// Returns the length of the encoded `T`. - pub fn encoded_len(&self) -> usize { - self.data.len() - } - - /// Returns the encoded data. - pub fn encoded(&self) -> &[u8] { - &self.data - } - - /// Create from the given encoded `data`. - pub fn from_encoded(data: Vec) -> Self { - Self { - data, - _phantom: PhantomDataSendSync::new(), - } - } - - /// Create from some raw value by encoding it. - pub fn from_value(value: T) -> Self - where - T: Encode, - { - Self { - data: value.encode(), - _phantom: PhantomDataSendSync::new(), - } - } -} - -impl EncodeAsType for WrapperKeepOpaque { - fn encode_as_type_to( - &self, - type_id: u32, - types: &scale_info::PortableRegistry, - out: &mut Vec, - ) -> Result<(), scale_encode::Error> { - use scale_encode::error::{Error, ErrorKind, Kind}; - - let Some(ty) = types.resolve(type_id) else { - return Err(Error::new(ErrorKind::TypeNotFound(type_id))); - }; - - // Do a basic check that the target shape lines up. - let scale_info::TypeDef::Composite(_) = &ty.type_def else { - return Err(Error::new(ErrorKind::WrongShape { - actual: Kind::Struct, - expected: type_id, - })); - }; - - // Check that the name also lines up. - if ty.path.ident().as_deref() != Some("WrapperKeepOpaque") { - return Err(Error::new(ErrorKind::WrongShape { - actual: Kind::Struct, - expected: type_id, - })); - } - - // Just blat the bytes out. - self.data.encode_to(out); - Ok(()) - } -} - -pub struct WrapperKeepOpaqueVisitor(std::marker::PhantomData); -impl Visitor for WrapperKeepOpaqueVisitor { - type Value<'scale, 'info> = WrapperKeepOpaque; - type Error = scale_decode::Error; - - fn visit_composite<'scale, 'info>( - self, - value: &mut scale_decode::visitor::types::Composite<'scale, 'info>, - _type_id: scale_decode::visitor::TypeId, - ) -> Result, Self::Error> { - use scale_decode::error::{Error, ErrorKind}; - - if value.path().ident().as_deref() != Some("WrapperKeepOpaque") { - return Err(Error::custom_str( - "Type to decode is not 'WrapperTypeKeepOpaque'", - )); - } - if value.remaining() != 2 { - return Err(Error::new(ErrorKind::WrongLength { - actual_len: value.remaining(), - expected_len: 2, - })); - } - - // The field to decode is a compact len followed by bytes. Decode the length, then grab the bytes. - let Compact(len) = value - .decode_item(Compact::::into_visitor()) - .expect("length checked")?; - let field = value.next().expect("length checked")?; - - // Sanity check that the compact length we decoded lines up with the number of bytes encoded in the next field. - if field.bytes().len() != len as usize { - return Err(Error::custom_str("WrapperTypeKeepOpaque compact encoded length doesn't line up with encoded byte len")); - } - - Ok(WrapperKeepOpaque { - data: field.bytes().to_vec(), - _phantom: PhantomDataSendSync::new(), - }) - } -} - -impl IntoVisitor for WrapperKeepOpaque { - type Visitor = WrapperKeepOpaqueVisitor; - fn into_visitor() -> Self::Visitor { - WrapperKeepOpaqueVisitor(std::marker::PhantomData) - } -} - -#[cfg(test)] -mod test { - use scale_decode::DecodeAsType; - - use super::*; - - // Copied from https://github.com/paritytech/substrate/blob/master/frame/support/src/traits/misc.rs - // and used for tests to check that we can work with the expected TypeInfo without needing to import - // the frame_support crate, which has quite a lot of dependencies. - impl scale_info::TypeInfo for WrapperKeepOpaque { - type Identity = Self; - fn type_info() -> scale_info::Type { - use scale_info::{build::Fields, meta_type, Path, Type, TypeParameter}; - - Type::builder() - .path(Path::new("WrapperKeepOpaque", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) - .composite( - Fields::unnamed() - .field(|f| f.compact::()) - .field(|f| f.ty::().type_name("T")), - ) - } - } - - /// Given a type definition, return type ID and registry representing it. - fn make_type() -> (u32, scale_info::PortableRegistry) { - let m = scale_info::MetaType::new::(); - let mut types = scale_info::Registry::new(); - let id = types.register_type(&m); - let portable_registry: scale_info::PortableRegistry = types.into(); - (id.id, portable_registry) - } - - fn roundtrips_like_scale_codec(t: T) - where - T: EncodeAsType - + DecodeAsType - + Encode - + Decode - + PartialEq - + std::fmt::Debug - + scale_info::TypeInfo - + 'static, - { - let (type_id, types) = make_type::(); - - let scale_codec_encoded = t.encode(); - let encode_as_type_encoded = t.encode_as_type(type_id, &types).unwrap(); - - assert_eq!( - scale_codec_encoded, encode_as_type_encoded, - "encoded bytes should match" - ); - - let decode_as_type_bytes = &mut &*scale_codec_encoded; - let decoded_as_type = T::decode_as_type(decode_as_type_bytes, type_id, &types) - .expect("decode-as-type decodes"); - - let decode_scale_codec_bytes = &mut &*scale_codec_encoded; - let decoded_scale_codec = T::decode(decode_scale_codec_bytes).expect("scale-codec decodes"); - - assert!( - decode_as_type_bytes.is_empty(), - "no bytes should remain in decode-as-type impl" - ); - assert!( - decode_scale_codec_bytes.is_empty(), - "no bytes should remain in codec-decode impl" - ); - - assert_eq!( - decoded_as_type, decoded_scale_codec, - "decoded values should match" - ); - } - - #[test] - fn wrapper_keep_opaque_roundtrips_ok() { - roundtrips_like_scale_codec(WrapperKeepOpaque::from_value(123u64)); - roundtrips_like_scale_codec(WrapperKeepOpaque::from_value(true)); - roundtrips_like_scale_codec(WrapperKeepOpaque::from_value(vec![1u8, 2, 3, 4])); - } -} From aed00e52f8782bd6be60cf6284a39710c1fb4d77 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 1 Feb 2024 14:36:43 +0100 Subject: [PATCH 10/37] start porting dynamic value stuff --- Cargo.toml | 8 +- core/Cargo.toml | 8 +- core/src/dynamic.rs | 131 +++++++++--------- core/src/lib.rs | 2 + core/src/metadata/metadata_type.rs | 5 +- core/src/metadata/mod.rs | 2 +- subxt/src/backend/legacy/rpc_methods.rs | 4 +- subxt/src/backend/mod.rs | 2 +- subxt/src/blocks/extrinsic_types.rs | 6 +- subxt/src/client/light_client/mod.rs | 6 +- subxt/src/constants/constant_address.rs | 3 +- subxt/src/constants/constants_client.rs | 3 +- .../src/custom_values/custom_value_address.rs | 2 +- .../src/custom_values/custom_values_client.rs | 2 +- subxt/src/dynamic.rs | 63 +-------- subxt/src/error/dispatch_error.rs | 4 +- subxt/src/error/mod.rs | 55 +------- subxt/src/events/events_type.rs | 2 +- subxt/src/lib.rs | 4 +- subxt/src/metadata/decode_encode_traits.rs | 51 ------- subxt/src/metadata/metadata_type.rs | 74 ---------- subxt/src/metadata/mod.rs | 14 -- subxt/src/runtime_api/runtime_payload.rs | 4 +- subxt/src/runtime_api/runtime_types.rs | 2 +- subxt/src/storage/storage_address.rs | 4 +- subxt/src/storage/storage_type.rs | 2 +- subxt/src/storage/utils.rs | 4 +- subxt/src/tx/tx_payload.rs | 2 +- subxt/src/tx/tx_progress.rs | 2 +- testing/no-std-tests/src/main.rs | 2 +- 30 files changed, 122 insertions(+), 351 deletions(-) delete mode 100644 subxt/src/metadata/decode_encode_traits.rs delete mode 100644 subxt/src/metadata/metadata_type.rs delete mode 100644 subxt/src/metadata/mod.rs diff --git a/Cargo.toml b/Cargo.toml index cf2eda7e784..14160571fc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,9 +71,9 @@ frame-metadata = { version = "16.0.0", default-features = false } futures = { version = "0.3.30", default-features = false, features = ["std"] } getrandom = { version = "0.2", default-features = false } hashbrown = "0.14.3" -hex = "0.4.3" +hex = { version = "0.4.3", default-features = false } heck = "0.4.1" -impl-serde = { version = "0.4.0" } +impl-serde = { version = "0.4.0", default-features = false } indoc = "2" jsonrpsee = { version = "0.21" } pretty_assertions = "1.4.0" @@ -87,8 +87,8 @@ scale-value = { version = "0.13.0", default-features = false } scale-bits = { version = "0.4.0", default-features = false } scale-decode = { version = "0.10.0", default-features = false } scale-encode = { version = "0.5.0", default-features = false } -serde = { version = "1.0.196" } -serde_json = { version = "1.0.113" } +serde = { version = "1.0.196", default-features = false, features = ["derive"] } +serde_json = { version = "1.0.113", default-features = false } syn = { version = "2.0.15", features = ["full", "extra-traits"] } thiserror = "1.0.53" tokio = { version = "1.35", default-features = false } diff --git a/core/Cargo.toml b/core/Cargo.toml index a184871dec7..1121a682cc0 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -31,16 +31,16 @@ frame-metadata = { workspace = true, default-features = false } subxt-metadata = { workspace = true, default-features = false } derivative = { workspace = true, features = ["use_core"] } derive_more = { workspace = true } -hex = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true, features = ["raw_value"] } +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"] } # 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 } +impl-serde = { workspace = true, default-features = false } primitive-types = { workspace = true, default-features = false, features = ["codec", "serde_no_std", "scale-info"] } sp-core-hashing = { workspace = true } diff --git a/core/src/dynamic.rs b/core/src/dynamic.rs index e0260b1920d..160864331db 100644 --- a/core/src/dynamic.rs +++ b/core/src/dynamic.rs @@ -1,20 +1,21 @@ -// // 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. +// 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. -// //! This module provides the entry points to create dynamic -// //! transactions, storage and constant lookups. +//! This module provides the entry points to create dynamic +//! transactions, storage and constant lookups. -// use crate::metadata::{DecodeWithMetadata, Metadata}; -// use scale_decode::DecodeAsType; +use crate::metadata::{DecodeWithMetadata, Metadata}; +use crate::prelude::*; +use scale_decode::DecodeAsType; +pub use scale_value::{At, Value}; +use vec::Vec; -// pub use scale_value::{At, Value}; - -// /// A [`scale_value::Value`] type endowed with contextual information -// /// regarding what type was used to decode each part of it. This implements -// /// [`crate::metadata::DecodeWithMetadata`], and is used as a return type -// /// for dynamic requests. -// pub type DecodedValue = scale_value::Value; +/// A [`scale_value::Value`] type endowed with contextual information +/// regarding what type was used to decode each part of it. This implements +/// [`subxt_core::metadata::DecodeWithMetadata`], and is used as a return type +/// for dynamic requests. +pub type DecodedValue = scale_value::Value; // // Submit dynamic transactions. // pub use crate::tx::dynamic as tx; @@ -28,56 +29,56 @@ // // Execute runtime API function call dynamically. // pub use crate::runtime_api::dynamic as runtime_api_call; -// /// This is the result of making a dynamic request to a node. From this, -// /// we can return the raw SCALE bytes that we were handed back, or we can -// /// complete the decoding of the bytes into a [`DecodedValue`] type. -// pub struct DecodedValueThunk { -// type_id: u32, -// metadata: Metadata, -// scale_bytes: Vec, -// } +/// This is the result of making a dynamic request to a node. From this, +/// we can return the raw SCALE bytes that we were handed back, or we can +/// complete the decoding of the bytes into a [`DecodedValue`] type. +pub struct DecodedValueThunk { + type_id: u32, + metadata: Metadata, + scale_bytes: Vec, +} -// impl DecodeWithMetadata for DecodedValueThunk { -// fn decode_with_metadata( -// bytes: &mut &[u8], -// type_id: u32, -// metadata: &Metadata, -// ) -> Result { -// let mut v = Vec::with_capacity(bytes.len()); -// v.extend_from_slice(bytes); -// *bytes = &[]; -// Ok(DecodedValueThunk { -// type_id, -// metadata: metadata.clone(), -// scale_bytes: v, -// }) -// } -// } +impl DecodeWithMetadata for DecodedValueThunk { + fn decode_with_metadata( + bytes: &mut &[u8], + type_id: u32, + metadata: &Metadata, + ) -> Result { + let mut v = Vec::with_capacity(bytes.len()); + v.extend_from_slice(bytes); + *bytes = &[]; + Ok(DecodedValueThunk { + type_id, + metadata: metadata.clone(), + scale_bytes: v, + }) + } +} -// impl DecodedValueThunk { -// /// Return the SCALE encoded bytes handed back from the node. -// pub fn into_encoded(self) -> Vec { -// self.scale_bytes -// } -// /// Return the SCALE encoded bytes handed back from the node without taking ownership of them. -// pub fn encoded(&self) -> &[u8] { -// &self.scale_bytes -// } -// /// Decode the SCALE encoded storage entry into a dynamic [`DecodedValue`] type. -// pub fn to_value(&self) -> Result { -// let val = DecodedValue::decode_as_type( -// &mut &*self.scale_bytes, -// self.type_id, -// self.metadata.types(), -// )?; -// Ok(val) -// } -// /// decode the `DecodedValueThunk` into a concrete type. -// pub fn as_type(&self) -> Result { -// T::decode_as_type( -// &mut &self.scale_bytes[..], -// self.type_id, -// self.metadata.types(), -// ) -// } -// } +impl DecodedValueThunk { + /// Return the SCALE encoded bytes handed back from the node. + pub fn into_encoded(self) -> Vec { + self.scale_bytes + } + /// Return the SCALE encoded bytes handed back from the node without taking ownership of them. + pub fn encoded(&self) -> &[u8] { + &self.scale_bytes + } + /// Decode the SCALE encoded storage entry into a dynamic [`DecodedValue`] type. + pub fn to_value(&self) -> Result { + let val = DecodedValue::decode_as_type( + &mut &*self.scale_bytes, + self.type_id, + self.metadata.types(), + )?; + Ok(val) + } + /// decode the `DecodedValueThunk` into a concrete type. + pub fn as_type(&self) -> Result { + T::decode_as_type( + &mut &self.scale_bytes[..], + self.type_id, + self.metadata.types(), + ) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index fe618ae1678..910e4204c71 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -21,5 +21,7 @@ pub use config::{ PolkadotExtrinsicParams, SubstrateConfig, SubstrateExtrinsicParams, }; +pub use metadata::Metadata; + #[macro_use] mod macros; diff --git a/core/src/metadata/metadata_type.rs b/core/src/metadata/metadata_type.rs index 24b052c2ad8..18e4d8843de 100644 --- a/core/src/metadata/metadata_type.rs +++ b/core/src/metadata/metadata_type.rs @@ -22,7 +22,7 @@ impl core::ops::Deref for Metadata { } impl Metadata { - pub(crate) fn new(md: subxt_metadata::Metadata) -> Self { + pub fn new(md: subxt_metadata::Metadata) -> Self { Metadata { inner: Arc::new(md), } @@ -125,3 +125,6 @@ pub enum MetadataError { #[display(fmt = "Custom value with name {_0} not found")] CustomValueNameNotFound(String), } + +#[cfg(feature = "std")] +impl std::error::Error for MetadataError {} diff --git a/core/src/metadata/mod.rs b/core/src/metadata/mod.rs index a6ef00ae6f6..0ba35ec5ecb 100644 --- a/core/src/metadata/mod.rs +++ b/core/src/metadata/mod.rs @@ -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, MetadataError}; // Expose metadata types under a sub module in case somebody needs to reference them: pub use subxt_metadata as types; diff --git a/subxt/src/backend/legacy/rpc_methods.rs b/subxt/src/backend/legacy/rpc_methods.rs index 4dde85ee98d..84a8200adb9 100644 --- a/subxt/src/backend/legacy/rpc_methods.rs +++ b/subxt/src/backend/legacy/rpc_methods.rs @@ -5,12 +5,12 @@ //! An interface to call the raw legacy RPC methods. use crate::backend::rpc::{rpc_params, RpcClient, RpcSubscription}; -use crate::metadata::Metadata; use crate::{Config, Error}; use codec::Decode; use derivative::Derivative; use primitive_types::U256; use serde::{Deserialize, Serialize}; +use subxt_core::metadata::Metadata; /// 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 @@ -530,7 +530,7 @@ impl DryRunResultBytes { /// Attempt to decode the error bytes into a [`DryRunResult`] using the provided [`Metadata`]. pub fn into_dry_run_result( self, - metadata: &crate::metadata::Metadata, + metadata: &subxt_core::metadata::Metadata, ) -> Result { // dryRun returns an ApplyExtrinsicResult, which is basically a // `Result, TransactionValidityError>`. diff --git a/subxt/src/backend/mod.rs b/subxt/src/backend/mod.rs index 3db06818b33..4bbd9f6159c 100644 --- a/subxt/src/backend/mod.rs +++ b/subxt/src/backend/mod.rs @@ -11,13 +11,13 @@ pub mod rpc; pub mod unstable; use crate::error::Error; -use crate::metadata::Metadata; use crate::Config; use async_trait::async_trait; use codec::{Decode, Encode}; use futures::{Stream, StreamExt}; use std::pin::Pin; use std::sync::Arc; +use subxt_core::metadata::Metadata; /// Prevent the backend trait being implemented externally. #[doc(hidden)] diff --git a/subxt/src/blocks/extrinsic_types.rs b/subxt/src/blocks/extrinsic_types.rs index 333ee5bfe8d..d7575123aad 100644 --- a/subxt/src/blocks/extrinsic_types.rs +++ b/subxt/src/blocks/extrinsic_types.rs @@ -7,11 +7,11 @@ use crate::{ client::{OfflineClientT, OnlineClientT}, config::{Config, Hasher}, error::{BlockError, Error, MetadataError}, - events, - metadata::types::PalletMetadata, - Metadata, + events, Metadata, }; +use subxt_core::metadata::types::PalletMetadata; + use crate::config::signed_extensions::{ ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce, }; diff --git a/subxt/src/client/light_client/mod.rs b/subxt/src/client/light_client/mod.rs index a4b116652e2..cc8c0229763 100644 --- a/subxt/src/client/light_client/mod.rs +++ b/subxt/src/client/light_client/mod.rs @@ -117,8 +117,8 @@ impl LightClient { // think about importing the OnlineClientT/OfflineClientT // traits to use these things: - /// Return the [`crate::Metadata`] used in this client. - fn metadata(&self) -> crate::Metadata { + /// Return the [`subxt_core::metadata`] used in this client. + fn metadata(&self) -> subxt_core::metadata { self.client.metadata() } @@ -180,7 +180,7 @@ impl OnlineClientT for LightClient { } impl OfflineClientT for LightClient { - fn metadata(&self) -> crate::Metadata { + fn metadata(&self) -> subxt_core::metadata { self.metadata() } diff --git a/subxt/src/constants/constant_address.rs b/subxt/src/constants/constant_address.rs index e9a0eb37b00..bac9677ccf4 100644 --- a/subxt/src/constants/constant_address.rs +++ b/subxt/src/constants/constant_address.rs @@ -2,9 +2,10 @@ // 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 crate::dynamic::DecodedValueThunk; use derivative::Derivative; use std::borrow::Cow; +use subxt_core::metadata::DecodeWithMetadata; /// This represents a constant address. Anything implementing this trait /// can be used to fetch constants. diff --git a/subxt/src/constants/constants_client.rs b/subxt/src/constants/constants_client.rs index de688bba6b1..1190811ac71 100644 --- a/subxt/src/constants/constants_client.rs +++ b/subxt/src/constants/constants_client.rs @@ -6,10 +6,11 @@ use super::ConstantAddress; use crate::{ client::OfflineClientT, error::{Error, MetadataError}, - metadata::DecodeWithMetadata, Config, }; + use derivative::Derivative; +use subxt_core::metadata::DecodeWithMetadata; /// A client for accessing constants. #[derive(Derivative)] diff --git a/subxt/src/custom_values/custom_value_address.rs b/subxt/src/custom_values/custom_value_address.rs index f8034a161bf..d1708eec463 100644 --- a/subxt/src/custom_values/custom_value_address.rs +++ b/subxt/src/custom_values/custom_value_address.rs @@ -2,7 +2,7 @@ use derivative::Derivative; use std::marker::PhantomData; use crate::dynamic::DecodedValueThunk; -use crate::metadata::DecodeWithMetadata; +use subxt_core::metadata::DecodeWithMetadata; /// This represents the address of a custom value in in the metadata. /// Anything, that implements the [CustomValueAddress] trait can be used, to fetch diff --git a/subxt/src/custom_values/custom_values_client.rs b/subxt/src/custom_values/custom_values_client.rs index 3b8c1f54c23..b45dd39ce83 100644 --- a/subxt/src/custom_values/custom_values_client.rs +++ b/subxt/src/custom_values/custom_values_client.rs @@ -1,9 +1,9 @@ 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 subxt_core::metadata::DecodeWithMetadata; /// A client for accessing custom values stored in the metadata. #[derive(Derivative)] diff --git a/subxt/src/dynamic.rs b/subxt/src/dynamic.rs index 2801feb6837..771815825ab 100644 --- a/subxt/src/dynamic.rs +++ b/subxt/src/dynamic.rs @@ -5,17 +5,16 @@ //! This module provides the entry points to create dynamic //! transactions, storage and constant lookups. -use crate::{ - error::Error, - metadata::{DecodeWithMetadata, Metadata}, -}; +use crate::error::Error; + use scale_decode::DecodeAsType; +use subxt_core::metadata::{DecodeWithMetadata, Metadata}; pub use scale_value::{At, Value}; /// A [`scale_value::Value`] type endowed with contextual information /// regarding what type was used to decode each part of it. This implements -/// [`crate::metadata::DecodeWithMetadata`], and is used as a return type +/// [`subxt_core::metadata::DecodeWithMetadata`], and is used as a return type /// for dynamic requests. pub type DecodedValue = scale_value::Value; @@ -30,57 +29,3 @@ pub use crate::storage::dynamic as storage; // Execute runtime API function call dynamically. pub use crate::runtime_api::dynamic as runtime_api_call; - -/// This is the result of making a dynamic request to a node. From this, -/// we can return the raw SCALE bytes that we were handed back, or we can -/// complete the decoding of the bytes into a [`DecodedValue`] type. -pub struct DecodedValueThunk { - type_id: u32, - metadata: Metadata, - scale_bytes: Vec, -} - -impl DecodeWithMetadata for DecodedValueThunk { - fn decode_with_metadata( - bytes: &mut &[u8], - type_id: u32, - metadata: &Metadata, - ) -> Result { - let mut v = Vec::with_capacity(bytes.len()); - v.extend_from_slice(bytes); - *bytes = &[]; - Ok(DecodedValueThunk { - type_id, - metadata: metadata.clone(), - scale_bytes: v, - }) - } -} - -impl DecodedValueThunk { - /// Return the SCALE encoded bytes handed back from the node. - pub fn into_encoded(self) -> Vec { - self.scale_bytes - } - /// Return the SCALE encoded bytes handed back from the node without taking ownership of them. - pub fn encoded(&self) -> &[u8] { - &self.scale_bytes - } - /// Decode the SCALE encoded storage entry into a dynamic [`DecodedValue`] type. - pub fn to_value(&self) -> Result { - let val = DecodedValue::decode_as_type( - &mut &*self.scale_bytes, - self.type_id, - self.metadata.types(), - )?; - Ok(val) - } - /// decode the `DecodedValueThunk` into a concrete type. - pub fn as_type(&self) -> Result { - T::decode_as_type( - &mut &self.scale_bytes[..], - self.type_id, - self.metadata.types(), - ) - } -} diff --git a/subxt/src/error/dispatch_error.rs b/subxt/src/error/dispatch_error.rs index 817a441dd60..2dd59da35ba 100644 --- a/subxt/src/error/dispatch_error.rs +++ b/subxt/src/error/dispatch_error.rs @@ -5,10 +5,10 @@ //! A representation of the dispatch error; an error returned when //! something fails in trying to submit/execute a transaction. -use crate::metadata::{DecodeWithMetadata, Metadata}; use core::fmt::Debug; use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType}; use std::borrow::Cow; +use subxt_core::metadata::{DecodeWithMetadata, Metadata}; use super::{Error, MetadataError}; @@ -214,7 +214,7 @@ impl ModuleError { /// Details about the module error. pub struct ModuleErrorDetails<'a> { /// The pallet that the error is in - pub pallet: crate::metadata::types::PalletMetadata<'a>, + pub pallet: subxt_core::metadata::types::PalletMetadata<'a>, /// The variant representing the error pub variant: &'a scale_info::Variant, } diff --git a/subxt/src/error/mod.rs b/subxt/src/error/mod.rs index 78c5528012f..062734dac25 100644 --- a/subxt/src/error/mod.rs +++ b/subxt/src/error/mod.rs @@ -17,11 +17,13 @@ pub use dispatch_error::{ ArithmeticError, DispatchError, ModuleError, TokenError, TransactionalError, }; +pub use subxt_core::metadata::MetadataError; + // 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::metadata::Metadata; pub use subxt_metadata::TryFromError as MetadataTryFromError; /// The underlying error enum, generic over the type held by the `Runtime` @@ -199,54 +201,3 @@ pub enum StorageAddressError { fields: usize, }, } - -/// 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), -} diff --git a/subxt/src/events/events_type.rs b/subxt/src/events/events_type.rs index 831268ad00c..079ce5f2082 100644 --- a/subxt/src/events/events_type.rs +++ b/subxt/src/events/events_type.rs @@ -9,13 +9,13 @@ 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; +use subxt_core::metadata::types::PalletMetadata; /// A collection of events obtained from a block, bundled with the necessary /// information needed to decode and iterate over them. diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index b50880e1ea9..e982f2b1388 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -51,7 +51,6 @@ 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; @@ -67,9 +66,10 @@ pub use crate::{ client::{OfflineClient, OnlineClient}, config::{Config, PolkadotConfig, SubstrateConfig}, error::Error, - metadata::Metadata, }; +use subxt_core::metadata::Metadata; + /// Re-export external crates that are made use of in the subxt API. pub mod ext { pub use codec; diff --git a/subxt/src/metadata/decode_encode_traits.rs b/subxt/src/metadata/decode_encode_traits.rs deleted file mode 100644 index 81dbaea131b..00000000000 --- a/subxt/src/metadata/decode_encode_traits.rs +++ /dev/null @@ -1,51 +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 super::Metadata; -use crate::error::Error; - -/// This trait is implemented for all types that also implement [`scale_decode::DecodeAsType`]. -pub trait DecodeWithMetadata: Sized { - /// Given some metadata and a type ID, attempt to SCALE decode the provided bytes into `Self`. - fn decode_with_metadata( - bytes: &mut &[u8], - type_id: u32, - metadata: &Metadata, - ) -> Result; -} - -impl DecodeWithMetadata for T { - fn decode_with_metadata( - bytes: &mut &[u8], - type_id: u32, - metadata: &Metadata, - ) -> Result { - let val = T::decode_as_type(bytes, type_id, metadata.types())?; - Ok(val) - } -} - -/// This trait is implemented for all types that also implement [`scale_encode::EncodeAsType`]. -pub trait EncodeWithMetadata { - /// SCALE encode this type to bytes, possibly with the help of metadata. - fn encode_with_metadata( - &self, - type_id: u32, - metadata: &Metadata, - bytes: &mut Vec, - ) -> Result<(), Error>; -} - -impl EncodeWithMetadata for T { - /// SCALE encode this type to bytes, possibly with the help of metadata. - fn encode_with_metadata( - &self, - type_id: u32, - metadata: &Metadata, - bytes: &mut Vec, - ) -> Result<(), Error> { - self.encode_as_type_to(type_id, metadata.types(), bytes)?; - Ok(()) - } -} diff --git a/subxt/src/metadata/metadata_type.rs b/subxt/src/metadata/metadata_type.rs deleted file mode 100644 index 29ae567afad..00000000000 --- a/subxt/src/metadata/metadata_type.rs +++ /dev/null @@ -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, -} - -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 { - 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 { - 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 { - self.runtime_api_trait_by_name(name) - .ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned())) - } -} - -impl From for Metadata { - fn from(md: subxt_metadata::Metadata) -> Self { - Metadata::new(md) - } -} - -impl TryFrom for Metadata { - type Error = subxt_metadata::TryFromError; - fn try_from(value: frame_metadata::RuntimeMetadataPrefixed) -> Result { - subxt_metadata::Metadata::try_from(value).map(Metadata::from) - } -} - -impl codec::Decode for Metadata { - fn decode(input: &mut I) -> Result { - subxt_metadata::Metadata::decode(input).map(Metadata::new) - } -} diff --git a/subxt/src/metadata/mod.rs b/subxt/src/metadata/mod.rs deleted file mode 100644 index a6ef00ae6f6..00000000000 --- a/subxt/src/metadata/mod.rs +++ /dev/null @@ -1,14 +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. - -//! Types representing the metadata obtained from a node. - -mod decode_encode_traits; -mod metadata_type; - -pub use decode_encode_traits::{DecodeWithMetadata, EncodeWithMetadata}; -pub use metadata_type::Metadata; - -// Expose metadata types under a sub module in case somebody needs to reference them: -pub use subxt_metadata as types; diff --git a/subxt/src/runtime_api/runtime_payload.rs b/subxt/src/runtime_api/runtime_payload.rs index cd5a3355b80..3725fcae22e 100644 --- a/subxt/src/runtime_api/runtime_payload.rs +++ b/subxt/src/runtime_api/runtime_payload.rs @@ -10,7 +10,9 @@ use std::borrow::Cow; use crate::dynamic::DecodedValueThunk; use crate::error::MetadataError; -use crate::{metadata::DecodeWithMetadata, Error, Metadata}; +use crate::Error; + +use subxt_core::metadata::{DecodeWithMetadata, Metadata}; /// This represents a runtime API payload that can call into the runtime of node. /// diff --git a/subxt/src/runtime_api/runtime_types.rs b/subxt/src/runtime_api/runtime_types.rs index 3ea8c93947e..f9247547dbf 100644 --- a/subxt/src/runtime_api/runtime_types.rs +++ b/subxt/src/runtime_api/runtime_types.rs @@ -6,12 +6,12 @@ use crate::{ backend::{BackendExt, BlockRef}, client::OnlineClientT, error::{Error, MetadataError}, - metadata::DecodeWithMetadata, Config, }; use codec::Decode; use derivative::Derivative; use std::{future::Future, marker::PhantomData}; +use subxt_core::metadata::DecodeWithMetadata; use super::RuntimeApiPayload; diff --git a/subxt/src/storage/storage_address.rs b/subxt/src/storage/storage_address.rs index 893474b518a..e1a3ef504f9 100644 --- a/subxt/src/storage/storage_address.rs +++ b/subxt/src/storage/storage_address.rs @@ -5,9 +5,11 @@ use crate::{ dynamic::DecodedValueThunk, error::{Error, MetadataError, StorageAddressError}, - metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata}, utils::{Encoded, Static}, }; + +use subxt_core::metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata}; + use derivative::Derivative; use scale_info::TypeDef; use std::borrow::Cow; diff --git a/subxt/src/storage/storage_type.rs b/subxt/src/storage/storage_type.rs index 2669c1abcec..26b7c60ccf8 100644 --- a/subxt/src/storage/storage_type.rs +++ b/subxt/src/storage/storage_type.rs @@ -8,13 +8,13 @@ use crate::{ backend::{BackendExt, BlockRef}, client::OnlineClientT, error::{Error, MetadataError}, - metadata::{DecodeWithMetadata, Metadata}, Config, }; use codec::Decode; use derivative::Derivative; use futures::StreamExt; use std::{future::Future, marker::PhantomData}; +use subxt_core::metadata::{DecodeWithMetadata, Metadata}; use subxt_metadata::{PalletMetadata, StorageEntryMetadata, StorageEntryType}; /// This is returned from a couple of storage functions. diff --git a/subxt/src/storage/utils.rs b/subxt/src/storage/utils.rs index 728a581baf2..33581a32610 100644 --- a/subxt/src/storage/utils.rs +++ b/subxt/src/storage/utils.rs @@ -7,7 +7,9 @@ //! the trait itself. use super::StorageAddress; -use crate::{error::Error, metadata::Metadata}; +use crate::error::Error; + +use subxt_core::metadata::Metadata; /// Return the root of a given [`StorageAddress`]: hash the pallet name and entry name /// and append those bytes to the output. diff --git a/subxt/src/tx/tx_payload.rs b/subxt/src/tx/tx_payload.rs index 508a6a99e64..1a7cdeb941f 100644 --- a/subxt/src/tx/tx_payload.rs +++ b/subxt/src/tx/tx_payload.rs @@ -8,12 +8,12 @@ use crate::{ dynamic::Value, error::{Error, MetadataError}, - metadata::Metadata, }; use codec::Encode; use scale_encode::EncodeAsFields; use scale_value::{Composite, ValueDef, Variant}; use std::{borrow::Cow, sync::Arc}; +use subxt_core::metadata::Metadata; /// This represents a transaction payload that can be submitted /// to a node. diff --git a/subxt/src/tx/tx_progress.rs b/subxt/src/tx/tx_progress.rs index b949de2be40..732c7387523 100644 --- a/subxt/src/tx/tx_progress.rs +++ b/subxt/src/tx/tx_progress.rs @@ -337,7 +337,7 @@ mod test { struct MockClient; impl OfflineClientT for MockClient { - fn metadata(&self) -> crate::Metadata { + fn metadata(&self) -> subxt_core::metadata { unimplemented!("just a mock impl to satisfy trait bounds") } diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 65f425028f5..72d4f351528 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -49,5 +49,5 @@ fn subxt_metadata_test() { } fn subxt_core_test() { - let era = subxt_core::utils::era::Era::Immortal; + let era = subxt_core::utils::Era::Immortal; } From 97aad71569e3eeb24e46e5ab27ca6ab2fd8036b4 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 1 Feb 2024 18:19:26 +0100 Subject: [PATCH 11/37] add storage address and custom error type to core crate --- Cargo.lock | 1 + Cargo.toml | 2 +- core/Cargo.toml | 1 + core/src/dynamic.rs | 2 +- core/src/error.rs | 99 ++++++++++ core/src/lib.rs | 5 + core/src/metadata/metadata_type.rs | 58 +----- core/src/metadata/mod.rs | 2 +- core/src/{tx => }/signer.rs | 0 core/src/storage/mod.rs | 51 ++++++ core/src/storage/storage_address.rs | 273 ++++++++++++++++++++++++++++ core/src/tx/mod.rs | 201 +++++++++++++++++++- core/src/tx/tx_payload.rs | 192 ------------------- subxt/src/tx/signer.rs | 100 ---------- subxt/src/tx/tx_payload.rs | 192 ------------------- 15 files changed, 633 insertions(+), 546 deletions(-) create mode 100644 core/src/error.rs rename core/src/{tx => }/signer.rs (100%) create mode 100644 core/src/storage/mod.rs create mode 100644 core/src/storage/storage_address.rs delete mode 100644 core/src/tx/tx_payload.rs delete mode 100644 subxt/src/tx/signer.rs delete mode 100644 subxt/src/tx/tx_payload.rs diff --git a/Cargo.lock b/Cargo.lock index f6cdbcf0e08..6c2fd6f688e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4544,6 +4544,7 @@ dependencies = [ "cfg-if", "derivative", "derive_more", + "either", "frame-metadata 16.0.0", "hex", "impl-serde", diff --git a/Cargo.toml b/Cargo.toml index 14160571fc7..1ecc821c31c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ console_error_panic_hook = "0.1.7" darling = "0.20.3" derivative = "2.2.0" derive_more = "0.99.17" -either = "1.9.0" +either = { version = "1.9.0", default-features = false } frame-metadata = { version = "16.0.0", default-features = false } futures = { version = "0.3.30", default-features = false, features = ["std"] } getrandom = { version = "0.2", default-features = false } diff --git a/core/Cargo.toml b/core/Cargo.toml index 1121a682cc0..8435c042098 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -48,6 +48,7 @@ sp-core-hashing = { workspace = true } sp-core = { workspace = true, optional = true } sp-runtime = { workspace = true, optional = true } cfg-if = { workspace = true } +either = { workspace = true } [dev-dependencies] bitvec = { workspace = true } diff --git a/core/src/dynamic.rs b/core/src/dynamic.rs index 160864331db..a1a8aeaa224 100644 --- a/core/src/dynamic.rs +++ b/core/src/dynamic.rs @@ -18,7 +18,7 @@ use vec::Vec; pub type DecodedValue = scale_value::Value; // // Submit dynamic transactions. -// pub use crate::tx::dynamic as tx; +pub use crate::tx::dynamic as tx; // // Lookup constants dynamically. // pub use crate::constants::dynamic as constant; diff --git a/core/src/error.rs b/core/src/error.rs new file mode 100644 index 00000000000..8ca8c6db6d2 --- /dev/null +++ b/core/src/error.rs @@ -0,0 +1,99 @@ +use derive_more::{Display, From}; + +#[derive(Debug, Display, From)] +pub enum Error { + #[display(fmt = "Metadata Error: {_0}")] + Metadata(MetadataError), + #[display(fmt = "Storage Error: {_0}")] + Storage(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), +} + +#[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 a storage address. +#[derive(Clone, Debug, Display)] +#[non_exhaustive] +pub enum StorageAddressError { + /// Storage map type must be a composite type. + #[display(fmt = "Storage map type must be a composite type")] + MapTypeMustBeTuple, + /// Storage lookup does not have the expected number of keys. + #[display(fmt = "Storage lookup requires {expected} keys but got {actual} keys")] + WrongNumberOfKeys { + /// The actual number of keys needed, based on the metadata. + actual: usize, + /// 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, + }, +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 910e4204c71..20d03f0deac 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -11,11 +11,16 @@ pub mod client; pub mod config; pub mod dynamic; +mod error; pub mod metadata; pub mod prelude; +pub mod signer; +pub mod storage; pub mod tx; pub mod utils; +pub use error::{Error, MetadataError, StorageAddressError}; + pub use config::{ BlockHash, Config, ExtrinsicParams, ExtrinsicParamsEncoder, PolkadotConfig, PolkadotExtrinsicParams, SubstrateConfig, SubstrateExtrinsicParams, diff --git a/core/src/metadata/metadata_type.rs b/core/src/metadata/metadata_type.rs index 18e4d8843de..48d822189ae 100644 --- a/core/src/metadata/metadata_type.rs +++ b/core/src/metadata/metadata_type.rs @@ -2,10 +2,8 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::prelude::*; +use crate::{prelude::*, MetadataError}; use borrow::ToOwned; -use derive_more::Display; -use string::String; use sync::Arc; /// A cheaply clone-able representation of the runtime metadata received from a node. @@ -74,57 +72,3 @@ impl codec::Decode for Metadata { subxt_metadata::Metadata::decode(input).map(Metadata::new) } } - -/// 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 {} diff --git a/core/src/metadata/mod.rs b/core/src/metadata/mod.rs index 0ba35ec5ecb..a6ef00ae6f6 100644 --- a/core/src/metadata/mod.rs +++ b/core/src/metadata/mod.rs @@ -8,7 +8,7 @@ mod decode_encode_traits; mod metadata_type; pub use decode_encode_traits::{DecodeWithMetadata, EncodeWithMetadata}; -pub use metadata_type::{Metadata, MetadataError}; +pub use metadata_type::Metadata; // Expose metadata types under a sub module in case somebody needs to reference them: pub use subxt_metadata as types; diff --git a/core/src/tx/signer.rs b/core/src/signer.rs similarity index 100% rename from core/src/tx/signer.rs rename to core/src/signer.rs diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs new file mode 100644 index 00000000000..e37eb0383a3 --- /dev/null +++ b/core/src/storage/mod.rs @@ -0,0 +1,51 @@ +// 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; +/// 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, make_static_storage_map_key, Address, DynamicAddress, StaticStorageMapKey, + StorageAddress, Yes, + }; +} + +use crate::Error; + +// 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}; + +use crate::Metadata; +/// Return the root of a given [`StorageAddress`]: hash the pallet name and entry name +/// and append those bytes to the output. +pub(crate) fn write_storage_address_root_bytes( + addr: &Address, + out: &mut Vec, +) { + out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes())); + out.extend(sp_core_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(crate) fn storage_address_bytes( + addr: &Address, + metadata: &Metadata, +) -> Result, 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(crate) fn storage_address_root_bytes(addr: &Address) -> Vec { + let mut bytes = Vec::new(); + write_storage_address_root_bytes(addr, &mut bytes); + bytes +} diff --git a/core/src/storage/storage_address.rs b/core/src/storage/storage_address.rs new file mode 100644 index 00000000000..925b0a012e8 --- /dev/null +++ b/core/src/storage/storage_address.rs @@ -0,0 +1,273 @@ +// 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::{ + dynamic::DecodedValueThunk, + error::StorageAddressError, + utils::{Encoded, Static}, + MetadataError, +}; + +use crate::metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata}; +use crate::Error; +use derivative::Derivative; +use scale_info::TypeDef; +use std::borrow::Cow; +use subxt_metadata::{StorageEntryType, StorageHasher}; + +/// This represents a storage address. Anything implementing this trait +/// can be used to fetch and iterate over storage entries. +pub trait StorageAddress { + /// The target type of the value that lives at this address. + type Target: DecodeWithMetadata; + /// Can an entry be fetched from this address? + /// Set this type to [`Yes`] to enable the corresponding calls to be made. + type IsFetchable; + /// Can a default entry be obtained from this address? + /// Set this type to [`Yes`] to enable the corresponding calls to be made. + type IsDefaultable; + /// Can this address be iterated over? + /// Set this type to [`Yes`] to enable the corresponding calls to be made. + type IsIterable; + + /// The name of the pallet that the entry lives under. + fn pallet_name(&self) -> &str; + + /// The name of the entry in a given pallet that the item is at. + fn entry_name(&self) -> &str; + + /// Output the non-prefix bytes; that is, any additional bytes that need + /// to be appended to the key to dig into maps. + fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error>; + + /// An optional hash which, if present, will be checked against + /// the node metadata to confirm that the return type matches what + /// we are expecting. + fn validation_hash(&self) -> Option<[u8; 32]> { + None + } +} + +/// 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 = "StorageKey: Clone"), + Debug(bound = "StorageKey: std::fmt::Debug") +)] +pub struct Address { + pallet_name: Cow<'static, str>, + entry_name: Cow<'static, str>, + storage_entry_keys: Vec, + validation_hash: Option<[u8; 32]>, + _marker: std::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>, +} + +/// A typical storage address constructed at runtime rather than via the `subxt` macro; this +/// has no restriction on what it can be used for (since we don't statically know). +pub type DynamicAddress = Address; + +impl + Address +where + StorageKey: EncodeWithMetadata, + ReturnTy: DecodeWithMetadata, +{ + /// Create a new [`Address`] to use to access a storage entry. + pub fn new( + pallet_name: impl Into, + entry_name: impl Into, + storage_entry_keys: Vec, + ) -> Self { + Self { + pallet_name: Cow::Owned(pallet_name.into()), + entry_name: Cow::Owned(entry_name.into()), + storage_entry_keys: storage_entry_keys.into_iter().collect(), + validation_hash: None, + _marker: std::marker::PhantomData, + } + } + + /// Create a new [`Address`] using static strings for the pallet and call name. + /// This is only expected to be used from codegen. + #[doc(hidden)] + pub fn new_static( + pallet_name: &'static str, + entry_name: &'static str, + storage_entry_keys: Vec, + hash: [u8; 32], + ) -> Self { + Self { + pallet_name: Cow::Borrowed(pallet_name), + entry_name: Cow::Borrowed(entry_name), + storage_entry_keys: storage_entry_keys.into_iter().collect(), + validation_hash: Some(hash), + _marker: std::marker::PhantomData, + } + } + + /// Do not validate this storage entry prior to accessing it. + pub fn unvalidated(self) -> Self { + Self { + validation_hash: None, + ..self + } + } + + /// 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. + pub fn to_root_bytes(&self) -> Vec { + super::storage_address_root_bytes(self) + } +} + +impl StorageAddress + for Address +where + StorageKey: EncodeWithMetadata, + ReturnTy: DecodeWithMetadata, +{ + type Target = ReturnTy; + type IsFetchable = Fetchable; + type IsDefaultable = Defaultable; + type IsIterable = Iterable; + + fn pallet_name(&self) -> &str { + &self.pallet_name + } + + fn entry_name(&self) -> &str { + &self.entry_name + } + + fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error> { + let pallet = metadata.pallet_by_name_err(self.pallet_name())?; + let storage = pallet + .storage() + .ok_or_else(|| MetadataError::StorageNotFoundInPallet(self.pallet_name().to_owned()))?; + let entry = storage + .entry_by_name(self.entry_name()) + .ok_or_else(|| MetadataError::StorageEntryNotFound(self.entry_name().to_owned()))?; + + match entry.entry_type() { + StorageEntryType::Plain(_) => { + if !self.storage_entry_keys.is_empty() { + Err(StorageAddressError::WrongNumberOfKeys { + expected: 0, + actual: self.storage_entry_keys.len(), + } + .into()) + } else { + Ok(()) + } + } + StorageEntryType::Map { + hashers, key_ty, .. + } => { + let ty = metadata + .types() + .resolve(*key_ty) + .ok_or(MetadataError::TypeNotFound(*key_ty))?; + + // If the provided keys are empty, the storage address must be + // equal to the storage root address. + if self.storage_entry_keys.is_empty() { + return Ok(()); + } + + // If the key is a tuple, we encode each value to the corresponding tuple type. + // If the key is not a tuple, encode a single value to the key type. + let type_ids = match &ty.type_def { + TypeDef::Tuple(tuple) => { + either::Either::Left(tuple.fields.iter().map(|f| f.id)) + } + _other => either::Either::Right(std::iter::once(*key_ty)), + }; + + if type_ids.len() < self.storage_entry_keys.len() { + // Provided more keys than fields. + return Err(StorageAddressError::WrongNumberOfKeys { + expected: type_ids.len(), + actual: self.storage_entry_keys.len(), + } + .into()); + } + + if hashers.len() == 1 { + // One hasher; hash a tuple of all SCALE encoded bytes with the one hash function. + let mut input = Vec::new(); + let iter = self.storage_entry_keys.iter().zip(type_ids); + for (key, type_id) in iter { + key.encode_with_metadata(type_id, metadata, &mut input)?; + } + hash_bytes(&input, &hashers[0], bytes); + Ok(()) + } else if hashers.len() >= type_ids.len() { + let iter = self.storage_entry_keys.iter().zip(type_ids).zip(hashers); + // A hasher per field; encode and hash each field independently. + for ((key, type_id), hasher) in iter { + let mut input = Vec::new(); + key.encode_with_metadata(type_id, metadata, &mut input)?; + hash_bytes(&input, hasher, bytes); + } + Ok(()) + } else { + // Provided more fields than hashers. + Err(StorageAddressError::WrongNumberOfHashers { + hashers: hashers.len(), + fields: type_ids.len(), + } + .into()) + } + } + } + } + + fn validation_hash(&self) -> Option<[u8; 32]> { + self.validation_hash + } +} + +/// A static storage key; this is some pre-encoded bytes +/// likely provided by the generated interface. +pub type StaticStorageMapKey = Static; + +// Used in codegen to construct the above. +#[doc(hidden)] +pub fn make_static_storage_map_key(t: T) -> StaticStorageMapKey { + Static(Encoded(t.encode())) +} + +/// Construct a new dynamic storage lookup. +pub fn dynamic( + pallet_name: impl Into, + entry_name: impl Into, + storage_entry_keys: Vec, +) -> DynamicAddress { + DynamicAddress::new(pallet_name, entry_name, storage_entry_keys) +} + +/// Take some SCALE encoded bytes and a [`StorageHasher`] and hash the bytes accordingly. +fn hash_bytes(input: &[u8], hasher: &StorageHasher, bytes: &mut Vec) { + match hasher { + StorageHasher::Identity => bytes.extend(input), + StorageHasher::Blake2_128 => bytes.extend(sp_core_hashing::blake2_128(input)), + StorageHasher::Blake2_128Concat => { + bytes.extend(sp_core_hashing::blake2_128(input)); + bytes.extend(input); + } + StorageHasher::Blake2_256 => bytes.extend(sp_core_hashing::blake2_256(input)), + StorageHasher::Twox128 => bytes.extend(sp_core_hashing::twox_128(input)), + StorageHasher::Twox256 => bytes.extend(sp_core_hashing::twox_256(input)), + StorageHasher::Twox64Concat => { + bytes.extend(sp_core_hashing::twox_64(input)); + bytes.extend(input); + } + } +} diff --git a/core/src/tx/mod.rs b/core/src/tx/mod.rs index 293cbd33c17..d9a7c839db5 100644 --- a/core/src/tx/mod.rs +++ b/core/src/tx/mod.rs @@ -1,2 +1,199 @@ -pub mod signer; -pub mod tx_payload; +// 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. + +//! This module contains the trait and types used to represent +//! transactions that can be submitted. + +use crate::prelude::*; +use crate::Error; +use crate::MetadataError; +use crate::{ + dynamic::Value, + metadata::{self, Metadata}, +}; +use borrow::Cow; +use borrow::ToOwned; +use codec::Encode; +use scale_encode::EncodeAsFields; +use scale_value::{Composite, ValueDef, Variant}; +use string::String; +use sync::Arc; +use vec::Vec; + +/// This represents a transaction payload that can be submitted +/// to a node. +pub trait TxPayload { + /// Encode call data to the provided output. + fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error>; + + /// Encode call data and return the output. This is a convenience + /// wrapper around [`TxPayload::encode_call_data_to`]. + fn encode_call_data(&self, metadata: &Metadata) -> Result, Error> { + let mut v = Vec::new(); + self.encode_call_data_to(metadata, &mut v)?; + Ok(v) + } + + /// Returns the details needed to validate the call, which + /// include a statically generated hash, the pallet name, + /// and the call name. + fn validation_details(&self) -> Option> { + None + } +} + +pub struct ValidationDetails<'a> { + /// The pallet name. + pub pallet_name: &'a str, + /// The call name. + pub call_name: &'a str, + /// A hash (this is generated at compile time in our codegen) + /// to compare against the runtime code. + pub hash: [u8; 32], +} + +/// A transaction payload containing some generic `CallData`. +#[derive(Clone, Debug)] +pub struct Payload { + pallet_name: Cow<'static, str>, + call_name: Cow<'static, str>, + call_data: CallData, + validation_hash: Option<[u8; 32]>, +} + +/// A boxed transaction payload. +// Dev Note: Arc used to enable easy cloning (given that we can't have dyn Clone). +pub type BoxedPayload = Payload>; + +/// The type of a payload typically used for dynamic transaction payloads. +pub type DynamicPayload = Payload>; + +impl Payload { + /// Create a new [`Payload`]. + pub fn new( + pallet_name: impl Into, + call_name: impl Into, + call_data: CallData, + ) -> Self { + Payload { + pallet_name: Cow::Owned(pallet_name.into()), + call_name: Cow::Owned(call_name.into()), + call_data, + validation_hash: None, + } + } + + /// Create a new [`Payload`] using static strings for the pallet and call name. + /// This is only expected to be used from codegen. + #[doc(hidden)] + pub fn new_static( + pallet_name: &'static str, + call_name: &'static str, + call_data: CallData, + validation_hash: [u8; 32], + ) -> Self { + Payload { + pallet_name: Cow::Borrowed(pallet_name), + call_name: Cow::Borrowed(call_name), + call_data, + validation_hash: Some(validation_hash), + } + } + + /// Box the payload. + pub fn boxed(self) -> BoxedPayload + where + CallData: EncodeAsFields + Send + Sync + 'static, + { + BoxedPayload { + pallet_name: self.pallet_name, + call_name: self.call_name, + call_data: Arc::new(self.call_data), + validation_hash: self.validation_hash, + } + } + + /// Do not validate this call prior to submitting it. + pub fn unvalidated(self) -> Self { + Self { + validation_hash: None, + ..self + } + } + + /// Returns the call data. + pub fn call_data(&self) -> &CallData { + &self.call_data + } + + /// Returns the pallet name. + pub fn pallet_name(&self) -> &str { + &self.pallet_name + } + + /// Returns the call name. + pub fn call_name(&self) -> &str { + &self.call_name + } +} + +impl Payload> { + /// Convert the dynamic `Composite` payload into a [`Value`]. + /// This is useful if you want to use this as an argument for a + /// larger dynamic call that wants to use this as a nested call. + pub fn into_value(self) -> Value<()> { + let call = Value { + context: (), + value: ValueDef::Variant(Variant { + name: self.call_name.into_owned(), + values: self.call_data, + }), + }; + + Value::unnamed_variant(self.pallet_name, [call]) + } +} + +impl TxPayload for Payload { + fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error> { + let pallet = metadata.pallet_by_name_err(&self.pallet_name)?; + let call = pallet + .call_variant_by_name(&self.call_name) + .ok_or_else(|| MetadataError::CallNameNotFound((*self.call_name).to_owned()))?; + + let pallet_index = pallet.index(); + let call_index = call.index; + + pallet_index.encode_to(out); + call_index.encode_to(out); + + let mut fields = call + .fields + .iter() + .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) + .expect("The fields are valid types from the metadata, qed;"); + Ok(()) + } + + fn validation_details(&self) -> Option> { + self.validation_hash.map(|hash| ValidationDetails { + pallet_name: &self.pallet_name, + call_name: &self.call_name, + hash, + }) + } +} + +/// Construct a transaction at runtime; essentially an alias to [`Payload::new()`] +/// which provides a [`Composite`] value for the call data. +pub fn dynamic( + pallet_name: impl Into, + call_name: impl Into, + call_data: impl Into>, +) -> DynamicPayload { + Payload::new(pallet_name, call_name, call_data.into()) +} diff --git a/core/src/tx/tx_payload.rs b/core/src/tx/tx_payload.rs deleted file mode 100644 index 6b65d8e4343..00000000000 --- a/core/src/tx/tx_payload.rs +++ /dev/null @@ -1,192 +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. - -// //! 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 codec::Encode; -// use scale_encode::EncodeAsFields; -// use scale_value::{Composite, ValueDef, Variant}; -// use core::{borrow::Cow, sync::Arc}; - -// /// This represents a transaction payload that can be submitted -// /// to a node. -// pub trait TxPayload { -// /// Encode call data to the provided output. -// fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error>; - -// /// Encode call data and return the output. This is a convenience -// /// wrapper around [`TxPayload::encode_call_data_to`]. -// fn encode_call_data(&self, metadata: &Metadata) -> Result, Error> { -// let mut v = Vec::new(); -// self.encode_call_data_to(metadata, &mut v)?; -// Ok(v) -// } - -// /// Returns the details needed to validate the call, which -// /// include a statically generated hash, the pallet name, -// /// and the call name. -// fn validation_details(&self) -> Option> { -// None -// } -// } - -// pub struct ValidationDetails<'a> { -// /// The pallet name. -// pub pallet_name: &'a str, -// /// The call name. -// pub call_name: &'a str, -// /// A hash (this is generated at compile time in our codegen) -// /// to compare against the runtime code. -// pub hash: [u8; 32], -// } - -// /// A transaction payload containing some generic `CallData`. -// #[derive(Clone, Debug)] -// pub struct Payload { -// pallet_name: Cow<'static, str>, -// call_name: Cow<'static, str>, -// call_data: CallData, -// validation_hash: Option<[u8; 32]>, -// } - -// /// A boxed transaction payload. -// // Dev Note: Arc used to enable easy cloning (given that we can't have dyn Clone). -// pub type BoxedPayload = Payload>; - -// /// The type of a payload typically used for dynamic transaction payloads. -// pub type DynamicPayload = Payload>; - -// impl Payload { -// /// Create a new [`Payload`]. -// pub fn new( -// pallet_name: impl Into, -// call_name: impl Into, -// call_data: CallData, -// ) -> Self { -// Payload { -// pallet_name: Cow::Owned(pallet_name.into()), -// call_name: Cow::Owned(call_name.into()), -// call_data, -// validation_hash: None, -// } -// } - -// /// Create a new [`Payload`] using static strings for the pallet and call name. -// /// This is only expected to be used from codegen. -// #[doc(hidden)] -// pub fn new_static( -// pallet_name: &'static str, -// call_name: &'static str, -// call_data: CallData, -// validation_hash: [u8; 32], -// ) -> Self { -// Payload { -// pallet_name: Cow::Borrowed(pallet_name), -// call_name: Cow::Borrowed(call_name), -// call_data, -// validation_hash: Some(validation_hash), -// } -// } - -// /// Box the payload. -// pub fn boxed(self) -> BoxedPayload -// where -// CallData: EncodeAsFields + Send + Sync + 'static, -// { -// BoxedPayload { -// pallet_name: self.pallet_name, -// call_name: self.call_name, -// call_data: Arc::new(self.call_data), -// validation_hash: self.validation_hash, -// } -// } - -// /// Do not validate this call prior to submitting it. -// pub fn unvalidated(self) -> Self { -// Self { -// validation_hash: None, -// ..self -// } -// } - -// /// Returns the call data. -// pub fn call_data(&self) -> &CallData { -// &self.call_data -// } - -// /// Returns the pallet name. -// pub fn pallet_name(&self) -> &str { -// &self.pallet_name -// } - -// /// Returns the call name. -// pub fn call_name(&self) -> &str { -// &self.call_name -// } -// } - -// impl Payload> { -// /// Convert the dynamic `Composite` payload into a [`Value`]. -// /// This is useful if you want to use this as an argument for a -// /// larger dynamic call that wants to use this as a nested call. -// pub fn into_value(self) -> Value<()> { -// let call = Value { -// context: (), -// value: ValueDef::Variant(Variant { -// name: self.call_name.into_owned(), -// values: self.call_data, -// }), -// }; - -// Value::unnamed_variant(self.pallet_name, [call]) -// } -// } - -// impl TxPayload for Payload { -// fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error> { -// let pallet = metadata.pallet_by_name_err(&self.pallet_name)?; -// let call = pallet -// .call_variant_by_name(&self.call_name) -// .ok_or_else(|| MetadataError::CallNameNotFound((*self.call_name).to_owned()))?; - -// let pallet_index = pallet.index(); -// let call_index = call.index; - -// pallet_index.encode_to(out); -// call_index.encode_to(out); - -// let mut fields = call -// .fields -// .iter() -// .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)?; -// Ok(()) -// } - -// fn validation_details(&self) -> Option> { -// self.validation_hash.map(|hash| ValidationDetails { -// pallet_name: &self.pallet_name, -// call_name: &self.call_name, -// hash, -// }) -// } -// } - -// /// Construct a transaction at runtime; essentially an alias to [`Payload::new()`] -// /// which provides a [`Composite`] value for the call data. -// pub fn dynamic( -// pallet_name: impl Into, -// call_name: impl Into, -// call_data: impl Into>, -// ) -> DynamicPayload { -// Payload::new(pallet_name, call_name, call_data.into()) -// } diff --git a/subxt/src/tx/signer.rs b/subxt/src/tx/signer.rs deleted file mode 100644 index 444aa464772..00000000000 --- a/subxt/src/tx/signer.rs +++ /dev/null @@ -1,100 +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. - -//! A library to **sub**mit e**xt**rinsics to a -//! [substrate](https://github.com/paritytech/substrate) node via RPC. - -use crate::macros::cfg_substrate_compat; -use crate::Config; - -/// Signing transactions requires a [`Signer`]. This is responsible for -/// providing the "from" account that the transaction is being signed by, -/// as well as actually signing a SCALE encoded payload. -pub trait Signer { - /// Return the "from" account ID. - fn account_id(&self) -> T::AccountId; - - /// Return the "from" address. - fn address(&self) -> T::Address; - - /// Takes a signer payload for an extrinsic, and returns a signature based on it. - /// - /// Some signers may fail, for instance because the hardware on which the keys are located has - /// refused the operation. - fn sign(&self, signer_payload: &[u8]) -> T::Signature; -} - -cfg_substrate_compat! { - pub use pair_signer::PairSigner; -} - -// A signer suitable for substrate based chains. This provides compatibility with Substrate -// packages like sp_keyring and such, and so relies on sp_core and sp_runtime to be included. -#[cfg(feature = "substrate-compat")] -mod pair_signer { - use super::Signer; - use crate::Config; - use sp_core::Pair as PairT; - use sp_runtime::{ - traits::{IdentifyAccount, Verify}, - AccountId32 as SpAccountId32, MultiSignature as SpMultiSignature, - }; - - /// A [`Signer`] implementation that can be constructed from an [`sp_core::Pair`]. - #[derive(Clone, Debug)] - pub struct PairSigner { - account_id: T::AccountId, - signer: Pair, - } - - impl PairSigner - where - T: Config, - Pair: PairT, - // We go via an `sp_runtime::MultiSignature`. We can probably generalise this - // by implementing some of these traits on our built-in MultiSignature and then - // requiring them on all T::Signatures, to avoid any go-between. - ::Signer: From, - T::AccountId: From, - { - /// Creates a new [`Signer`] from an [`sp_core::Pair`]. - pub fn new(signer: Pair) -> Self { - let account_id = - ::Signer::from(signer.public()).into_account(); - Self { - account_id: account_id.into(), - signer, - } - } - - /// Returns the [`sp_core::Pair`] implementation used to construct this. - pub fn signer(&self) -> &Pair { - &self.signer - } - - /// Return the account ID. - pub fn account_id(&self) -> &T::AccountId { - &self.account_id - } - } - - impl Signer for PairSigner - where - T: Config, - Pair: PairT, - Pair::Signature: Into, - { - fn account_id(&self) -> T::AccountId { - self.account_id.clone() - } - - fn address(&self) -> T::Address { - self.account_id.clone().into() - } - - fn sign(&self, signer_payload: &[u8]) -> T::Signature { - self.signer.sign(signer_payload).into() - } - } -} diff --git a/subxt/src/tx/tx_payload.rs b/subxt/src/tx/tx_payload.rs deleted file mode 100644 index 1a7cdeb941f..00000000000 --- a/subxt/src/tx/tx_payload.rs +++ /dev/null @@ -1,192 +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. - -//! This module contains the trait and types used to represent -//! transactions that can be submitted. - -use crate::{ - dynamic::Value, - error::{Error, MetadataError}, -}; -use codec::Encode; -use scale_encode::EncodeAsFields; -use scale_value::{Composite, ValueDef, Variant}; -use std::{borrow::Cow, sync::Arc}; -use subxt_core::metadata::Metadata; - -/// This represents a transaction payload that can be submitted -/// to a node. -pub trait TxPayload { - /// Encode call data to the provided output. - fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error>; - - /// Encode call data and return the output. This is a convenience - /// wrapper around [`TxPayload::encode_call_data_to`]. - fn encode_call_data(&self, metadata: &Metadata) -> Result, Error> { - let mut v = Vec::new(); - self.encode_call_data_to(metadata, &mut v)?; - Ok(v) - } - - /// Returns the details needed to validate the call, which - /// include a statically generated hash, the pallet name, - /// and the call name. - fn validation_details(&self) -> Option> { - None - } -} - -pub struct ValidationDetails<'a> { - /// The pallet name. - pub pallet_name: &'a str, - /// The call name. - pub call_name: &'a str, - /// A hash (this is generated at compile time in our codegen) - /// to compare against the runtime code. - pub hash: [u8; 32], -} - -/// A transaction payload containing some generic `CallData`. -#[derive(Clone, Debug)] -pub struct Payload { - pallet_name: Cow<'static, str>, - call_name: Cow<'static, str>, - call_data: CallData, - validation_hash: Option<[u8; 32]>, -} - -/// A boxed transaction payload. -// Dev Note: Arc used to enable easy cloning (given that we can't have dyn Clone). -pub type BoxedPayload = Payload>; - -/// The type of a payload typically used for dynamic transaction payloads. -pub type DynamicPayload = Payload>; - -impl Payload { - /// Create a new [`Payload`]. - pub fn new( - pallet_name: impl Into, - call_name: impl Into, - call_data: CallData, - ) -> Self { - Payload { - pallet_name: Cow::Owned(pallet_name.into()), - call_name: Cow::Owned(call_name.into()), - call_data, - validation_hash: None, - } - } - - /// Create a new [`Payload`] using static strings for the pallet and call name. - /// This is only expected to be used from codegen. - #[doc(hidden)] - pub fn new_static( - pallet_name: &'static str, - call_name: &'static str, - call_data: CallData, - validation_hash: [u8; 32], - ) -> Self { - Payload { - pallet_name: Cow::Borrowed(pallet_name), - call_name: Cow::Borrowed(call_name), - call_data, - validation_hash: Some(validation_hash), - } - } - - /// Box the payload. - pub fn boxed(self) -> BoxedPayload - where - CallData: EncodeAsFields + Send + Sync + 'static, - { - BoxedPayload { - pallet_name: self.pallet_name, - call_name: self.call_name, - call_data: Arc::new(self.call_data), - validation_hash: self.validation_hash, - } - } - - /// Do not validate this call prior to submitting it. - pub fn unvalidated(self) -> Self { - Self { - validation_hash: None, - ..self - } - } - - /// Returns the call data. - pub fn call_data(&self) -> &CallData { - &self.call_data - } - - /// Returns the pallet name. - pub fn pallet_name(&self) -> &str { - &self.pallet_name - } - - /// Returns the call name. - pub fn call_name(&self) -> &str { - &self.call_name - } -} - -impl Payload> { - /// Convert the dynamic `Composite` payload into a [`Value`]. - /// This is useful if you want to use this as an argument for a - /// larger dynamic call that wants to use this as a nested call. - pub fn into_value(self) -> Value<()> { - let call = Value { - context: (), - value: ValueDef::Variant(Variant { - name: self.call_name.into_owned(), - values: self.call_data, - }), - }; - - Value::unnamed_variant(self.pallet_name, [call]) - } -} - -impl TxPayload for Payload { - fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error> { - let pallet = metadata.pallet_by_name_err(&self.pallet_name)?; - let call = pallet - .call_variant_by_name(&self.call_name) - .ok_or_else(|| MetadataError::CallNameNotFound((*self.call_name).to_owned()))?; - - let pallet_index = pallet.index(); - let call_index = call.index; - - pallet_index.encode_to(out); - call_index.encode_to(out); - - let mut fields = call - .fields - .iter() - .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)?; - Ok(()) - } - - fn validation_details(&self) -> Option> { - self.validation_hash.map(|hash| ValidationDetails { - pallet_name: &self.pallet_name, - call_name: &self.call_name, - hash, - }) - } -} - -/// Construct a transaction at runtime; essentially an alias to [`Payload::new()`] -/// which provides a [`Composite`] value for the call data. -pub fn dynamic( - pallet_name: impl Into, - call_name: impl Into, - call_data: impl Into>, -) -> DynamicPayload { - Payload::new(pallet_name, call_name, call_data.into()) -} From 7aa69891a9ab841e55676c307ecac1bd54418563 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 1 Feb 2024 18:42:47 +0100 Subject: [PATCH 12/37] remove prelude from subxt-metadata --- Cargo.lock | 1 - metadata/Cargo.toml | 1 - metadata/src/from_into/mod.rs | 3 +- metadata/src/from_into/v14.rs | 15 ++++---- metadata/src/from_into/v15.rs | 4 +- metadata/src/lib.rs | 12 +++--- metadata/src/prelude.rs | 59 ----------------------------- metadata/src/utils/ordered_map.rs | 12 +++--- metadata/src/utils/retain.rs | 3 +- metadata/src/utils/validation.rs | 3 +- metadata/src/utils/variant_index.rs | 9 ++--- testing/no-std-tests/Cargo.lock | 1 - 12 files changed, 29 insertions(+), 94 deletions(-) delete mode 100644 metadata/src/prelude.rs diff --git a/Cargo.lock b/Cargo.lock index 1cb864285b0..4fd98a19cda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4578,7 +4578,6 @@ version = "0.34.0" dependencies = [ "assert_matches", "bitvec", - "cfg-if", "criterion", "derive_more", "frame-metadata 16.0.0", diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index e1391650b6e..a6e15de0aa8 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -24,7 +24,6 @@ codec = { package = "parity-scale-codec", workspace = true, default-features = f sp-core-hashing = { workspace = true } hashbrown = { workspace = true } derive_more = { workspace = true } -cfg-if = { workspace = true } [dev-dependencies] bitvec = { workspace = true, features = ["alloc"] } diff --git a/metadata/src/from_into/mod.rs b/metadata/src/from_into/mod.rs index 33074a2db05..1a950e1d5a3 100644 --- a/metadata/src/from_into/mod.rs +++ b/metadata/src/from_into/mod.rs @@ -2,9 +2,8 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::prelude::*; +use alloc::string::String; use derive_more::Display; -use string::String; mod v14; mod v15; diff --git a/metadata/src/from_into/v14.rs b/metadata/src/from_into/v14.rs index a324aa89534..dec3a4c566f 100644 --- a/metadata/src/from_into/v14.rs +++ b/metadata/src/from_into/v14.rs @@ -3,15 +3,15 @@ // see LICENSE for license details. use super::TryFromError; -use crate::prelude::*; use crate::Metadata; -use borrow::ToOwned; -use fmt::Write; +use alloc::borrow::ToOwned; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; + use frame_metadata::{v14, v15}; use hashbrown::HashMap; use scale_info::TypeDef; -use string::String; -use vec::Vec; impl TryFrom for Metadata { type Error = TryFromError; @@ -374,8 +374,9 @@ fn generate_outer_error_enum_type( // Note: using the `format!` macro like in `let path = format!("{}Error", pallet.name);` // leads to linker errors about extern function `_Unwind_Resume` not being defined. - let mut path = String::new(); - write!(path, "{}Error", pallet.name).expect("Cannot panic, qed;"); + // let mut path = String::new(); + // write!(path, "{}Error", pallet.name).expect("Cannot panic, qed;"); + let path = alloc::format!("{}Error", pallet.name); let ty = error.ty.id.into(); Some(scale_info::Variant { diff --git a/metadata/src/from_into/v15.rs b/metadata/src/from_into/v15.rs index 5021da1b8d1..37e43365430 100644 --- a/metadata/src/from_into/v15.rs +++ b/metadata/src/from_into/v15.rs @@ -3,7 +3,7 @@ // see LICENSE for license details. use super::TryFromError; -use crate::prelude::*; + use crate::utils::variant_index::VariantIndex; use crate::{ utils::ordered_map::OrderedMap, ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata, @@ -11,7 +11,7 @@ use crate::{ RuntimeApiMethodParamMetadata, SignedExtensionMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata, }; -use borrow::ToOwned; +use alloc::borrow::ToOwned; use frame_metadata::v15; use hashbrown::HashMap; use scale_info::form::PortableForm; diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index ad2503235aa..59a3ba484b8 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -17,18 +17,18 @@ #![cfg_attr(not(feature = "std"), no_std)] #![deny(missing_docs)] +extern crate alloc; + mod from_into; -mod prelude; mod utils; +use alloc::string::String; +use alloc::sync::Arc; +use alloc::vec::Vec; use hashbrown::HashMap; -use prelude::*; use scale_info::{form::PortableForm, PortableRegistry, Variant}; -use string::String; -use sync::Arc; use utils::variant_index::VariantIndex; use utils::{ordered_map::OrderedMap, validation::outer_enum_hashes::OuterEnumHashes}; -use vec::Vec; type ArcStr = Arc; @@ -205,7 +205,7 @@ impl Metadata { // 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 = format!("{name}{visited_count}"); + *name = alloc::format!("{name}{visited_count}"); } } } diff --git a/metadata/src/prelude.rs b/metadata/src/prelude.rs deleted file mode 100644 index 007bd44665a..00000000000 --- a/metadata/src/prelude.rs +++ /dev/null @@ -1,59 +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. - -#[cfg(not(feature = "std"))] -extern crate alloc; - -use cfg_if::cfg_if; - -cfg_if! { - if #[cfg(feature = "std")] { - #[allow(unused)] - pub use std::{ - any, - borrow, - boxed, - cmp, - collections, - fmt, - format, - hash, - marker, - mem, - num, - ops, - string, - sync, - time, - vec, - rc, - iter, - }; - } else { - #[allow(unused)] - pub use alloc::{ - borrow, - boxed, - collections, - format, - string, - sync, - vec, - rc - }; - #[allow(unused)] - pub use core::{ - any, - cmp, - fmt, - hash, - marker, - mem, - num, - ops, - time, - iter, - }; - } -} diff --git a/metadata/src/utils/ordered_map.rs b/metadata/src/utils/ordered_map.rs index 8df2ade3a34..614c25cf65a 100644 --- a/metadata/src/utils/ordered_map.rs +++ b/metadata/src/utils/ordered_map.rs @@ -2,9 +2,9 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::prelude::*; +use alloc::vec::Vec; +use core::mem; use hashbrown::HashMap; -use vec::Vec; /// A minimal ordered map to let one search for /// things by key or get the values in insert order. @@ -25,7 +25,7 @@ impl Default for OrderedMap { impl OrderedMap where - K: PartialEq + Eq + hash::Hash, + K: PartialEq + Eq + core::hash::Hash, { /// Create a new, empty [`OrderedMap`]. pub fn new() -> Self { @@ -80,8 +80,8 @@ where /// Get an item by its key. pub fn get_by_key(&self, key: &Q) -> Option<&V> where - K: borrow::Borrow, - Q: hash::Hash + Eq + ?Sized, + K: alloc::borrow::Borrow, + Q: core::hash::Hash + Eq + ?Sized, { self.map.get(key).and_then(|&v| self.values.get(v)) } @@ -109,7 +109,7 @@ where impl FromIterator<(K, V)> for OrderedMap where - K: PartialEq + Eq + hash::Hash, + K: PartialEq + Eq + core::hash::Hash, { fn from_iter>(iter: T) -> Self { let mut map = OrderedMap::new(); diff --git a/metadata/src/utils/retain.rs b/metadata/src/utils/retain.rs index 5e26bd2e83a..a160609597f 100644 --- a/metadata/src/utils/retain.rs +++ b/metadata/src/utils/retain.rs @@ -4,12 +4,11 @@ //! Utility functions to generate a subset of the metadata. -use crate::prelude::*; use crate::{ ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner, StorageEntryType, }; -use collections::BTreeMap; +use alloc::collections::BTreeMap; use hashbrown::HashSet; use scale_info::TypeDef; diff --git a/metadata/src/utils/validation.rs b/metadata/src/utils/validation.rs index e36d05810d9..2b1b1f5e337 100644 --- a/metadata/src/utils/validation.rs +++ b/metadata/src/utils/validation.rs @@ -4,15 +4,14 @@ //! Utility functions for metadata validation. -use crate::prelude::*; use crate::{ CustomMetadata, CustomValueMetadata, ExtrinsicMetadata, Metadata, PalletMetadata, RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata, StorageEntryType, }; +use alloc::vec::Vec; use hashbrown::HashMap; use outer_enum_hashes::OuterEnumHashes; use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefVariant, Variant}; -use vec::Vec; pub mod outer_enum_hashes; diff --git a/metadata/src/utils/variant_index.rs b/metadata/src/utils/variant_index.rs index 31aed098b64..9019aee3ab1 100644 --- a/metadata/src/utils/variant_index.rs +++ b/metadata/src/utils/variant_index.rs @@ -2,11 +2,10 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::prelude::*; -use borrow::ToOwned; +use alloc::borrow::ToOwned; +use alloc::string::String; use hashbrown::HashMap; use scale_info::{form::PortableForm, PortableRegistry, TypeDef, Variant}; -use string::String; /// Given some type ID and type registry, build a couple of /// indexes to look up variants by index or name. If the ID provided @@ -65,8 +64,8 @@ impl VariantIndex { types: &'a PortableRegistry, ) -> Option<&'a Variant> where - String: borrow::Borrow, - K: hash::Hash + Eq + ?Sized, + String: alloc::borrow::Borrow, + K: core::hash::Hash + Eq + ?Sized, { let pos = *self.by_name.get(name)?; let variants = Self::get(variant_id, types)?; diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index e1d9e0d46e0..277d009358b 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -371,7 +371,6 @@ dependencies = [ name = "subxt-metadata" version = "0.34.0" dependencies = [ - "cfg-if", "derive_more", "frame-metadata", "hashbrown", From 87200b441a193f2d77b579511773aba85fcad97c Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 1 Feb 2024 18:46:20 +0100 Subject: [PATCH 13/37] revert autoformatting of Cargo.toml --- subxt/Cargo.toml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index 6f6b664a0f9..0c181be3dbd 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -24,11 +24,23 @@ default = ["jsonrpsee", "native"] # Enable this for native (ie non web/wasm builds). # Exactly 1 of "web" and "native" is expected. -native = ["jsonrpsee?/async-client", "jsonrpsee?/client-ws-transport-native-tls", "subxt-lightclient?/native", "tokio-util"] +native = [ + "jsonrpsee?/async-client", + "jsonrpsee?/client-ws-transport-native-tls", + "subxt-lightclient?/native", + "tokio-util" +] # Enable this for web/wasm builds. # Exactly 1 of "web" and "native" is expected. -web = ["jsonrpsee?/async-wasm-client", "jsonrpsee?/client-web-transport", "getrandom/js", "subxt-lightclient?/web", "subxt-macro/web", "instant/wasm-bindgen"] +web = [ + "jsonrpsee?/async-wasm-client", + "jsonrpsee?/client-web-transport", + "getrandom/js", + "subxt-lightclient?/web", + "subxt-macro/web", + "instant/wasm-bindgen" +] # Enable this to use jsonrpsee (allowing for example `OnlineClient::from_url`). jsonrpsee = ["dep:jsonrpsee"] From 57743c3a1a6d95535f1955e8f915539a3d9b18fc Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 09:13:27 +0100 Subject: [PATCH 14/37] remove alloc::format! again, still causes linker errors --- metadata/src/from_into/v14.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/metadata/src/from_into/v14.rs b/metadata/src/from_into/v14.rs index dec3a4c566f..b9b13cf6817 100644 --- a/metadata/src/from_into/v14.rs +++ b/metadata/src/from_into/v14.rs @@ -8,7 +8,7 @@ use alloc::borrow::ToOwned; use alloc::string::String; use alloc::vec; use alloc::vec::Vec; - +use core::fmt::Write; use frame_metadata::{v14, v15}; use hashbrown::HashMap; use scale_info::TypeDef; @@ -372,11 +372,10 @@ fn generate_outer_error_enum_type( return None; }; - // Note: using the `format!` macro like in `let path = format!("{}Error", pallet.name);` + // Note: using the `alloc::format!` macro like in `let path = format!("{}Error", pallet.name);` // leads to linker errors about extern function `_Unwind_Resume` not being defined. - // let mut path = String::new(); - // write!(path, "{}Error", pallet.name).expect("Cannot panic, qed;"); - let path = alloc::format!("{}Error", pallet.name); + let mut path = String::new(); + write!(path, "{}Error", pallet.name).expect("Cannot panic, qed;"); let ty = error.ty.id.into(); Some(scale_info::Variant { From e427529341d1a4ad29ec361d961b5810d7ac0b62 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 09:42:24 +0100 Subject: [PATCH 15/37] remove prelude from core, use alloc directly --- Cargo.lock | 3 +- core/Cargo.toml | 1 - core/src/config/extrinsic_params.rs | 5 +- core/src/config/signed_extensions.rs | 9 ++-- core/src/config/substrate.rs | 9 ++-- core/src/dynamic.rs | 3 +- core/src/error.rs | 1 + core/src/lib.rs | 10 +++- core/src/marker.rs | 1 + core/src/metadata/decode_encode_traits.rs | 3 +- core/src/metadata/metadata_type.rs | 6 +-- core/src/prelude.rs | 59 ----------------------- core/src/storage/mod.rs | 5 +- core/src/storage/storage_address.rs | 14 +++--- core/src/tx/mod.rs | 15 ++---- core/src/utils/account_id.rs | 8 +-- core/src/utils/bits.rs | 4 +- core/src/utils/mod.rs | 5 +- core/src/utils/multi_address.rs | 3 +- core/src/utils/static_type.rs | 3 +- core/src/utils/unchecked_extrinsic.rs | 3 +- core/src/utils/wrapper_opaque.rs | 3 +- signer/Cargo.toml | 9 ++-- testing/no-std-tests/Cargo.lock | 2 +- testing/no-std-tests/src/main.rs | 3 +- 25 files changed, 63 insertions(+), 124 deletions(-) create mode 100644 core/src/marker.rs delete mode 100644 core/src/prelude.rs diff --git a/Cargo.lock b/Cargo.lock index 685b40afcc0..7dc9b6274ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4541,7 +4541,6 @@ dependencies = [ "base58", "bitvec", "blake2", - "cfg-if", "derivative", "derive_more", "either", @@ -4636,7 +4635,7 @@ dependencies = [ "sp-core", "sp-core-hashing", "sp-keyring", - "subxt", + "subxt-core", "thiserror", "zeroize", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index 8435c042098..0b950f4ae91 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -47,7 +47,6 @@ sp-core-hashing = { workspace = true } # Included if the "substrate-compat" feature is enabled. sp-core = { workspace = true, optional = true } sp-runtime = { workspace = true, optional = true } -cfg-if = { workspace = true } either = { workspace = true } [dev-dependencies] diff --git a/core/src/config/extrinsic_params.rs b/core/src/config/extrinsic_params.rs index 9e6396d1b1f..f0efbf01535 100644 --- a/core/src/config/extrinsic_params.rs +++ b/core/src/config/extrinsic_params.rs @@ -10,10 +10,9 @@ use crate::client::ClientBase; use super::Config; -use crate::prelude::*; +use alloc::string::String; +use alloc::vec::Vec; use core::fmt::Debug; -use string::String; -use vec::Vec; use derive_more::Display; diff --git a/core/src/config/signed_extensions.rs b/core/src/config/signed_extensions.rs index 2bf6a1bc519..d09704beb23 100644 --- a/core/src/config/signed_extensions.rs +++ b/core/src/config/signed_extensions.rs @@ -10,17 +10,16 @@ use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; use super::Config; use crate::client::ClientBase; -use crate::prelude::*; use crate::utils::Era; -use borrow::ToOwned; -use boxed::Box; +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::vec::Vec; use codec::{Compact, Encode}; -use collections::BTreeMap; use core::fmt::Debug; use derivative::Derivative; use scale_decode::DecodeAsType; use scale_info::PortableRegistry; -use vec::Vec; /// A single [`SignedExtension`] has a unique name, but is otherwise the /// same as [`ExtrinsicParams`] in describing how to encode the extra and diff --git a/core/src/config/substrate.rs b/core/src/config/substrate.rs index b8e930c8827..a7c5e992159 100644 --- a/core/src/config/substrate.rs +++ b/core/src/config/substrate.rs @@ -5,13 +5,12 @@ //! Substrate specific configuration use super::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder, Hasher, Header}; -use crate::prelude::*; pub use crate::utils::{AccountId32, MultiAddress, MultiSignature}; +use alloc::string::String; +use alloc::vec::Vec; use codec::{Decode, Encode}; pub use primitive_types::{H256, U256}; use serde::{Deserialize, Serialize}; -use string::String; -use vec::Vec; /// Default set of commonly used types by Substrate runtimes. // Note: We only use this at the type level, so it should be impossible to @@ -209,7 +208,7 @@ impl<'a> serde::Deserialize<'a> for DigestItem { { let r = impl_serde::serialize::deserialize(de)?; Decode::decode(&mut &r[..]) - .map_err(|e| serde::de::Error::custom(format!("Decode error: {e}"))) + .map_err(|e| serde::de::Error::custom(alloc::format!("Decode error: {e}"))) } } @@ -292,7 +291,7 @@ impl From for NumberOrHex { /// A quick helper to encode some bytes to hex. fn to_hex(bytes: impl AsRef<[u8]>) -> String { - format!("0x{}", hex::encode(bytes.as_ref())) + alloc::format!("0x{}", hex::encode(bytes.as_ref())) } #[cfg(test)] diff --git a/core/src/dynamic.rs b/core/src/dynamic.rs index a1a8aeaa224..c3686b483c9 100644 --- a/core/src/dynamic.rs +++ b/core/src/dynamic.rs @@ -6,10 +6,9 @@ //! transactions, storage and constant lookups. use crate::metadata::{DecodeWithMetadata, Metadata}; -use crate::prelude::*; +use alloc::vec::Vec; use scale_decode::DecodeAsType; pub use scale_value::{At, Value}; -use vec::Vec; /// A [`scale_value::Value`] type endowed with contextual information /// regarding what type was used to decode each part of it. This implements diff --git a/core/src/error.rs b/core/src/error.rs index 8ca8c6db6d2..038ee747475 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use derive_more::{Display, From}; #[derive(Debug, Display, From)] diff --git a/core/src/lib.rs b/core/src/lib.rs index 20d03f0deac..11b8a23eec5 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -8,12 +8,13 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod client; pub mod config; pub mod dynamic; mod error; pub mod metadata; -pub mod prelude; pub mod signer; pub mod storage; pub mod tx; @@ -26,7 +27,14 @@ pub use config::{ PolkadotExtrinsicParams, SubstrateConfig, SubstrateExtrinsicParams, }; +pub use signer::Signer; + pub use metadata::Metadata; #[macro_use] mod macros; + +mod marker { + /// A unit marker struct that is only used for specialized generics. + pub struct Yes; +} diff --git a/core/src/marker.rs b/core/src/marker.rs new file mode 100644 index 00000000000..aaf7f841f03 --- /dev/null +++ b/core/src/marker.rs @@ -0,0 +1 @@ +pub struct Yes; diff --git a/core/src/metadata/decode_encode_traits.rs b/core/src/metadata/decode_encode_traits.rs index 4b9089a0015..3bc4dc87b01 100644 --- a/core/src/metadata/decode_encode_traits.rs +++ b/core/src/metadata/decode_encode_traits.rs @@ -3,8 +3,7 @@ // see LICENSE for license details. use super::Metadata; -use crate::prelude::*; -use vec::Vec; +use alloc::vec::Vec; /// This trait is implemented for all types that also implement [`scale_decode::DecodeAsType`]. pub trait DecodeWithMetadata: Sized { diff --git a/core/src/metadata/metadata_type.rs b/core/src/metadata/metadata_type.rs index 48d822189ae..7bd5c5c0805 100644 --- a/core/src/metadata/metadata_type.rs +++ b/core/src/metadata/metadata_type.rs @@ -2,9 +2,9 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::{prelude::*, MetadataError}; -use borrow::ToOwned; -use sync::Arc; +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)] diff --git a/core/src/prelude.rs b/core/src/prelude.rs deleted file mode 100644 index 007bd44665a..00000000000 --- a/core/src/prelude.rs +++ /dev/null @@ -1,59 +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. - -#[cfg(not(feature = "std"))] -extern crate alloc; - -use cfg_if::cfg_if; - -cfg_if! { - if #[cfg(feature = "std")] { - #[allow(unused)] - pub use std::{ - any, - borrow, - boxed, - cmp, - collections, - fmt, - format, - hash, - marker, - mem, - num, - ops, - string, - sync, - time, - vec, - rc, - iter, - }; - } else { - #[allow(unused)] - pub use alloc::{ - borrow, - boxed, - collections, - format, - string, - sync, - vec, - rc - }; - #[allow(unused)] - pub use core::{ - any, - cmp, - fmt, - hash, - marker, - mem, - num, - ops, - time, - iter, - }; - } -} diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index e37eb0383a3..a1976ecd166 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -4,9 +4,12 @@ //! Types associated with accessing and working with storage items. -mod storage_address; +use alloc::vec::Vec; + /// Types representing an address which describes where a storage /// entry lives and how to properly decode it. +pub mod storage_address; + pub mod address { pub use super::storage_address::{ dynamic, make_static_storage_map_key, Address, DynamicAddress, StaticStorageMapKey, diff --git a/core/src/storage/storage_address.rs b/core/src/storage/storage_address.rs index 925b0a012e8..49d24598a37 100644 --- a/core/src/storage/storage_address.rs +++ b/core/src/storage/storage_address.rs @@ -11,9 +11,11 @@ use crate::{ use crate::metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata}; use crate::Error; +use alloc::borrow::{Cow, ToOwned}; +use alloc::string::String; +use alloc::vec::Vec; use derivative::Derivative; use scale_info::TypeDef; -use std::borrow::Cow; use subxt_metadata::{StorageEntryType, StorageHasher}; /// This represents a storage address. Anything implementing this trait @@ -58,14 +60,14 @@ pub struct Yes; #[derive(Derivative)] #[derivative( Clone(bound = "StorageKey: Clone"), - Debug(bound = "StorageKey: std::fmt::Debug") + Debug(bound = "StorageKey: core::fmt::Debug") )] pub struct Address { pallet_name: Cow<'static, str>, entry_name: Cow<'static, str>, storage_entry_keys: Vec, 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 @@ -89,7 +91,7 @@ where entry_name: Cow::Owned(entry_name.into()), storage_entry_keys: storage_entry_keys.into_iter().collect(), validation_hash: None, - _marker: std::marker::PhantomData, + _marker: core::marker::PhantomData, } } @@ -107,7 +109,7 @@ where entry_name: Cow::Borrowed(entry_name), storage_entry_keys: storage_entry_keys.into_iter().collect(), validation_hash: Some(hash), - _marker: std::marker::PhantomData, + _marker: core::marker::PhantomData, } } @@ -187,7 +189,7 @@ where TypeDef::Tuple(tuple) => { either::Either::Left(tuple.fields.iter().map(|f| f.id)) } - _other => either::Either::Right(std::iter::once(*key_ty)), + _other => either::Either::Right(core::iter::once(*key_ty)), }; if type_ids.len() < self.storage_entry_keys.len() { diff --git a/core/src/tx/mod.rs b/core/src/tx/mod.rs index d9a7c839db5..0b7975671c4 100644 --- a/core/src/tx/mod.rs +++ b/core/src/tx/mod.rs @@ -5,21 +5,16 @@ //! This module contains the trait and types used to represent //! transactions that can be submitted. -use crate::prelude::*; use crate::Error; use crate::MetadataError; -use crate::{ - dynamic::Value, - metadata::{self, Metadata}, -}; -use borrow::Cow; -use borrow::ToOwned; +use crate::{dynamic::Value, metadata::Metadata}; +use alloc::borrow::{Cow, ToOwned}; +use alloc::string::String; +use alloc::sync::Arc; +use alloc::vec::Vec; use codec::Encode; use scale_encode::EncodeAsFields; use scale_value::{Composite, ValueDef, Variant}; -use string::String; -use sync::Arc; -use vec::Vec; /// This represents a transaction payload that can be submitted /// to a node. diff --git a/core/src/utils/account_id.rs b/core/src/utils/account_id.rs index 7a7ef7949fe..28ef331f1d4 100644 --- a/core/src/utils/account_id.rs +++ b/core/src/utils/account_id.rs @@ -6,12 +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 crate::prelude::*; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; use codec::{Decode, Encode}; use derive_more::Display; use serde::{Deserialize, Serialize}; -use string::String; -use vec::Vec; /// A 32-byte cryptographic identifier. This is a simplified version of Substrate's /// `sp_core::crypto::AccountId32`. To obtain more functionality, convert this into @@ -145,7 +145,7 @@ impl<'de> Deserialize<'de> for AccountId32 { D: serde::Deserializer<'de>, { AccountId32::from_ss58check(&String::deserialize(deserializer)?) - .map_err(|e| serde::de::Error::custom(format!("{e:?}"))) + .map_err(|e| serde::de::Error::custom(alloc::format!("{e:?}"))) } } diff --git a/core/src/utils/bits.rs b/core/src/utils/bits.rs index 5774cd9fcf8..dc195095374 100644 --- a/core/src/utils/bits.rs +++ b/core/src/utils/bits.rs @@ -4,7 +4,8 @@ //! Generic `scale_bits` over `bitvec`-like `BitOrder` and `BitFormat` types. -use crate::prelude::*; +use alloc::vec; +use alloc::vec::Vec; use codec::{Compact, Input}; use core::marker::PhantomData; use scale_bits::{ @@ -12,7 +13,6 @@ use scale_bits::{ Bits, }; use scale_decode::IntoVisitor; -use vec::Vec; /// Associates `bitvec::store::BitStore` trait with corresponding, type-erased `scale_bits::StoreFormat` enum. /// diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index 067c43f3f25..54eaf230ed2 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -4,11 +4,10 @@ //! Miscellaneous utility helpers. -use crate::prelude::*; -use borrow::ToOwned; +use alloc::borrow::ToOwned; +use alloc::vec::Vec; use codec::{Compact, Decode, Encode}; use derivative::Derivative; -use vec::Vec; mod account_id; pub mod bits; diff --git a/core/src/utils/multi_address.rs b/core/src/utils/multi_address.rs index c742c349371..3a2d97297da 100644 --- a/core/src/utils/multi_address.rs +++ b/core/src/utils/multi_address.rs @@ -6,9 +6,8 @@ //! 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 crate::prelude::*; +use alloc::vec::Vec; use codec::{Decode, Encode}; -use vec::Vec; /// A multi-format address wrapper for on-chain accounts. This is a simplified version of Substrate's /// `sp_runtime::MultiAddress`. To obtain more functionality, convert this into that type (this conversion diff --git a/core/src/utils/static_type.rs b/core/src/utils/static_type.rs index 9484706b850..257b2d9eaec 100644 --- a/core/src/utils/static_type.rs +++ b/core/src/utils/static_type.rs @@ -2,11 +2,10 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::prelude::*; +use alloc::vec::Vec; use codec::{Decode, Encode}; use scale_decode::{visitor::DecodeAsTypeResult, IntoVisitor, Visitor}; use scale_encode::EncodeAsType; -use 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`]. diff --git a/core/src/utils/unchecked_extrinsic.rs b/core/src/utils/unchecked_extrinsic.rs index 89744ac12a8..e661620ec8d 100644 --- a/core/src/utils/unchecked_extrinsic.rs +++ b/core/src/utils/unchecked_extrinsic.rs @@ -10,11 +10,10 @@ //! bytes to be re-encoded (length prefixed). use super::{Encoded, Static}; -use crate::prelude::*; +use alloc::vec::Vec; use codec::{Decode, Encode}; use core::marker::PhantomData; use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType, IntoVisitor, Visitor}; -use vec::Vec; /// The unchecked extrinsic from substrate. #[derive(Clone, Debug, Eq, PartialEq, Encode)] diff --git a/core/src/utils/wrapper_opaque.rs b/core/src/utils/wrapper_opaque.rs index 8ca9ca1758b..3c73a1bad13 100644 --- a/core/src/utils/wrapper_opaque.rs +++ b/core/src/utils/wrapper_opaque.rs @@ -3,12 +3,11 @@ // see LICENSE for license details. use super::PhantomDataSendSync; -use crate::prelude::*; +use alloc::vec::Vec; use codec::{Compact, Decode, DecodeAll, Encode}; use derivative::Derivative; use scale_decode::{IntoVisitor, Visitor}; use scale_encode::EncodeAsType; -use vec::Vec; /// A wrapper for any type `T` which implement encode/decode in a way compatible with `Vec`. /// [`WrapperKeepOpaque`] stores the type only in its opaque format, aka as a `Vec`. To diff --git a/signer/Cargo.toml b/signer/Cargo.toml index 226237c315a..4192e27a094 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -15,7 +15,7 @@ description = "Sign extrinsics to be submitted by Subxt" keywords = ["parity", "subxt", "extrinsic", "signer"] [features] -default = ["sr25519", "ecdsa", "subxt", "native"] +default = ["sr25519", "ecdsa", "subxt"] # Pick the signer implementation(s) you need by enabling the # corresponding features. Note: I had more difficulties getting @@ -26,15 +26,14 @@ 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, default-features = false } +subxt-core = { workspace = true, optional = true, default-features = false } regex = { workspace = true } hex = { workspace = true } codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] } diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index d304f1eb854..184ac589001 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -675,9 +675,9 @@ version = "0.34.0" dependencies = [ "base58", "blake2", - "cfg-if", "derivative", "derive_more", + "either", "frame-metadata 16.0.0", "hex", "impl-serde", diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 72d4f351528..2812cae0987 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -36,6 +36,7 @@ extern crate alloc; // Note: Panics in this function will lead to `Aborted (core dumped)` and a non-zero exit status => suitable for CI tests. fn run_tests() { subxt_metadata_test(); + subxt_core_test(); } /// Makes sure, subxt-metadata works in a no-std-context: @@ -49,5 +50,5 @@ fn subxt_metadata_test() { } fn subxt_core_test() { - let era = subxt_core::utils::Era::Immortal; + let _ = subxt_core::utils::Era::Immortal; } From e855aac44b934c28df60cf93120a6ce78b179ec3 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 10:44:57 +0100 Subject: [PATCH 16/37] constants and storage entries --- core/src/constants/constant_address.rs | 101 ++++++++++++++++++++ core/src/constants/mod.rs | 58 ++++++++++++ core/src/dynamic.rs | 4 +- core/src/lib.rs | 5 +- core/src/marker.rs | 1 - core/src/metadata/metadata_type.rs | 25 ++++- core/src/metadata/mod.rs | 2 +- core/src/storage/mod.rs | 126 +++++++++++++++++-------- core/src/storage/storage_address.rs | 10 +- core/src/tx/mod.rs | 1 + subxt/src/storage/storage_type.rs | 62 ------------ 11 files changed, 282 insertions(+), 113 deletions(-) create mode 100644 core/src/constants/constant_address.rs create mode 100644 core/src/constants/mod.rs delete mode 100644 core/src/marker.rs diff --git a/core/src/constants/constant_address.rs b/core/src/constants/constant_address.rs new file mode 100644 index 00000000000..a8ad4a40586 --- /dev/null +++ b/core/src/constants/constant_address.rs @@ -0,0 +1,101 @@ +// 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::dynamic::DecodedValueThunk; +use crate::metadata::DecodeWithMetadata; +use alloc::borrow::Cow; +use alloc::string::String; +use derivative::Derivative; + +/// This represents a constant address. Anything implementing this trait +/// can be used to fetch constants. +pub trait ConstantAddress { + /// The target type of the value that lives at this address. + type Target: DecodeWithMetadata; + + /// The name of the pallet that the constant lives under. + fn pallet_name(&self) -> &str; + + /// The name of the constant in a given pallet. + fn constant_name(&self) -> &str; + + /// An optional hash which, if present, will be checked against + /// the node metadata to confirm that the return type matches what + /// we are expecting. + fn validation_hash(&self) -> Option<[u8; 32]> { + None + } +} + +/// This represents the address of a constant. +#[derive(Derivative)] +#[derivative(Clone(bound = ""), Debug(bound = ""))] +pub struct Address { + pallet_name: Cow<'static, str>, + constant_name: Cow<'static, str>, + constant_hash: Option<[u8; 32]>, + _marker: core::marker::PhantomData, +} + +/// The type of address typically used to return dynamic constant values. +pub type DynamicAddress = Address; + +impl Address { + /// Create a new [`Address`] to use to look up a constant. + pub fn new(pallet_name: impl Into, constant_name: impl Into) -> Self { + Self { + pallet_name: Cow::Owned(pallet_name.into()), + constant_name: Cow::Owned(constant_name.into()), + constant_hash: None, + _marker: core::marker::PhantomData, + } + } + + /// Create a new [`Address`] that will be validated + /// against node metadata using the hash given. + #[doc(hidden)] + pub fn new_static( + pallet_name: &'static str, + constant_name: &'static str, + hash: [u8; 32], + ) -> Self { + Self { + pallet_name: Cow::Borrowed(pallet_name), + constant_name: Cow::Borrowed(constant_name), + constant_hash: Some(hash), + _marker: core::marker::PhantomData, + } + } + + /// Do not validate this constant prior to accessing it. + pub fn unvalidated(self) -> Self { + Self { + pallet_name: self.pallet_name, + constant_name: self.constant_name, + constant_hash: None, + _marker: self._marker, + } + } +} + +impl ConstantAddress for Address { + type Target = ReturnTy; + + fn pallet_name(&self) -> &str { + &self.pallet_name + } + + fn constant_name(&self) -> &str { + &self.constant_name + } + + fn validation_hash(&self) -> Option<[u8; 32]> { + self.constant_hash + } +} + +/// Construct a new dynamic constant lookup. +pub fn dynamic(pallet_name: impl Into, constant_name: impl Into) -> DynamicAddress { + DynamicAddress::new(pallet_name, constant_name) +} diff --git a/core/src/constants/mod.rs b/core/src/constants/mod.rs new file mode 100644 index 00000000000..a9195d9e149 --- /dev/null +++ b/core/src/constants/mod.rs @@ -0,0 +1,58 @@ +// 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::{ + metadata::{DecodeWithMetadata, MetadatExt}, + Error, Metadata, MetadataError, +}; + +/// 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( + 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 access_constant( + metadata: &Metadata, + address: &Address, +) -> Result { + // 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 = ::decode_with_metadata( + &mut constant.value(), + constant.ty(), + &metadata, + )?; + Ok(value) +} diff --git a/core/src/dynamic.rs b/core/src/dynamic.rs index c3686b483c9..bfab442e65a 100644 --- a/core/src/dynamic.rs +++ b/core/src/dynamic.rs @@ -20,10 +20,10 @@ pub type DecodedValue = scale_value::Value; pub use crate::tx::dynamic as tx; // // Lookup constants dynamically. -// pub use crate::constants::dynamic as constant; +pub use crate::constants::dynamic as constant; // // Lookup storage values dynamically. -// pub use crate::storage::dynamic as storage; +pub use crate::storage::dynamic as storage; // // Execute runtime API function call dynamically. // pub use crate::runtime_api::dynamic as runtime_api_call; diff --git a/core/src/lib.rs b/core/src/lib.rs index 11b8a23eec5..a8b1af07954 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; pub mod client; pub mod config; +pub mod constants; pub mod dynamic; mod error; pub mod metadata; @@ -35,6 +36,8 @@ pub use metadata::Metadata; mod macros; mod marker { - /// A unit marker struct that is only used for specialized generics. + /// A unit marker struct signalling that some property is true pub struct Yes; } + +pub use marker::Yes; diff --git a/core/src/marker.rs b/core/src/marker.rs deleted file mode 100644 index aaf7f841f03..00000000000 --- a/core/src/marker.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct Yes; diff --git a/core/src/metadata/metadata_type.rs b/core/src/metadata/metadata_type.rs index 7bd5c5c0805..07e74d9b364 100644 --- a/core/src/metadata/metadata_type.rs +++ b/core/src/metadata/metadata_type.rs @@ -25,9 +25,28 @@ impl Metadata { inner: Arc::new(md), } } +} + +pub trait MetadatExt { + fn pallet_by_name_err( + &self, + name: &str, + ) -> Result; + + fn pallet_by_index_err( + &self, + index: u8, + ) -> Result; + + fn runtime_api_trait_by_name_err( + &self, + name: &str, + ) -> Result; +} +impl MetadatExt for subxt_metadata::Metadata { /// Identical to `metadata.pallet_by_name()`, but returns an error if the pallet is not found. - pub fn pallet_by_name_err( + fn pallet_by_name_err( &self, name: &str, ) -> Result { @@ -36,7 +55,7 @@ impl Metadata { } /// Identical to `metadata.pallet_by_index()`, but returns an error if the pallet is not found. - pub fn pallet_by_index_err( + fn pallet_by_index_err( &self, index: u8, ) -> Result { @@ -45,7 +64,7 @@ impl Metadata { } /// 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( + fn runtime_api_trait_by_name_err( &self, name: &str, ) -> Result { diff --git a/core/src/metadata/mod.rs b/core/src/metadata/mod.rs index a6ef00ae6f6..b268486bb4f 100644 --- a/core/src/metadata/mod.rs +++ b/core/src/metadata/mod.rs @@ -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::{MetadatExt, Metadata}; // Expose metadata types under a sub module in case somebody needs to reference them: pub use subxt_metadata as types; diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index a1976ecd166..8bf68024e1b 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -4,51 +4,103 @@ //! Types associated with accessing and working with storage items. -use alloc::vec::Vec; - /// Types representing an address which describes where a storage /// entry lives and how to properly decode it. pub mod storage_address; -pub mod address { - pub use super::storage_address::{ - dynamic, make_static_storage_map_key, Address, DynamicAddress, StaticStorageMapKey, - StorageAddress, Yes, - }; -} - -use crate::Error; - // 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}; -use crate::Metadata; -/// Return the root of a given [`StorageAddress`]: hash the pallet name and entry name -/// and append those bytes to the output. -pub(crate) fn write_storage_address_root_bytes( - addr: &Address, - out: &mut Vec, -) { - out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes())); - out.extend(sp_core_hashing::twox_128(addr.entry_name().as_bytes())); -} +pub mod utils { + use crate::{ + metadata::{DecodeWithMetadata, MetadatExt}, + Error, Metadata, MetadataError, + }; + use alloc::borrow::ToOwned; + use alloc::vec::Vec; + use subxt_metadata::{PalletMetadata, StorageEntryMetadata}; -/// Outputs the [`storage_address_root_bytes`] as well as any additional bytes that represent -/// a lookup in a storage map at that location. -pub(crate) fn storage_address_bytes( - addr: &Address, - metadata: &Metadata, -) -> Result, Error> { - let mut bytes = Vec::new(); - write_storage_address_root_bytes(addr, &mut bytes); - addr.append_entry_bytes(metadata, &mut bytes)?; - Ok(bytes) -} + use super::StorageAddress; + /// 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( + addr: &Address, + out: &mut Vec, + ) { + out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes())); + out.extend(sp_core_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( + addr: &Address, + metadata: &Metadata, + ) -> Result, 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(addr: &Address) -> Vec { + let mut bytes = Vec::new(); + write_storage_address_root_bytes(addr, &mut bytes); + bytes + } + + /// 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: &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. + pub 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(()) + } -/// Outputs a vector containing the bytes written by [`write_storage_address_root_bytes`]. -pub(crate) fn storage_address_root_bytes(addr: &Address) -> Vec { - let mut bytes = Vec::new(); - write_storage_address_root_bytes(addr, &mut bytes); - bytes + /// Given some bytes, a pallet and storage name, decode the response. + pub fn decode_storage_with_metadata( + bytes: &mut &[u8], + metadata: &Metadata, + storage_metadata: &StorageEntryMetadata, + ) -> Result { + let return_ty = storage_metadata.entry_type().value_ty(); + let val = T::decode_with_metadata(bytes, return_ty, metadata)?; + Ok(val) + } } diff --git a/core/src/storage/storage_address.rs b/core/src/storage/storage_address.rs index 49d24598a37..fafc306ff53 100644 --- a/core/src/storage/storage_address.rs +++ b/core/src/storage/storage_address.rs @@ -5,8 +5,9 @@ use crate::{ dynamic::DecodedValueThunk, error::StorageAddressError, + metadata::MetadatExt, utils::{Encoded, Static}, - MetadataError, + MetadataError, Yes, }; use crate::metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata}; @@ -14,6 +15,7 @@ use crate::Error; use alloc::borrow::{Cow, ToOwned}; use alloc::string::String; use alloc::vec::Vec; + use derivative::Derivative; use scale_info::TypeDef; use subxt_metadata::{StorageEntryType, StorageHasher}; @@ -51,10 +53,6 @@ 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)] @@ -125,7 +123,7 @@ where /// the pallet and entry name). Use [`crate::storage::StorageClient::address_bytes()`] /// to obtain the bytes representing the entire address. pub fn to_root_bytes(&self) -> Vec { - super::storage_address_root_bytes(self) + super::utils::storage_address_root_bytes(self) } } diff --git a/core/src/tx/mod.rs b/core/src/tx/mod.rs index 0b7975671c4..bcf66f93cf9 100644 --- a/core/src/tx/mod.rs +++ b/core/src/tx/mod.rs @@ -5,6 +5,7 @@ //! This module contains the trait and types used to represent //! transactions that can be submitted. +use crate::metadata::MetadatExt; use crate::Error; use crate::MetadataError; use crate::{dynamic::Value, metadata::Metadata}; diff --git a/subxt/src/storage/storage_type.rs b/subxt/src/storage/storage_type.rs index 26b7c60ccf8..d31991c6adf 100644 --- a/subxt/src/storage/storage_type.rs +++ b/subxt/src/storage/storage_type.rs @@ -289,65 +289,3 @@ where }) } } - -/// Validate a storage address against the metadata. -pub(crate) fn validate_storage_address( - address: &Address, - pallet: PalletMetadata<'_>, -) -> Result<(), Error> { - if let Some(hash) = address.validation_hash() { - validate_storage(pallet, address.entry_name(), hash)?; - } - Ok(()) -} - -/// Return details about the given storage entry. -fn lookup_entry_details<'a>( - pallet_name: &str, - entry_name: &str, - metadata: &'a 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 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(()) -} - -/// Fetch the return type out of a [`StorageEntryType`]. -fn return_type_from_storage_entry_type(entry: &StorageEntryType) -> u32 { - match entry { - StorageEntryType::Plain(ty) => *ty, - StorageEntryType::Map { value_ty, .. } => *value_ty, - } -} - -/// Given some bytes, a pallet and storage name, decode the response. -fn decode_storage_with_metadata( - bytes: &mut &[u8], - metadata: &Metadata, - storage_metadata: &StorageEntryMetadata, -) -> Result { - let ty = storage_metadata.entry_type(); - let return_ty = return_type_from_storage_entry_type(ty); - let val = T::decode_with_metadata(bytes, return_ty, metadata)?; - Ok(val) -} From 930f2c4300e65a83c66a55a06e4e20ee38b50c0b Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 12:06:06 +0100 Subject: [PATCH 17/37] custom values and runtime apis --- core/src/constants/mod.rs | 2 +- .../src/custom_values/custom_value_address.rs | 79 ++++++++ core/src/custom_values/mod.rs | 150 ++++++++++++++ core/src/dynamic.rs | 10 +- core/src/lib.rs | 2 + core/src/metadata/metadata_type.rs | 15 ++ core/src/runtime_api/mod.rs | 189 ++++++++++++++++++ core/src/storage/mod.rs | 94 +-------- core/src/storage/utils.rs | 90 +++++++++ 9 files changed, 532 insertions(+), 99 deletions(-) create mode 100644 core/src/custom_values/custom_value_address.rs create mode 100644 core/src/custom_values/mod.rs create mode 100644 core/src/runtime_api/mod.rs create mode 100644 core/src/storage/utils.rs diff --git a/core/src/constants/mod.rs b/core/src/constants/mod.rs index a9195d9e149..607877c0018 100644 --- a/core/src/constants/mod.rs +++ b/core/src/constants/mod.rs @@ -37,7 +37,7 @@ pub fn validate_constant( Ok(()) } -pub fn access_constant( +pub fn get_constant( metadata: &Metadata, address: &Address, ) -> Result { diff --git a/core/src/custom_values/custom_value_address.rs b/core/src/custom_values/custom_value_address.rs new file mode 100644 index 00000000000..997d6c8e420 --- /dev/null +++ b/core/src/custom_values/custom_value_address.rs @@ -0,0 +1,79 @@ +use derivative::Derivative; + +use crate::dynamic::DecodedValueThunk; +use crate::metadata::DecodeWithMetadata; + +/// This represents the address of a custom value in in the metadata. +/// Anything, that implements the [CustomValueAddress] trait can be used, to fetch +/// custom values from the metadata. +/// The trait is implemented by [str] for dynamic loopup and [StaticAddress] for static queries. +pub trait CustomValueAddress { + /// The type of the custom value. + type Target: DecodeWithMetadata; + /// Should be set to `Yes` for Dynamic values and static values that have a valid type. + /// Should be `()` for custom values, that have an invalid type id. + type IsDecodable; + + /// the name (key) by which the custom value can be accessed in the metadata. + fn name(&self) -> &str; + + /// An optional hash which, if present, can be checked against node metadata. + fn validation_hash(&self) -> Option<[u8; 32]> { + None + } +} + +impl CustomValueAddress for str { + type Target = DecodedValueThunk; + type IsDecodable = Yes; + + fn name(&self) -> &str { + self + } +} + +/// 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 = ""))] +pub struct StaticAddress { + name: &'static str, + hash: Option<[u8; 32]>, + phantom: core::marker::PhantomData<(ReturnTy, IsDecodable)>, +} + +impl StaticAddress { + #[doc(hidden)] + /// Creates a new StaticAddress. + pub fn new_static(name: &'static str, hash: [u8; 32]) -> StaticAddress { + StaticAddress:: { + name, + hash: Some(hash), + phantom: core::marker::PhantomData, + } + } + + /// Do not validate this custom value prior to accessing it. + pub fn unvalidated(self) -> Self { + Self { + name: self.name, + hash: None, + phantom: self.phantom, + } + } +} + +impl CustomValueAddress for StaticAddress { + type Target = R; + type IsDecodable = Y; + + fn name(&self) -> &str { + self.name + } + + fn validation_hash(&self) -> Option<[u8; 32]> { + self.hash + } +} diff --git a/core/src/custom_values/mod.rs b/core/src/custom_values/mod.rs new file mode 100644 index 00000000000..d0d418bc0e3 --- /dev/null +++ b/core/src/custom_values/mod.rs @@ -0,0 +1,150 @@ +// 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; + +pub use custom_value_address::{CustomValueAddress, StaticAddress, Yes}; + +use crate::{ + metadata::{DecodeWithMetadata, MetadatExt}, + Error, Metadata, MetadataError, +}; +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( + 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 + ?Sized>( + metadata: &Metadata, + address: &Address, +) -> Result { + // 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 = ::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( + metadata: &Metadata, + address: &Address, +) -> Result, 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 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::(); + 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 = + 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_string(), 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() + } + ) + } +} diff --git a/core/src/dynamic.rs b/core/src/dynamic.rs index bfab442e65a..fdf2d7b58fc 100644 --- a/core/src/dynamic.rs +++ b/core/src/dynamic.rs @@ -16,17 +16,17 @@ pub use scale_value::{At, Value}; /// for dynamic requests. pub type DecodedValue = scale_value::Value; -// // Submit dynamic transactions. +/// Submit dynamic transactions. pub use crate::tx::dynamic as tx; -// // Lookup constants dynamically. +/// Lookup constants dynamically. pub use crate::constants::dynamic as constant; -// // Lookup storage values dynamically. +/// Lookup storage values dynamically. pub use crate::storage::dynamic as storage; -// // Execute runtime API function call dynamically. -// pub use crate::runtime_api::dynamic as runtime_api_call; +/// Execute runtime API function call dynamically. +pub use crate::runtime_api::dynamic as runtime_api_call; /// This is the result of making a dynamic request to a node. From this, /// we can return the raw SCALE bytes that we were handed back, or we can diff --git a/core/src/lib.rs b/core/src/lib.rs index a8b1af07954..fa2df7b65a3 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -13,9 +13,11 @@ extern crate alloc; pub mod client; pub mod config; pub mod constants; +pub mod custom_values; pub mod dynamic; mod error; pub mod metadata; +pub mod runtime_api; pub mod signer; pub mod storage; pub mod tx; diff --git a/core/src/metadata/metadata_type.rs b/core/src/metadata/metadata_type.rs index 07e74d9b364..4aaace7291b 100644 --- a/core/src/metadata/metadata_type.rs +++ b/core/src/metadata/metadata_type.rs @@ -42,6 +42,11 @@ pub trait MetadatExt { &self, name: &str, ) -> Result; + + fn custom_value_by_name_err( + &self, + name: &str, + ) -> Result; } impl MetadatExt for subxt_metadata::Metadata { @@ -71,6 +76,16 @@ impl MetadatExt for subxt_metadata::Metadata { 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 { + self.custom() + .get(name) + .ok_or_else(|| MetadataError::CustomValueNameNotFound(name.to_owned())) + } } impl From for Metadata { diff --git a/core/src/runtime_api/mod.rs b/core/src/runtime_api/mod.rs new file mode 100644 index 00000000000..8f71771c687 --- /dev/null +++ b/core/src/runtime_api/mod.rs @@ -0,0 +1,189 @@ +// 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::borrow::Cow; +use alloc::borrow::ToOwned; +use alloc::string::String; +use alloc::vec::Vec; +use core::marker::PhantomData; +use derivative::Derivative; +use scale_encode::EncodeAsFields; +use scale_value::Composite; + +use crate::dynamic::DecodedValueThunk; +use crate::error::MetadataError; +use crate::Error; + +use crate::metadata::{DecodeWithMetadata, MetadatExt, Metadata}; + +/// This represents a runtime API payload that can call into the runtime of node. +/// +/// # Components +/// +/// - associated return type +/// +/// Resulting bytes of the call are interpreted into this type. +/// +/// - runtime function name +/// +/// The function name of the runtime API call. This is obtained by concatenating +/// the runtime trait name with the trait's method. +/// +/// For example, the substrate runtime trait [Metadata](https://github.com/paritytech/substrate/blob/cb954820a8d8d765ce75021e244223a3b4d5722d/primitives/api/src/lib.rs#L745) +/// contains the `metadata_at_version` function. The corresponding runtime function +/// is `Metadata_metadata_at_version`. +/// +/// - encoded arguments +/// +/// Each argument of the runtime function must be scale-encoded. +pub trait RuntimeApiPayload { + /// The return type of the function call. + // Note: `DecodeWithMetadata` is needed to decode the function call result + // with the `subxt::Metadata. + type ReturnType: DecodeWithMetadata; + + /// The runtime API trait name. + fn trait_name(&self) -> &str; + + /// The runtime API method name. + fn method_name(&self) -> &str; + + /// Scale encode the arguments data. + fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error>; + + /// Encode arguments data and return the output. This is a convenience + /// wrapper around [`RuntimeApiPayload::encode_args_to`]. + fn encode_args(&self, metadata: &Metadata) -> Result, Error> { + let mut v = Vec::new(); + self.encode_args_to(metadata, &mut v)?; + Ok(v) + } + + /// Returns the statically generated validation hash. + fn validation_hash(&self) -> Option<[u8; 32]> { + None + } +} + +/// A runtime API payload containing the generic argument data +/// and interpreting the result of the call as `ReturnTy`. +/// +/// 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: core::fmt::Debug") +)] +pub struct Payload { + trait_name: Cow<'static, str>, + method_name: Cow<'static, str>, + args_data: ArgsData, + validation_hash: Option<[u8; 32]>, + _marker: PhantomData, +} + +impl RuntimeApiPayload + for Payload +{ + type ReturnType = ReturnTy; + + fn trait_name(&self) -> &str { + &self.trait_name + } + + fn method_name(&self) -> &str { + &self.method_name + } + + fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error> { + let api_method = metadata + .runtime_api_trait_by_name_err(&self.trait_name)? + .method_by_name(&self.method_name) + .ok_or_else(|| MetadataError::RuntimeMethodNotFound((*self.method_name).to_owned()))?; + let mut fields = api_method + .inputs() + .map(|input| scale_encode::Field::named(input.ty, &input.name)); + + self.args_data + .encode_as_fields_to(&mut fields, metadata.types(), out)?; + Ok(()) + } + + fn validation_hash(&self) -> Option<[u8; 32]> { + self.validation_hash + } +} + +/// A dynamic runtime API payload. +pub type DynamicRuntimeApiPayload = Payload, DecodedValueThunk>; + +impl Payload { + /// Create a new [`Payload`]. + pub fn new( + trait_name: impl Into, + method_name: impl Into, + args_data: ArgsData, + ) -> Self { + Payload { + trait_name: Cow::Owned(trait_name.into()), + method_name: Cow::Owned(method_name.into()), + args_data, + validation_hash: None, + _marker: PhantomData, + } + } + + /// Create a new static [`Payload`] using static function name + /// and scale-encoded argument data. + /// + /// This is only expected to be used from codegen. + #[doc(hidden)] + pub fn new_static( + trait_name: &'static str, + method_name: &'static str, + args_data: ArgsData, + hash: [u8; 32], + ) -> Payload { + Payload { + trait_name: Cow::Borrowed(trait_name), + method_name: Cow::Borrowed(method_name), + args_data, + validation_hash: Some(hash), + _marker: core::marker::PhantomData, + } + } + + /// Do not validate this call prior to submitting it. + pub fn unvalidated(self) -> Self { + Self { + validation_hash: None, + ..self + } + } + + /// Returns the trait name. + pub fn trait_name(&self) -> &str { + &self.trait_name + } + + /// Returns the method name. + pub fn method_name(&self) -> &str { + &self.method_name + } + + /// Returns the arguments data. + pub fn args_data(&self) -> &ArgsData { + &self.args_data + } +} + +/// Create a new [`DynamicRuntimeApiPayload`]. +pub fn dynamic( + trait_name: impl Into, + method_name: impl Into, + args_data: impl Into>, +) -> DynamicRuntimeApiPayload { + Payload::new(trait_name, method_name, args_data.into()) +} diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 8bf68024e1b..07b745d0811 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -7,100 +7,8 @@ /// Types representing an address which describes where a storage /// entry lives and how to properly decode it. pub mod storage_address; +pub mod utils; // 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 mod utils { - use crate::{ - metadata::{DecodeWithMetadata, MetadatExt}, - Error, Metadata, MetadataError, - }; - use alloc::borrow::ToOwned; - use alloc::vec::Vec; - use subxt_metadata::{PalletMetadata, StorageEntryMetadata}; - - use super::StorageAddress; - /// 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( - addr: &Address, - out: &mut Vec, - ) { - out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes())); - out.extend(sp_core_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( - addr: &Address, - metadata: &Metadata, - ) -> Result, 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(addr: &Address) -> Vec { - let mut bytes = Vec::new(); - write_storage_address_root_bytes(addr, &mut bytes); - bytes - } - - /// 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: &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. - pub 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( - bytes: &mut &[u8], - metadata: &Metadata, - storage_metadata: &StorageEntryMetadata, - ) -> Result { - let return_ty = storage_metadata.entry_type().value_ty(); - let val = T::decode_with_metadata(bytes, return_ty, metadata)?; - Ok(val) - } -} diff --git a/core/src/storage/utils.rs b/core/src/storage/utils.rs new file mode 100644 index 00000000000..f4bfab8d29b --- /dev/null +++ b/core/src/storage/utils.rs @@ -0,0 +1,90 @@ +use crate::{ + metadata::{DecodeWithMetadata, MetadatExt}, + Error, Metadata, MetadataError, +}; +use alloc::borrow::ToOwned; +use alloc::vec::Vec; +use subxt_metadata::{PalletMetadata, StorageEntryMetadata}; + +use super::StorageAddress; +/// 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( + addr: &Address, + out: &mut Vec, +) { + out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes())); + out.extend(sp_core_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( + addr: &Address, + metadata: &Metadata, +) -> Result, 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(addr: &Address) -> Vec { + let mut bytes = Vec::new(); + write_storage_address_root_bytes(addr, &mut bytes); + bytes +} + +/// 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: &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. +pub 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( + bytes: &mut &[u8], + metadata: &Metadata, + storage_metadata: &StorageEntryMetadata, +) -> Result { + let return_ty = storage_metadata.entry_type().value_ty(); + let val = T::decode_with_metadata(bytes, return_ty, metadata)?; + Ok(val) +} From 14b21ab0dfd290bb3cb2611e468ea8617d4e51aa Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 14:39:03 +0100 Subject: [PATCH 18/37] port subxt-signer to no-std (such a pain) --- Cargo.lock | 19 +- Cargo.toml | 15 +- core/src/lib.rs | 2 + signer/Cargo.toml | 26 +-- signer/src/crypto/secret_uri.rs | 8 +- signer/src/crypto/seed_from_entropy.rs | 1 + signer/src/ecdsa.rs | 56 ++++-- signer/src/lib.rs | 9 +- signer/src/sr25519.rs | 30 +-- signer/src/utils.rs | 4 +- testing/no-std-tests/Cargo.lock | 261 +++++++++++++++++++++++++ testing/no-std-tests/Cargo.toml | 1 + testing/no-std-tests/src/main.rs | 8 + 13 files changed, 378 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7dc9b6274ba..2b28f2d1fde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1076,6 +1076,12 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -2732,6 +2738,10 @@ name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "oorandom" @@ -2979,6 +2989,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -4622,9 +4638,11 @@ name = "subxt-signer" version = "0.34.0" dependencies = [ "bip39", + "derive_more", "getrandom", "hex", "hmac 0.12.1", + "once_cell", "parity-scale-codec", "pbkdf2 0.12.2", "regex", @@ -4636,7 +4654,6 @@ dependencies = [ "sp-core-hashing", "sp-keyring", "subxt-core", - "thiserror", "zeroize", ] diff --git a/Cargo.toml b/Cargo.toml index 1ecc821c31c..86fced57fad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,12 +76,13 @@ heck = "0.4.1" impl-serde = { version = "0.4.0", default-features = false } indoc = "2" jsonrpsee = { version = "0.21" } +once_cell = { version = "1.19.0", default-features = false } pretty_assertions = "1.4.0" primitive-types = { version = "0.12.2", default-features = false } proc-macro-error = "1.0.4" proc-macro2 = "1.0.78" quote = "1.0.35" -regex = "1.10.3" +regex = { version = "1.10.3", default-features = false } scale-info = { version = "2.10.0", default-features = false } scale-value = { version = "0.13.0", default-features = false } scale-bits = { version = "0.4.0", default-features = false } @@ -134,19 +135,19 @@ subxt-macro = { version = "0.34.0", path = "macro" } subxt-core = { version = "0.34.0", path = "core", default-features = false } subxt-metadata = { version = "0.34.0", path = "metadata", default-features = false } subxt-codegen = { version = "0.34.0", path = "codegen" } -subxt-signer = { version = "0.34.0", path = "signer" } +subxt-signer = { version = "0.34.0", path = "signer", default-features = false } subxt-lightclient = { version = "0.34.0", path = "lightclient", default-features = false } test-runtime = { path = "testing/test-runtime" } substrate-runner = { path = "testing/substrate-runner" } # subxt-signer deps that I expect aren't useful anywhere else: -bip39 = "2.0.0" -hmac = "0.12.1" +bip39 = { version = "2.0.0", default-features = false } +hmac = { version = "0.12.1", default-features = false } pbkdf2 = { version = "0.12.2", default-features = false } -schnorrkel = "0.11.4" -secp256k1 = "0.28.1" +schnorrkel = { version = "0.11.4", default-features = false } +secp256k1 = { version = "0.28.1", default-features = false } secrecy = "0.8.0" -sha2 = "0.10.8" +sha2 = { version = "0.10.8", default-features = false } zeroize = { version = "1", default-features = false } [profile.dev.package.smoldot-light] diff --git a/core/src/lib.rs b/core/src/lib.rs index fa2df7b65a3..72720ece285 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,6 +30,8 @@ pub use config::{ PolkadotExtrinsicParams, SubstrateConfig, SubstrateExtrinsicParams, }; +pub use utils::{AccountId32, MultiAddress, MultiSignature, H160, H256, H512}; + pub use signer::Signer; pub use metadata::Metadata; diff --git a/signer/Cargo.toml b/signer/Cargo.toml index 4192e27a094..f60ad4e9424 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -15,7 +15,8 @@ description = "Sign extrinsics to be submitted by Subxt" keywords = ["parity", "subxt", "extrinsic", "signer"] [features] -default = ["sr25519", "ecdsa", "subxt"] +default = ["sr25519", "ecdsa", "subxt", "std"] +std = [] # Pick the signer implementation(s) you need by enabling the # corresponding features. Note: I had more difficulties getting @@ -34,25 +35,28 @@ web = ["getrandom/js"] [dependencies] subxt-core = { workspace = true, optional = true, default-features = false } -regex = { workspace = true } +regex = { workspace = true, default-features = false } hex = { workspace = true } codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] } -sp-core-hashing = { workspace = true } -thiserror = { workspace = true } -pbkdf2 = { workspace = true } -sha2 = { workspace = true } -hmac = { workspace = true } +sp-core-hashing = { workspace = true, default-features = false } +derive_more = { workspace = true } +pbkdf2 = { workspace = true, default-features = false } +sha2 = { workspace = true, default-features = false } +hmac = { workspace = true, default-features = false } zeroize = { workspace = true } -bip39 = { workspace = true } -schnorrkel = { workspace = true, optional = true } -secp256k1 = { workspace = true, features = ["recovery", "global-context"], optional = true } +bip39 = { workspace = true, default-features = false, features = ["unicode-normalization"] } +schnorrkel = { workspace = true, optional = true, default-features = false } +secp256k1 = { workspace = true, optional = true, default-features = false, features = ["alloc", "recovery"] } # features = ["recovery", "global-context"], secrecy = { workspace = true } +# We only pull this in because `std::sync::OnceLock` is not available in no-std contexts. +once_cell = { workspace = true, default-features = false, features = ["alloc", "critical-section"] } + # 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, features = ["std"] } +sp-core = { workspace = true, default-features = false } sp-keyring = { workspace = true } [package.metadata.cargo-machete] diff --git a/signer/src/crypto/secret_uri.rs b/signer/src/crypto/secret_uri.rs index 7be43b8286e..027d938b7e3 100644 --- a/signer/src/crypto/secret_uri.rs +++ b/signer/src/crypto/secret_uri.rs @@ -3,6 +3,8 @@ // see LICENSE for license details. use super::DeriveJunction; +use alloc::vec::Vec; +use derive_more::Display; use regex::Regex; use secrecy::SecretString; @@ -88,7 +90,7 @@ pub struct SecretUri { pub junctions: Vec, } -impl std::str::FromStr for SecretUri { +impl core::str::FromStr for SecretUri { type Err = SecretUriError; fn from_str(s: &str) -> Result { @@ -115,10 +117,10 @@ impl std::str::FromStr for SecretUri { } /// This is returned if `FromStr` cannot parse a string into a `SecretUri`. -#[derive(Debug, Copy, Clone, PartialEq, thiserror::Error)] +#[derive(Debug, Copy, Clone, PartialEq, Display)] pub enum SecretUriError { /// Parsing the secret URI from a string failed; wrong format. - #[error("Invalid secret phrase format")] + #[display(fmt = "Invalid secret phrase format")] InvalidFormat, } diff --git a/signer/src/crypto/seed_from_entropy.rs b/signer/src/crypto/seed_from_entropy.rs index 7643ff78355..e665c132abb 100644 --- a/signer/src/crypto/seed_from_entropy.rs +++ b/signer/src/crypto/seed_from_entropy.rs @@ -2,6 +2,7 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. +use alloc::string::String; use hmac::Hmac; use pbkdf2::pbkdf2; use sha2::Sha512; diff --git a/signer/src/ecdsa.rs b/signer/src/ecdsa.rs index a76054abf16..6946446f60c 100644 --- a/signer/src/ecdsa.rs +++ b/signer/src/ecdsa.rs @@ -6,10 +6,22 @@ use codec::Encode; use crate::crypto::{seed_from_entropy, DeriveJunction, SecretUri}; +use core::str::FromStr; +use derive_more::{Display, From}; use hex::FromHex; -use secp256k1::{ecdsa::RecoverableSignature, Message, SecretKey, SECP256K1}; +use secp256k1::{ecdsa::RecoverableSignature, All, Message, Secp256k1, SecretKey}; use secrecy::ExposeSecret; +struct GlobalSecp256K1Context; +impl core::ops::Deref for GlobalSecp256K1Context { + type Target = Secp256k1; + + fn deref(&self) -> &Self::Target { + static ONCE: once_cell::sync::OnceCell> = once_cell::sync::OnceCell::new(); + ONCE.get_or_init(|| Secp256k1::new()) + } +} + const SEED_LENGTH: usize = 32; /// Seed bytes used to generate a key pair. @@ -68,7 +80,7 @@ impl Keypair { let seed = Seed::from_hex(hex_str)?; Self::from_seed(seed)? } else { - let phrase = bip39::Mnemonic::parse(phrase.expose_secret().as_str())?; + let phrase = bip39::Mnemonic::from_str(phrase.expose_secret().as_str())?; let pass_str = password.as_ref().map(|p| p.expose_secret().as_str()); Self::from_phrase(&phrase, pass_str)? }; @@ -91,8 +103,9 @@ impl Keypair { /// keypair.sign(b"Hello world!"); /// ``` pub fn from_phrase(mnemonic: &bip39::Mnemonic, password: Option<&str>) -> Result { - let big_seed = seed_from_entropy(&mnemonic.to_entropy(), password.unwrap_or("")) - .ok_or(Error::InvalidSeed)?; + let (arr, len) = mnemonic.to_entropy_array(); + let big_seed = + seed_from_entropy(&arr[0..len], password.unwrap_or("")).ok_or(Error::InvalidSeed)?; let seed: Seed = big_seed[..SEED_LENGTH] .try_into() @@ -109,7 +122,8 @@ impl Keypair { pub fn from_seed(seed: Seed) -> Result { let secret = SecretKey::from_slice(&seed).map_err(|_| Error::InvalidSeed)?; Ok(Self(secp256k1::Keypair::from_secret_key( - SECP256K1, &secret, + &GlobalSecp256K1Context, + &secret, ))) } @@ -161,9 +175,9 @@ impl Keypair { // From sp_core::ecdsa::sign_prehashed: let wrapped = Message::from_digest_slice(&message_hash).expect("Message is 32 bytes; qed"); let recsig: RecoverableSignature = - SECP256K1.sign_ecdsa_recoverable(&wrapped, &self.0.secret_key()); + GlobalSecp256K1Context.sign_ecdsa_recoverable(&wrapped, &self.0.secret_key()); // From sp_core::ecdsa's `impl From for Signature`: - let (recid, sig) = recsig.serialize_compact(); + let (recid, sig): (_, [u8; 64]) = recsig.serialize_compact(); let mut signature_bytes: [u8; 65] = [0; 65]; signature_bytes[..64].copy_from_slice(&sig); signature_bytes[64] = (recid.to_i32() & 0xFF) as u8; @@ -192,31 +206,35 @@ pub fn verify>(sig: &Signature, message: M, pubkey: &PublicKey) - }; let message_hash = sp_core_hashing::blake2_256(message.as_ref()); let wrapped = Message::from_digest_slice(&message_hash).expect("Message is 32 bytes; qed"); - signature.verify(&wrapped, &public).is_ok() + GlobalSecp256K1Context + .verify_ecdsa(&wrapped, &signature, &public) + .is_ok() } /// An error handed back if creating a keypair fails. -#[derive(Debug, PartialEq, thiserror::Error)] +#[derive(Debug, PartialEq, Display, From)] pub enum Error { /// Invalid seed. - #[error("Invalid seed (was it the wrong length?)")] + #[display(fmt = "Invalid seed (was it the wrong length?)")] + #[from(ignore)] InvalidSeed, /// Invalid seed. - #[error("Invalid seed for ECDSA, contained soft junction")] + #[display(fmt = "Invalid seed for ECDSA, contained soft junction")] + #[from(ignore)] SoftJunction, /// Invalid phrase. - #[error("Cannot parse phrase: {0}")] - Phrase(#[from] bip39::Error), + #[display(fmt = "Cannot parse phrase: {_0}")] + Phrase(bip39::Error), /// Invalid hex. - #[error("Cannot parse hex string: {0}")] - Hex(#[from] hex::FromHexError), + #[display(fmt = "Cannot parse hex string: {_0}")] + Hex(hex::FromHexError), } /// Dev accounts, helpful for testing but not to be used in production, /// since the secret keys are known. pub mod dev { use super::*; - use std::str::FromStr; + use core::str::FromStr; once_static_cloned! { /// Equivalent to `{DEV_PHRASE}//Alice`. @@ -260,9 +278,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::utils::{AccountId32, MultiAddress, MultiSignature}; + use subxt_core::Signer as SignerT; impl From for MultiSignature { fn from(value: Signature) -> Self { diff --git a/signer/src/lib.rs b/signer/src/lib.rs index 2c79d6a1ec9..db13732327c 100644 --- a/signer/src/lib.rs +++ b/signer/src/lib.rs @@ -14,6 +14,9 @@ //! subxt transactions for chains supporting sr25519 signatures. #![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; #[macro_use] mod utils; @@ -40,9 +43,3 @@ pub use secrecy::{ExposeSecret, SecretString}; // SecretUri's can be parsed from strings and used to generate key pairs. // DeriveJunctions are the "path" part of these SecretUris. pub use crypto::{DeriveJunction, SecretUri, SecretUriError, DEV_PHRASE}; - -#[cfg(any( - all(feature = "web", feature = "native"), - not(any(feature = "web", feature = "native")) -))] -compile_error!("subxt-signer: exactly one of the 'web' and 'native' features should be used."); diff --git a/signer/src/sr25519.rs b/signer/src/sr25519.rs index 01e6cc84b15..fcf48f85551 100644 --- a/signer/src/sr25519.rs +++ b/signer/src/sr25519.rs @@ -4,7 +4,11 @@ //! An sr25519 keypair implementation. +use core::str::FromStr; + use crate::crypto::{seed_from_entropy, DeriveJunction, SecretUri}; + +use derive_more::{Display, From}; use hex::FromHex; use schnorrkel::{ derive::{ChainCode, Derivation}, @@ -72,7 +76,7 @@ impl Keypair { let seed = Seed::from_hex(hex_str)?; Self::from_seed(seed)? } else { - let phrase = bip39::Mnemonic::parse(phrase.expose_secret().as_str())?; + let phrase = bip39::Mnemonic::from_str(phrase.expose_secret().as_str())?; let pass_str = password.as_ref().map(|p| p.expose_secret().as_str()); Self::from_phrase(&phrase, pass_str)? }; @@ -95,8 +99,9 @@ impl Keypair { /// keypair.sign(b"Hello world!"); /// ``` pub fn from_phrase(mnemonic: &bip39::Mnemonic, password: Option<&str>) -> Result { - let big_seed = seed_from_entropy(&mnemonic.to_entropy(), password.unwrap_or("")) - .ok_or(Error::InvalidSeed)?; + let (arr, len) = mnemonic.to_entropy_array(); + let big_seed = + seed_from_entropy(&arr[0..len], password.unwrap_or("")).ok_or(Error::InvalidSeed)?; let seed: Seed = big_seed[..SEED_LENGTH] .try_into() @@ -187,24 +192,25 @@ pub fn verify>(sig: &Signature, message: M, pubkey: &PublicKey) - } /// An error handed back if creating a keypair fails. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Display, From)] pub enum Error { /// Invalid seed. - #[error("Invalid seed (was it the wrong length?)")] + #[display(fmt = "Invalid seed (was it the wrong length?)")] + #[from(ignore)] InvalidSeed, /// Invalid phrase. - #[error("Cannot parse phrase: {0}")] - Phrase(#[from] bip39::Error), + #[display(fmt = "Cannot parse phrase: {_0}")] + Phrase(bip39::Error), /// Invalid hex. - #[error("Cannot parse hex string: {0}")] - Hex(#[from] hex::FromHexError), + #[display(fmt = "Cannot parse hex string: {_0}")] + Hex(hex::FromHexError), } /// Dev accounts, helpful for testing but not to be used in production, /// since the secret keys are known. pub mod dev { use super::*; - use std::str::FromStr; + use core::str::FromStr; once_static_cloned! { /// Equivalent to `{DEV_PHRASE}//Alice`. @@ -249,9 +255,7 @@ 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::{AccountId32, Config, MultiAddress, MultiSignature, Signer as SignerT}; impl From for MultiSignature { fn from(value: Signature) -> Self { diff --git a/signer/src/utils.rs b/signer/src/utils.rs index 2e126654190..52a935a1fd8 100644 --- a/signer/src/utils.rs +++ b/signer/src/utils.rs @@ -19,7 +19,7 @@ macro_rules! once_static { $( $(#[$attr])* $vis fn $name() -> &'static $ty { - static VAR: std::sync::OnceLock<$ty> = std::sync::OnceLock::new(); + static VAR: once_cell::sync::OnceCell<$ty> = once_cell::sync::OnceCell::new(); VAR.get_or_init(|| { $expr }) } )+ @@ -33,7 +33,7 @@ macro_rules! once_static_cloned { $( $(#[$attr])* $vis fn $name() -> $ty { - static VAR: std::sync::OnceLock<$ty> = std::sync::OnceLock::new(); + static VAR: once_cell::sync::OnceCell<$ty> = once_cell::sync::OnceCell::new(); VAR.get_or_init(|| { $expr }).clone() } )+ diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index 184ac589001..b13c83f0973 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -38,6 +38,22 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" +[[package]] +name = "bip39" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" +dependencies = [ + "bitcoin_hashes", + "unicode-normalization", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" + [[package]] name = "bitvec" version = "1.0.1" @@ -91,6 +107,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -118,6 +143,12 @@ dependencies = [ "libc", ] +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crunchy" version = "0.2.2" @@ -134,6 +165,34 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "darling" version = "0.14.4" @@ -216,6 +275,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -269,6 +334,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom_or_panic" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" +dependencies = [ + "rand_core", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -285,6 +359,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -363,11 +446,27 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] + [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "parity-scale-codec" @@ -393,6 +492,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", +] + +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "primitive-types" version = "0.12.2" @@ -450,6 +570,37 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rustc_version" version = "0.4.0" @@ -573,6 +724,50 @@ dependencies = [ "scale-info", ] +[[package]] +name = "schnorrkel" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0" +dependencies = [ + "arrayref", + "arrayvec", + "curve25519-dalek", + "getrandom_or_panic", + "merlin", + "rand_core", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "semver" version = "1.0.21" @@ -703,6 +898,7 @@ dependencies = [ "parity-scale-codec", "subxt-core", "subxt-metadata", + "subxt-signer", ] [[package]] @@ -717,6 +913,27 @@ dependencies = [ "sp-core-hashing", ] +[[package]] +name = "subxt-signer" +version = "0.34.0" +dependencies = [ + "bip39", + "derive_more", + "hex", + "hmac", + "once_cell", + "parity-scale-codec", + "pbkdf2", + "regex", + "schnorrkel", + "secp256k1", + "secrecy", + "sha2", + "sp-core-hashing", + "subxt-core", + "zeroize", +] + [[package]] name = "syn" version = "1.0.109" @@ -745,6 +962,21 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml_datetime" version = "0.6.3" @@ -808,6 +1040,15 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "version_check" version = "0.9.4" @@ -851,3 +1092,23 @@ dependencies = [ "quote", "syn 2.0.48", ] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/testing/no-std-tests/Cargo.toml b/testing/no-std-tests/Cargo.toml index 5e6f14c0744..2d6da33488d 100644 --- a/testing/no-std-tests/Cargo.toml +++ b/testing/no-std-tests/Cargo.toml @@ -8,6 +8,7 @@ resolver = "2" [dependencies] subxt-metadata = { path = "../../metadata", default-features = false } subxt-core = { path = "../../core", default-features = false } +subxt-signer = { path = "../../signer", default-features = false, features = ["sr25519", "ecdsa", "subxt"] } codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] } libc = { version = "0.2", default-features = false } libc_alloc = { version = "1.0.6" } diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 2812cae0987..9b1dd79e60c 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -49,6 +49,14 @@ fn subxt_metadata_test() { subxt_metadata::Metadata::decode(&mut &METADATA[..]).expect("should be valid metadata"); } +fn subxt_signer_test() { + use subxt_signer::{ SecretUri, ecdsa::Keypair }; + use core::str::FromStr; + let uri = SecretUri::from_str("//Alice").unwrap(); + let keypair = Keypair::from_uri(&uri).unwrap(); +} + fn subxt_core_test() { let _ = subxt_core::utils::Era::Immortal; } + From 4e2d3fd9cf3d568c6b8885258e0f238f14159718 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 16:12:15 +0100 Subject: [PATCH 19/37] core integration into subxt, except for examples --- codegen/Cargo.toml | 2 +- core/src/client/mod.rs | 11 +- core/src/config/extrinsic_params.rs | 43 +- core/src/config/mod.rs | 2 +- core/src/config/signed_extensions.rs | 3 +- core/src/error.rs | 53 +- core/src/lib.rs | 11 +- subxt/Cargo.toml | 16 +- subxt/src/backend/mod.rs | 21 +- subxt/src/blocks/extrinsic_types.rs | 5 +- subxt/src/client/offline_client.rs | 28 +- subxt/src/client/online_client.rs | 29 +- subxt/src/config/default_extrinsic_params.rs | 144 ----- subxt/src/config/extrinsic_params.rs | 81 --- subxt/src/config/mod.rs | 154 ------ subxt/src/config/polkadot.rs | 33 -- subxt/src/config/signed_extensions.rs | 496 ------------------ subxt/src/config/substrate.rs | 278 ---------- subxt/src/constants/constant_address.rs | 100 ---- subxt/src/constants/constants_client.rs | 32 +- subxt/src/constants/mod.rs | 3 +- .../src/custom_values/custom_value_address.rs | 3 +- .../src/custom_values/custom_values_client.rs | 2 +- subxt/src/error/dispatch_error.rs | 2 +- subxt/src/error/mod.rs | 48 +- subxt/src/events/events_type.rs | 2 +- subxt/src/lib.rs | 5 +- subxt/src/runtime_api/mod.rs | 3 +- subxt/src/runtime_api/runtime_payload.rs | 186 ------- subxt/src/runtime_api/runtime_types.rs | 2 +- subxt/src/storage/mod.rs | 11 +- subxt/src/storage/storage_address.rs | 272 ---------- subxt/src/storage/storage_client.rs | 17 +- subxt/src/storage/storage_type.rs | 19 +- subxt/src/storage/utils.rs | 41 -- subxt/src/tx/mod.rs | 14 +- subxt/src/tx/tx_client.rs | 6 +- subxt/src/tx/tx_progress.rs | 8 +- 38 files changed, 173 insertions(+), 2013 deletions(-) delete mode 100644 subxt/src/config/default_extrinsic_params.rs delete mode 100644 subxt/src/config/extrinsic_params.rs delete mode 100644 subxt/src/config/mod.rs delete mode 100644 subxt/src/config/polkadot.rs delete mode 100644 subxt/src/config/signed_extensions.rs delete mode 100644 subxt/src/config/substrate.rs delete mode 100644 subxt/src/constants/constant_address.rs delete mode 100644 subxt/src/runtime_api/runtime_payload.rs delete mode 100644 subxt/src/storage/storage_address.rs delete mode 100644 subxt/src/storage/utils.rs diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 1cf9638bae3..5ffde7e92fe 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -26,7 +26,7 @@ syn = { workspace = true } scale-info = { workspace = true } subxt-metadata = { workspace = true } jsonrpsee = { workspace = true, features = ["async-client", "client-ws-transport-native-tls", "http-client"], optional = true } -hex = { workspace = true } +hex = { workspace = true, features = ["std"] } tokio = { workspace = true, features = ["rt-multi-thread"], optional = true } thiserror = { workspace = true } scale-typegen = { workspace = true } diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index d768cbbd9d1..dd922383d29 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -8,7 +8,7 @@ use crate::{config::Config, metadata::Metadata}; /// - metadata #[derive(Derivative)] -#[derivative(Debug(bound = ""))] +#[derivative(Debug(bound = ""), Clone(bound = ""))] pub struct ClientBase { pub genesis_hash: C::Hash, pub runtime_version: RuntimeVersion, @@ -17,6 +17,15 @@ pub struct ClientBase { } impl ClientBase { + pub fn new(genesis_hash: C::Hash, runtime_version: RuntimeVersion, metadata: Metadata) -> Self { + Self { + genesis_hash, + runtime_version, + metadata, + marker: core::marker::PhantomData, + } + } + pub fn metadata(&self) -> Metadata { self.metadata.clone() } diff --git a/core/src/config/extrinsic_params.rs b/core/src/config/extrinsic_params.rs index f0efbf01535..5965843c5bb 100644 --- a/core/src/config/extrinsic_params.rs +++ b/core/src/config/extrinsic_params.rs @@ -10,54 +10,13 @@ use crate::client::ClientBase; use super::Config; +use crate::ExtrinsicParamsError; use alloc::string::String; use alloc::vec::Vec; use core::fmt::Debug; use derive_more::Display; -/// 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(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}")] - #[cfg(feature = "std")] - Custom(CustomExtrinsicParamsError), -} - -/// A custom error. -#[cfg(feature = "std")] -pub type CustomExtrinsicParamsError = Box; - -impl From for ExtrinsicParamsError { - fn from(value: core::convert::Infallible) -> Self { - match value {} - } -} - -#[cfg(feature = "std")] -impl From for ExtrinsicParamsError { - fn from(value: CustomExtrinsicParamsError) -> Self { - ExtrinsicParamsError::Custom(value) - } -} - /// This trait allows you to configure the "signed extra" and /// "additional" parameters that are a part of the transaction payload /// or the signer payload respectively. diff --git a/core/src/config/mod.rs b/core/src/config/mod.rs index f14849df6ec..1776a78d395 100644 --- a/core/src/config/mod.rs +++ b/core/src/config/mod.rs @@ -23,7 +23,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 signed_extensions::SignedExtension; pub use substrate::{SubstrateConfig, SubstrateExtrinsicParams, SubstrateExtrinsicParamsBuilder}; diff --git a/core/src/config/signed_extensions.rs b/core/src/config/signed_extensions.rs index d09704beb23..79a8f027d68 100644 --- a/core/src/config/signed_extensions.rs +++ b/core/src/config/signed_extensions.rs @@ -7,10 +7,11 @@ //! [`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, ExtrinsicParamsEncoder}; use super::Config; use crate::client::ClientBase; use crate::utils::Era; +use crate::ExtrinsicParamsError; use alloc::borrow::ToOwned; use alloc::boxed::Box; use alloc::collections::BTreeMap; diff --git a/core/src/error.rs b/core/src/error.rs index 038ee747475..276ee004f10 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -6,13 +6,16 @@ pub enum Error { #[display(fmt = "Metadata Error: {_0}")] Metadata(MetadataError), #[display(fmt = "Storage Error: {_0}")] - Storage(StorageAddressError), + 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), } #[cfg(feature = "std")] @@ -98,3 +101,51 @@ pub enum StorageAddressError { fields: usize, }, } + +#[cfg(feature = "std")] +impl std::error::Error for StorageAddressError {} + +/// 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(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}")] + #[cfg(feature = "std")] + Custom(CustomExtrinsicParamsError), +} + +/// A custom error. +#[cfg(feature = "std")] +pub type CustomExtrinsicParamsError = Box; + +#[cfg(feature = "std")] +impl std::error::Error for ExtrinsicParamsError {} + +impl From for ExtrinsicParamsError { + fn from(value: core::convert::Infallible) -> Self { + match value {} + } +} + +#[cfg(feature = "std")] +impl From for ExtrinsicParamsError { + fn from(value: CustomExtrinsicParamsError) -> Self { + ExtrinsicParamsError::Custom(value) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 72720ece285..e071e28c20b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -23,18 +23,17 @@ pub mod storage; pub mod tx; pub mod utils; -pub use error::{Error, MetadataError, StorageAddressError}; +pub use error::{Error, ExtrinsicParamsError, MetadataError, StorageAddressError}; +pub use client::{ClientBase, RuntimeVersion}; pub use config::{ BlockHash, Config, ExtrinsicParams, ExtrinsicParamsEncoder, PolkadotConfig, PolkadotExtrinsicParams, SubstrateConfig, SubstrateExtrinsicParams, }; - -pub use utils::{AccountId32, MultiAddress, MultiSignature, H160, H256, H512}; - -pub use signer::Signer; - pub use metadata::Metadata; +pub use signer::Signer; +pub use storage::StorageAddress; +pub use utils::{AccountId32, MultiAddress, MultiSignature, H160, H256, H512}; #[macro_use] mod macros; diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index a02611b87a4..8ef6cd7ba77 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -24,23 +24,11 @@ default = ["jsonrpsee", "native"] # Enable this for native (ie non web/wasm builds). # Exactly 1 of "web" and "native" is expected. -native = [ - "jsonrpsee?/async-client", - "jsonrpsee?/client-ws-transport-native-tls", - "subxt-lightclient?/native", - "tokio-util" -] +native = ["jsonrpsee?/async-client", "jsonrpsee?/client-ws-transport-native-tls", "subxt-lightclient?/native", "tokio-util"] # Enable this for web/wasm builds. # Exactly 1 of "web" and "native" is expected. -web = [ - "jsonrpsee?/async-wasm-client", - "jsonrpsee?/client-web-transport", - "getrandom/js", - "subxt-lightclient?/web", - "subxt-macro/web", - "instant/wasm-bindgen" -] +web = ["jsonrpsee?/async-wasm-client", "jsonrpsee?/client-web-transport", "getrandom/js", "subxt-lightclient?/web", "subxt-macro/web", "instant/wasm-bindgen"] # Enable this to use jsonrpsee (allowing for example `OnlineClient::from_url`). jsonrpsee = ["dep:jsonrpsee"] diff --git a/subxt/src/backend/mod.rs b/subxt/src/backend/mod.rs index 4bbd9f6159c..25d72b9a574 100644 --- a/subxt/src/backend/mod.rs +++ b/subxt/src/backend/mod.rs @@ -17,6 +17,7 @@ use codec::{Decode, Encode}; use futures::{Stream, StreamExt}; use std::pin::Pin; use std::sync::Arc; +use subxt_core::client::RuntimeVersion; use subxt_core::metadata::Metadata; /// Prevent the backend trait being implemented externally. @@ -276,26 +277,6 @@ impl StreamOf { /// A stream of [`Result`]. pub type StreamOfResults = StreamOf>; -/// 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`], diff --git a/subxt/src/blocks/extrinsic_types.rs b/subxt/src/blocks/extrinsic_types.rs index d7575123aad..e90e3c3bdca 100644 --- a/subxt/src/blocks/extrinsic_types.rs +++ b/subxt/src/blocks/extrinsic_types.rs @@ -10,7 +10,7 @@ use crate::{ events, Metadata, }; -use subxt_core::metadata::types::PalletMetadata; +use subxt_core::metadata::{types::PalletMetadata, MetadatExt}; use crate::config::signed_extensions::{ ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce, @@ -770,7 +770,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 +781,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. diff --git a/subxt/src/client/offline_client.rs b/subxt/src/client/offline_client.rs index 4477e21b7ad..1787170a000 100644 --- a/subxt/src/client/offline_client.rs +++ b/subxt/src/client/offline_client.rs @@ -4,11 +4,12 @@ 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 subxt_core::client::ClientBase; +use subxt_core::RuntimeVersion; use std::sync::Arc; @@ -21,6 +22,8 @@ pub trait OfflineClientT: Clone + Send + Sync + 'static { fn genesis_hash(&self) -> T::Hash; /// Return the provided [`RuntimeVersion`]. fn runtime_version(&self) -> RuntimeVersion; + /// Return the inner [`subxt_core::ClientBase`]. + fn base(&self) -> ClientBase; /// Work with transactions. fn tx(&self) -> TxClient { @@ -63,15 +66,7 @@ pub trait OfflineClientT: Clone + Send + Sync + 'static { #[derive(Derivative)] #[derivative(Debug(bound = ""), Clone(bound = ""))] pub struct OfflineClient { - inner: Arc>, -} - -#[derive(Derivative)] -#[derivative(Debug(bound = ""), Clone(bound = ""))] -struct Inner { - genesis_hash: T::Hash, - runtime_version: RuntimeVersion, - metadata: Metadata, + inner: Arc>, } impl OfflineClient { @@ -83,11 +78,11 @@ impl OfflineClient { metadata: impl Into, ) -> OfflineClient { OfflineClient { - inner: Arc::new(Inner { + inner: Arc::new(ClientBase::new( genesis_hash, runtime_version, - metadata: metadata.into(), - }), + metadata.into(), + )), } } @@ -145,6 +140,9 @@ impl OfflineClientT for OfflineClient { fn metadata(&self) -> Metadata { self.metadata() } + fn base(&self) -> ClientBase { + (*self.inner).clone() + } } // For ergonomics; cloning a client is deliberately fairly cheap (via Arc), diff --git a/subxt/src/client/online_client.rs b/subxt/src/client/online_client.rs index f4e2f8c723e..36a742ee3f5 100644 --- a/subxt/src/client/online_client.rs +++ b/subxt/src/client/online_client.rs @@ -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, @@ -19,7 +17,9 @@ use crate::{ }; use derivative::Derivative; use futures::future; +use std::borrow::Borrow; use std::sync::{Arc, RwLock}; +use subxt_core::{ClientBase, RuntimeVersion}; /// A trait representing a client that can perform /// online actions. @@ -33,18 +33,10 @@ pub trait OnlineClientT: OfflineClientT { #[derive(Derivative)] #[derivative(Clone(bound = ""))] pub struct OnlineClient { - inner: Arc>>, + inner: Arc>>, backend: Arc>, } -#[derive(Derivative)] -#[derivative(Debug(bound = ""))] -struct Inner { - genesis_hash: T::Hash, - runtime_version: RuntimeVersion, - metadata: Metadata, -} - impl std::fmt::Debug for OnlineClient { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Client") @@ -146,11 +138,11 @@ impl OnlineClient { backend: Arc, ) -> Result, Error> { Ok(OnlineClient { - inner: Arc::new(RwLock::new(Inner { + inner: Arc::new(RwLock::new(ClientBase::new( genesis_hash, runtime_version, - metadata: metadata.into(), - })), + metadata.into(), + ))), backend, }) } @@ -360,6 +352,11 @@ impl OfflineClientT for OnlineClient { fn runtime_version(&self) -> RuntimeVersion { self.runtime_version() } + + fn base(&self) -> ClientBase { + let inner = self.inner.read().expect("shouldn't be poisoned"); + inner.clone() + } } impl OnlineClientT for OnlineClient { @@ -521,7 +518,7 @@ async fn wait_runtime_upgrade_in_finalized_block( 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 diff --git a/subxt/src/config/default_extrinsic_params.rs b/subxt/src/config/default_extrinsic_params.rs deleted file mode 100644 index dce83853bf2..00000000000 --- a/subxt/src/config/default_extrinsic_params.rs +++ /dev/null @@ -1,144 +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 super::{signed_extensions, ExtrinsicParams}; -use super::{Config, Header}; - -/// The default [`super::ExtrinsicParams`] implementation understands common signed extensions -/// and how to apply them to a given chain. -pub type DefaultExtrinsicParams = signed_extensions::AnyOf< - T, - ( - signed_extensions::CheckSpecVersion, - signed_extensions::CheckTxVersion, - signed_extensions::CheckNonce, - signed_extensions::CheckGenesis, - signed_extensions::CheckMortality, - signed_extensions::ChargeAssetTxPayment, - signed_extensions::ChargeTransactionPayment, - ), ->; - -/// A builder that outputs the set of [`super::ExtrinsicParams::OtherParams`] required for -/// [`DefaultExtrinsicParams`]. This may expose methods that aren't applicable to the current -/// chain; such values will simply be ignored if so. -pub struct DefaultExtrinsicParamsBuilder { - /// `None` means the tx will be immortal. - mortality: Option>, - /// `None` means we'll use the native token. - tip_of_asset_id: Option, - tip: u128, - tip_of: u128, -} - -struct Mortality { - /// Block hash that mortality starts from - checkpoint_hash: Hash, - /// Block number that mortality starts from (must - // point to the same block as the hash above) - checkpoint_number: u64, - /// How many blocks the tx is mortal for - period: u64, -} - -impl Default for DefaultExtrinsicParamsBuilder { - fn default() -> Self { - Self { - mortality: None, - tip: 0, - tip_of: 0, - tip_of_asset_id: None, - } - } -} - -impl DefaultExtrinsicParamsBuilder { - /// Configure new extrinsic params. We default to providing no tip - /// and using an immortal transaction unless otherwise configured - pub fn new() -> Self { - Default::default() - } - - /// Make the transaction mortal, given a block header that it should be mortal from, - /// and the number of blocks (roughly; it'll be rounded to a power of two) that it will - /// be mortal for. - pub fn mortal(mut self, from_block: &T::Header, for_n_blocks: u64) -> Self { - self.mortality = Some(Mortality { - checkpoint_hash: from_block.hash(), - checkpoint_number: from_block.number().into(), - period: for_n_blocks, - }); - self - } - - /// Make the transaction mortal, given a block number and block hash (which must both point to - /// the same block) that it should be mortal from, and the number of blocks (roughly; it'll be - /// rounded to a power of two) that it will be mortal for. - /// - /// Prefer to use [`DefaultExtrinsicParamsBuilder::mortal()`], which ensures that the block hash - /// and number align. - pub fn mortal_unchecked( - mut self, - from_block_number: u64, - from_block_hash: T::Hash, - for_n_blocks: u64, - ) -> Self { - self.mortality = Some(Mortality { - checkpoint_hash: from_block_hash, - checkpoint_number: from_block_number, - period: for_n_blocks, - }); - self - } - - /// Provide a tip to the block author in the chain's native token. - pub fn tip(mut self, tip: u128) -> Self { - self.tip = tip; - self.tip_of = tip; - self.tip_of_asset_id = None; - self - } - - /// Provide a tip to the block author using the token denominated by the `asset_id` provided. This - /// is not applicable on chains which don't use the `ChargeAssetTxPayment` signed extension; in this - /// case, no tip will be given. - pub fn tip_of(mut self, tip: u128, asset_id: T::AssetId) -> Self { - self.tip = 0; - self.tip_of = tip; - self.tip_of_asset_id = Some(asset_id); - self - } - - /// Build the extrinsic parameters. - pub fn build(self) -> as ExtrinsicParams>::OtherParams { - let check_mortality_params = if let Some(mortality) = self.mortality { - signed_extensions::CheckMortalityParams::mortal( - mortality.period, - mortality.checkpoint_number, - mortality.checkpoint_hash, - ) - } else { - signed_extensions::CheckMortalityParams::immortal() - }; - - let charge_asset_tx_params = if let Some(asset_id) = self.tip_of_asset_id { - signed_extensions::ChargeAssetTxPaymentParams::tip_of(self.tip, asset_id) - } else { - signed_extensions::ChargeAssetTxPaymentParams::tip(self.tip) - }; - - let charge_transaction_params = - signed_extensions::ChargeTransactionPaymentParams::tip(self.tip); - - ( - (), - (), - (), - (), - check_mortality_params, - charge_asset_tx_params, - charge_transaction_params, - ) - } -} diff --git a/subxt/src/config/extrinsic_params.rs b/subxt/src/config/extrinsic_params.rs deleted file mode 100644 index 42f3b620afc..00000000000 --- a/subxt/src/config/extrinsic_params.rs +++ /dev/null @@ -1,81 +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. - -//! This module contains a trait which controls the parameters that must -//! be provided in order to successfully construct an extrinsic. -//! [`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; - -/// 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; - -impl From for ExtrinsicParamsError { - fn from(value: std::convert::Infallible) -> Self { - match value {} - } -} -impl From for ExtrinsicParamsError { - fn from(value: CustomExtrinsicParamsError) -> Self { - ExtrinsicParamsError::Custom(value) - } -} - -/// This trait allows you to configure the "signed extra" and -/// "additional" parameters that are a part of the transaction payload -/// or the signer payload respectively. -pub trait ExtrinsicParams: ExtrinsicParamsEncoder + Sized + 'static { - /// These parameters can be provided to the constructor along with - /// some default parameters that `subxt` understands, in order to - /// help construct your [`ExtrinsicParams`] object. - type OtherParams; - - /// Construct a new instance of our [`ExtrinsicParams`]. - fn new>( - nonce: u64, - client: Client, - other_params: Self::OtherParams, - ) -> Result; -} - -/// This trait is expected to be implemented for any [`ExtrinsicParams`], and -/// defines how to encode the "additional" and "extra" params. Both functions -/// are optional and will encode nothing by default. -pub trait ExtrinsicParamsEncoder: 'static { - /// This is expected to SCALE encode the "signed extra" parameters - /// to some buffer that has been provided. These are the parameters - /// which are sent along with the transaction, as well as taken into - /// account when signing the transaction. - fn encode_extra_to(&self, _v: &mut Vec) {} - - /// This is expected to SCALE encode the "additional" parameters - /// to some buffer that has been provided. These parameters are _not_ - /// sent along with the transaction, but are taken into account when - /// signing it, meaning the client and node must agree on their values. - fn encode_additional_to(&self, _v: &mut Vec) {} -} diff --git a/subxt/src/config/mod.rs b/subxt/src/config/mod.rs deleted file mode 100644 index ddfa2466c7a..00000000000 --- a/subxt/src/config/mod.rs +++ /dev/null @@ -1,154 +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. - -//! 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. - -mod default_extrinsic_params; -mod extrinsic_params; - -pub mod polkadot; -pub mod signed_extensions; -pub mod substrate; - -use crate::macros::cfg_substrate_compat; -use codec::{Decode, Encode}; -use core::fmt::Debug; -use scale_decode::DecodeAsType; -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 polkadot::{PolkadotConfig, PolkadotExtrinsicParams, PolkadotExtrinsicParamsBuilder}; -pub use signed_extensions::SignedExtension; -pub use substrate::{SubstrateConfig, SubstrateExtrinsicParams, SubstrateExtrinsicParamsBuilder}; - -/// Runtime types. -// Note: the `Send + Sync + 'static` bound isn't strictly required, but currently deriving -// TypeInfo automatically applies a 'static bound to all generic types (including this one), -// And we want the compiler to infer `Send` and `Sync` OK for things which have `T: Config` -// rather than having to `unsafe impl` them ourselves. -pub trait Config: Sized + Send + Sync + 'static { - /// The output of the `Hasher` function. - type Hash: BlockHash; - - /// The account ID type. - type AccountId: Debug + Clone + Encode; - - /// The address type. - type Address: Debug + Encode + From; - - /// The signature type. - type Signature: Debug + Encode; - - /// The hashing system (algorithm) being used in the runtime (e.g. Blake2). - type Hasher: Debug + Hasher; - - /// The block header. - type Header: Debug + Header + Sync + Send + DeserializeOwned; - - /// This type defines the extrinsic extra and additional parameters. - type ExtrinsicParams: ExtrinsicParams; - - /// This is used to identify an asset in the `ChargeAssetTxPayment` signed extension. - type AssetId: Debug + Clone + Encode + DecodeAsType + EncodeAsType; -} - -/// given some [`Config`], this return the other params needed for its `ExtrinsicParams`. -pub type OtherParamsFor = <::ExtrinsicParams as ExtrinsicParams>::OtherParams; - -/// Block hashes must conform to a bunch of things to be used in Subxt. -pub trait BlockHash: - Debug - + Copy - + Send - + Sync - + Decode - + AsRef<[u8]> - + Serialize - + DeserializeOwned - + Encode - + PartialEq - + Eq - + std::hash::Hash -{ -} -impl BlockHash for T where - T: Debug - + Copy - + Send - + Sync - + Decode - + AsRef<[u8]> - + Serialize - + DeserializeOwned - + Encode - + PartialEq - + Eq - + std::hash::Hash -{ -} - -/// This represents the hasher used by a node to hash things like block headers -/// and extrinsics. -pub trait Hasher { - /// The type given back from the hash operation - type Output; - - /// Hash some bytes to the given output type. - fn hash(s: &[u8]) -> Self::Output; - - /// Hash some SCALE encodable type to the given output type. - fn hash_of(s: &S) -> Self::Output { - let out = s.encode(); - Self::hash(&out) - } -} - -/// This represents the block header type used by a node. -pub trait Header: Sized + Encode + Decode { - /// The block number type for this header. - type Number: Into; - /// The hasher used to hash this header. - type Hasher: Hasher; - - /// Return the block number of this header. - fn number(&self) -> Self::Number; - - /// Hash this header. - fn hash(&self) -> ::Output { - Self::Hasher::hash_of(self) - } -} - -cfg_substrate_compat! { - /// implement subxt's Hasher and Header traits for some substrate structs - mod substrate_impls { - use super::*; - - impl Header for T - where - ::Number: Into, - { - type Number = T::Number; - type Hasher = T::Hashing; - - fn number(&self) -> Self::Number { - *self.number() - } - } - - impl Hasher for T { - type Output = T::Output; - - fn hash(s: &[u8]) -> Self::Output { - ::hash(s) - } - } - } -} diff --git a/subxt/src/config/polkadot.rs b/subxt/src/config/polkadot.rs deleted file mode 100644 index 209c3b8fb6b..00000000000 --- a/subxt/src/config/polkadot.rs +++ /dev/null @@ -1,33 +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. - -//! Polkadot specific configuration - -use super::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder}; - -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. -pub enum PolkadotConfig {} - -impl Config for PolkadotConfig { - type Hash = ::Hash; - type AccountId = ::AccountId; - type Address = MultiAddress; - type Signature = ::Signature; - type Hasher = ::Hasher; - type Header = ::Header; - type ExtrinsicParams = PolkadotExtrinsicParams; - type AssetId = u32; -} - -/// A struct representing the signed extra and additional parameters required -/// to construct a transaction for a polkadot node. -pub type PolkadotExtrinsicParams = DefaultExtrinsicParams; - -/// A builder which leads to [`PolkadotExtrinsicParams`] being constructed. -/// This is what you provide to methods like `sign_and_submit()`. -pub type PolkadotExtrinsicParamsBuilder = DefaultExtrinsicParamsBuilder; diff --git a/subxt/src/config/signed_extensions.rs b/subxt/src/config/signed_extensions.rs deleted file mode 100644 index f0864cee61c..00000000000 --- a/subxt/src/config/signed_extensions.rs +++ /dev/null @@ -1,496 +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. - -//! This module contains implementations for common signed extensions, each -//! of which implements [`SignedExtension`], and can be used in conjunction with -//! [`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 crate::utils::Era; -use crate::{client::OfflineClientT, Config}; -use codec::{Compact, Encode}; -use core::fmt::Debug; -use derivative::Derivative; -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. -pub trait SignedExtension: ExtrinsicParams { - /// The type representing the `extra` bytes of a signed extension. - /// Decoding from this type should be symmetrical to the respective - /// `ExtrinsicParamsEncoder::encode_extra_to()` implementation of this signed extension. - type Decoded: DecodeAsType; - - /// This should return true if the signed extension matches the details given. - /// Often, this will involve just checking that the identifier given matches that of the - /// extension in question. - fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool; -} - -/// The [`CheckSpecVersion`] signed extension. -pub struct CheckSpecVersion(u32); - -impl ExtrinsicParams for CheckSpecVersion { - type OtherParams = (); - - fn new>( - _nonce: u64, - client: Client, - _other_params: Self::OtherParams, - ) -> Result { - Ok(CheckSpecVersion(client.runtime_version().spec_version)) - } -} - -impl ExtrinsicParamsEncoder for CheckSpecVersion { - fn encode_additional_to(&self, v: &mut Vec) { - self.0.encode_to(v); - } -} - -impl SignedExtension for CheckSpecVersion { - type Decoded = (); - fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { - identifier == "CheckSpecVersion" - } -} - -/// The [`CheckNonce`] signed extension. -pub struct CheckNonce(Compact); - -impl ExtrinsicParams for CheckNonce { - type OtherParams = (); - - fn new>( - nonce: u64, - _client: Client, - _other_params: Self::OtherParams, - ) -> Result { - Ok(CheckNonce(Compact(nonce))) - } -} - -impl ExtrinsicParamsEncoder for CheckNonce { - fn encode_extra_to(&self, v: &mut Vec) { - self.0.encode_to(v); - } -} - -impl SignedExtension for CheckNonce { - type Decoded = u64; - fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { - identifier == "CheckNonce" - } -} - -/// The [`CheckTxVersion`] signed extension. -pub struct CheckTxVersion(u32); - -impl ExtrinsicParams for CheckTxVersion { - type OtherParams = (); - - fn new>( - _nonce: u64, - client: Client, - _other_params: Self::OtherParams, - ) -> Result { - Ok(CheckTxVersion(client.runtime_version().transaction_version)) - } -} - -impl ExtrinsicParamsEncoder for CheckTxVersion { - fn encode_additional_to(&self, v: &mut Vec) { - self.0.encode_to(v); - } -} - -impl SignedExtension for CheckTxVersion { - type Decoded = (); - fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { - identifier == "CheckTxVersion" - } -} - -/// The [`CheckGenesis`] signed extension. -pub struct CheckGenesis(T::Hash); - -impl ExtrinsicParams for CheckGenesis { - type OtherParams = (); - - fn new>( - _nonce: u64, - client: Client, - _other_params: Self::OtherParams, - ) -> Result { - Ok(CheckGenesis(client.genesis_hash())) - } -} - -impl ExtrinsicParamsEncoder for CheckGenesis { - fn encode_additional_to(&self, v: &mut Vec) { - self.0.encode_to(v); - } -} - -impl SignedExtension for CheckGenesis { - type Decoded = (); - fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { - identifier == "CheckGenesis" - } -} - -/// The [`CheckMortality`] signed extension. -pub struct CheckMortality { - era: Era, - checkpoint: T::Hash, -} - -/// Parameters to configure the [`CheckMortality`] signed extension. -pub struct CheckMortalityParams { - era: Era, - checkpoint: Option, -} - -impl Default for CheckMortalityParams { - fn default() -> Self { - Self { - era: Default::default(), - checkpoint: Default::default(), - } - } -} - -impl CheckMortalityParams { - /// Configure a mortal transaction. The `period` is (roughly) how many - /// blocks the transaction will be valid for. The `block_number` and - /// `block_hash` should both point to the same block, and are the block that - /// the transaction is mortal from. - pub fn mortal(period: u64, block_number: u64, block_hash: T::Hash) -> Self { - CheckMortalityParams { - era: Era::mortal(period, block_number), - checkpoint: Some(block_hash), - } - } - /// An immortal transaction. - pub fn immortal() -> Self { - CheckMortalityParams { - era: Era::Immortal, - checkpoint: None, - } - } -} - -impl ExtrinsicParams for CheckMortality { - type OtherParams = CheckMortalityParams; - - fn new>( - _nonce: u64, - client: Client, - other_params: Self::OtherParams, - ) -> Result { - Ok(CheckMortality { - era: other_params.era, - checkpoint: other_params.checkpoint.unwrap_or(client.genesis_hash()), - }) - } -} - -impl ExtrinsicParamsEncoder for CheckMortality { - fn encode_extra_to(&self, v: &mut Vec) { - self.era.encode_to(v); - } - fn encode_additional_to(&self, v: &mut Vec) { - self.checkpoint.encode_to(v); - } -} - -impl SignedExtension for CheckMortality { - type Decoded = Era; - fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { - identifier == "CheckMortality" - } -} - -/// The [`ChargeAssetTxPayment`] signed extension. -#[derive(Derivative, DecodeAsType)] -#[derivative(Clone(bound = "T::AssetId: Clone"), Debug(bound = "T::AssetId: Debug"))] -#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")] -pub struct ChargeAssetTxPayment { - tip: Compact, - asset_id: Option, -} - -impl ChargeAssetTxPayment { - /// Tip to the extrinsic author in the native chain token. - pub fn tip(&self) -> u128 { - self.tip.0 - } - - /// Tip to the extrinsic author using the asset ID given. - pub fn asset_id(&self) -> Option<&T::AssetId> { - self.asset_id.as_ref() - } -} - -/// Parameters to configure the [`ChargeAssetTxPayment`] signed extension. -pub struct ChargeAssetTxPaymentParams { - tip: u128, - asset_id: Option, -} - -impl Default for ChargeAssetTxPaymentParams { - fn default() -> Self { - ChargeAssetTxPaymentParams { - tip: Default::default(), - asset_id: Default::default(), - } - } -} - -impl ChargeAssetTxPaymentParams { - /// Don't provide a tip to the extrinsic author. - pub fn no_tip() -> Self { - ChargeAssetTxPaymentParams { - tip: 0, - asset_id: None, - } - } - /// Tip the extrinsic author in the native chain token. - pub fn tip(tip: u128) -> Self { - ChargeAssetTxPaymentParams { - tip, - asset_id: None, - } - } - /// Tip the extrinsic author using the asset ID given. - pub fn tip_of(tip: u128, asset_id: T::AssetId) -> Self { - ChargeAssetTxPaymentParams { - tip, - asset_id: Some(asset_id), - } - } -} - -impl ExtrinsicParams for ChargeAssetTxPayment { - type OtherParams = ChargeAssetTxPaymentParams; - - fn new>( - _nonce: u64, - _client: Client, - other_params: Self::OtherParams, - ) -> Result { - Ok(ChargeAssetTxPayment { - tip: Compact(other_params.tip), - asset_id: other_params.asset_id, - }) - } -} - -impl ExtrinsicParamsEncoder for ChargeAssetTxPayment { - fn encode_extra_to(&self, v: &mut Vec) { - (self.tip, &self.asset_id).encode_to(v); - } -} - -impl SignedExtension for ChargeAssetTxPayment { - type Decoded = Self; - fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { - identifier == "ChargeAssetTxPayment" - } -} - -/// The [`ChargeTransactionPayment`] signed extension. -#[derive(Clone, Debug, DecodeAsType)] -pub struct ChargeTransactionPayment { - tip: Compact, -} - -impl ChargeTransactionPayment { - /// Tip to the extrinsic author in the native chain token. - pub fn tip(&self) -> u128 { - self.tip.0 - } -} - -/// Parameters to configure the [`ChargeTransactionPayment`] signed extension. -#[derive(Default)] -pub struct ChargeTransactionPaymentParams { - tip: u128, -} - -impl ChargeTransactionPaymentParams { - /// Don't provide a tip to the extrinsic author. - pub fn no_tip() -> Self { - ChargeTransactionPaymentParams { tip: 0 } - } - /// Tip the extrinsic author in the native chain token. - pub fn tip(tip: u128) -> Self { - ChargeTransactionPaymentParams { tip } - } -} - -impl ExtrinsicParams for ChargeTransactionPayment { - type OtherParams = ChargeTransactionPaymentParams; - - fn new>( - _nonce: u64, - _client: Client, - other_params: Self::OtherParams, - ) -> Result { - Ok(ChargeTransactionPayment { - tip: Compact(other_params.tip), - }) - } -} - -impl ExtrinsicParamsEncoder for ChargeTransactionPayment { - fn encode_extra_to(&self, v: &mut Vec) { - self.tip.encode_to(v); - } -} - -impl SignedExtension for ChargeTransactionPayment { - type Decoded = Self; - fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool { - identifier == "ChargeTransactionPayment" - } -} - -/// This accepts a tuple of [`SignedExtension`]s, and will dynamically make use of whichever -/// ones are actually required for the chain in the correct order, ignoring the rest. This -/// is a sensible default, and allows for a single configuration to work across multiple chains. -pub struct AnyOf { - params: Vec>, - _marker: std::marker::PhantomData<(T, Params)>, -} - -macro_rules! impl_tuples { - ($($ident:ident $index:tt),+) => { - // We do some magic when the tuple is wrapped in AnyOf. We - // look at the metadata, and use this to select and make use of only the extensions - // that we actually need for the chain we're dealing with. - impl ExtrinsicParams for AnyOf - where - T: Config, - $($ident: SignedExtension,)+ - { - type OtherParams = ($($ident::OtherParams,)+); - - fn new>( - nonce: u64, - client: Client, - other_params: Self::OtherParams, - ) -> Result { - let metadata = client.metadata(); - let types = metadata.types(); - - // For each signed extension in the tuple, find the matching index in the metadata, if - // there is one, and add it to a map with that index as the key. - let mut exts_by_index = HashMap::new(); - $({ - for (idx, e) in metadata.extrinsic().signed_extensions().iter().enumerate() { - // Skip over any exts that have a match already: - if exts_by_index.contains_key(&idx) { - continue - } - // Break and record as soon as we find a match: - if $ident::matches(e.identifier(), e.extra_ty(), types) { - let ext = $ident::new(nonce, client.clone(), other_params.$index)?; - let boxed_ext: Box = Box::new(ext); - exts_by_index.insert(idx, boxed_ext); - break - } - } - })+ - - // Next, turn these into an ordered vec, erroring if we haven't matched on any exts yet. - let mut params = Vec::new(); - for (idx, e) in metadata.extrinsic().signed_extensions().iter().enumerate() { - let Some(ext) = exts_by_index.remove(&idx) else { - if is_type_empty(e.extra_ty(), types) { - continue - } else { - return Err(ExtrinsicParamsError::UnknownSignedExtension(e.identifier().to_owned())); - } - }; - params.push(ext); - } - - Ok(AnyOf { - params, - _marker: std::marker::PhantomData - }) - } - } - - impl ExtrinsicParamsEncoder for AnyOf - where - T: Config, - $($ident: SignedExtension,)+ - { - fn encode_extra_to(&self, v: &mut Vec) { - for ext in &self.params { - ext.encode_extra_to(v); - } - } - fn encode_additional_to(&self, v: &mut Vec) { - for ext in &self.params { - ext.encode_additional_to(v); - } - } - } - } -} - -#[rustfmt::skip] -const _: () = { - impl_tuples!(A 0); - impl_tuples!(A 0, B 1); - impl_tuples!(A 0, B 1, C 2); - impl_tuples!(A 0, B 1, C 2, D 3); - impl_tuples!(A 0, B 1, C 2, D 3, E 4); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19, V 20); -}; - -/// Checks to see whether the type being given is empty, ie would require -/// 0 bytes to encode. -fn is_type_empty(type_id: u32, types: &scale_info::PortableRegistry) -> bool { - let Some(ty) = types.resolve(type_id) else { - // Can't resolve; type may not be empty. Not expected to hit this. - return false; - }; - - use scale_info::TypeDef; - match &ty.type_def { - TypeDef::Composite(c) => c.fields.iter().all(|f| is_type_empty(f.ty.id, types)), - TypeDef::Array(a) => a.len == 0 || is_type_empty(a.type_param.id, types), - TypeDef::Tuple(t) => t.fields.iter().all(|f| is_type_empty(f.id, types)), - // Explicitly list these in case any additions are made in the future. - TypeDef::BitSequence(_) - | TypeDef::Variant(_) - | TypeDef::Sequence(_) - | TypeDef::Compact(_) - | TypeDef::Primitive(_) => false, - } -} diff --git a/subxt/src/config/substrate.rs b/subxt/src/config/substrate.rs deleted file mode 100644 index 44cfcf7b73b..00000000000 --- a/subxt/src/config/substrate.rs +++ /dev/null @@ -1,278 +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. - -//! Substrate specific configuration - -use super::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder, Hasher, Header}; -use codec::{Decode, Encode}; -use serde::{Deserialize, Serialize}; - -pub use crate::utils::{AccountId32, MultiAddress, MultiSignature}; -pub use primitive_types::{H256, U256}; - -/// Default set of commonly used types by Substrate runtimes. -// Note: We only use this at the type level, so it should be impossible to -// create an instance of it. -pub enum SubstrateConfig {} - -impl Config for SubstrateConfig { - type Hash = H256; - type AccountId = AccountId32; - type Address = MultiAddress; - type Signature = MultiSignature; - type Hasher = BlakeTwo256; - type Header = SubstrateHeader; - type ExtrinsicParams = SubstrateExtrinsicParams; - type AssetId = u32; -} - -/// A struct representing the signed extra and additional parameters required -/// to construct a transaction for the default substrate node. -pub type SubstrateExtrinsicParams = DefaultExtrinsicParams; - -/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed. -/// This is what you provide to methods like `sign_and_submit()`. -pub type SubstrateExtrinsicParamsBuilder = DefaultExtrinsicParamsBuilder; - -/// A type that can hash values using the blaks2_256 algorithm. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode)] -pub struct BlakeTwo256; - -impl Hasher for BlakeTwo256 { - type Output = H256; - fn hash(s: &[u8]) -> Self::Output { - sp_core_hashing::blake2_256(s).into() - } -} - -/// A generic Substrate header type, adapted from `sp_runtime::generic::Header`. -/// The block number and hasher can be configured to adapt this for other nodes. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SubstrateHeader + TryFrom, H: Hasher> { - /// The parent hash. - pub parent_hash: H::Output, - /// The block number. - #[serde( - serialize_with = "serialize_number", - deserialize_with = "deserialize_number" - )] - #[codec(compact)] - pub number: N, - /// The state trie merkle root - pub state_root: H::Output, - /// The merkle root of the extrinsics. - pub extrinsics_root: H::Output, - /// A chain-specific digest of data useful for light clients or referencing auxiliary data. - pub digest: Digest, -} - -impl Header for SubstrateHeader -where - N: Copy + Into + Into + TryFrom + Encode, - H: Hasher + Encode, - SubstrateHeader: Encode + Decode, -{ - type Number = N; - type Hasher = H; - fn number(&self) -> Self::Number { - self.number - } -} - -/// Generic header digest. From `sp_runtime::generic::digest`. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)] -pub struct Digest { - /// A list of digest items. - pub logs: Vec, -} - -/// Digest item that is able to encode/decode 'system' digest items and -/// provide opaque access to other items. From `sp_runtime::generic::digest`. -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum DigestItem { - /// A pre-runtime digest. - /// - /// These are messages from the consensus engine to the runtime, although - /// the consensus engine can (and should) read them itself to avoid - /// code and state duplication. It is erroneous for a runtime to produce - /// these, but this is not (yet) checked. - /// - /// NOTE: the runtime is not allowed to panic or fail in an `on_initialize` - /// call if an expected `PreRuntime` digest is not present. It is the - /// responsibility of a external block verifier to check this. Runtime API calls - /// will initialize the block without pre-runtime digests, so initialization - /// cannot fail when they are missing. - PreRuntime(ConsensusEngineId, Vec), - - /// A message from the runtime to the consensus engine. This should *never* - /// be generated by the native code of any consensus engine, but this is not - /// checked (yet). - Consensus(ConsensusEngineId, Vec), - - /// Put a Seal on it. This is only used by native code, and is never seen - /// by runtimes. - Seal(ConsensusEngineId, Vec), - - /// Some other thing. Unsupported and experimental. - Other(Vec), - - /// An indication for the light clients that the runtime execution - /// environment is updated. - /// - /// Currently this is triggered when: - /// 1. Runtime code blob is changed or - /// 2. `heap_pages` value is changed. - RuntimeEnvironmentUpdated, -} - -// From sp_runtime::generic, DigestItem enum indexes are encoded using this: -#[repr(u32)] -#[derive(Encode, Decode)] -enum DigestItemType { - Other = 0u32, - Consensus = 4u32, - Seal = 5u32, - PreRuntime = 6u32, - RuntimeEnvironmentUpdated = 8u32, -} -impl Encode for DigestItem { - fn encode(&self) -> Vec { - let mut v = Vec::new(); - - match self { - Self::Consensus(val, data) => { - DigestItemType::Consensus.encode_to(&mut v); - (val, data).encode_to(&mut v); - } - Self::Seal(val, sig) => { - DigestItemType::Seal.encode_to(&mut v); - (val, sig).encode_to(&mut v); - } - Self::PreRuntime(val, data) => { - DigestItemType::PreRuntime.encode_to(&mut v); - (val, data).encode_to(&mut v); - } - Self::Other(val) => { - DigestItemType::Other.encode_to(&mut v); - val.encode_to(&mut v); - } - Self::RuntimeEnvironmentUpdated => { - DigestItemType::RuntimeEnvironmentUpdated.encode_to(&mut v); - } - } - - v - } -} -impl Decode for DigestItem { - fn decode(input: &mut I) -> Result { - let item_type: DigestItemType = Decode::decode(input)?; - match item_type { - DigestItemType::PreRuntime => { - let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; - Ok(Self::PreRuntime(vals.0, vals.1)) - } - DigestItemType::Consensus => { - let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; - Ok(Self::Consensus(vals.0, vals.1)) - } - DigestItemType::Seal => { - let vals: (ConsensusEngineId, Vec) = Decode::decode(input)?; - Ok(Self::Seal(vals.0, vals.1)) - } - DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)), - DigestItemType::RuntimeEnvironmentUpdated => Ok(Self::RuntimeEnvironmentUpdated), - } - } -} - -/// Consensus engine unique ID. From `sp_runtime::ConsensusEngineId`. -pub type ConsensusEngineId = [u8; 4]; - -impl serde::Serialize for DigestItem { - fn serialize(&self, seq: S) -> Result - where - S: serde::Serializer, - { - self.using_encoded(|bytes| impl_serde::serialize::serialize(bytes, seq)) - } -} - -impl<'a> serde::Deserialize<'a> for DigestItem { - fn deserialize(de: D) -> Result - where - D: serde::Deserializer<'a>, - { - let r = impl_serde::serialize::deserialize(de)?; - Decode::decode(&mut &r[..]) - .map_err(|e| serde::de::Error::custom(format!("Decode error: {e}"))) - } -} - -fn serialize_number>(val: &T, s: S) -> Result -where - S: serde::Serializer, -{ - let u256: U256 = (*val).into(); - serde::Serialize::serialize(&u256, s) -} - -fn deserialize_number<'a, D, T: TryFrom>(d: D) -> Result -where - D: serde::Deserializer<'a>, -{ - // 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")) -} - -#[cfg(test)] -mod test { - use super::*; - - // Smoldot returns numeric block numbers in the header at the time of writing; - // ensure we can deserialize them properly. - #[test] - fn can_deserialize_numeric_block_number() { - let numeric_block_number_json = r#" - { - "digest": { - "logs": [] - }, - "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "number": 4, - "parentHash": "0xcb2690b2c85ceab55be03fc7f7f5f3857e7efeb7a020600ebd4331e10be2f7a5", - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - "#; - - let header: SubstrateHeader = - serde_json::from_str(numeric_block_number_json).expect("valid block header"); - assert_eq!(header.number(), 4); - } - - // Substrate returns hex block numbers; ensure we can also deserialize those OK. - #[test] - fn can_deserialize_hex_block_number() { - let numeric_block_number_json = r#" - { - "digest": { - "logs": [] - }, - "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "number": "0x04", - "parentHash": "0xcb2690b2c85ceab55be03fc7f7f5f3857e7efeb7a020600ebd4331e10be2f7a5", - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - "#; - - let header: SubstrateHeader = - serde_json::from_str(numeric_block_number_json).expect("valid block header"); - assert_eq!(header.number(), 4); - } -} diff --git a/subxt/src/constants/constant_address.rs b/subxt/src/constants/constant_address.rs deleted file mode 100644 index bac9677ccf4..00000000000 --- a/subxt/src/constants/constant_address.rs +++ /dev/null @@ -1,100 +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::dynamic::DecodedValueThunk; -use derivative::Derivative; -use std::borrow::Cow; -use subxt_core::metadata::DecodeWithMetadata; - -/// This represents a constant address. Anything implementing this trait -/// can be used to fetch constants. -pub trait ConstantAddress { - /// The target type of the value that lives at this address. - type Target: DecodeWithMetadata; - - /// The name of the pallet that the constant lives under. - fn pallet_name(&self) -> &str; - - /// The name of the constant in a given pallet. - fn constant_name(&self) -> &str; - - /// An optional hash which, if present, will be checked against - /// the node metadata to confirm that the return type matches what - /// we are expecting. - fn validation_hash(&self) -> Option<[u8; 32]> { - None - } -} - -/// This represents the address of a constant. -#[derive(Derivative)] -#[derivative(Clone(bound = ""), Debug(bound = ""))] -pub struct Address { - pallet_name: Cow<'static, str>, - constant_name: Cow<'static, str>, - constant_hash: Option<[u8; 32]>, - _marker: std::marker::PhantomData, -} - -/// The type of address typically used to return dynamic constant values. -pub type DynamicAddress = Address; - -impl Address { - /// Create a new [`Address`] to use to look up a constant. - pub fn new(pallet_name: impl Into, constant_name: impl Into) -> Self { - Self { - pallet_name: Cow::Owned(pallet_name.into()), - constant_name: Cow::Owned(constant_name.into()), - constant_hash: None, - _marker: std::marker::PhantomData, - } - } - - /// Create a new [`Address`] that will be validated - /// against node metadata using the hash given. - #[doc(hidden)] - pub fn new_static( - pallet_name: &'static str, - constant_name: &'static str, - hash: [u8; 32], - ) -> Self { - Self { - pallet_name: Cow::Borrowed(pallet_name), - constant_name: Cow::Borrowed(constant_name), - constant_hash: Some(hash), - _marker: std::marker::PhantomData, - } - } - - /// Do not validate this constant prior to accessing it. - pub fn unvalidated(self) -> Self { - Self { - pallet_name: self.pallet_name, - constant_name: self.constant_name, - constant_hash: None, - _marker: self._marker, - } - } -} - -impl ConstantAddress for Address { - type Target = ReturnTy; - - fn pallet_name(&self) -> &str { - &self.pallet_name - } - - fn constant_name(&self) -> &str { - &self.constant_name - } - - fn validation_hash(&self) -> Option<[u8; 32]> { - self.constant_hash - } -} - -/// Construct a new dynamic constant lookup. -pub fn dynamic(pallet_name: impl Into, constant_name: impl Into) -> DynamicAddress { - DynamicAddress::new(pallet_name, constant_name) -} diff --git a/subxt/src/constants/constants_client.rs b/subxt/src/constants/constants_client.rs index 1190811ac71..b3e4b74acac 100644 --- a/subxt/src/constants/constants_client.rs +++ b/subxt/src/constants/constants_client.rs @@ -36,19 +36,7 @@ impl> ConstantsClient { /// 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(&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()); - } - } + subxt_core::constants::validate_constant(&self.client.metadata(), address)?; Ok(()) } @@ -59,23 +47,7 @@ impl> ConstantsClient { &self, address: &Address, ) -> Result { - 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 = ::decode_with_metadata( - &mut constant.value(), - constant.ty(), - &metadata, - )?; + let value = subxt_core::constants::get_constant(&self.client.metadata(), address)?; Ok(value) } } diff --git a/subxt/src/constants/mod.rs b/subxt/src/constants/mod.rs index 9a9ccade84d..a5321e7cb93 100644 --- a/subxt/src/constants/mod.rs +++ b/subxt/src/constants/mod.rs @@ -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}; diff --git a/subxt/src/custom_values/custom_value_address.rs b/subxt/src/custom_values/custom_value_address.rs index d1708eec463..d67b0b0ae53 100644 --- a/subxt/src/custom_values/custom_value_address.rs +++ b/subxt/src/custom_values/custom_value_address.rs @@ -1,8 +1,7 @@ use derivative::Derivative; use std::marker::PhantomData; -use crate::dynamic::DecodedValueThunk; -use subxt_core::metadata::DecodeWithMetadata; +use subxt_core::{dynamic::DecodedValueThunk, metadata::DecodeWithMetadata}; /// This represents the address of a custom value in in the metadata. /// Anything, that implements the [CustomValueAddress] trait can be used, to fetch diff --git a/subxt/src/custom_values/custom_values_client.rs b/subxt/src/custom_values/custom_values_client.rs index b45dd39ce83..4d0939e7111 100644 --- a/subxt/src/custom_values/custom_values_client.rs +++ b/subxt/src/custom_values/custom_values_client.rs @@ -93,7 +93,6 @@ impl> CustomValuesClient { #[cfg(test)] mod tests { - use crate::backend::RuntimeVersion; use crate::custom_values::CustomValuesClient; use crate::{Metadata, OfflineClient, SubstrateConfig}; use codec::Encode; @@ -101,6 +100,7 @@ mod tests { use scale_info::form::PortableForm; use scale_info::TypeInfo; use std::collections::BTreeMap; + use subxt_core::RuntimeVersion; #[derive(Debug, Clone, PartialEq, Eq, Encode, TypeInfo, DecodeAsType)] pub struct Person { diff --git a/subxt/src/error/dispatch_error.rs b/subxt/src/error/dispatch_error.rs index 2dd59da35ba..2c066176743 100644 --- a/subxt/src/error/dispatch_error.rs +++ b/subxt/src/error/dispatch_error.rs @@ -8,7 +8,7 @@ use core::fmt::Debug; use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType}; use std::borrow::Cow; -use subxt_core::metadata::{DecodeWithMetadata, Metadata}; +use subxt_core::metadata::{DecodeWithMetadata, MetadatExt, Metadata}; use super::{Error, MetadataError}; diff --git a/subxt/src/error/mod.rs b/subxt/src/error/mod.rs index 062734dac25..dbfb33dbbed 100644 --- a/subxt/src/error/mod.rs +++ b/subxt/src/error/mod.rs @@ -17,13 +17,12 @@ pub use dispatch_error::{ ArithmeticError, DispatchError, ModuleError, TokenError, TransactionalError, }; -pub use subxt_core::metadata::MetadataError; +pub use subxt_core::MetadataError; // Re-expose the errors we use from other crates here: -pub use crate::config::ExtrinsicParamsError; pub use scale_decode::Error as DecodeError; pub use scale_encode::Error as EncodeError; -pub use subxt_core::metadata::Metadata; +pub use subxt_core::{ExtrinsicParamsError, Metadata}; pub use subxt_metadata::TryFromError as MetadataTryFromError; /// The underlying error enum, generic over the type held by the `Runtime` @@ -46,7 +45,7 @@ pub enum Error { Serialization(#[from] serde_json::error::Error), /// Error working with metadata. #[error("Metadata error: {0}")] - Metadata(#[from] MetadataError), + Metadata(#[from] subxt_core::MetadataError), /// Error decoding metadata. #[error("Metadata Decoding error: {0}")] MetadataDecoding(#[from] MetadataTryFromError), @@ -64,13 +63,13 @@ pub enum Error { Transaction(#[from] TransactionError), /// Error constructing the appropriate extrinsic params. #[error("Extrinsic params error: {0}")] - ExtrinsicParams(#[from] ExtrinsicParamsError), + ExtrinsicParams(#[from] subxt_core::ExtrinsicParamsError), /// Block related error. #[error("Block error: {0}")] Block(#[from] BlockError), /// An error encoding a storage address. #[error("Error encoding storage address: {0}")] - StorageAddress(#[from] StorageAddressError), + StorageAddress(#[from] subxt_core::StorageAddressError), /// The bytes representing an error that we were unable to decode. #[error("An error occurred but it could not be decoded: {0:?}")] Unknown(Vec), @@ -84,6 +83,18 @@ pub enum Error { Other(String), } +impl From for Error { + fn from(value: subxt_core::Error) -> Self { + match value { + 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()) @@ -176,28 +187,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 map type must be a composite type. - #[error("Storage map type must be a composite type")] - MapTypeMustBeTuple, - /// Storage lookup does not have the expected number of keys. - #[error("Storage lookup requires {expected} keys but got {actual} keys")] - WrongNumberOfKeys { - /// The actual number of keys needed, based on the metadata. - actual: usize, - /// 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, - }, -} diff --git a/subxt/src/events/events_type.rs b/subxt/src/events/events_type.rs index 079ce5f2082..9a13d3eac12 100644 --- a/subxt/src/events/events_type.rs +++ b/subxt/src/events/events_type.rs @@ -15,7 +15,7 @@ use codec::{Compact, Decode}; use derivative::Derivative; use scale_decode::DecodeAsType; use std::sync::Arc; -use subxt_core::metadata::types::PalletMetadata; +use subxt_core::metadata::{types::PalletMetadata, MetadatExt}; /// A collection of events obtained from a block, bundled with the necessary /// information needed to decode and iterate over them. diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index e982f2b1388..94fa7697f84 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -45,7 +45,6 @@ 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; @@ -64,10 +63,12 @@ mod macros; // but leave most types behind their respective modules. pub use crate::{ client::{OfflineClient, OnlineClient}, - config::{Config, PolkadotConfig, SubstrateConfig}, error::Error, }; +pub use subxt_core::config; +pub use subxt_core::config::{Config, PolkadotConfig, SubstrateConfig}; + use subxt_core::metadata::Metadata; /// Re-export external crates that are made use of in the subxt API. diff --git a/subxt/src/runtime_api/mod.rs b/subxt/src/runtime_api/mod.rs index 49a17a4dd7f..9eecb129788 100644 --- a/subxt/src/runtime_api/mod.rs +++ b/subxt/src/runtime_api/mod.rs @@ -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}; diff --git a/subxt/src/runtime_api/runtime_payload.rs b/subxt/src/runtime_api/runtime_payload.rs deleted file mode 100644 index 3725fcae22e..00000000000 --- a/subxt/src/runtime_api/runtime_payload.rs +++ /dev/null @@ -1,186 +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 core::marker::PhantomData; -use derivative::Derivative; -use scale_encode::EncodeAsFields; -use scale_value::Composite; -use std::borrow::Cow; - -use crate::dynamic::DecodedValueThunk; -use crate::error::MetadataError; -use crate::Error; - -use subxt_core::metadata::{DecodeWithMetadata, Metadata}; - -/// This represents a runtime API payload that can call into the runtime of node. -/// -/// # Components -/// -/// - associated return type -/// -/// Resulting bytes of the call are interpreted into this type. -/// -/// - runtime function name -/// -/// The function name of the runtime API call. This is obtained by concatenating -/// the runtime trait name with the trait's method. -/// -/// For example, the substrate runtime trait [Metadata](https://github.com/paritytech/substrate/blob/cb954820a8d8d765ce75021e244223a3b4d5722d/primitives/api/src/lib.rs#L745) -/// contains the `metadata_at_version` function. The corresponding runtime function -/// is `Metadata_metadata_at_version`. -/// -/// - encoded arguments -/// -/// Each argument of the runtime function must be scale-encoded. -pub trait RuntimeApiPayload { - /// The return type of the function call. - // Note: `DecodeWithMetadata` is needed to decode the function call result - // with the `subxt::Metadata. - type ReturnType: DecodeWithMetadata; - - /// The runtime API trait name. - fn trait_name(&self) -> &str; - - /// The runtime API method name. - fn method_name(&self) -> &str; - - /// Scale encode the arguments data. - fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error>; - - /// Encode arguments data and return the output. This is a convenience - /// wrapper around [`RuntimeApiPayload::encode_args_to`]. - fn encode_args(&self, metadata: &Metadata) -> Result, Error> { - let mut v = Vec::new(); - self.encode_args_to(metadata, &mut v)?; - Ok(v) - } - - /// Returns the statically generated validation hash. - fn validation_hash(&self) -> Option<[u8; 32]> { - None - } -} - -/// A runtime API payload containing the generic argument data -/// and interpreting the result of the call as `ReturnTy`. -/// -/// 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") -)] -pub struct Payload { - trait_name: Cow<'static, str>, - method_name: Cow<'static, str>, - args_data: ArgsData, - validation_hash: Option<[u8; 32]>, - _marker: PhantomData, -} - -impl RuntimeApiPayload - for Payload -{ - type ReturnType = ReturnTy; - - fn trait_name(&self) -> &str { - &self.trait_name - } - - fn method_name(&self) -> &str { - &self.method_name - } - - fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec) -> Result<(), Error> { - let api_method = metadata - .runtime_api_trait_by_name_err(&self.trait_name)? - .method_by_name(&self.method_name) - .ok_or_else(|| MetadataError::RuntimeMethodNotFound((*self.method_name).to_owned()))?; - let mut fields = api_method - .inputs() - .map(|input| scale_encode::Field::named(input.ty, &input.name)); - - self.args_data - .encode_as_fields_to(&mut fields, metadata.types(), out)?; - Ok(()) - } - - fn validation_hash(&self) -> Option<[u8; 32]> { - self.validation_hash - } -} - -/// A dynamic runtime API payload. -pub type DynamicRuntimeApiPayload = Payload, DecodedValueThunk>; - -impl Payload { - /// Create a new [`Payload`]. - pub fn new( - trait_name: impl Into, - method_name: impl Into, - args_data: ArgsData, - ) -> Self { - Payload { - trait_name: Cow::Owned(trait_name.into()), - method_name: Cow::Owned(method_name.into()), - args_data, - validation_hash: None, - _marker: PhantomData, - } - } - - /// Create a new static [`Payload`] using static function name - /// and scale-encoded argument data. - /// - /// This is only expected to be used from codegen. - #[doc(hidden)] - pub fn new_static( - trait_name: &'static str, - method_name: &'static str, - args_data: ArgsData, - hash: [u8; 32], - ) -> Payload { - Payload { - trait_name: Cow::Borrowed(trait_name), - method_name: Cow::Borrowed(method_name), - args_data, - validation_hash: Some(hash), - _marker: std::marker::PhantomData, - } - } - - /// Do not validate this call prior to submitting it. - pub fn unvalidated(self) -> Self { - Self { - validation_hash: None, - ..self - } - } - - /// Returns the trait name. - pub fn trait_name(&self) -> &str { - &self.trait_name - } - - /// Returns the method name. - pub fn method_name(&self) -> &str { - &self.method_name - } - - /// Returns the arguments data. - pub fn args_data(&self) -> &ArgsData { - &self.args_data - } -} - -/// Create a new [`DynamicRuntimeApiPayload`]. -pub fn dynamic( - trait_name: impl Into, - method_name: impl Into, - args_data: impl Into>, -) -> DynamicRuntimeApiPayload { - Payload::new(trait_name, method_name, args_data.into()) -} diff --git a/subxt/src/runtime_api/runtime_types.rs b/subxt/src/runtime_api/runtime_types.rs index f9247547dbf..0bc518fe6b1 100644 --- a/subxt/src/runtime_api/runtime_types.rs +++ b/subxt/src/runtime_api/runtime_types.rs @@ -11,7 +11,7 @@ use crate::{ use codec::Decode; use derivative::Derivative; use std::{future::Future, marker::PhantomData}; -use subxt_core::metadata::DecodeWithMetadata; +use subxt_core::metadata::{DecodeWithMetadata, MetadatExt}; use super::RuntimeApiPayload; diff --git a/subxt/src/storage/mod.rs b/subxt/src/storage/mod.rs index 0219cd8caf1..6fda23d5e8d 100644 --- a/subxt/src/storage/mod.rs +++ b/subxt/src/storage/mod.rs @@ -4,25 +4,22 @@ //! Types associated with accessing and working with storage items. -mod storage_address; mod storage_client; mod storage_type; -pub mod utils; - pub use storage_client::StorageClient; - pub use storage_type::Storage; /// 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::{ + pub use subxt_core::storage::storage_address::{ dynamic, make_static_storage_map_key, Address, DynamicAddress, StaticStorageMapKey, - StorageAddress, Yes, + StorageAddress, }; + pub use subxt_core::Yes; } // 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 subxt_core::storage::storage_address::{dynamic, Address, DynamicAddress, StorageAddress}; diff --git a/subxt/src/storage/storage_address.rs b/subxt/src/storage/storage_address.rs deleted file mode 100644 index e1a3ef504f9..00000000000 --- a/subxt/src/storage/storage_address.rs +++ /dev/null @@ -1,272 +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::{ - dynamic::DecodedValueThunk, - error::{Error, MetadataError, StorageAddressError}, - utils::{Encoded, Static}, -}; - -use subxt_core::metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata}; - -use derivative::Derivative; -use scale_info::TypeDef; -use std::borrow::Cow; -use subxt_metadata::{StorageEntryType, StorageHasher}; - -/// This represents a storage address. Anything implementing this trait -/// can be used to fetch and iterate over storage entries. -pub trait StorageAddress { - /// The target type of the value that lives at this address. - type Target: DecodeWithMetadata; - /// Can an entry be fetched from this address? - /// Set this type to [`Yes`] to enable the corresponding calls to be made. - type IsFetchable; - /// Can a default entry be obtained from this address? - /// Set this type to [`Yes`] to enable the corresponding calls to be made. - type IsDefaultable; - /// Can this address be iterated over? - /// Set this type to [`Yes`] to enable the corresponding calls to be made. - type IsIterable; - - /// The name of the pallet that the entry lives under. - fn pallet_name(&self) -> &str; - - /// The name of the entry in a given pallet that the item is at. - fn entry_name(&self) -> &str; - - /// Output the non-prefix bytes; that is, any additional bytes that need - /// to be appended to the key to dig into maps. - fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error>; - - /// An optional hash which, if present, will be checked against - /// the node metadata to confirm that the return type matches what - /// we are expecting. - fn validation_hash(&self) -> Option<[u8; 32]> { - None - } -} - -/// 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 = "StorageKey: Clone"), - Debug(bound = "StorageKey: std::fmt::Debug") -)] -pub struct Address { - pallet_name: Cow<'static, str>, - entry_name: Cow<'static, str>, - storage_entry_keys: Vec, - validation_hash: Option<[u8; 32]>, - _marker: std::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>, -} - -/// A typical storage address constructed at runtime rather than via the `subxt` macro; this -/// has no restriction on what it can be used for (since we don't statically know). -pub type DynamicAddress = Address; - -impl - Address -where - StorageKey: EncodeWithMetadata, - ReturnTy: DecodeWithMetadata, -{ - /// Create a new [`Address`] to use to access a storage entry. - pub fn new( - pallet_name: impl Into, - entry_name: impl Into, - storage_entry_keys: Vec, - ) -> Self { - Self { - pallet_name: Cow::Owned(pallet_name.into()), - entry_name: Cow::Owned(entry_name.into()), - storage_entry_keys: storage_entry_keys.into_iter().collect(), - validation_hash: None, - _marker: std::marker::PhantomData, - } - } - - /// Create a new [`Address`] using static strings for the pallet and call name. - /// This is only expected to be used from codegen. - #[doc(hidden)] - pub fn new_static( - pallet_name: &'static str, - entry_name: &'static str, - storage_entry_keys: Vec, - hash: [u8; 32], - ) -> Self { - Self { - pallet_name: Cow::Borrowed(pallet_name), - entry_name: Cow::Borrowed(entry_name), - storage_entry_keys: storage_entry_keys.into_iter().collect(), - validation_hash: Some(hash), - _marker: std::marker::PhantomData, - } - } - - /// Do not validate this storage entry prior to accessing it. - pub fn unvalidated(self) -> Self { - Self { - validation_hash: None, - ..self - } - } - - /// 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. - pub fn to_root_bytes(&self) -> Vec { - super::utils::storage_address_root_bytes(self) - } -} - -impl StorageAddress - for Address -where - StorageKey: EncodeWithMetadata, - ReturnTy: DecodeWithMetadata, -{ - type Target = ReturnTy; - type IsFetchable = Fetchable; - type IsDefaultable = Defaultable; - type IsIterable = Iterable; - - fn pallet_name(&self) -> &str { - &self.pallet_name - } - - fn entry_name(&self) -> &str { - &self.entry_name - } - - fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error> { - let pallet = metadata.pallet_by_name_err(self.pallet_name())?; - let storage = pallet - .storage() - .ok_or_else(|| MetadataError::StorageNotFoundInPallet(self.pallet_name().to_owned()))?; - let entry = storage - .entry_by_name(self.entry_name()) - .ok_or_else(|| MetadataError::StorageEntryNotFound(self.entry_name().to_owned()))?; - - match entry.entry_type() { - StorageEntryType::Plain(_) => { - if !self.storage_entry_keys.is_empty() { - Err(StorageAddressError::WrongNumberOfKeys { - expected: 0, - actual: self.storage_entry_keys.len(), - } - .into()) - } else { - Ok(()) - } - } - StorageEntryType::Map { - hashers, key_ty, .. - } => { - let ty = metadata - .types() - .resolve(*key_ty) - .ok_or(MetadataError::TypeNotFound(*key_ty))?; - - // If the provided keys are empty, the storage address must be - // equal to the storage root address. - if self.storage_entry_keys.is_empty() { - return Ok(()); - } - - // If the key is a tuple, we encode each value to the corresponding tuple type. - // If the key is not a tuple, encode a single value to the key type. - let type_ids = match &ty.type_def { - TypeDef::Tuple(tuple) => { - either::Either::Left(tuple.fields.iter().map(|f| f.id)) - } - _other => either::Either::Right(std::iter::once(*key_ty)), - }; - - if type_ids.len() < self.storage_entry_keys.len() { - // Provided more keys than fields. - return Err(StorageAddressError::WrongNumberOfKeys { - expected: type_ids.len(), - actual: self.storage_entry_keys.len(), - } - .into()); - } - - if hashers.len() == 1 { - // One hasher; hash a tuple of all SCALE encoded bytes with the one hash function. - let mut input = Vec::new(); - let iter = self.storage_entry_keys.iter().zip(type_ids); - for (key, type_id) in iter { - key.encode_with_metadata(type_id, metadata, &mut input)?; - } - hash_bytes(&input, &hashers[0], bytes); - Ok(()) - } else if hashers.len() >= type_ids.len() { - let iter = self.storage_entry_keys.iter().zip(type_ids).zip(hashers); - // A hasher per field; encode and hash each field independently. - for ((key, type_id), hasher) in iter { - let mut input = Vec::new(); - key.encode_with_metadata(type_id, metadata, &mut input)?; - hash_bytes(&input, hasher, bytes); - } - Ok(()) - } else { - // Provided more fields than hashers. - Err(StorageAddressError::WrongNumberOfHashers { - hashers: hashers.len(), - fields: type_ids.len(), - } - .into()) - } - } - } - } - - fn validation_hash(&self) -> Option<[u8; 32]> { - self.validation_hash - } -} - -/// A static storage key; this is some pre-encoded bytes -/// likely provided by the generated interface. -pub type StaticStorageMapKey = Static; - -// Used in codegen to construct the above. -#[doc(hidden)] -pub fn make_static_storage_map_key(t: T) -> StaticStorageMapKey { - Static(Encoded(t.encode())) -} - -/// Construct a new dynamic storage lookup. -pub fn dynamic( - pallet_name: impl Into, - entry_name: impl Into, - storage_entry_keys: Vec, -) -> DynamicAddress { - DynamicAddress::new(pallet_name, entry_name, storage_entry_keys) -} - -/// Take some SCALE encoded bytes and a [`StorageHasher`] and hash the bytes accordingly. -fn hash_bytes(input: &[u8], hasher: &StorageHasher, bytes: &mut Vec) { - match hasher { - StorageHasher::Identity => bytes.extend(input), - StorageHasher::Blake2_128 => bytes.extend(sp_core_hashing::blake2_128(input)), - StorageHasher::Blake2_128Concat => { - bytes.extend(sp_core_hashing::blake2_128(input)); - bytes.extend(input); - } - StorageHasher::Blake2_256 => bytes.extend(sp_core_hashing::blake2_256(input)), - StorageHasher::Twox128 => bytes.extend(sp_core_hashing::twox_128(input)), - StorageHasher::Twox256 => bytes.extend(sp_core_hashing::twox_256(input)), - StorageHasher::Twox64Concat => { - bytes.extend(sp_core_hashing::twox_64(input)); - bytes.extend(input); - } - } -} diff --git a/subxt/src/storage/storage_client.rs b/subxt/src/storage/storage_client.rs index 55ab339aeab..7b62ed6b9af 100644 --- a/subxt/src/storage/storage_client.rs +++ b/subxt/src/storage/storage_client.rs @@ -2,10 +2,7 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use super::{ - storage_type::{validate_storage_address, Storage}, - utils, StorageAddress, -}; +use super::{storage_type::Storage, StorageAddress}; use crate::{ backend::BlockRef, client::{OfflineClientT, OnlineClientT}, @@ -14,6 +11,10 @@ use crate::{ }; use derivative::Derivative; use std::{future::Future, marker::PhantomData}; +use subxt_core::{ + metadata::MetadatExt, + storage::utils::{storage_address_bytes, storage_address_root_bytes, validate_storage_address}, +}; /// Query the runtime storage. #[derive(Derivative)] @@ -45,13 +46,14 @@ where pub fn validate(&self, address: &Address) -> Result<(), Error> { let metadata = self.client.metadata(); let pallet_metadata = metadata.pallet_by_name_err(address.pallet_name())?; - validate_storage_address(address, pallet_metadata) + validate_storage_address(address, pallet_metadata)?; + Ok(()) } /// 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(&self, address: &Address) -> Vec { - utils::storage_address_root_bytes(address) + storage_address_root_bytes(address) } /// Convert some storage address into the raw bytes that would be submitted to the node in order @@ -63,7 +65,8 @@ where &self, address: &Address, ) -> Result, Error> { - utils::storage_address_bytes(address, &self.client.metadata()) + let bytes = storage_address_bytes(address, &self.client.metadata())?; + Ok(bytes) } } diff --git a/subxt/src/storage/storage_type.rs b/subxt/src/storage/storage_type.rs index d31991c6adf..51cc4cac189 100644 --- a/subxt/src/storage/storage_type.rs +++ b/subxt/src/storage/storage_type.rs @@ -2,7 +2,13 @@ // 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 subxt_core::{ + storage::utils::{ + decode_storage_with_metadata, lookup_entry_details, storage_address_bytes, + validate_storage_address, + }, + StorageAddress, Yes, +}; use crate::{ backend::{BackendExt, BlockRef}, @@ -14,8 +20,7 @@ use codec::Decode; use derivative::Derivative; use futures::StreamExt; use std::{future::Future, marker::PhantomData}; -use subxt_core::metadata::{DecodeWithMetadata, Metadata}; -use subxt_metadata::{PalletMetadata, StorageEntryMetadata, StorageEntryType}; +use subxt_core::metadata::DecodeWithMetadata; /// This is returned from a couple of storage functions. pub use crate::backend::StreamOfResults; @@ -132,7 +137,7 @@ 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 = storage_address_bytes(address, &metadata)?; if let Some(data) = client.fetch_raw(lookup_bytes).await? { let val = decode_storage_with_metadata::(&mut &*data, &metadata, entry)?; @@ -163,7 +168,7 @@ where let (_pallet_metadata, storage_entry) = lookup_entry_details(pallet_name, entry_name, &metadata)?; - let return_ty_id = return_type_from_storage_entry_type(storage_entry.entry_type()); + let return_ty_id = storage_entry.entry_type().value_ty(); let bytes = &mut storage_entry.default_bytes(); let val = Address::Target::decode_with_metadata(bytes, return_ty_id, &metadata)?; @@ -226,10 +231,10 @@ where // Look up the return type for flexible decoding. Do this once here to avoid // potentially doing it every iteration if we used `decode_storage_with_metadata` // in the iterator. - let return_type_id = return_type_from_storage_entry_type(entry.entry_type()); + let return_type_id = entry.entry_type().value_ty(); // The address bytes of this entry: - let address_bytes = super::utils::storage_address_bytes(&address, &metadata)?; + let address_bytes = storage_address_bytes(&address, &metadata)?; let s = client .backend() diff --git a/subxt/src/storage/utils.rs b/subxt/src/storage/utils.rs deleted file mode 100644 index 33581a32610..00000000000 --- a/subxt/src/storage/utils.rs +++ /dev/null @@ -1,41 +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. - -//! 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 super::StorageAddress; -use crate::error::Error; - -use subxt_core::metadata::Metadata; - -/// Return the root of a given [`StorageAddress`]: hash the pallet name and entry name -/// and append those bytes to the output. -pub(crate) fn write_storage_address_root_bytes( - addr: &Address, - out: &mut Vec, -) { - out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes())); - out.extend(sp_core_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(crate) fn storage_address_bytes( - addr: &Address, - metadata: &Metadata, -) -> Result, 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(crate) fn storage_address_root_bytes(addr: &Address) -> Vec { - let mut bytes = Vec::new(); - write_storage_address_root_bytes(addr, &mut bytes); - bytes -} diff --git a/subxt/src/tx/mod.rs b/subxt/src/tx/mod.rs index 0bb2d9f3ebd..655198712e3 100644 --- a/subxt/src/tx/mod.rs +++ b/subxt/src/tx/mod.rs @@ -11,23 +11,17 @@ use crate::macros::cfg_substrate_compat; -mod signer; mod tx_client; -mod tx_payload; mod tx_progress; -// 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 self::{ - signer::Signer, tx_client::{ PartialExtrinsic, SubmittableExtrinsic, TransactionInvalid, TransactionUnknown, TxClient, ValidationResult, }, - tx_payload::{dynamic, BoxedPayload, DynamicPayload, Payload, TxPayload}, tx_progress::{TxInBlock, TxProgress, TxStatus}, }; +pub use subxt_core::{ + signer::Signer, + tx::{dynamic, BoxedPayload, DynamicPayload, Payload, TxPayload}, +}; diff --git a/subxt/src/tx/tx_client.rs b/subxt/src/tx/tx_client.rs index 6bf6c9ab57f..d3121d9eb73 100644 --- a/subxt/src/tx/tx_client.rs +++ b/subxt/src/tx/tx_client.rs @@ -9,12 +9,14 @@ use crate::{ client::{OfflineClientT, OnlineClientT}, config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher}, error::{Error, MetadataError}, - tx::{Signer as SignerT, TxPayload, TxProgress}, utils::{Encoded, PhantomDataSendSync}, }; use codec::{Compact, Decode, Encode}; use derivative::Derivative; use sp_core_hashing::blake2_256; +use subxt_core::{metadata::MetadatExt, tx::TxPayload, Signer as SignerT}; + +use super::TxProgress; /// A client for working with transactions. #[derive(Derivative)] @@ -122,7 +124,7 @@ impl> TxClient { // 3. Construct our custom additional/extra params. let additional_and_extra_params = >::new( account_nonce, - self.client.clone(), + &self.client.base(), other_params, )?; diff --git a/subxt/src/tx/tx_progress.rs b/subxt/src/tx/tx_progress.rs index 732c7387523..166b2cda330 100644 --- a/subxt/src/tx/tx_progress.rs +++ b/subxt/src/tx/tx_progress.rs @@ -337,7 +337,7 @@ mod test { struct MockClient; impl OfflineClientT for MockClient { - fn metadata(&self) -> subxt_core::metadata { + fn metadata(&self) -> subxt_core::Metadata { unimplemented!("just a mock impl to satisfy trait bounds") } @@ -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) -> subxt_core::RuntimeVersion { + unimplemented!("just a mock impl to satisfy trait bounds") + } + + fn base(&self) -> subxt_core::ClientBase { unimplemented!("just a mock impl to satisfy trait bounds") } } From fce85c4af017757bc17990b1d8beba7d69e5639c Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 16:50:41 +0100 Subject: [PATCH 20/37] start fixing examples and cleaning up interface --- core/src/config/extrinsic_params.rs | 7 ++--- core/src/config/signed_extensions.rs | 18 +++++------ core/src/config/substrate.rs | 6 +--- core/src/constants/mod.rs | 2 +- core/src/custom_values/mod.rs | 2 +- core/src/lib.rs | 8 +---- core/src/utils/mod.rs | 8 +++++ subxt/examples/setup_client_offline.rs | 2 +- subxt/examples/setup_config_custom.rs | 3 +- .../examples/setup_config_signed_extension.rs | 8 ++--- subxt/src/backend/legacy/rpc_methods.rs | 7 +---- subxt/src/backend/unstable/rpc_methods.rs | 5 +-- subxt/src/client/light_client/mod.rs | 12 ++++--- subxt/src/client/online_client.rs | 2 +- subxt/src/constants/constants_client.rs | 7 +---- subxt/src/dynamic.rs | 31 ------------------- subxt/src/lib.rs | 9 +++--- subxt/src/macros.rs | 2 +- subxt/src/tx/mod.rs | 2 -- subxt/src/tx/tx_client.rs | 2 +- 20 files changed, 48 insertions(+), 95 deletions(-) delete mode 100644 subxt/src/dynamic.rs diff --git a/core/src/config/extrinsic_params.rs b/core/src/config/extrinsic_params.rs index 5965843c5bb..5e7b702a78a 100644 --- a/core/src/config/extrinsic_params.rs +++ b/core/src/config/extrinsic_params.rs @@ -11,11 +11,8 @@ use crate::client::ClientBase; use super::Config; use crate::ExtrinsicParamsError; -use alloc::string::String; -use alloc::vec::Vec; -use core::fmt::Debug; -use derive_more::Display; +use alloc::vec::Vec; /// This trait allows you to configure the "signed extra" and /// "additional" parameters that are a part of the transaction payload @@ -29,7 +26,7 @@ pub trait ExtrinsicParams: ExtrinsicParamsEncoder + Sized + 'static { /// Construct a new instance of our [`ExtrinsicParams`]. fn new( nonce: u64, - client: &ClientBase, + client: ClientBase, other_params: Self::OtherParams, ) -> Result; } diff --git a/core/src/config/signed_extensions.rs b/core/src/config/signed_extensions.rs index 79a8f027d68..e42e8e2d1df 100644 --- a/core/src/config/signed_extensions.rs +++ b/core/src/config/signed_extensions.rs @@ -45,7 +45,7 @@ impl ExtrinsicParams for CheckSpecVersion { fn new( _nonce: u64, - client: &ClientBase, + client: ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CheckSpecVersion(client.runtime_version().spec_version)) @@ -73,7 +73,7 @@ impl ExtrinsicParams for CheckNonce { fn new( nonce: u64, - _client: &ClientBase, + _client: ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CheckNonce(Compact(nonce))) @@ -101,7 +101,7 @@ impl ExtrinsicParams for CheckTxVersion { fn new( _nonce: u64, - client: &ClientBase, + client: ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CheckTxVersion(client.runtime_version().transaction_version)) @@ -129,7 +129,7 @@ impl ExtrinsicParams for CheckGenesis { fn new( _nonce: u64, - client: &ClientBase, + client: ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CheckGenesis(client.genesis_hash())) @@ -195,7 +195,7 @@ impl ExtrinsicParams for CheckMortality { fn new( _nonce: u64, - client: &ClientBase, + client: ClientBase, other_params: Self::OtherParams, ) -> Result { Ok(CheckMortality { @@ -286,7 +286,7 @@ impl ExtrinsicParams for ChargeAssetTxPayment { fn new( _nonce: u64, - _client: &ClientBase, + _client: ClientBase, other_params: Self::OtherParams, ) -> Result { Ok(ChargeAssetTxPayment { @@ -344,7 +344,7 @@ impl ExtrinsicParams for ChargeTransactionPayment { fn new( _nonce: u64, - _client: &ClientBase, + _client: ClientBase, other_params: Self::OtherParams, ) -> Result { Ok(ChargeTransactionPayment { @@ -388,7 +388,7 @@ macro_rules! impl_tuples { fn new( nonce: u64, - client: &ClientBase, + client: ClientBase, other_params: Self::OtherParams, ) -> Result { let metadata = client.metadata(); @@ -405,7 +405,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(nonce, client, other_params.$index)?; + let ext = $ident::new(nonce, client.clone(), other_params.$index)?; let boxed_ext: Box = Box::new(ext); exts_by_index.insert(idx, boxed_ext); break diff --git a/core/src/config/substrate.rs b/core/src/config/substrate.rs index a7c5e992159..30b333c2e7f 100644 --- a/core/src/config/substrate.rs +++ b/core/src/config/substrate.rs @@ -6,7 +6,6 @@ use super::{Config, DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder, Hasher, Header}; pub use crate::utils::{AccountId32, MultiAddress, MultiSignature}; -use alloc::string::String; use alloc::vec::Vec; use codec::{Decode, Encode}; pub use primitive_types::{H256, U256}; @@ -289,10 +288,7 @@ impl From for NumberOrHex { } } -/// A quick helper to encode some bytes to hex. -fn to_hex(bytes: impl AsRef<[u8]>) -> String { - alloc::format!("0x{}", hex::encode(bytes.as_ref())) -} + #[cfg(test)] mod test { diff --git a/core/src/constants/mod.rs b/core/src/constants/mod.rs index 607877c0018..2d98e0e2a7c 100644 --- a/core/src/constants/mod.rs +++ b/core/src/constants/mod.rs @@ -52,7 +52,7 @@ pub fn get_constant( let value = ::decode_with_metadata( &mut constant.value(), constant.ty(), - &metadata, + metadata, )?; Ok(value) } diff --git a/core/src/custom_values/mod.rs b/core/src/custom_values/mod.rs index d0d418bc0e3..9a58c94db17 100644 --- a/core/src/custom_values/mod.rs +++ b/core/src/custom_values/mod.rs @@ -51,7 +51,7 @@ pub fn get_custom_value + ?Sized> let value = ::decode_with_metadata( &mut custom_value.bytes(), custom_value.type_id(), - &metadata, + metadata, )?; Ok(value) } diff --git a/core/src/lib.rs b/core/src/lib.rs index e071e28c20b..76239d19d7a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -33,14 +33,8 @@ pub use config::{ pub use metadata::Metadata; pub use signer::Signer; pub use storage::StorageAddress; -pub use utils::{AccountId32, MultiAddress, MultiSignature, H160, H256, H512}; +pub use utils::{AccountId32, MultiAddress, MultiSignature, H160, H256, H512, Yes, to_hex}; #[macro_use] mod macros; -mod marker { - /// A unit marker struct signalling that some property is true - pub struct Yes; -} - -pub use marker::Yes; diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index 54eaf230ed2..1d0f2540753 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -78,3 +78,11 @@ unsafe impl Sync for PhantomDataSendSync {} /// 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 = 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 { + alloc::format!("0x{}", hex::encode(bytes.as_ref())) +} \ No newline at end of file diff --git a/subxt/examples/setup_client_offline.rs b/subxt/examples/setup_client_offline.rs index 70afc0a41cb..b2e952c2fd3 100644 --- a/subxt/examples/setup_client_offline.rs +++ b/subxt/examples/setup_client_offline.rs @@ -16,7 +16,7 @@ async fn main() -> Result<(), Box> { }; // 2. A runtime version (system_version constant on a Substrate node has these): - let runtime_version = subxt::backend::RuntimeVersion { + let runtime_version = subxt_core::RuntimeVersion { spec_version: 9370, transaction_version: 20, }; diff --git a/subxt/examples/setup_config_custom.rs b/subxt/examples/setup_config_custom.rs index 4748202e1f5..713145d6dc9 100644 --- a/subxt/examples/setup_config_custom.rs +++ b/subxt/examples/setup_config_custom.rs @@ -1,7 +1,8 @@ #![allow(missing_docs)] use codec::Encode; use subxt::client::OfflineClientT; -use subxt::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; +use subxt::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder}; +use subxt_core::error::ExtrinsicParamsError; use subxt_signer::sr25519::dev; #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale")] diff --git a/subxt/examples/setup_config_signed_extension.rs b/subxt/examples/setup_config_signed_extension.rs index 2442de629bf..a5492ff91c9 100644 --- a/subxt/examples/setup_config_signed_extension.rs +++ b/subxt/examples/setup_config_signed_extension.rs @@ -3,11 +3,11 @@ use codec::Encode; use scale_encode::EncodeAsType; use scale_info::PortableRegistry; use subxt::client::OfflineClientT; -use subxt::config::signed_extensions; use subxt::config::{ Config, DefaultExtrinsicParamsBuilder, ExtrinsicParams, ExtrinsicParamsEncoder, - ExtrinsicParamsError, }; +use subxt_core::config::signed_extensions; +use subxt_core::error::ExtrinsicParamsError; use subxt_signer::sr25519::dev; #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")] @@ -60,9 +60,9 @@ impl signed_extensions::SignedExtension for CustomSignedExtension impl ExtrinsicParams for CustomSignedExtension { type OtherParams = (); - fn new>( + fn new( _nonce: u64, - _client: Client, + _client: subxt_core::ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CustomSignedExtension) diff --git a/subxt/src/backend/legacy/rpc_methods.rs b/subxt/src/backend/legacy/rpc_methods.rs index 84a8200adb9..616cc0adcaf 100644 --- a/subxt/src/backend/legacy/rpc_methods.rs +++ b/subxt/src/backend/legacy/rpc_methods.rs @@ -10,7 +10,7 @@ use codec::Decode; use derivative::Derivative; use primitive_types::U256; use serde::{Deserialize, Serialize}; -use subxt_core::metadata::Metadata; +use subxt_core::{metadata::Metadata, to_hex}; /// 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 @@ -656,11 +656,6 @@ impl From for NumberOrHex { } } -/// A quick helper to encode some bytes to hex. -fn to_hex(bytes: impl AsRef<[u8]>) -> String { - format!("0x{}", hex::encode(bytes.as_ref())) -} - /// Hex-serialized shim for `Vec`. #[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Hash, PartialOrd, Ord, Debug)] pub struct Bytes(#[serde(with = "impl_serde::serialize")] pub Vec); diff --git a/subxt/src/backend/unstable/rpc_methods.rs b/subxt/src/backend/unstable/rpc_methods.rs index c51e5020cb7..a397d2fbdb2 100644 --- a/subxt/src/backend/unstable/rpc_methods.rs +++ b/subxt/src/backend/unstable/rpc_methods.rs @@ -14,6 +14,7 @@ use futures::{Stream, StreamExt}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; use std::task::Poll; +use subxt_core::to_hex; /// 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 @@ -739,10 +740,6 @@ impl From> for Bytes { } } -fn to_hex(bytes: impl AsRef<[u8]>) -> String { - format!("0x{}", hex::encode(bytes.as_ref())) -} - /// Attempt to deserialize either a string or integer into an integer. /// See pub(crate) mod unsigned_number_as_string { diff --git a/subxt/src/client/light_client/mod.rs b/subxt/src/client/light_client/mod.rs index cc8c0229763..15846355e82 100644 --- a/subxt/src/client/light_client/mod.rs +++ b/subxt/src/client/light_client/mod.rs @@ -118,7 +118,7 @@ impl LightClient { // traits to use these things: /// Return the [`subxt_core::metadata`] used in this client. - fn metadata(&self) -> subxt_core::metadata { + fn metadata(&self) -> subxt_core::Metadata { self.client.metadata() } @@ -128,7 +128,7 @@ impl LightClient { } /// Return the runtime version. - fn runtime_version(&self) -> crate::backend::RuntimeVersion { + fn runtime_version(&self) -> subxt_core::RuntimeVersion { self.client.runtime_version() } @@ -180,7 +180,7 @@ impl OnlineClientT for LightClient { } impl OfflineClientT for LightClient { - fn metadata(&self) -> subxt_core::metadata { + fn metadata(&self) -> subxt_core::Metadata { self.metadata() } @@ -188,7 +188,11 @@ impl OfflineClientT for LightClient { self.genesis_hash() } - fn runtime_version(&self) -> crate::backend::RuntimeVersion { + fn runtime_version(&self) -> subxt_core::RuntimeVersion { self.runtime_version() } + + fn base(&self) -> subxt_core::ClientBase { + self.client.base() + } } diff --git a/subxt/src/client/online_client.rs b/subxt/src/client/online_client.rs index 36a742ee3f5..8fbc9c821e8 100644 --- a/subxt/src/client/online_client.rs +++ b/subxt/src/client/online_client.rs @@ -17,7 +17,7 @@ use crate::{ }; use derivative::Derivative; use futures::future; -use std::borrow::Borrow; + use std::sync::{Arc, RwLock}; use subxt_core::{ClientBase, RuntimeVersion}; diff --git a/subxt/src/constants/constants_client.rs b/subxt/src/constants/constants_client.rs index b3e4b74acac..4af0fb96ce0 100644 --- a/subxt/src/constants/constants_client.rs +++ b/subxt/src/constants/constants_client.rs @@ -3,14 +3,9 @@ // see LICENSE for license details. use super::ConstantAddress; -use crate::{ - client::OfflineClientT, - error::{Error, MetadataError}, - Config, -}; +use crate::{client::OfflineClientT, error::Error, Config}; use derivative::Derivative; -use subxt_core::metadata::DecodeWithMetadata; /// A client for accessing constants. #[derive(Derivative)] diff --git a/subxt/src/dynamic.rs b/subxt/src/dynamic.rs deleted file mode 100644 index 771815825ab..00000000000 --- a/subxt/src/dynamic.rs +++ /dev/null @@ -1,31 +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. - -//! This module provides the entry points to create dynamic -//! transactions, storage and constant lookups. - -use crate::error::Error; - -use scale_decode::DecodeAsType; -use subxt_core::metadata::{DecodeWithMetadata, Metadata}; - -pub use scale_value::{At, Value}; - -/// A [`scale_value::Value`] type endowed with contextual information -/// regarding what type was used to decode each part of it. This implements -/// [`subxt_core::metadata::DecodeWithMetadata`], and is used as a return type -/// for dynamic requests. -pub type DecodedValue = scale_value::Value; - -// Submit dynamic transactions. -pub use crate::tx::dynamic as tx; - -// Lookup constants dynamically. -pub use crate::constants::dynamic as constant; - -// Lookup storage values dynamically. -pub use crate::storage::dynamic as storage; - -// Execute runtime API function call dynamically. -pub use crate::runtime_api::dynamic as runtime_api_call; diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index 94fa7697f84..723a363a1f5 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -47,7 +47,6 @@ pub mod blocks; pub mod client; pub mod constants; pub mod custom_values; -pub mod dynamic; pub mod error; pub mod events; pub mod runtime_api; @@ -66,10 +65,10 @@ pub use crate::{ error::Error, }; -pub use subxt_core::config; -pub use subxt_core::config::{Config, PolkadotConfig, SubstrateConfig}; - -use subxt_core::metadata::Metadata; +// pub use subxt_core::config; +// pub use subxt_core::config::{Config, PolkadotConfig, SubstrateConfig}; +// pub use subxt_core::dynamic; +pub use subxt_core::*; /// Re-export external crates that are made use of in the subxt API. pub mod ext { diff --git a/subxt/src/macros.rs b/subxt/src/macros.rs index a8d86ada69b..4b2e91766e4 100644 --- a/subxt/src/macros.rs +++ b/subxt/src/macros.rs @@ -52,7 +52,7 @@ macro_rules! cfg_jsonrpsee_web { } } -pub(crate) use {cfg_feature, cfg_jsonrpsee, cfg_substrate_compat, cfg_unstable_light_client}; +pub(crate) use {cfg_feature, cfg_jsonrpsee, cfg_unstable_light_client}; // Only used by light-client. #[allow(unused)] diff --git a/subxt/src/tx/mod.rs b/subxt/src/tx/mod.rs index 655198712e3..b1e988c40d1 100644 --- a/subxt/src/tx/mod.rs +++ b/subxt/src/tx/mod.rs @@ -9,8 +9,6 @@ //! additional and signed extra parameters are used when constructing an extrinsic, and is a part //! of the chain configuration (see [`crate::config::Config`]). -use crate::macros::cfg_substrate_compat; - mod tx_client; mod tx_progress; diff --git a/subxt/src/tx/tx_client.rs b/subxt/src/tx/tx_client.rs index d3121d9eb73..8029781f910 100644 --- a/subxt/src/tx/tx_client.rs +++ b/subxt/src/tx/tx_client.rs @@ -124,7 +124,7 @@ impl> TxClient { // 3. Construct our custom additional/extra params. let additional_and_extra_params = >::new( account_nonce, - &self.client.base(), + self.client.base(), other_params, )?; From e5ee97148a74c6c2ce7b71b598bb71b995ba4e9b Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 16:54:12 +0100 Subject: [PATCH 21/37] discover linker errors for subxt-signer critical section of once_cell --- core/src/utils/mod.rs | 2 ++ testing/no-std-tests/src/main.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index 1d0f2540753..dc679e85e46 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -6,6 +6,7 @@ use alloc::borrow::ToOwned; use alloc::vec::Vec; +use alloc::string::String; use codec::{Compact, Decode, Encode}; use derivative::Derivative; @@ -26,6 +27,7 @@ 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}; diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 9b1dd79e60c..8f8eace2118 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -36,6 +36,7 @@ extern crate alloc; // Note: Panics in this function will lead to `Aborted (core dumped)` and a non-zero exit status => suitable for CI tests. fn run_tests() { subxt_metadata_test(); + subxt_signer_test(); subxt_core_test(); } From bc6cd9b8855e22e8e4991e4e60c5a8723cea0909 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 2 Feb 2024 17:02:00 +0100 Subject: [PATCH 22/37] fmt for whole workspace --- core/src/config/substrate.rs | 2 - core/src/lib.rs | 3 +- core/src/utils/mod.rs | 11 +- testing/no-std-tests/tree.txt | 215 ---------------------------------- 4 files changed, 6 insertions(+), 225 deletions(-) delete mode 100644 testing/no-std-tests/tree.txt diff --git a/core/src/config/substrate.rs b/core/src/config/substrate.rs index 30b333c2e7f..0d0f485279e 100644 --- a/core/src/config/substrate.rs +++ b/core/src/config/substrate.rs @@ -288,8 +288,6 @@ impl From for NumberOrHex { } } - - #[cfg(test)] mod test { use super::*; diff --git a/core/src/lib.rs b/core/src/lib.rs index 76239d19d7a..455d0b8b07b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -33,8 +33,7 @@ pub use config::{ pub use metadata::Metadata; pub use signer::Signer; pub use storage::StorageAddress; -pub use utils::{AccountId32, MultiAddress, MultiSignature, H160, H256, H512, Yes, to_hex}; +pub use utils::{to_hex, AccountId32, MultiAddress, MultiSignature, Yes, H160, H256, H512}; #[macro_use] mod macros; - diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index dc679e85e46..ff0fb1af1e5 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -5,8 +5,8 @@ //! Miscellaneous utility helpers. use alloc::borrow::ToOwned; -use alloc::vec::Vec; use alloc::string::String; +use alloc::vec::Vec; use codec::{Compact, Decode, Encode}; use derivative::Derivative; @@ -27,7 +27,6 @@ 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}; @@ -81,10 +80,10 @@ unsafe impl Sync for PhantomDataSendSync {} /// as `BTreeMap` which allows us to easily swap the two during codegen. pub type KeyedVec = Vec<(K, V)>; - /// A unit marker struct signalling that some property is true - pub struct Yes; +/// A unit marker struct signalling that some property is true +pub struct Yes; - /// A quick helper to encode some bytes to hex. +/// A quick helper to encode some bytes to hex. pub fn to_hex(bytes: impl AsRef<[u8]>) -> String { alloc::format!("0x{}", hex::encode(bytes.as_ref())) -} \ No newline at end of file +} diff --git a/testing/no-std-tests/tree.txt b/testing/no-std-tests/tree.txt deleted file mode 100644 index 613fae578be..00000000000 --- a/testing/no-std-tests/tree.txt +++ /dev/null @@ -1,215 +0,0 @@ -subxt-core-no-std-tests v0.0.0 (/home/tadeo/code/subxt2/testing/no-std-tests) -├── libc v0.2.152 -├── libc_alloc v1.0.6 -├── parity-scale-codec v3.6.9 -│ ├── arrayvec v0.7.4 -│ ├── byte-slice-cast v1.2.2 -│ ├── impl-trait-for-tuples v0.2.2 (proc-macro) -│ │ ├── proc-macro2 v1.0.78 -│ │ │ └── unicode-ident v1.0.12 -│ │ ├── quote v1.0.35 -│ │ │ └── proc-macro2 v1.0.78 (*) -│ │ └── syn v1.0.109 -│ │ ├── proc-macro2 v1.0.78 (*) -│ │ ├── quote v1.0.35 (*) -│ │ └── unicode-ident v1.0.12 -│ ├── parity-scale-codec-derive v3.6.9 (proc-macro) -│ │ ├── proc-macro-crate v2.0.1 -│ │ │ ├── toml_datetime v0.6.3 -│ │ │ └── toml_edit v0.20.2 -│ │ │ ├── indexmap v2.1.0 -│ │ │ │ ├── equivalent v1.0.1 -│ │ │ │ └── hashbrown v0.14.3 -│ │ │ ├── toml_datetime v0.6.3 -│ │ │ └── winnow v0.5.34 -│ │ ├── proc-macro2 v1.0.78 (*) -│ │ ├── quote v1.0.35 (*) -│ │ └── syn v1.0.109 (*) -│ └── serde v1.0.196 -│ └── serde_derive v1.0.196 (proc-macro) -│ ├── proc-macro2 v1.0.78 (*) -│ ├── quote v1.0.35 (*) -│ └── syn v2.0.48 -│ ├── proc-macro2 v1.0.78 (*) -│ ├── quote v1.0.35 (*) -│ └── unicode-ident v1.0.12 -├── subxt-core v0.34.0 (/home/tadeo/code/subxt2/core) -│ ├── base58 v0.2.0 -│ ├── blake2 v0.10.6 -│ │ └── digest v0.10.7 -│ │ ├── block-buffer v0.10.4 -│ │ │ └── generic-array v0.14.7 -│ │ │ └── typenum v1.17.0 -│ │ │ [build-dependencies] -│ │ │ └── version_check v0.9.4 -│ │ ├── crypto-common v0.1.6 -│ │ │ ├── generic-array v0.14.7 (*) -│ │ │ └── typenum v1.17.0 -│ │ └── subtle v2.5.0 -│ ├── cfg-if v1.0.0 -│ ├── derivative v2.2.0 (proc-macro) -│ │ ├── proc-macro2 v1.0.78 (*) -│ │ ├── quote v1.0.35 (*) -│ │ └── syn v1.0.109 (*) -│ ├── derive_more v0.99.17 (proc-macro) -│ │ ├── convert_case v0.4.0 -│ │ ├── proc-macro2 v1.0.78 (*) -│ │ ├── quote v1.0.35 (*) -│ │ └── syn v1.0.109 (*) -│ │ [build-dependencies] -│ │ └── rustc_version v0.4.0 -│ │ └── semver v1.0.21 -│ ├── frame-metadata v16.0.0 -│ │ ├── cfg-if v1.0.0 -│ │ ├── parity-scale-codec v3.6.9 (*) -│ │ ├── scale-info v2.10.0 -│ │ │ ├── bitvec v1.0.1 -│ │ │ │ ├── funty v2.0.0 -│ │ │ │ ├── radium v0.7.0 -│ │ │ │ ├── tap v1.0.1 -│ │ │ │ └── wyz v0.5.1 -│ │ │ │ └── tap v1.0.1 -│ │ │ ├── cfg-if v1.0.0 -│ │ │ ├── derive_more v0.99.17 (proc-macro) (*) -│ │ │ ├── parity-scale-codec v3.6.9 (*) -│ │ │ ├── scale-info-derive v2.10.0 (proc-macro) -│ │ │ │ ├── proc-macro-crate v1.3.1 -│ │ │ │ │ ├── once_cell v1.19.0 -│ │ │ │ │ └── toml_edit v0.19.15 -│ │ │ │ │ ├── indexmap v2.1.0 (*) -│ │ │ │ │ ├── toml_datetime v0.6.3 -│ │ │ │ │ └── winnow v0.5.34 -│ │ │ │ ├── proc-macro2 v1.0.78 (*) -│ │ │ │ ├── quote v1.0.35 (*) -│ │ │ │ └── syn v1.0.109 (*) -│ │ │ └── serde v1.0.196 (*) -│ │ └── serde v1.0.196 (*) -│ ├── hex v0.4.3 -│ ├── impl-serde v0.4.0 -│ │ └── serde v1.0.196 (*) -│ ├── parity-scale-codec v3.6.9 (*) -│ ├── primitive-types v0.12.2 -│ │ ├── fixed-hash v0.8.0 -│ │ │ ├── byteorder v1.5.0 -│ │ │ ├── rand v0.8.5 -│ │ │ │ ├── libc v0.2.152 -│ │ │ │ ├── rand_chacha v0.3.1 -│ │ │ │ │ ├── ppv-lite86 v0.2.17 -│ │ │ │ │ └── rand_core v0.6.4 -│ │ │ │ │ └── getrandom v0.2.12 -│ │ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ │ └── libc v0.2.152 -│ │ │ │ └── rand_core v0.6.4 (*) -│ │ │ ├── rustc-hex v2.1.0 -│ │ │ └── static_assertions v1.1.0 -│ │ ├── impl-codec v0.6.0 -│ │ │ └── parity-scale-codec v3.6.9 (*) -│ │ ├── impl-serde v0.4.0 (*) -│ │ ├── scale-info v2.10.0 (*) -│ │ └── uint v0.9.5 -│ │ ├── byteorder v1.5.0 -│ │ ├── crunchy v0.2.2 -│ │ ├── hex v0.4.3 -│ │ └── static_assertions v1.1.0 -│ ├── scale-bits v0.4.0 -│ │ ├── parity-scale-codec v3.6.9 (*) -│ │ ├── scale-info v2.10.0 (*) -│ │ └── serde v1.0.196 (*) -│ ├── scale-decode v0.10.0 -│ │ ├── derive_more v0.99.17 (proc-macro) (*) -│ │ ├── parity-scale-codec v3.6.9 (*) -│ │ ├── primitive-types v0.12.2 (*) -│ │ ├── scale-bits v0.4.0 (*) -│ │ ├── scale-decode-derive v0.10.0 (proc-macro) -│ │ │ ├── darling v0.14.4 -│ │ │ │ ├── darling_core v0.14.4 -│ │ │ │ │ ├── fnv v1.0.7 -│ │ │ │ │ ├── ident_case v1.0.1 -│ │ │ │ │ ├── proc-macro2 v1.0.78 (*) -│ │ │ │ │ ├── quote v1.0.35 (*) -│ │ │ │ │ ├── strsim v0.10.0 -│ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ └── darling_macro v0.14.4 (proc-macro) -│ │ │ │ ├── darling_core v0.14.4 (*) -│ │ │ │ ├── quote v1.0.35 (*) -│ │ │ │ └── syn v1.0.109 (*) -│ │ │ ├── proc-macro-crate v1.3.1 (*) -│ │ │ ├── proc-macro2 v1.0.78 (*) -│ │ │ ├── quote v1.0.35 (*) -│ │ │ └── syn v1.0.109 (*) -│ │ ├── scale-info v2.10.0 (*) -│ │ └── smallvec v1.13.1 -│ ├── scale-encode v0.5.0 -│ │ ├── derive_more v0.99.17 (proc-macro) (*) -│ │ ├── parity-scale-codec v3.6.9 (*) -│ │ ├── primitive-types v0.12.2 (*) -│ │ ├── scale-bits v0.4.0 (*) -│ │ ├── scale-encode-derive v0.5.0 (proc-macro) -│ │ │ ├── darling v0.14.4 (*) -│ │ │ ├── proc-macro-crate v1.3.1 (*) -│ │ │ ├── proc-macro2 v1.0.78 (*) -│ │ │ ├── quote v1.0.35 (*) -│ │ │ └── syn v1.0.109 (*) -│ │ ├── scale-info v2.10.0 (*) -│ │ └── smallvec v1.13.1 -│ ├── scale-info v2.10.0 (*) -│ ├── scale-value v0.13.0 -│ │ ├── derive_more v0.99.17 (proc-macro) (*) -│ │ ├── either v1.9.0 -│ │ ├── frame-metadata v15.1.0 -│ │ │ ├── cfg-if v1.0.0 -│ │ │ ├── parity-scale-codec v3.6.9 (*) -│ │ │ └── scale-info v2.10.0 (*) -│ │ ├── parity-scale-codec v3.6.9 (*) -│ │ ├── scale-bits v0.4.0 (*) -│ │ ├── scale-decode v0.10.0 (*) -│ │ ├── scale-encode v0.5.0 (*) -│ │ └── scale-info v2.10.0 (*) -│ ├── serde v1.0.196 (*) -│ ├── serde_json v1.0.113 -│ │ ├── itoa v1.0.10 -│ │ ├── ryu v1.0.16 -│ │ └── serde v1.0.196 (*) -│ ├── sp-core-hashing v15.0.0 -│ │ ├── blake2b_simd v1.0.2 -│ │ │ ├── arrayref v0.3.7 -│ │ │ ├── arrayvec v0.7.4 -│ │ │ └── constant_time_eq v0.3.0 -│ │ ├── byteorder v1.5.0 -│ │ ├── digest v0.10.7 (*) -│ │ ├── sha2 v0.10.8 -│ │ │ ├── cfg-if v1.0.0 -│ │ │ ├── cpufeatures v0.2.12 -│ │ │ └── digest v0.10.7 (*) -│ │ ├── sha3 v0.10.8 -│ │ │ ├── digest v0.10.7 (*) -│ │ │ └── keccak v0.1.5 -│ │ └── twox-hash v1.6.3 -│ │ ├── cfg-if v1.0.0 -│ │ ├── digest v0.10.7 (*) -│ │ └── static_assertions v1.1.0 -│ ├── subxt-metadata v0.34.0 (/home/tadeo/code/subxt2/metadata) -│ │ ├── cfg-if v1.0.0 -│ │ ├── derive_more v0.99.17 (proc-macro) (*) -│ │ ├── frame-metadata v16.0.0 (*) -│ │ ├── hashbrown v0.14.3 -│ │ │ ├── ahash v0.8.7 -│ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ ├── once_cell v1.19.0 -│ │ │ │ └── zerocopy v0.7.32 -│ │ │ │ [build-dependencies] -│ │ │ │ └── version_check v0.9.4 -│ │ │ └── allocator-api2 v0.2.16 -│ │ ├── parity-scale-codec v3.6.9 (*) -│ │ ├── scale-info v2.10.0 (*) -│ │ └── sp-core-hashing v15.0.0 (*) -│ └── url v2.5.0 -│ ├── form_urlencoded v1.2.1 -│ │ └── percent-encoding v2.3.1 -│ ├── idna v0.5.0 -│ │ ├── unicode-bidi v0.3.15 -│ │ └── unicode-normalization v0.1.22 -│ │ └── tinyvec v1.6.0 -│ │ └── tinyvec_macros v0.1.1 -│ └── percent-encoding v2.3.1 -└── subxt-metadata v0.34.0 (/home/tadeo/code/subxt2/metadata) (*) From f41f0908ec2c7d2e3fd14e1e45d419c177141b18 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Mon, 5 Feb 2024 16:53:57 +0100 Subject: [PATCH 23/37] add no-std-build for thumbv7em-none-eabi target --- .github/workflows/rust.yml | 12 +++++++++--- testing/no-std-tests/src/main.rs | 16 +++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 95c971b4015..9283dbf342e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -437,13 +437,19 @@ jobs: toolchain: nightly override: true + - name: Add the `thumbv7em-none-eabi` target + run: | + rustup target add `thumbv7em-none-eabi` + - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 - # Note: in `no_std` no real tests are possible, we just run a binary with some tests in it which panic upon failure. - - name: Run no_std tests + # Note: We currently do not have a way to run real tests in a `no_std` environment. + # We can only make sure that they compile to ARM thumb ISA. + # Running the binary and inspecting the output would require an actual machine with matching ISA or some sort of emulator. + - name: Compile `no-std-tests` crate to `thumbv7em-none-eabi` target. run: | - cargo run + cargo build --target thumbv7em-none-eabi working-directory: testing/no-std-tests - if: "failure()" diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 0caff7a433b..09d8775fc52 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -9,7 +9,7 @@ #[start] fn start(_argc: isize, _argv: *const *const u8) -> isize { - run_tests(); + compile_test(); 0 } @@ -19,9 +19,7 @@ pub extern "C" fn rust_eh_personality() {} #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { - unsafe { - libc::abort(); - } + loop {} } use libc_alloc::LibcAlloc; @@ -33,13 +31,13 @@ static ALLOCATOR: LibcAlloc = LibcAlloc; extern crate alloc; -// Note: Panics in this function will lead to `Aborted (core dumped)` and a non-zero exit status => suitable for CI tests. -fn run_tests() { - subxt_metadata_test(); +/// Including code here makes sure it is not pruned. +/// We want all code included to compile fine for the `thumbv7em-none-eabi` target. +fn compile_test() { + subxt_metadata_compiles(); } -/// Makes sure, subxt-metadata works in a no-std-context: -fn subxt_metadata_test() { +fn subxt_metadata_compiles() { use codec::Decode; let bytes: alloc::vec::Vec = alloc::vec![0, 1, 2, 3, 4]; subxt_metadata::Metadata::decode(&mut &bytes[..]).expect_err("invalid byte sequence"); From 2b38a0fce130422ba051ab39e0e183be5f6f8358 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Wed, 7 Feb 2024 14:55:26 +0100 Subject: [PATCH 24/37] try compiling with different methods, no success --- subxt/examples/setup_config_custom.rs | 2 +- .../examples/setup_config_signed_extension.rs | 2 +- testing/no-std-tests/Cargo.lock | 8 ++--- testing/no-std-tests/Cargo.toml | 2 +- testing/no-std-tests/README.md | 11 +++++++ testing/no-std-tests/src/main.rs | 33 +++++++++++-------- 6 files changed, 37 insertions(+), 21 deletions(-) create mode 100644 testing/no-std-tests/README.md diff --git a/subxt/examples/setup_config_custom.rs b/subxt/examples/setup_config_custom.rs index 713145d6dc9..0f2ad2e27b4 100644 --- a/subxt/examples/setup_config_custom.rs +++ b/subxt/examples/setup_config_custom.rs @@ -2,7 +2,7 @@ use codec::Encode; use subxt::client::OfflineClientT; use subxt::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder}; -use subxt_core::error::ExtrinsicParamsError; +use subxt_core::ExtrinsicParamsError; use subxt_signer::sr25519::dev; #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale")] diff --git a/subxt/examples/setup_config_signed_extension.rs b/subxt/examples/setup_config_signed_extension.rs index a5492ff91c9..a48e17570f3 100644 --- a/subxt/examples/setup_config_signed_extension.rs +++ b/subxt/examples/setup_config_signed_extension.rs @@ -7,7 +7,7 @@ use subxt::config::{ Config, DefaultExtrinsicParamsBuilder, ExtrinsicParams, ExtrinsicParamsEncoder, }; use subxt_core::config::signed_extensions; -use subxt_core::error::ExtrinsicParamsError; +use subxt_core::ExtrinsicParamsError; use subxt_signer::sr25519::dev; #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")] diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index b13c83f0973..f000fd232a4 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -167,9 +167,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.1" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if", "cpufeatures", @@ -277,9 +277,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "fiat-crypto" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" [[package]] name = "fixed-hash" diff --git a/testing/no-std-tests/Cargo.toml b/testing/no-std-tests/Cargo.toml index 2d6da33488d..4f29b68aafe 100644 --- a/testing/no-std-tests/Cargo.toml +++ b/testing/no-std-tests/Cargo.toml @@ -7,8 +7,8 @@ resolver = "2" [dependencies] subxt-metadata = { path = "../../metadata", default-features = false } -subxt-core = { path = "../../core", default-features = false } subxt-signer = { path = "../../signer", default-features = false, features = ["sr25519", "ecdsa", "subxt"] } +subxt-core = { path = "../../core", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] } libc = { version = "0.2", default-features = false } libc_alloc = { version = "1.0.6" } diff --git a/testing/no-std-tests/README.md b/testing/no-std-tests/README.md new file mode 100644 index 00000000000..b6e108e0544 --- /dev/null +++ b/testing/no-std-tests/README.md @@ -0,0 +1,11 @@ +# no_std tests + +This crate makes sure some of the subxt-* crates work in a no-std environment. + +We would like it to run in a no-std environment. You can try any of the following to get it to compile: +``` +cargo run +cargo build --target thumbv7em-none-eabi +cargo build --target aarch64-unknown-none +``` +Currently it does not compile due to linker errors and I have no idea how to resovle these. \ No newline at end of file diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 7a7b4114796..ae1860f311d 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -9,7 +9,7 @@ #[start] fn start(_argc: isize, _argv: *const *const u8) -> isize { - compile_test(); + test(); 0 } @@ -19,7 +19,9 @@ pub extern "C" fn rust_eh_personality() {} #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} + unsafe { + libc::abort(); + } } use libc_alloc::LibcAlloc; @@ -33,27 +35,30 @@ extern crate alloc; /// Including code here makes sure it is not pruned. /// We want all code included to compile fine for the `thumbv7em-none-eabi` target. -fn compile_test() { - subxt_metadata_compiles(); -} +fn test() { + // ///////////////////////////////////////////////////////////////////////////// + // subxt-metadata + // ///////////////////////////////////////////////////////////////////////////// -fn subxt_metadata_compiles() { use codec::Decode; let bytes: alloc::vec::Vec = alloc::vec![0, 1, 2, 3, 4]; subxt_metadata::Metadata::decode(&mut &bytes[..]).expect_err("invalid byte sequence"); const METADATA: &[u8] = include_bytes!("../../../artifacts/polkadot_metadata_small.scale"); subxt_metadata::Metadata::decode(&mut &METADATA[..]).expect("should be valid metadata"); -} -fn subxt_signer_test() { - use subxt_signer::{ SecretUri, ecdsa::Keypair }; + // ///////////////////////////////////////////////////////////////////////////// + // subxt-signer + // ///////////////////////////////////////////////////////////////////////////// + use core::str::FromStr; + use subxt_signer::{ecdsa::Keypair, SecretUri}; let uri = SecretUri::from_str("//Alice").unwrap(); - let keypair = Keypair::from_uri(&uri).unwrap(); -} + let _keypair = Keypair::from_uri(&uri).unwrap(); -fn subxt_core_test() { - let _ = subxt_core::utils::Era::Immortal; -} + // ///////////////////////////////////////////////////////////////////////////// + // subxt-core + // ///////////////////////////////////////////////////////////////////////////// + let _era = subxt_core::utils::Era::Immortal; +} From ce17d825891fa8c2c9d695c1c9154625f22014dc Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Wed, 14 Feb 2024 10:42:04 +0100 Subject: [PATCH 25/37] remove std feature flag --- subxt/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index 0c181be3dbd..7655a0c5f4c 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -97,7 +97,7 @@ sp-runtime = { workspace = true, optional = true } # Other subxt crates we depend on. subxt-macro = { workspace = true } -subxt-metadata = { workspace = true, features = ["std"] } +subxt-metadata = { workspace = true } subxt-lightclient = { workspace = true, optional = true, default-features = false } # Light client support: From 64987fe44ceae86be156a72f405ab474d6e3e93e Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Wed, 14 Feb 2024 15:51:34 +0100 Subject: [PATCH 26/37] adjust subxt signer static once locks --- Cargo.lock | 18 +----------------- signer/Cargo.toml | 3 +-- signer/src/ecdsa.rs | 19 +++++-------------- signer/src/utils.rs | 27 +++++++++++++++++++++------ 4 files changed, 28 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b28f2d1fde..8cf73fb2350 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1076,12 +1076,6 @@ dependencies = [ "itertools 0.10.5", ] -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -2738,10 +2732,6 @@ name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -dependencies = [ - "critical-section", - "portable-atomic", -] [[package]] name = "oorandom" @@ -2989,12 +2979,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "portable-atomic" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -4638,11 +4622,11 @@ name = "subxt-signer" version = "0.34.0" dependencies = [ "bip39", + "cfg-if", "derive_more", "getrandom", "hex", "hmac 0.12.1", - "once_cell", "parity-scale-codec", "pbkdf2 0.12.2", "regex", diff --git a/signer/Cargo.toml b/signer/Cargo.toml index f60ad4e9424..9efd2e1ca7f 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -37,6 +37,7 @@ web = ["getrandom/js"] subxt-core = { workspace = true, optional = true, default-features = false } regex = { workspace = true, default-features = false } hex = { workspace = true } +cfg-if = { workspace = true } codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] } sp-core-hashing = { workspace = true, default-features = false } derive_more = { workspace = true } @@ -49,8 +50,6 @@ schnorrkel = { workspace = true, optional = true, default-features = false } secp256k1 = { workspace = true, optional = true, default-features = false, features = ["alloc", "recovery"] } # features = ["recovery", "global-context"], secrecy = { workspace = true } -# We only pull this in because `std::sync::OnceLock` is not available in no-std contexts. -once_cell = { workspace = true, default-features = false, features = ["alloc", "critical-section"] } # We only pull this in to enable the JS flag for schnorrkel to use. getrandom = { workspace = true, optional = true } diff --git a/signer/src/ecdsa.rs b/signer/src/ecdsa.rs index 6946446f60c..549f9f30c2c 100644 --- a/signer/src/ecdsa.rs +++ b/signer/src/ecdsa.rs @@ -9,19 +9,9 @@ use crate::crypto::{seed_from_entropy, DeriveJunction, SecretUri}; use core::str::FromStr; use derive_more::{Display, From}; use hex::FromHex; -use secp256k1::{ecdsa::RecoverableSignature, All, Message, Secp256k1, SecretKey}; +use secp256k1::{ecdsa::RecoverableSignature, Message, Secp256k1, SecretKey}; use secrecy::ExposeSecret; -struct GlobalSecp256K1Context; -impl core::ops::Deref for GlobalSecp256K1Context { - type Target = Secp256k1; - - fn deref(&self) -> &Self::Target { - static ONCE: once_cell::sync::OnceCell> = once_cell::sync::OnceCell::new(); - ONCE.get_or_init(|| Secp256k1::new()) - } -} - const SEED_LENGTH: usize = 32; /// Seed bytes used to generate a key pair. @@ -122,7 +112,7 @@ impl Keypair { pub fn from_seed(seed: Seed) -> Result { let secret = SecretKey::from_slice(&seed).map_err(|_| Error::InvalidSeed)?; Ok(Self(secp256k1::Keypair::from_secret_key( - &GlobalSecp256K1Context, + &Secp256k1::signing_only(), &secret, ))) } @@ -175,7 +165,7 @@ impl Keypair { // From sp_core::ecdsa::sign_prehashed: let wrapped = Message::from_digest_slice(&message_hash).expect("Message is 32 bytes; qed"); let recsig: RecoverableSignature = - GlobalSecp256K1Context.sign_ecdsa_recoverable(&wrapped, &self.0.secret_key()); + Secp256k1::signing_only().sign_ecdsa_recoverable(&wrapped, &self.0.secret_key()); // From sp_core::ecdsa's `impl From for Signature`: let (recid, sig): (_, [u8; 64]) = recsig.serialize_compact(); let mut signature_bytes: [u8; 65] = [0; 65]; @@ -206,7 +196,8 @@ pub fn verify>(sig: &Signature, message: M, pubkey: &PublicKey) - }; let message_hash = sp_core_hashing::blake2_256(message.as_ref()); let wrapped = Message::from_digest_slice(&message_hash).expect("Message is 32 bytes; qed"); - GlobalSecp256K1Context + + Secp256k1::verification_only() .verify_ecdsa(&wrapped, &signature, &public) .is_ok() } diff --git a/signer/src/utils.rs b/signer/src/utils.rs index 52a935a1fd8..756d050a0a3 100644 --- a/signer/src/utils.rs +++ b/signer/src/utils.rs @@ -17,10 +17,19 @@ macro_rules! once_static { ($($(#[$attr:meta])* $vis:vis fn $name:ident() -> $ty:ty { $expr:expr } )+) => { $( - $(#[$attr])* - $vis fn $name() -> &'static $ty { - static VAR: once_cell::sync::OnceCell<$ty> = once_cell::sync::OnceCell::new(); - VAR.get_or_init(|| { $expr }) + cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + $(#[$attr])* + $vis fn $name() -> &'static $ty { + static VAR: std::sync::OnceLock<$ty> = std::sync::OnceLock::new(); + VAR.get_or_init(|| { $expr }) + } + } else { + $(#[$attr])* + $vis fn $name() -> $ty { + $expr + } + } } )+ }; @@ -33,8 +42,14 @@ macro_rules! once_static_cloned { $( $(#[$attr])* $vis fn $name() -> $ty { - static VAR: once_cell::sync::OnceCell<$ty> = once_cell::sync::OnceCell::new(); - VAR.get_or_init(|| { $expr }).clone() + cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + static VAR: std::sync::OnceLock<$ty> = std::sync::OnceLock::new(); + VAR.get_or_init(|| { $expr }).clone() + } else { + { $expr } + } + } } )+ }; From d447f7cc972c7995d8f4589c2ee0d15175600118 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Wed, 14 Feb 2024 15:57:23 +0100 Subject: [PATCH 27/37] remove libc and add small readme with test instructions --- testing/no-std-tests/Cargo.lock | 1 - testing/no-std-tests/Cargo.toml | 1 - testing/no-std-tests/README.md | 7 +++++++ 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 testing/no-std-tests/README.md diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index 277d009358b..ee4afadfe70 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -361,7 +361,6 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" name = "subxt-core-no-std-tests" version = "0.0.0" dependencies = [ - "libc", "libc_alloc", "parity-scale-codec", "subxt-metadata", diff --git a/testing/no-std-tests/Cargo.toml b/testing/no-std-tests/Cargo.toml index 29a4bca2423..503326d5b55 100644 --- a/testing/no-std-tests/Cargo.toml +++ b/testing/no-std-tests/Cargo.toml @@ -8,7 +8,6 @@ resolver = "2" [dependencies] subxt-metadata = { path = "../../metadata", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] } -libc = { version = "0.2", default-features = false } libc_alloc = { version = "1.0.6" } [profile.dev] diff --git a/testing/no-std-tests/README.md b/testing/no-std-tests/README.md new file mode 100644 index 00000000000..2b662bb4555 --- /dev/null +++ b/testing/no-std-tests/README.md @@ -0,0 +1,7 @@ +# No-Std Testing Crate + +To test the no-std compatibility of various subxt-* crates, please run: + +```bash +cargo build --target thumbv7em-none-eabi +``` \ No newline at end of file From cc33870c6baba06eb67bdd9cdc02f60dd350d09d Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Wed, 14 Feb 2024 16:12:53 +0100 Subject: [PATCH 28/37] remove unicode normalization and std references --- core/src/constants/constant_address.rs | 2 +- core/src/runtime_api/mod.rs | 10 +++--- core/src/storage/storage_address.rs | 10 +++--- core/src/tx/mod.rs | 10 +++--- signer/Cargo.toml | 2 +- testing/no-std-tests/Cargo.lock | 48 ++------------------------ testing/no-std-tests/Cargo.toml | 2 +- testing/no-std-tests/src/main.rs | 6 ++-- 8 files changed, 23 insertions(+), 67 deletions(-) diff --git a/core/src/constants/constant_address.rs b/core/src/constants/constant_address.rs index bdd1782d355..7fb543e276c 100644 --- a/core/src/constants/constant_address.rs +++ b/core/src/constants/constant_address.rs @@ -46,7 +46,7 @@ pub struct Address { // Manual implementation to work around https://github.com/mcarton/rust-derivative/issues/115. impl PartialOrd for Address { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } diff --git a/core/src/runtime_api/mod.rs b/core/src/runtime_api/mod.rs index 721f59f6a89..80f9475f2b7 100644 --- a/core/src/runtime_api/mod.rs +++ b/core/src/runtime_api/mod.rs @@ -74,11 +74,11 @@ pub trait RuntimeApiPayload { #[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") + Debug(bound = "ArgsData: core::fmt::Debug"), + Eq(bound = "ArgsData: core::cmp::Eq"), + Ord(bound = "ArgsData: core::cmp::Ord"), + PartialEq(bound = "ArgsData: core::cmp::PartialEq"), + PartialOrd(bound = "ArgsData: core::cmp::PartialOrd") )] pub struct Payload { trait_name: Cow<'static, str>, diff --git a/core/src/storage/storage_address.rs b/core/src/storage/storage_address.rs index c3749a8e36a..9f0e23355c9 100644 --- a/core/src/storage/storage_address.rs +++ b/core/src/storage/storage_address.rs @@ -58,11 +58,11 @@ pub trait StorageAddress { #[derive(Derivative)] #[derivative( Clone(bound = "StorageKey: Clone"), - Debug(bound = "StorageKey: std::fmt::Debug"), - Eq(bound = "StorageKey: std::cmp::Eq"), - Ord(bound = "StorageKey: std::cmp::Ord"), - PartialEq(bound = "StorageKey: std::cmp::PartialEq"), - PartialOrd(bound = "StorageKey: std::cmp::PartialOrd") + Debug(bound = "StorageKey: core::fmt::Debug"), + Eq(bound = "StorageKey: core::cmp::Eq"), + Ord(bound = "StorageKey: core::cmp::Ord"), + PartialEq(bound = "StorageKey: core::cmp::PartialEq"), + PartialOrd(bound = "StorageKey: core::cmp::PartialOrd") )] pub struct Address { pallet_name: Cow<'static, str>, diff --git a/core/src/tx/mod.rs b/core/src/tx/mod.rs index 8ba13d116ec..de2f45b64a4 100644 --- a/core/src/tx/mod.rs +++ b/core/src/tx/mod.rs @@ -54,11 +54,11 @@ pub struct ValidationDetails<'a> { #[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") + Debug(bound = "CallData: core::fmt::Debug"), + Eq(bound = "CallData: core::cmp::Eq"), + Ord(bound = "CallData: core::cmp::Ord"), + PartialEq(bound = "CallData: core::cmp::PartialEq"), + PartialOrd(bound = "CallData: core::cmp::PartialOrd") )] pub struct Payload { pallet_name: Cow<'static, str>, diff --git a/signer/Cargo.toml b/signer/Cargo.toml index 9efd2e1ca7f..c9650e0db54 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -45,7 +45,7 @@ pbkdf2 = { workspace = true, default-features = false } sha2 = { workspace = true, default-features = false } hmac = { workspace = true, default-features = false } zeroize = { workspace = true } -bip39 = { workspace = true, default-features = false, features = ["unicode-normalization"] } +bip39 = { workspace = true, default-features = false } schnorrkel = { workspace = true, optional = true, default-features = false } secp256k1 = { workspace = true, optional = true, default-features = false, features = ["alloc", "recovery"] } # features = ["recovery", "global-context"], secrecy = { workspace = true } diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index e2703c0f6d5..ef41c297556 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -45,7 +45,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "unicode-normalization", ] [[package]] @@ -143,12 +142,6 @@ dependencies = [ "libc", ] -[[package]] -name = "critical-section" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - [[package]] name = "crunchy" version = "0.2.2" @@ -265,9 +258,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "equivalent" @@ -463,10 +456,6 @@ name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -dependencies = [ - "critical-section", - "portable-atomic", -] [[package]] name = "parity-scale-codec" @@ -507,12 +496,6 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" -[[package]] -name = "portable-atomic" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" - [[package]] name = "primitive-types" version = "0.12.2" @@ -895,7 +878,6 @@ version = "0.0.0" dependencies = [ "libc_alloc", "parity-scale-codec", - "subxt-core", "subxt-metadata", "subxt-signer", ] @@ -917,10 +899,10 @@ name = "subxt-signer" version = "0.34.0" dependencies = [ "bip39", + "cfg-if", "derive_more", "hex", "hmac", - "once_cell", "parity-scale-codec", "pbkdf2", "regex", @@ -961,21 +943,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "toml_datetime" version = "0.6.3" @@ -1039,15 +1006,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "version_check" version = "0.9.4" diff --git a/testing/no-std-tests/Cargo.toml b/testing/no-std-tests/Cargo.toml index b336627c8ff..8d021bd8173 100644 --- a/testing/no-std-tests/Cargo.toml +++ b/testing/no-std-tests/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [dependencies] subxt-metadata = { path = "../../metadata", default-features = false } subxt-signer = { path = "../../signer", default-features = false, features = ["sr25519", "ecdsa", "subxt"] } -subxt-core = { path = "../../core", default-features = false } +# subxt-core = { path = "../../core", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] } libc_alloc = { version = "1.0.6" } diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index ae1860f311d..4f5b1b377cd 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -19,9 +19,7 @@ pub extern "C" fn rust_eh_personality() {} #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { - unsafe { - libc::abort(); - } + loop {} } use libc_alloc::LibcAlloc; @@ -60,5 +58,5 @@ fn test() { // subxt-core // ///////////////////////////////////////////////////////////////////////////// - let _era = subxt_core::utils::Era::Immortal; + // let _era = subxt_core::utils::Era::Immortal; } From b86b1ea009289eabf1d5be9ece3d7c084b933d9e Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Wed, 14 Feb 2024 17:14:02 +0100 Subject: [PATCH 29/37] subxt signer linker errors but core compiles --- subxt/src/lib.rs | 4 +--- testing/no-std-tests/Cargo.lock | 1 + testing/no-std-tests/Cargo.toml | 2 +- testing/no-std-tests/src/main.rs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index 723a363a1f5..5842f513e9d 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -65,9 +65,7 @@ pub use crate::{ error::Error, }; -// pub use subxt_core::config; -// pub use subxt_core::config::{Config, PolkadotConfig, SubstrateConfig}; -// pub use subxt_core::dynamic; +// We replace this by proper exports, once the API of subxt_core is aggreed upon. pub use subxt_core::*; /// Re-export external crates that are made use of in the subxt API. diff --git a/testing/no-std-tests/Cargo.lock b/testing/no-std-tests/Cargo.lock index ef41c297556..9f7dd11516e 100644 --- a/testing/no-std-tests/Cargo.lock +++ b/testing/no-std-tests/Cargo.lock @@ -878,6 +878,7 @@ version = "0.0.0" dependencies = [ "libc_alloc", "parity-scale-codec", + "subxt-core", "subxt-metadata", "subxt-signer", ] diff --git a/testing/no-std-tests/Cargo.toml b/testing/no-std-tests/Cargo.toml index 8d021bd8173..b336627c8ff 100644 --- a/testing/no-std-tests/Cargo.toml +++ b/testing/no-std-tests/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [dependencies] subxt-metadata = { path = "../../metadata", default-features = false } subxt-signer = { path = "../../signer", default-features = false, features = ["sr25519", "ecdsa", "subxt"] } -# subxt-core = { path = "../../core", default-features = false } +subxt-core = { path = "../../core", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.9", default-features = false, features = ["derive"] } libc_alloc = { version = "1.0.6" } diff --git a/testing/no-std-tests/src/main.rs b/testing/no-std-tests/src/main.rs index 4f5b1b377cd..fac3807ba35 100644 --- a/testing/no-std-tests/src/main.rs +++ b/testing/no-std-tests/src/main.rs @@ -58,5 +58,5 @@ fn test() { // subxt-core // ///////////////////////////////////////////////////////////////////////////// - // let _era = subxt_core::utils::Era::Immortal; + let _era = subxt_core::utils::Era::Immortal; } From 1fe8ec3e4fb06be2ce5fe2b190d7029dbc59eebd Mon Sep 17 00:00:00 2001 From: Tadeo Hepperle <62739623+tadeohepperle@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:02:51 +0100 Subject: [PATCH 30/37] [Fix] Auto-generated PR: delete polkadot binaries from working directory. (#1431) * delete substrate and polkadot binaries * use nodes should move instead of copy and remove the archive --- .github/workflows/actions/use-nodes/action.yml | 9 +++++---- .github/workflows/update-artifacts.yml | 3 --- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/actions/use-nodes/action.yml b/.github/workflows/actions/use-nodes/action.yml index b1785f9067b..bb2b9723d44 100644 --- a/.github/workflows/actions/use-nodes/action.yml +++ b/.github/workflows/actions/use-nodes/action.yml @@ -33,7 +33,8 @@ runs: ./substrate-node --version ./polkadot --version mkdir -p ~/.local/bin - cp ./substrate-node ~/.local/bin - cp ./polkadot ~/.local/bin - cp ./polkadot-execute-worker ~/.local/bin - cp ./polkadot-prepare-worker ~/.local/bin + mv ./substrate-node ~/.local/bin + mv ./polkadot ~/.local/bin + mv ./polkadot-execute-worker ~/.local/bin + mv ./polkadot-prepare-worker ~/.local/bin + rm ./polkadot.tar.gz diff --git a/.github/workflows/update-artifacts.yml b/.github/workflows/update-artifacts.yml index 49c2a86d18f..491f8d40990 100644 --- a/.github/workflows/update-artifacts.yml +++ b/.github/workflows/update-artifacts.yml @@ -41,9 +41,6 @@ jobs: - name: Fetch Artifacts run: cargo run --bin artifacts - - name: Delete substrate node binary - run: rm ./substrate-node - - uses: actions/create-github-app-token@v1 id: app-token with: From db00212ba66617913af2ec57c59345a39a9ef7a1 Mon Sep 17 00:00:00 2001 From: Tadeo Hepperle <62739623+tadeohepperle@users.noreply.github.com> Date: Wed, 14 Feb 2024 17:17:08 +0100 Subject: [PATCH 31/37] Add missing codegen mod in integration tests (#1418) * add codgen mod to the module tree * port codegen tests to scale-typegen * remove test flag --- .../src/full_client/codegen/codegen_tests.rs | 214 ------------------ .../src/full_client/codegen/documentation.rs | 2 +- .../src/full_client/codegen/mod.rs | 1 - .../integration-tests/src/full_client/mod.rs | 7 +- 4 files changed, 2 insertions(+), 222 deletions(-) delete mode 100644 testing/integration-tests/src/full_client/codegen/codegen_tests.rs diff --git a/testing/integration-tests/src/full_client/codegen/codegen_tests.rs b/testing/integration-tests/src/full_client/codegen/codegen_tests.rs deleted file mode 100644 index 5722270e244..00000000000 --- a/testing/integration-tests/src/full_client/codegen/codegen_tests.rs +++ /dev/null @@ -1,214 +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 frame_metadata::{ - v15::{ExtrinsicMetadata, RuntimeMetadataV15}, - RuntimeMetadataPrefixed, -}; -use scale_info::{meta_type, IntoPortable, PortableRegistry, Registry, TypeInfo}; -use subxt_codegen::CodegenBuilder; -use syn::__private::quote; - -fn generate_runtime_interface_from_metadata(metadata: RuntimeMetadataPrefixed) -> String { - // Generate a runtime interface from the provided metadata. - let metadata = metadata - .try_into() - .expect("frame_metadata should be convertible into Metadata"); - - CodegenBuilder::new() - .no_docs() - .generate(metadata) - .expect("API generation must be valid") - .to_string() -} - -fn generate_runtime_interface_with_type_registry(f: F) -> String -where - F: Fn(&mut scale_info::Registry), -{ - #[derive(TypeInfo)] - struct Runtime; - #[derive(TypeInfo)] - enum RuntimeCall {} - #[derive(TypeInfo)] - enum RuntimeEvent {} - #[derive(TypeInfo)] - pub enum DispatchError {} - - // We need these types for codegen to work: - let mut registry = scale_info::Registry::new(); - let ty = registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - - // Allow custom types to be added for testing: - f(&mut registry); - - let extrinsic = ExtrinsicMetadata { - ty: meta_type::<()>(), - version: 0, - signed_extensions: vec![], - } - .into_portable(&mut registry); - let metadata = RuntimeMetadataV15 { - types: registry.into(), - pallets: Vec::new(), - extrinsic, - ty, - apis: vec![], - }; - - let metadata = RuntimeMetadataPrefixed::from(metadata); - generate_runtime_interface_from_metadata(metadata) -} - -#[test] -fn dupe_types_do_not_overwrite_each_other() { - let interface = generate_runtime_interface_with_type_registry(|registry| { - // Now we duplicate some types with same type info. We need two unique types here, - // and can't just add one type to the registry twice, because the registry knows if - // type IDs are the same. - enum Foo {} - impl TypeInfo for Foo { - type Identity = Self; - fn type_info() -> scale_info::Type { - scale_info::Type::builder() - .path(scale_info::Path::new("DuplicateType", "dupe_mod")) - .variant( - scale_info::build::Variants::new() - .variant("FirstDupeTypeVariant", |builder| builder.index(0)), - ) - } - } - enum Bar {} - impl TypeInfo for Bar { - type Identity = Self; - fn type_info() -> scale_info::Type { - scale_info::Type::builder() - .path(scale_info::Path::new("DuplicateType", "dupe_mod")) - .variant( - scale_info::build::Variants::new() - .variant("SecondDupeTypeVariant", |builder| builder.index(0)), - ) - } - } - - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - }); - - assert!(interface.contains("DuplicateType")); - assert!(interface.contains("FirstDupeTypeVariant")); - - assert!(interface.contains("DuplicateType2")); - assert!(interface.contains("SecondDupeTypeVariant")); -} - -#[test] -fn generic_types_overwrite_each_other() { - let interface = generate_runtime_interface_with_type_registry(|registry| { - // If we have two types mentioned in the registry that have generic params, - // only one type will be output (the codegen assumes that the generic param will disambiguate) - enum Foo {} - impl TypeInfo for Foo { - type Identity = Self; - fn type_info() -> scale_info::Type { - scale_info::Type::builder() - .path(scale_info::Path::new("DuplicateType", "dupe_mod")) - .type_params([scale_info::TypeParameter::new("T", Some(meta_type::()))]) - .variant(scale_info::build::Variants::new()) - } - } - enum Bar {} - impl TypeInfo for Bar { - type Identity = Self; - fn type_info() -> scale_info::Type { - scale_info::Type::builder() - .path(scale_info::Path::new("DuplicateType", "dupe_mod")) - .type_params([scale_info::TypeParameter::new("T", Some(meta_type::()))]) - .variant(scale_info::build::Variants::new()) - } - } - - registry.register_type(&meta_type::()); - registry.register_type(&meta_type::()); - }); - - assert!(interface.contains("DuplicateType")); - // We do _not_ expect this to exist, since a generic is present on the type: - assert!(!interface.contains("DuplicateType2")); -} - -#[test] -fn more_than_1_generic_parameters_work() { - #[allow(unused)] - #[derive(TypeInfo)] - struct Foo { - a: T, - b: U, - c: V, - d: W, - } - - #[allow(unused)] - #[derive(TypeInfo)] - struct Bar { - p: Foo, - q: Foo, - } - - let mut registry = Registry::new(); - registry.register_type(&meta_type::()); - let portable_types: PortableRegistry = registry.into(); - - let type_gen = subxt_codegen::TypeGenerator::new( - &portable_types, - "root", - Default::default(), - Default::default(), - CratePath::default(), - false, - ); - - let types = type_gen.generate_types_mod().unwrap(); - let generated_mod = quote::quote!( #types); - - let expected_mod = quote::quote! { - pub mod root { - use super::root; - pub mod integration_tests { - use super::root; - pub mod codegen { - use super::root; - pub mod codegen_tests { - use super::root; - pub struct Bar { - pub p: root::integration_tests::codegen::codegen_tests::Foo< - ::core::primitive::u32, - ::core::primitive::u32, - ::core::primitive::u64, - ::core::primitive::u128 - >, - pub q: root::integration_tests::codegen::codegen_tests::Foo< - ::core::primitive::u8, - ::core::primitive::u8, - ::core::primitive::u8, - ::core::primitive::u8 - >, - } - pub struct Foo<_0, _1, _2, _3> { - pub a: _0, - pub b: _1, - pub c: _2, - pub d: _3, - } - } - } - } - } - }; - - assert_eq!(generated_mod.to_string(), expected_mod.to_string()); -} diff --git a/testing/integration-tests/src/full_client/codegen/documentation.rs b/testing/integration-tests/src/full_client/codegen/documentation.rs index 8eead477609..fea4e78f643 100644 --- a/testing/integration-tests/src/full_client/codegen/documentation.rs +++ b/testing/integration-tests/src/full_client/codegen/documentation.rs @@ -4,7 +4,7 @@ use codec::Decode; use regex::Regex; -use subxt_codegen::{ syn, CodegenBuilder }; +use subxt_codegen::{syn, CodegenBuilder}; use subxt_metadata::Metadata; fn load_test_metadata() -> Metadata { diff --git a/testing/integration-tests/src/full_client/codegen/mod.rs b/testing/integration-tests/src/full_client/codegen/mod.rs index f2b3a976580..e17b92a0b73 100644 --- a/testing/integration-tests/src/full_client/codegen/mod.rs +++ b/testing/integration-tests/src/full_client/codegen/mod.rs @@ -14,5 +14,4 @@ #[allow(clippy::all)] mod polkadot; -mod codegen_tests; mod documentation; diff --git a/testing/integration-tests/src/full_client/mod.rs b/testing/integration-tests/src/full_client/mod.rs index 5b496ccea35..f5e0a725104 100644 --- a/testing/integration-tests/src/full_client/mod.rs +++ b/testing/integration-tests/src/full_client/mod.rs @@ -2,15 +2,10 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -#[cfg(test)] mod blocks; -#[cfg(test)] mod client; -#[cfg(test)] +mod codegen; mod frame; -#[cfg(test)] mod metadata; -#[cfg(test)] mod runtime_api; -#[cfg(test)] mod storage; From fda9a5fd0c7384a1755d91adcccf514f185cbb5a Mon Sep 17 00:00:00 2001 From: Tadeo Hepperle <62739623+tadeohepperle@users.noreply.github.com> Date: Thu, 15 Feb 2024 10:49:15 +0100 Subject: [PATCH 32/37] Subxt Metadata: `#[no_std]` compatibility (#1401) * no-std tests and porting of subxt-metadata * update pipeline * fix generate custom metadata test * fix cargo run command * adjust pipeline * remove prelude from subxt-metadata * revert autoformatting of Cargo.toml * remove alloc::format! again, still causes linker errors * add no-std-build for thumbv7em-none-eabi target * remove std feature flag * remove libc and add small readme with test instructions * change ci for nightly no std --- .github/workflows/rust.yml | 9 +- Cargo.lock | 246 +++++++++++++++++++++---------------- subxt/Cargo.toml | 16 ++- 3 files changed, 161 insertions(+), 110 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 468fa0b1a4c..327a77bd312 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -436,10 +436,10 @@ jobs: profile: minimal toolchain: nightly override: true + target: thumbv7em-none-eabi - - name: Add the `thumbv7em-none-eabi` target - run: | - rustup target add `thumbv7em-none-eabi` + - name: Install the gcc-arm-none-eabi linker + run: sudo apt install gcc-arm-none-eabi - name: Rust Cache uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 @@ -448,8 +448,7 @@ jobs: # We can only make sure that they compile to ARM thumb ISA. # Running the binary and inspecting the output would require an actual machine with matching ISA or some sort of emulator. - name: Compile `no-std-tests` crate to `thumbv7em-none-eabi` target. - run: | - cargo build --target thumbv7em-none-eabi + run: cargo build --target thumbv7em-none-eabi working-directory: testing/no-std-tests - if: "failure()" diff --git a/Cargo.lock b/Cargo.lock index 12313c1d225..4c12e4f3580 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -59,9 +59,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "getrandom", @@ -131,9 +131,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -346,13 +346,13 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-channel" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 4.0.3", - "event-listener-strategy", + "event-listener 5.0.0", + "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", ] @@ -373,9 +373,9 @@ dependencies = [ [[package]] name = "async-fs" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1f344136bad34df1f83a47f3fd7f2ab85d75cb8a940af4ccf6d482a84ea01b" +checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" dependencies = [ "async-lock 3.3.0", "blocking", @@ -384,9 +384,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744" +checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" dependencies = [ "async-lock 3.3.0", "cfg-if", @@ -395,7 +395,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.30", + "rustix 0.38.31", "slab", "tracing", "windows-sys 0.52.0", @@ -417,7 +417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", - "event-listener-strategy", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -446,7 +446,7 @@ dependencies = [ "cfg-if", "event-listener 4.0.3", "futures-lite", - "rustix 0.38.30", + "rustix 0.38.31", "windows-sys 0.52.0", ] @@ -462,7 +462,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.30", + "rustix 0.38.31", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -1033,9 +1033,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -1175,9 +1175,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.1" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if", "cpufeatures", @@ -1213,12 +1213,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.5" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" +checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" dependencies = [ - "darling_core 0.20.5", - "darling_macro 0.20.5", + "darling_core 0.20.6", + "darling_macro 0.20.6", ] [[package]] @@ -1237,9 +1237,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.5" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" +checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" dependencies = [ "fnv", "ident_case", @@ -1262,11 +1262,11 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.5" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" +checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" dependencies = [ - "darling_core 0.20.5", + "darling_core 0.20.6", "quote", "syn 2.0.48", ] @@ -1423,11 +1423,11 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "ed25519", "serde", "sha2 0.10.8", @@ -1455,7 +1455,7 @@ version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" dependencies = [ - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "ed25519", "hashbrown 0.14.3", "hex", @@ -1509,6 +1509,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72557800024fabbaa2449dd4bf24e37b93702d457a4d4f2b0dd1f0f039f20c1" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" version = "0.4.0" @@ -1519,6 +1530,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.0.0", + "pin-project-lite", +] + [[package]] name = "expander" version = "2.0.0" @@ -1534,9 +1555,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -1562,9 +1583,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fiat-crypto" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" [[package]] name = "fixed-hash" @@ -1882,7 +1903,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -1920,7 +1941,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -1929,7 +1950,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", ] [[package]] @@ -1938,7 +1959,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "allocator-api2", "serde", ] @@ -1960,9 +1981,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "hex" @@ -2095,9 +2116,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2180,9 +2201,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2262,7 +2283,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.4", + "hermit-abi 0.3.6", "libc", "windows-sys 0.48.0", ] @@ -2278,9 +2299,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -2424,9 +2445,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libm" @@ -2512,9 +2533,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22" dependencies = [ "hashbrown 0.14.3", ] @@ -2549,7 +2570,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.30", + "rustix 0.38.31", ] [[package]] @@ -2602,9 +2623,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -2681,11 +2702,10 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] @@ -2703,9 +2723,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -2716,7 +2736,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.4", + "hermit-abi 0.3.6", "libc", ] @@ -2970,14 +2990,14 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" +checksum = "30054e72317ab98eddd8561db0f6524df3367636884b7b21b703e4b280a84a14" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.30", + "rustix 0.38.31", "tracing", "windows-sys 0.52.0", ] @@ -3047,7 +3067,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.21.0", + "toml_edit 0.21.1", ] [[package]] @@ -3309,9 +3329,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -3341,7 +3361,7 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.1", + "rustls-webpki 0.102.2", "subtle", "zeroize", ] @@ -3392,9 +3412,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" +checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" [[package]] name = "rustls-webpki" @@ -3408,9 +3428,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.1" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", "rustls-pki-types", @@ -3608,7 +3628,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "cfg-if", "hashbrown 0.13.2", ] @@ -3638,7 +3658,7 @@ dependencies = [ "aead", "arrayref", "arrayvec 0.7.4", - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "getrandom_or_panic", "merlin 3.0.0", "rand_core 0.6.4", @@ -3936,7 +3956,7 @@ dependencies = [ "hashbrown 0.14.3", "hex", "hmac 0.12.1", - "itertools 0.12.0", + "itertools 0.12.1", "libm", "libsecp256k1", "merlin 3.0.0", @@ -3985,7 +4005,7 @@ dependencies = [ "futures-util", "hashbrown 0.14.3", "hex", - "itertools 0.12.0", + "itertools 0.12.1", "log", "lru", "no-std-net", @@ -4318,7 +4338,7 @@ version = "29.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e4d24d84a0beb44a71dcac1b41980e1edf7fb722c7f3046710136a283cd479b" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.8", "hash-db", "lazy_static", "memory-db", @@ -4630,7 +4650,7 @@ dependencies = [ name = "subxt-macro" version = "0.34.0" dependencies = [ - "darling 0.20.5", + "darling 0.20.6", "parity-scale-codec", "proc-macro-error", "quote", @@ -4886,14 +4906,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.5", ] [[package]] @@ -4911,9 +4931,9 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] @@ -4922,22 +4942,33 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e68c159e8f5ba8a28c4eb7b0c0c190d77bb479047ca713270048145a9ad28a" +dependencies = [ + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.1", ] [[package]] @@ -5356,9 +5387,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -5579,9 +5610,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -5596,7 +5627,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.30", + "rustix 0.38.31", "windows-sys 0.48.0", ] @@ -5840,9 +5871,18 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401" dependencies = [ "memchr", ] @@ -5858,11 +5898,11 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek 4.1.1", + "curve25519-dalek 4.1.2", "rand_core 0.6.4", "serde", "zeroize", diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index f364bcdceec..cb3fc3363fc 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -24,11 +24,23 @@ default = ["jsonrpsee", "native"] # Enable this for native (ie non web/wasm builds). # Exactly 1 of "web" and "native" is expected. -native = ["jsonrpsee?/async-client", "jsonrpsee?/client-ws-transport-native-tls", "subxt-lightclient?/native", "tokio-util"] +native = [ + "jsonrpsee?/async-client", + "jsonrpsee?/client-ws-transport-native-tls", + "subxt-lightclient?/native", + "tokio-util" +] # Enable this for web/wasm builds. # Exactly 1 of "web" and "native" is expected. -web = ["jsonrpsee?/async-wasm-client", "jsonrpsee?/client-web-transport", "getrandom/js", "subxt-lightclient?/web", "subxt-macro/web", "instant/wasm-bindgen"] +web = [ + "jsonrpsee?/async-wasm-client", + "jsonrpsee?/client-web-transport", + "getrandom/js", + "subxt-lightclient?/web", + "subxt-macro/web", + "instant/wasm-bindgen" +] # Enable this to use the reconnecting rpc client unstable-reconnecting-rpc-client = ["dep:reconnecting-jsonrpsee-ws-client"] From ac3c7d7467ea7ad26d4b4ac810d13fccfc3febcb Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 15 Feb 2024 17:38:26 +0100 Subject: [PATCH 33/37] fix examples with core crate --- Cargo.lock | 4 +-- cli/src/commands/explore/pallets/calls.rs | 2 +- core/src/lib.rs | 16 +++++++----- core/src/tx/mod.rs | 14 ++++++++++ subxt/Cargo.toml | 18 +++---------- subxt/examples/setup_config_custom.rs | 6 ++--- .../examples/setup_config_signed_extension.rs | 3 +-- subxt/src/backend/legacy/mod.rs | 3 ++- subxt/src/backend/unstable/follow_stream.rs | 4 +-- .../backend/unstable/follow_stream_driver.rs | 2 +- .../backend/unstable/follow_stream_unpin.rs | 6 ++--- subxt/src/backend/unstable/mod.rs | 2 +- subxt/src/backend/unstable/rpc_methods.rs | 2 +- subxt/src/backend/unstable/storage_items.rs | 2 +- subxt/src/blocks/block_types.rs | 5 ++-- subxt/src/blocks/blocks_client.rs | 4 +-- subxt/src/blocks/extrinsic_types.rs | 18 ++++++------- subxt/src/client/mod.rs | 1 + subxt/src/lib.rs | 11 +++++++- subxt/src/macros.rs | 2 +- subxt/src/runtime_api/runtime_client.rs | 3 ++- subxt/src/runtime_api/runtime_types.rs | 2 +- subxt/src/storage/storage_client.rs | 2 +- subxt/src/storage/storage_type.rs | 2 +- subxt/src/tx/tx_client.rs | 26 ++++++------------- subxt/src/tx/tx_progress.rs | 10 ++++--- subxt/src/utils.rs | 2 +- 27 files changed, 89 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c12e4f3580..343d08e3cd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -793,9 +793,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", diff --git a/cli/src/commands/explore/pallets/calls.rs b/cli/src/commands/explore/pallets/calls.rs index f5d9609a2e7..13edbd83ec1 100644 --- a/cli/src/commands/explore/pallets/calls.rs +++ b/cli/src/commands/explore/pallets/calls.rs @@ -154,7 +154,7 @@ fn mocked_offline_client(metadata: Metadata) -> OfflineClient { H256::from_str("91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3") .expect("Valid hash; qed"); - let runtime_version = subxt::backend::RuntimeVersion { + let runtime_version = subxt::RuntimeVersion { spec_version: 9370, transaction_version: 20, }; diff --git a/core/src/lib.rs b/core/src/lib.rs index 455d0b8b07b..ed04b547758 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -15,7 +15,7 @@ pub mod config; pub mod constants; pub mod custom_values; pub mod dynamic; -mod error; +pub mod error; pub mod metadata; pub mod runtime_api; pub mod signer; @@ -23,17 +23,21 @@ pub mod storage; pub mod tx; pub mod utils; -pub use error::{Error, ExtrinsicParamsError, MetadataError, StorageAddressError}; - pub use client::{ClientBase, RuntimeVersion}; pub use config::{ - BlockHash, Config, ExtrinsicParams, ExtrinsicParamsEncoder, PolkadotConfig, - PolkadotExtrinsicParams, SubstrateConfig, SubstrateExtrinsicParams, + signed_extensions, BlockHash, Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, + PolkadotConfig, PolkadotExtrinsicParams, SignedExtension, SubstrateConfig, + SubstrateExtrinsicParams, }; +pub use dynamic::{DecodedValue, DecodedValueThunk}; +pub use error::{Error, ExtrinsicParamsError, MetadataError, StorageAddressError}; pub use metadata::Metadata; pub use signer::Signer; pub use storage::StorageAddress; -pub use utils::{to_hex, AccountId32, MultiAddress, MultiSignature, Yes, H160, H256, H512}; +pub use utils::{ + strip_compact_prefix, to_hex, AccountId32, KeyedVec, MultiAddress, MultiSignature, Yes, H160, + H256, H512, +}; #[macro_use] mod macros; diff --git a/core/src/tx/mod.rs b/core/src/tx/mod.rs index de2f45b64a4..ebf5ca73f1a 100644 --- a/core/src/tx/mod.rs +++ b/core/src/tx/mod.rs @@ -38,6 +38,20 @@ pub trait TxPayload { fn validation_details(&self) -> Option> { None } + + fn validate(&self, metadata: &Metadata) -> Result<(), Error> { + if let Some(details) = self.validation_details() { + let expected_hash = metadata + .pallet_by_name_err(details.pallet_name)? + .call_hash(details.call_name) + .ok_or_else(|| MetadataError::CallNameNotFound(details.call_name.to_owned()))?; + + if details.hash != expected_hash { + return Err(MetadataError::IncompatibleCodegen.into()); + } + } + Ok(()) + } } pub struct ValidationDetails<'a> { diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index cb3fc3363fc..80786edc7ab 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -24,23 +24,11 @@ default = ["jsonrpsee", "native"] # Enable this for native (ie non web/wasm builds). # Exactly 1 of "web" and "native" is expected. -native = [ - "jsonrpsee?/async-client", - "jsonrpsee?/client-ws-transport-native-tls", - "subxt-lightclient?/native", - "tokio-util" -] +native = ["jsonrpsee?/async-client", "jsonrpsee?/client-ws-transport-native-tls", "subxt-lightclient?/native", "tokio-util"] # Enable this for web/wasm builds. # Exactly 1 of "web" and "native" is expected. -web = [ - "jsonrpsee?/async-wasm-client", - "jsonrpsee?/client-web-transport", - "getrandom/js", - "subxt-lightclient?/web", - "subxt-macro/web", - "instant/wasm-bindgen" -] +web = ["jsonrpsee?/async-wasm-client", "jsonrpsee?/client-web-transport", "getrandom/js", "subxt-lightclient?/web", "subxt-macro/web", "instant/wasm-bindgen"] # Enable this to use the reconnecting rpc client unstable-reconnecting-rpc-client = ["dep:reconnecting-jsonrpsee-ws-client"] @@ -51,7 +39,7 @@ jsonrpsee = ["dep: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 = ["sp-core", "sp-runtime", "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 diff --git a/subxt/examples/setup_config_custom.rs b/subxt/examples/setup_config_custom.rs index 0f2ad2e27b4..886999df7ad 100644 --- a/subxt/examples/setup_config_custom.rs +++ b/subxt/examples/setup_config_custom.rs @@ -1,6 +1,6 @@ #![allow(missing_docs)] use codec::Encode; -use subxt::client::OfflineClientT; +use subxt::client::{ClientBase}; use subxt::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder}; use subxt_core::ExtrinsicParamsError; use subxt_signer::sr25519::dev; @@ -57,9 +57,9 @@ impl ExtrinsicParams for CustomExtrinsicParams { type OtherParams = CustomExtrinsicParamsBuilder; // Gather together all of the params we will need to encode: - fn new>( + fn new( _nonce: u64, - client: Client, + client: ClientBase, other_params: Self::OtherParams, ) -> Result { Ok(Self { diff --git a/subxt/examples/setup_config_signed_extension.rs b/subxt/examples/setup_config_signed_extension.rs index a48e17570f3..01593f7fed7 100644 --- a/subxt/examples/setup_config_signed_extension.rs +++ b/subxt/examples/setup_config_signed_extension.rs @@ -2,7 +2,6 @@ use codec::Encode; use scale_encode::EncodeAsType; use scale_info::PortableRegistry; -use subxt::client::OfflineClientT; use subxt::config::{ Config, DefaultExtrinsicParamsBuilder, ExtrinsicParams, ExtrinsicParamsEncoder, }; @@ -62,7 +61,7 @@ impl ExtrinsicParams for CustomSignedExtension { fn new( _nonce: u64, - _client: subxt_core::ClientBase, + _client: subxt_core::ClientBase, _other_params: Self::OtherParams, ) -> Result { Ok(CustomSignedExtension) diff --git a/subxt/src/backend/legacy/mod.rs b/subxt/src/backend/legacy/mod.rs index cffb3f44750..507d6d9ce5e 100644 --- a/subxt/src/backend/legacy/mod.rs +++ b/subxt/src/backend/legacy/mod.rs @@ -12,12 +12,13 @@ use crate::backend::{ rpc::RpcClient, Backend, BlockRef, RuntimeVersion, StorageResponse, StreamOf, StreamOfResults, TransactionStatus, }; -use crate::{config::Header, Config, Error}; +use crate::Error; use async_trait::async_trait; use futures::{future, future::Either, stream, Future, FutureExt, Stream, StreamExt}; use std::collections::VecDeque; use std::pin::Pin; use std::task::{Context, Poll}; +use subxt_core::{config::Header, Config}; // Expose the RPC methods. pub use rpc_methods::LegacyRpcMethods; diff --git a/subxt/src/backend/unstable/follow_stream.rs b/subxt/src/backend/unstable/follow_stream.rs index 9474f7a3028..6a55def6c0b 100644 --- a/subxt/src/backend/unstable/follow_stream.rs +++ b/subxt/src/backend/unstable/follow_stream.rs @@ -3,12 +3,12 @@ // see LICENSE for license details. use super::rpc_methods::{FollowEvent, UnstableRpcMethods}; -use crate::config::Config; use crate::error::Error; use futures::{FutureExt, Stream, StreamExt}; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; +use subxt_core::config::Config; /// A `Stream` whose goal is to remain subscribed to `chainHead_follow`. It will re-subscribe if the subscription /// is ended for any reason, and it will return the current `subscription_id` as an event, along with the other @@ -206,9 +206,9 @@ pub(super) mod test_utils { use crate::backend::unstable::rpc_methods::{ BestBlockChanged, Finalized, Initialized, NewBlock, }; - use crate::config::substrate::H256; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; + use subxt_core::config::substrate::H256; /// Given some events, returns a follow stream getter that we can use in /// place of the usual RPC method. diff --git a/subxt/src/backend/unstable/follow_stream_driver.rs b/subxt/src/backend/unstable/follow_stream_driver.rs index d32c98a1048..295bca5ab8a 100644 --- a/subxt/src/backend/unstable/follow_stream_driver.rs +++ b/subxt/src/backend/unstable/follow_stream_driver.rs @@ -4,7 +4,6 @@ use super::follow_stream_unpin::{BlockRef, FollowStreamMsg, FollowStreamUnpin}; use crate::backend::unstable::rpc_methods::{FollowEvent, Initialized, RuntimeEvent}; -use crate::config::BlockHash; use crate::error::Error; use futures::stream::{Stream, StreamExt}; use std::collections::{HashMap, HashSet, VecDeque}; @@ -12,6 +11,7 @@ use std::ops::DerefMut; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Waker}; +use subxt_core::config::BlockHash; /// A `Stream` which builds on `FollowStreamDriver`, and allows multiple subscribers to obtain events /// from the single underlying subscription (each being provided an `Initialized` message and all new diff --git a/subxt/src/backend/unstable/follow_stream_unpin.rs b/subxt/src/backend/unstable/follow_stream_unpin.rs index ca00e37690b..7b97ae33de3 100644 --- a/subxt/src/backend/unstable/follow_stream_unpin.rs +++ b/subxt/src/backend/unstable/follow_stream_unpin.rs @@ -7,7 +7,6 @@ use super::UnstableRpcMethods; use crate::backend::unstable::rpc_methods::{ BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock, }; -use crate::config::{BlockHash, Config}; use crate::error::Error; use futures::stream::{FuturesUnordered, Stream, StreamExt}; use std::collections::{HashMap, HashSet}; @@ -15,6 +14,7 @@ use std::future::Future; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Waker}; +use subxt_core::config::{BlockHash, Config}; /// The type of stream item. pub use super::follow_stream::FollowStreamMsg; @@ -455,7 +455,7 @@ impl Drop for BlockRef { pub(super) mod test_utils { use super::super::follow_stream::{test_utils::test_stream_getter, FollowStream}; use super::*; - use crate::config::substrate::H256; + use subxt_core::config::substrate::H256; pub type UnpinRx = std::sync::mpsc::Receiver<(Hash, Arc)>; @@ -542,7 +542,7 @@ mod test { }; use super::test_utils::{assert_from_unpin_rx, ev_new_block_ref, test_unpin_stream_getter}; use super::*; - use crate::config::substrate::H256; + use subxt_core::config::substrate::H256; #[tokio::test] async fn hands_back_blocks() { diff --git a/subxt/src/backend/unstable/mod.rs b/subxt/src/backend/unstable/mod.rs index 82a2d50d48a..8d6af54cd95 100644 --- a/subxt/src/backend/unstable/mod.rs +++ b/subxt/src/backend/unstable/mod.rs @@ -25,7 +25,6 @@ use crate::backend::{ rpc::RpcClient, Backend, BlockRef, BlockRefT, RuntimeVersion, StorageResponse, StreamOf, StreamOfResults, TransactionStatus, }; -use crate::config::BlockHash; use crate::error::{Error, RpcError}; use crate::Config; use async_trait::async_trait; @@ -35,6 +34,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::task::Poll; use storage_items::StorageItems; +use subxt_core::config::BlockHash; // Expose the RPC methods. pub use rpc_methods::UnstableRpcMethods; diff --git a/subxt/src/backend/unstable/rpc_methods.rs b/subxt/src/backend/unstable/rpc_methods.rs index a397d2fbdb2..107f6151872 100644 --- a/subxt/src/backend/unstable/rpc_methods.rs +++ b/subxt/src/backend/unstable/rpc_methods.rs @@ -7,13 +7,13 @@ //! methods exposed here. use crate::backend::rpc::{rpc_params, RpcClient, RpcSubscription}; -use crate::config::BlockHash; use crate::{Config, Error}; use derivative::Derivative; use futures::{Stream, StreamExt}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; use std::task::Poll; +use subxt_core::config::BlockHash; use subxt_core::to_hex; /// An interface to call the unstable RPC methods. This interface is instantiated with diff --git a/subxt/src/backend/unstable/storage_items.rs b/subxt/src/backend/unstable/storage_items.rs index 73790765ead..148eac15f25 100644 --- a/subxt/src/backend/unstable/storage_items.rs +++ b/subxt/src/backend/unstable/storage_items.rs @@ -7,7 +7,6 @@ use super::follow_stream_unpin::BlockRef; use super::rpc_methods::{ FollowEvent, MethodResponse, StorageQuery, StorageResult, UnstableRpcMethods, }; -use crate::config::Config; use crate::error::{Error, RpcError}; use futures::{FutureExt, Stream, StreamExt}; use std::collections::VecDeque; @@ -15,6 +14,7 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; +use subxt_core::config::Config; /// Obtain a stream of storage items given some query. this handles continuing /// and stopping under the hood, and returns a stream of `StorageResult`s. diff --git a/subxt/src/blocks/block_types.rs b/subxt/src/blocks/block_types.rs index a1cb01ec250..f6c27fe2d83 100644 --- a/subxt/src/blocks/block_types.rs +++ b/subxt/src/blocks/block_types.rs @@ -6,16 +6,15 @@ use crate::{ backend::BlockRef, blocks::{extrinsic_types::ExtrinsicPartTypeIds, Extrinsics}, client::{OfflineClientT, OnlineClientT}, - config::{Config, Header}, error::{BlockError, DecodeError, Error}, events, runtime_api::RuntimeApi, storage::Storage, }; - use codec::{Decode, Encode}; use futures::lock::Mutex as AsyncMutex; use std::sync::Arc; +use subxt_core::{Config, Header}; /// A representation of a block. pub struct Block { @@ -57,7 +56,7 @@ where } /// Return the block number. - pub fn number(&self) -> ::Number { + pub fn number(&self) -> ::Number { self.header().number() } diff --git a/subxt/src/blocks/blocks_client.rs b/subxt/src/blocks/blocks_client.rs index 9ec031a35aa..7d86de3d017 100644 --- a/subxt/src/blocks/blocks_client.rs +++ b/subxt/src/blocks/blocks_client.rs @@ -6,13 +6,13 @@ use super::Block; use crate::{ backend::{BlockRef, StreamOfResults}, client::OnlineClientT, - config::Config, error::{BlockError, Error}, - utils::PhantomDataSendSync, }; use derivative::Derivative; use futures::StreamExt; use std::future::Future; +use subxt_core::utils::PhantomDataSendSync; +use subxt_core::Config; type BlockStream = StreamOfResults; type BlockStreamRes = Result, Error>; diff --git a/subxt/src/blocks/extrinsic_types.rs b/subxt/src/blocks/extrinsic_types.rs index e90e3c3bdca..734862d1ff2 100644 --- a/subxt/src/blocks/extrinsic_types.rs +++ b/subxt/src/blocks/extrinsic_types.rs @@ -5,22 +5,20 @@ use crate::{ blocks::block_types::{get_events, CachedEvents}, client::{OfflineClientT, OnlineClientT}, - config::{Config, Hasher}, error::{BlockError, Error, MetadataError}, - events, Metadata, + events, }; - -use subxt_core::metadata::{types::PalletMetadata, MetadatExt}; - -use crate::config::signed_extensions::{ - ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce, +use subxt_core::{ + metadata::MetadatExt, + signed_extensions::{ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce}, + strip_compact_prefix, Config, Hasher, Metadata, }; -use crate::config::SignedExtension; -use crate::dynamic::DecodedValue; -use crate::utils::strip_compact_prefix; + use codec::Decode; use derivative::Derivative; use scale_decode::{DecodeAsFields, DecodeAsType}; +use subxt_core::{DecodedValue, SignedExtension}; +use subxt_metadata::PalletMetadata; use std::sync::Arc; diff --git a/subxt/src/client/mod.rs b/subxt/src/client/mod.rs index c764af4b59d..1eabeeed52f 100644 --- a/subxt/src/client/mod.rs +++ b/subxt/src/client/mod.rs @@ -23,3 +23,4 @@ pub use offline_client::{OfflineClient, OfflineClientT}; pub use online_client::{ ClientRuntimeUpdater, OnlineClient, OnlineClientT, RuntimeUpdaterStream, Update, UpgradeError, }; +pub use subxt_core::ClientBase; \ No newline at end of file diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index 5842f513e9d..6774f2eecb5 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -66,7 +66,15 @@ pub use crate::{ }; // We replace this by proper exports, once the API of subxt_core is aggreed upon. -pub use subxt_core::*; +// pub use subxt_core::*; + +pub use subxt_core::{ + dynamic, signed_extensions, AccountId32, BlockHash, ClientBase, Config, DecodedValue, + DecodedValueThunk, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, Metadata, + MultiAddress, PolkadotConfig, PolkadotExtrinsicParams, RuntimeVersion, Signer, StorageAddress, + SubstrateConfig, SubstrateExtrinsicParams, + config, metadata, +}; /// Re-export external crates that are made use of in the subxt API. pub mod ext { @@ -77,6 +85,7 @@ 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; diff --git a/subxt/src/macros.rs b/subxt/src/macros.rs index 47362f18ca5..3c6d2a8e6cb 100644 --- a/subxt/src/macros.rs +++ b/subxt/src/macros.rs @@ -64,7 +64,7 @@ macro_rules! cfg_reconnecting_rpc_client { } pub(crate) use { - cfg_feature, cfg_jsonrpsee, cfg_reconnecting_rpc_client, cfg_substrate_compat, + cfg_feature, cfg_jsonrpsee, cfg_reconnecting_rpc_client, cfg_unstable_light_client, }; diff --git a/subxt/src/runtime_api/runtime_client.rs b/subxt/src/runtime_api/runtime_client.rs index 5770285b83c..708c0fb2536 100644 --- a/subxt/src/runtime_api/runtime_client.rs +++ b/subxt/src/runtime_api/runtime_client.rs @@ -4,9 +4,10 @@ use super::runtime_types::RuntimeApi; -use crate::{backend::BlockRef, client::OnlineClientT, error::Error, Config}; +use crate::{backend::BlockRef, client::OnlineClientT, error::Error}; use derivative::Derivative; use std::{future::Future, marker::PhantomData}; +use subxt_core::Config; /// Execute runtime API calls. #[derive(Derivative)] diff --git a/subxt/src/runtime_api/runtime_types.rs b/subxt/src/runtime_api/runtime_types.rs index 0bc518fe6b1..03c8256788b 100644 --- a/subxt/src/runtime_api/runtime_types.rs +++ b/subxt/src/runtime_api/runtime_types.rs @@ -6,12 +6,12 @@ use crate::{ backend::{BackendExt, BlockRef}, client::OnlineClientT, error::{Error, MetadataError}, - Config, }; use codec::Decode; use derivative::Derivative; use std::{future::Future, marker::PhantomData}; use subxt_core::metadata::{DecodeWithMetadata, MetadatExt}; +use subxt_core::Config; use super::RuntimeApiPayload; diff --git a/subxt/src/storage/storage_client.rs b/subxt/src/storage/storage_client.rs index 7b62ed6b9af..98cca519a87 100644 --- a/subxt/src/storage/storage_client.rs +++ b/subxt/src/storage/storage_client.rs @@ -7,10 +7,10 @@ use crate::{ backend::BlockRef, client::{OfflineClientT, OnlineClientT}, error::Error, - Config, }; use derivative::Derivative; use std::{future::Future, marker::PhantomData}; +use subxt_core::Config; use subxt_core::{ metadata::MetadatExt, storage::utils::{storage_address_bytes, storage_address_root_bytes, validate_storage_address}, diff --git a/subxt/src/storage/storage_type.rs b/subxt/src/storage/storage_type.rs index 51cc4cac189..8761ed0108e 100644 --- a/subxt/src/storage/storage_type.rs +++ b/subxt/src/storage/storage_type.rs @@ -14,13 +14,13 @@ use crate::{ backend::{BackendExt, BlockRef}, client::OnlineClientT, error::{Error, MetadataError}, - Config, }; use codec::Decode; use derivative::Derivative; use futures::StreamExt; use std::{future::Future, marker::PhantomData}; use subxt_core::metadata::DecodeWithMetadata; +use subxt_core::Config; /// This is returned from a couple of storage functions. pub use crate::backend::StreamOfResults; diff --git a/subxt/src/tx/tx_client.rs b/subxt/src/tx/tx_client.rs index 8029781f910..0e2eca679fb 100644 --- a/subxt/src/tx/tx_client.rs +++ b/subxt/src/tx/tx_client.rs @@ -4,19 +4,20 @@ use std::borrow::Cow; +use super::TxProgress; use crate::{ backend::{BackendExt, BlockRef, TransactionStatus}, client::{OfflineClientT, OnlineClientT}, - config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher}, - error::{Error, MetadataError}, - utils::{Encoded, PhantomDataSendSync}, + error::Error, }; use codec::{Compact, Decode, Encode}; use derivative::Derivative; use sp_core_hashing::blake2_256; -use subxt_core::{metadata::MetadatExt, tx::TxPayload, Signer as SignerT}; - -use super::TxProgress; +use subxt_core::{ + tx::TxPayload, + utils::{Encoded, PhantomDataSendSync}, + Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Signer as SignerT, +}; /// A client for working with transactions. #[derive(Derivative)] @@ -45,18 +46,7 @@ impl> TxClient { where Call: TxPayload, { - if let Some(details) = call.validation_details() { - let expected_hash = self - .client - .metadata() - .pallet_by_name_err(details.pallet_name)? - .call_hash(details.call_name) - .ok_or_else(|| MetadataError::CallNameNotFound(details.call_name.to_owned()))?; - - if details.hash != expected_hash { - return Err(MetadataError::IncompatibleCodegen.into()); - } - } + call.validate(&self.client.metadata())?; Ok(()) } diff --git a/subxt/src/tx/tx_progress.rs b/subxt/src/tx/tx_progress.rs index 166b2cda330..77b3f98cfbf 100644 --- a/subxt/src/tx/tx_progress.rs +++ b/subxt/src/tx/tx_progress.rs @@ -6,16 +6,16 @@ use std::task::Poll; -use crate::utils::strip_compact_prefix; use crate::{ backend::{BlockRef, StreamOfResults, TransactionStatus as BackendTxStatus}, client::OnlineClientT, error::{DispatchError, Error, RpcError, TransactionError}, events::EventsClient, - Config, }; use derivative::Derivative; use futures::{Stream, StreamExt}; +use subxt_core::utils::strip_compact_prefix; +use subxt_core::Config; /// This struct represents a subscription to the progress of some transaction. pub struct TxProgress { @@ -296,7 +296,7 @@ impl> TxInBlock { let extrinsic_idx = block_body .iter() .position(|ext| { - use crate::config::Hasher; + use subxt_core::Hasher; let Ok((_, stripped)) = strip_compact_prefix(ext) else { return false; }; @@ -325,9 +325,11 @@ mod test { backend::{StreamOfResults, TransactionStatus}, client::{OfflineClientT, OnlineClientT}, tx::TxProgress, - Config, Error, SubstrateConfig, + Error, }; + use subxt_core::{Config, SubstrateConfig}; + type MockTxProgress = TxProgress; type MockHash = ::Hash; type MockSubstrateTxStatus = TransactionStatus; diff --git a/subxt/src/utils.rs b/subxt/src/utils.rs index b02acebba06..d7cedf98daf 100644 --- a/subxt/src/utils.rs +++ b/subxt/src/utils.rs @@ -4,7 +4,7 @@ //! Utility functions used in subxt. Reexports all elements from [`subxt_core::utils`]; -pub use subxt_core::utils::*; +pub use subxt_core::utils::{strip_compact_prefix, to_hex,UncheckedExtrinsic, AccountId32, MultiAddress, MultiSignature, KeyedVec, Yes, H160, H256, H512, bits }; use url::Url; From e5276874d26ef3621558e5234ec0a6a3326a40cf Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 15 Feb 2024 17:40:52 +0100 Subject: [PATCH 34/37] formatting --- subxt/examples/setup_config_custom.rs | 2 +- subxt/src/client/mod.rs | 2 +- subxt/src/client/offline_client.rs | 2 +- subxt/src/client/online_client.rs | 4 ++-- subxt/src/lib.rs | 9 ++++----- subxt/src/macros.rs | 3 +-- subxt/src/utils.rs | 5 ++++- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/subxt/examples/setup_config_custom.rs b/subxt/examples/setup_config_custom.rs index 886999df7ad..ea918c578bd 100644 --- a/subxt/examples/setup_config_custom.rs +++ b/subxt/examples/setup_config_custom.rs @@ -1,6 +1,6 @@ #![allow(missing_docs)] use codec::Encode; -use subxt::client::{ClientBase}; +use subxt::client::ClientBase; use subxt::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder}; use subxt_core::ExtrinsicParamsError; use subxt_signer::sr25519::dev; diff --git a/subxt/src/client/mod.rs b/subxt/src/client/mod.rs index 1eabeeed52f..c9e725b3c70 100644 --- a/subxt/src/client/mod.rs +++ b/subxt/src/client/mod.rs @@ -23,4 +23,4 @@ pub use offline_client::{OfflineClient, OfflineClientT}; pub use online_client::{ ClientRuntimeUpdater, OnlineClient, OnlineClientT, RuntimeUpdaterStream, Update, UpgradeError, }; -pub use subxt_core::ClientBase; \ No newline at end of file +pub use subxt_core::ClientBase; diff --git a/subxt/src/client/offline_client.rs b/subxt/src/client/offline_client.rs index 1787170a000..d4830dd5897 100644 --- a/subxt/src/client/offline_client.rs +++ b/subxt/src/client/offline_client.rs @@ -93,7 +93,7 @@ impl OfflineClient { /// 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. diff --git a/subxt/src/client/online_client.rs b/subxt/src/client/online_client.rs index 8fbc9c821e8..5898cdbdda0 100644 --- a/subxt/src/client/online_client.rs +++ b/subxt/src/client/online_client.rs @@ -274,7 +274,7 @@ impl OnlineClient { /// 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 } /// Change the [`RuntimeVersion`] used in this client. @@ -298,7 +298,7 @@ impl OnlineClient { 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(), ) } diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index 6774f2eecb5..fecb5260831 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -69,11 +69,10 @@ pub use crate::{ // pub use subxt_core::*; pub use subxt_core::{ - dynamic, signed_extensions, AccountId32, BlockHash, ClientBase, Config, DecodedValue, - DecodedValueThunk, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, Metadata, - MultiAddress, PolkadotConfig, PolkadotExtrinsicParams, RuntimeVersion, Signer, StorageAddress, - SubstrateConfig, SubstrateExtrinsicParams, - config, metadata, + config, dynamic, metadata, signed_extensions, AccountId32, BlockHash, ClientBase, Config, + DecodedValue, DecodedValueThunk, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, + Metadata, MultiAddress, PolkadotConfig, PolkadotExtrinsicParams, RuntimeVersion, Signer, + StorageAddress, SubstrateConfig, SubstrateExtrinsicParams, }; /// Re-export external crates that are made use of in the subxt API. diff --git a/subxt/src/macros.rs b/subxt/src/macros.rs index 3c6d2a8e6cb..2f7f4834d76 100644 --- a/subxt/src/macros.rs +++ b/subxt/src/macros.rs @@ -64,8 +64,7 @@ macro_rules! cfg_reconnecting_rpc_client { } pub(crate) use { - cfg_feature, cfg_jsonrpsee, cfg_reconnecting_rpc_client, - cfg_unstable_light_client, + cfg_feature, cfg_jsonrpsee, cfg_reconnecting_rpc_client, cfg_unstable_light_client, }; // Only used by light-client. diff --git a/subxt/src/utils.rs b/subxt/src/utils.rs index d7cedf98daf..756dc44c98b 100644 --- a/subxt/src/utils.rs +++ b/subxt/src/utils.rs @@ -4,7 +4,10 @@ //! Utility functions used in subxt. Reexports all elements from [`subxt_core::utils`]; -pub use subxt_core::utils::{strip_compact_prefix, to_hex,UncheckedExtrinsic, AccountId32, MultiAddress, MultiSignature, KeyedVec, Yes, H160, H256, H512, bits }; +pub use subxt_core::utils::{ + bits, strip_compact_prefix, to_hex, AccountId32, KeyedVec, MultiAddress, MultiSignature, + UncheckedExtrinsic, Yes, H160, H256, H512, +}; use url::Url; From 6147ac9418ec68e822f8459e9ef5e962504dd01d Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Thu, 15 Feb 2024 17:51:30 +0100 Subject: [PATCH 35/37] fix unused deps --- Cargo.lock | 2 -- subxt/Cargo.toml | 4 ---- 2 files changed, 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 343d08e3cd8..df17609ab43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4502,9 +4502,7 @@ version = "0.34.0" dependencies = [ "assert_matches", "async-trait", - "base58", "bitvec", - "blake2", "derivative", "either", "frame-metadata 16.0.0", diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index f9ebd92afdc..f289d13b01d 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -88,10 +88,6 @@ impl-serde = { workspace = true } primitive-types = { workspace = true, features = ["codec", "scale-info", "serde"] } sp-core-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"] } From fbf2cc9b68c5695647fc8f35b0673461984394f7 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 16 Feb 2024 10:48:02 +0100 Subject: [PATCH 36/37] fix doc tests and docs --- cli/src/commands/explore/runtime_apis/mod.rs | 2 +- subxt/src/lib.rs | 2 +- subxt/src/tx/mod.rs | 1 + subxt/src/utils.rs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/src/commands/explore/runtime_apis/mod.rs b/cli/src/commands/explore/runtime_apis/mod.rs index cdd5681e26b..9fe4079bca5 100644 --- a/cli/src/commands/explore/runtime_apis/mod.rs +++ b/cli/src/commands/explore/runtime_apis/mod.rs @@ -19,7 +19,7 @@ use subxt_metadata::RuntimeApiMetadata; /// Runs for a specified runtime API trait. /// Cases to consider: -/// ```norun +/// ```txt /// method is: /// None => Show pallet docs + available methods /// Some (invalid) => Show Error + available methods diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index fecb5260831..e0a69a2872b 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -72,7 +72,7 @@ pub use subxt_core::{ config, dynamic, metadata, signed_extensions, AccountId32, BlockHash, ClientBase, Config, DecodedValue, DecodedValueThunk, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, Metadata, MultiAddress, PolkadotConfig, PolkadotExtrinsicParams, RuntimeVersion, Signer, - StorageAddress, SubstrateConfig, SubstrateExtrinsicParams, + StorageAddress, SubstrateConfig, SubstrateExtrinsicParams }; /// Re-export external crates that are made use of in the subxt API. diff --git a/subxt/src/tx/mod.rs b/subxt/src/tx/mod.rs index b1e988c40d1..8d26b8009f8 100644 --- a/subxt/src/tx/mod.rs +++ b/subxt/src/tx/mod.rs @@ -21,5 +21,6 @@ pub use self::{ }; pub use subxt_core::{ signer::Signer, + tx, tx::{dynamic, BoxedPayload, DynamicPayload, Payload, TxPayload}, }; diff --git a/subxt/src/utils.rs b/subxt/src/utils.rs index 756dc44c98b..cf2de8e4fd0 100644 --- a/subxt/src/utils.rs +++ b/subxt/src/utils.rs @@ -6,7 +6,7 @@ pub use subxt_core::utils::{ bits, strip_compact_prefix, to_hex, AccountId32, KeyedVec, MultiAddress, MultiSignature, - UncheckedExtrinsic, Yes, H160, H256, H512, + UncheckedExtrinsic, Yes, H160, H256, H512, Era,Static, }; use url::Url; From aae7636612295ce72d5c32183717f29123133d93 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Fri, 16 Feb 2024 12:31:47 +0100 Subject: [PATCH 37/37] fmt --- subxt/src/lib.rs | 2 +- subxt/src/utils.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index e0a69a2872b..fecb5260831 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -72,7 +72,7 @@ pub use subxt_core::{ config, dynamic, metadata, signed_extensions, AccountId32, BlockHash, ClientBase, Config, DecodedValue, DecodedValueThunk, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, Metadata, MultiAddress, PolkadotConfig, PolkadotExtrinsicParams, RuntimeVersion, Signer, - StorageAddress, SubstrateConfig, SubstrateExtrinsicParams + StorageAddress, SubstrateConfig, SubstrateExtrinsicParams, }; /// Re-export external crates that are made use of in the subxt API. diff --git a/subxt/src/utils.rs b/subxt/src/utils.rs index cf2de8e4fd0..050d8114bd3 100644 --- a/subxt/src/utils.rs +++ b/subxt/src/utils.rs @@ -5,8 +5,8 @@ //! Utility functions used in subxt. Reexports all elements from [`subxt_core::utils`]; pub use subxt_core::utils::{ - bits, strip_compact_prefix, to_hex, AccountId32, KeyedVec, MultiAddress, MultiSignature, - UncheckedExtrinsic, Yes, H160, H256, H512, Era,Static, + bits, strip_compact_prefix, to_hex, AccountId32, Era, KeyedVec, MultiAddress, MultiSignature, + Static, UncheckedExtrinsic, Yes, H160, H256, H512, }; use url::Url;