diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 1cf9638bae..5ffde7e92f 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 d768cbbd9d..dd922383d2 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 f0efbf0153..5965843c5b 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 f14849df6e..1776a78d39 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 d09704beb2..79a8f027d6 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 038ee74747..276ee004f1 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 72720ece28..e071e28c20 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 a02611b87a..8ef6cd7ba7 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 4bbd9f6159..25d72b9a57 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 d7575123aa..e90e3c3bdc 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 4477e21b7a..1787170a00 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 f4e2f8c723..36a742ee3f 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 dce83853bf..0000000000 --- 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 42f3b620af..0000000000 --- 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 ddfa2466c7..0000000000 --- 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 209c3b8fb6..0000000000 --- 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 f0864cee61..0000000000 --- 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 44cfcf7b73..0000000000 --- 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 bac9677ccf..0000000000 --- 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 1190811ac7..b3e4b74aca 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 9a9ccade84..a5321e7cb9 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 d1708eec46..d67b0b0ae5 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 b45dd39ce8..4d0939e711 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 2dd59da35b..2c06617674 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 062734dac2..dbfb33dbbe 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 079ce5f208..9a13d3eac1 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 e982f2b138..94fa7697f8 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 49a17a4dd7..9eecb12978 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 3725fcae22..0000000000 --- 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 f9247547db..0bc518fe6b 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 0219cd8caf..6fda23d5e8 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 e1a3ef504f..0000000000 --- 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 55ab339aea..7b62ed6b9a 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 d31991c6ad..51cc4cac18 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 33581a3261..0000000000 --- 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 0bb2d9f3eb..655198712e 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 6bf6c9ab57..d3121d9eb7 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 732c738752..166b2cda33 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") } }