mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-29 06:47:57 +00:00
init of subxt-core crate
This commit is contained in:
Generated
+30
@@ -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"
|
||||
|
||||
+5
-4
@@ -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"] }
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
# Subxt-Core
|
||||
|
||||
This library provides core functionality using in `subxt` and `subxt-signer`. It should be no-std compatible.
|
||||
@@ -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<C: Config> {
|
||||
pub genesis_hash: C::Hash,
|
||||
pub runtime_version: RuntimeVersion,
|
||||
pub metadata: Metadata,
|
||||
marker: core::marker::PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: Config> MinimalClient<C> {
|
||||
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,
|
||||
}
|
||||
@@ -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<T> = signed_extensions::AnyOf<
|
||||
T,
|
||||
(
|
||||
signed_extensions::CheckSpecVersion,
|
||||
signed_extensions::CheckTxVersion,
|
||||
signed_extensions::CheckNonce,
|
||||
signed_extensions::CheckGenesis<T>,
|
||||
signed_extensions::CheckMortality<T>,
|
||||
signed_extensions::ChargeAssetTxPayment<T>,
|
||||
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<T: Config> {
|
||||
/// `None` means the tx will be immortal.
|
||||
mortality: Option<Mortality<T::Hash>>,
|
||||
/// `None` means we'll use the native token.
|
||||
tip_of_asset_id: Option<T::AssetId>,
|
||||
tip: u128,
|
||||
tip_of: u128,
|
||||
}
|
||||
|
||||
struct Mortality<Hash> {
|
||||
/// 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<T: Config> Default for DefaultExtrinsicParamsBuilder<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mortality: None,
|
||||
tip: 0,
|
||||
tip_of: 0,
|
||||
tip_of_asset_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> DefaultExtrinsicParamsBuilder<T> {
|
||||
/// 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) -> <DefaultExtrinsicParams<T> as ExtrinsicParams<T>>::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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<dyn std::error::Error + Send + Sync + 'static>;
|
||||
|
||||
impl From<core::convert::Infallible> for ExtrinsicParamsError {
|
||||
fn from(value: core::convert::Infallible) -> Self {
|
||||
match value {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<CustomExtrinsicParamsError> 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<T: Config>: 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<T>,
|
||||
other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError>;
|
||||
}
|
||||
|
||||
/// 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<u8>) {}
|
||||
|
||||
/// 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<u8>) {}
|
||||
}
|
||||
@@ -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<Self::AccountId>;
|
||||
|
||||
/// The signature type.
|
||||
type Signature: Debug + Encode;
|
||||
|
||||
/// The hashing system (algorithm) being used in the runtime (e.g. Blake2).
|
||||
type Hasher: Debug + Hasher<Output = Self::Hash>;
|
||||
|
||||
/// The block header.
|
||||
type Header: Debug + Header<Hasher = Self::Hasher> + Sync + Send + DeserializeOwned;
|
||||
|
||||
/// This type defines the extrinsic extra and additional parameters.
|
||||
type ExtrinsicParams: ExtrinsicParams<Self>;
|
||||
|
||||
/// 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<T> = <<T as Config>::ExtrinsicParams as ExtrinsicParams<T>>::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<T> 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: Encode>(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<u64>;
|
||||
/// 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) -> <Self::Hasher as Hasher>::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<T: sp_runtime::traits::Header> Header for T
|
||||
where
|
||||
<T as sp_runtime::traits::Header>::Number: Into<u64>,
|
||||
{
|
||||
type Number = T::Number;
|
||||
type Hasher = T::Hashing;
|
||||
|
||||
fn number(&self) -> Self::Number {
|
||||
*self.number()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: sp_runtime::traits::Hash> Hasher for T {
|
||||
type Output = T::Output;
|
||||
|
||||
fn hash(s: &[u8]) -> Self::Output {
|
||||
<T as sp_runtime::traits::Hash>::hash(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 = <SubstrateConfig as Config>::Hash;
|
||||
type AccountId = <SubstrateConfig as Config>::AccountId;
|
||||
type Address = MultiAddress<Self::AccountId, ()>;
|
||||
type Signature = <SubstrateConfig as Config>::Signature;
|
||||
type Hasher = <SubstrateConfig as Config>::Hasher;
|
||||
type Header = <SubstrateConfig as Config>::Header;
|
||||
type ExtrinsicParams = PolkadotExtrinsicParams<Self>;
|
||||
type AssetId = u32;
|
||||
}
|
||||
|
||||
/// A struct representing the signed extra and additional parameters required
|
||||
/// to construct a transaction for a polkadot node.
|
||||
pub type PolkadotExtrinsicParams<T> = DefaultExtrinsicParams<T>;
|
||||
|
||||
/// A builder which leads to [`PolkadotExtrinsicParams`] being constructed.
|
||||
/// This is what you provide to methods like `sign_and_submit()`.
|
||||
pub type PolkadotExtrinsicParamsBuilder<T> = DefaultExtrinsicParamsBuilder<T>;
|
||||
@@ -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<T: Config>: ExtrinsicParams<T> {
|
||||
/// 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<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
|
||||
type OtherParams = ();
|
||||
|
||||
fn new(
|
||||
_nonce: u64,
|
||||
client: &MinimalClient<T>,
|
||||
_other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(CheckSpecVersion(client.runtime_version().spec_version))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for CheckSpecVersion {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckSpecVersion {
|
||||
type Decoded = ();
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckSpecVersion"
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CheckNonce`] signed extension.
|
||||
pub struct CheckNonce(Compact<u64>);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckNonce {
|
||||
type OtherParams = ();
|
||||
|
||||
fn new(
|
||||
nonce: u64,
|
||||
_client: &MinimalClient<T>,
|
||||
_other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(CheckNonce(Compact(nonce)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for CheckNonce {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> 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<T: Config> ExtrinsicParams<T> for CheckTxVersion {
|
||||
type OtherParams = ();
|
||||
|
||||
fn new(
|
||||
_nonce: u64,
|
||||
client: &MinimalClient<T>,
|
||||
_other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(CheckTxVersion(client.runtime_version().transaction_version))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for CheckTxVersion {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckTxVersion {
|
||||
type Decoded = ();
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckTxVersion"
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CheckGenesis`] signed extension.
|
||||
pub struct CheckGenesis<T: Config>(T::Hash);
|
||||
|
||||
impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
|
||||
type OtherParams = ();
|
||||
|
||||
fn new(
|
||||
_nonce: u64,
|
||||
client: &MinimalClient<T>,
|
||||
_other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(CheckGenesis(client.genesis_hash()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParamsEncoder for CheckGenesis<T> {
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
self.0.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckGenesis<T> {
|
||||
type Decoded = ();
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CheckGenesis"
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`CheckMortality`] signed extension.
|
||||
pub struct CheckMortality<T: Config> {
|
||||
era: Era,
|
||||
checkpoint: T::Hash,
|
||||
}
|
||||
|
||||
/// Parameters to configure the [`CheckMortality`] signed extension.
|
||||
pub struct CheckMortalityParams<T: Config> {
|
||||
era: Era,
|
||||
checkpoint: Option<T::Hash>,
|
||||
}
|
||||
|
||||
impl<T: Config> Default for CheckMortalityParams<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
era: Default::default(),
|
||||
checkpoint: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> CheckMortalityParams<T> {
|
||||
/// 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<T: Config> ExtrinsicParams<T> for CheckMortality<T> {
|
||||
type OtherParams = CheckMortalityParams<T>;
|
||||
|
||||
fn new(
|
||||
_nonce: u64,
|
||||
client: &MinimalClient<T>,
|
||||
other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(CheckMortality {
|
||||
era: other_params.era,
|
||||
checkpoint: other_params.checkpoint.unwrap_or(client.genesis_hash()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParamsEncoder for CheckMortality<T> {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.era.encode_to(v);
|
||||
}
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
self.checkpoint.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for CheckMortality<T> {
|
||||
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<T: Config> {
|
||||
tip: Compact<u128>,
|
||||
asset_id: Option<T::AssetId>,
|
||||
}
|
||||
|
||||
impl<T: Config> ChargeAssetTxPayment<T> {
|
||||
/// 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<T: Config> {
|
||||
tip: u128,
|
||||
asset_id: Option<T::AssetId>,
|
||||
}
|
||||
|
||||
impl<T: Config> Default for ChargeAssetTxPaymentParams<T> {
|
||||
fn default() -> Self {
|
||||
ChargeAssetTxPaymentParams {
|
||||
tip: Default::default(),
|
||||
asset_id: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ChargeAssetTxPaymentParams<T> {
|
||||
/// 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<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment<T> {
|
||||
type OtherParams = ChargeAssetTxPaymentParams<T>;
|
||||
|
||||
fn new(
|
||||
_nonce: u64,
|
||||
_client: &MinimalClient<T>,
|
||||
other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(ChargeAssetTxPayment {
|
||||
tip: Compact(other_params.tip),
|
||||
asset_id: other_params.asset_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExtrinsicParamsEncoder for ChargeAssetTxPayment<T> {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
(self.tip, &self.asset_id).encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> for ChargeAssetTxPayment<T> {
|
||||
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<u128>,
|
||||
}
|
||||
|
||||
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<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
|
||||
type OtherParams = ChargeTransactionPaymentParams;
|
||||
|
||||
fn new(
|
||||
_nonce: u64,
|
||||
_client: &MinimalClient<T>,
|
||||
other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(ChargeTransactionPayment {
|
||||
tip: Compact(other_params.tip),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
self.tip.encode_to(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> SignedExtension<T> 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<T, Params> {
|
||||
params: Vec<Box<dyn ExtrinsicParamsEncoder>>,
|
||||
_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 <T, $($ident),+> ExtrinsicParams<T> for AnyOf<T, ($($ident,)+)>
|
||||
where
|
||||
T: Config,
|
||||
$($ident: SignedExtension<T>,)+
|
||||
{
|
||||
type OtherParams = ($($ident::OtherParams,)+);
|
||||
|
||||
fn new(
|
||||
nonce: u64,
|
||||
client: &MinimalClient<T>,
|
||||
other_params: Self::OtherParams,
|
||||
) -> Result<Self, ExtrinsicParamsError> {
|
||||
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<dyn ExtrinsicParamsEncoder> = 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 <T, $($ident),+> ExtrinsicParamsEncoder for AnyOf<T, ($($ident,)+)>
|
||||
where
|
||||
T: Config,
|
||||
$($ident: SignedExtension<T>,)+
|
||||
{
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
for ext in &self.params {
|
||||
ext.encode_extra_to(v);
|
||||
}
|
||||
}
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -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<Self::AccountId, u32>;
|
||||
type Signature = MultiSignature;
|
||||
type Hasher = BlakeTwo256;
|
||||
type Header = SubstrateHeader<u32, BlakeTwo256>;
|
||||
type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
|
||||
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<T> = DefaultExtrinsicParams<T>;
|
||||
|
||||
/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed.
|
||||
/// This is what you provide to methods like `sign_and_submit()`.
|
||||
pub type SubstrateExtrinsicParamsBuilder<T> = DefaultExtrinsicParamsBuilder<T>;
|
||||
|
||||
/// 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<N: Copy + Into<U256> + TryFrom<U256>, 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<N, H> Header for SubstrateHeader<N, H>
|
||||
where
|
||||
N: Copy + Into<u64> + Into<U256> + TryFrom<U256> + Encode,
|
||||
H: Hasher + Encode,
|
||||
SubstrateHeader<N, H>: 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<DigestItem>,
|
||||
}
|
||||
|
||||
/// 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<u8>),
|
||||
|
||||
/// 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<u8>),
|
||||
|
||||
/// Put a Seal on it. This is only used by native code, and is never seen
|
||||
/// by runtimes.
|
||||
Seal(ConsensusEngineId, Vec<u8>),
|
||||
|
||||
/// Some other thing. Unsupported and experimental.
|
||||
Other(Vec<u8>),
|
||||
|
||||
/// 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<u8> {
|
||||
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<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
let item_type: DigestItemType = Decode::decode(input)?;
|
||||
match item_type {
|
||||
DigestItemType::PreRuntime => {
|
||||
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
|
||||
Ok(Self::PreRuntime(vals.0, vals.1))
|
||||
}
|
||||
DigestItemType::Consensus => {
|
||||
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
|
||||
Ok(Self::Consensus(vals.0, vals.1))
|
||||
}
|
||||
DigestItemType::Seal => {
|
||||
let vals: (ConsensusEngineId, Vec<u8>) = 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<S>(&self, seq: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.using_encoded(|bytes| impl_serde::serialize::serialize(bytes, seq))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> serde::Deserialize<'a> for DigestItem {
|
||||
fn deserialize<D>(de: D) -> Result<Self, D::Error>
|
||||
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<S, T: Copy + Into<U256>>(val: &T, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let u256: U256 = (*val).into();
|
||||
serde::Serialize::serialize(&u256, s)
|
||||
}
|
||||
|
||||
fn deserialize_number<'a, D, T: TryFrom<U256>>(d: D) -> Result<T, D::Error>
|
||||
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<NumberOrHex> for U256 {
|
||||
fn from(num_or_hex: NumberOrHex) -> U256 {
|
||||
num_or_hex.into_u256()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! into_number_or_hex {
|
||||
($($t: ty)+) => {
|
||||
$(
|
||||
impl From<$t> for NumberOrHex {
|
||||
fn from(x: $t) -> Self {
|
||||
NumberOrHex::Number(x.into())
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
into_number_or_hex!(u8 u16 u32 u64);
|
||||
|
||||
impl From<u128> for NumberOrHex {
|
||||
fn from(n: u128) -> Self {
|
||||
NumberOrHex::Hex(n.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<U256> for NumberOrHex {
|
||||
fn from(n: U256) -> Self {
|
||||
NumberOrHex::Hex(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<u32, BlakeTwo256> =
|
||||
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<u32, BlakeTwo256> =
|
||||
serde_json::from_str(numeric_block_number_json).expect("valid block header");
|
||||
assert_eq!(header.number(), 4);
|
||||
}
|
||||
}
|
||||
@@ -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<scale_value::scale::TypeId>;
|
||||
|
||||
// // 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<u8>,
|
||||
// }
|
||||
|
||||
// impl DecodeWithMetadata for DecodedValueThunk {
|
||||
// fn decode_with_metadata(
|
||||
// bytes: &mut &[u8],
|
||||
// type_id: u32,
|
||||
// metadata: &Metadata,
|
||||
// ) -> Result<Self, Error> {
|
||||
// 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<u8> {
|
||||
// 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<DecodedValue, Error> {
|
||||
// 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<T: DecodeAsType>(&self) -> Result<T, scale_decode::Error> {
|
||||
// T::decode_as_type(
|
||||
// &mut &self.scale_bytes[..],
|
||||
// self.type_id,
|
||||
// self.metadata.types(),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
@@ -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;
|
||||
@@ -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};
|
||||
@@ -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<Self, scale_decode::Error>;
|
||||
}
|
||||
|
||||
impl<T: scale_decode::DecodeAsType> DecodeWithMetadata for T {
|
||||
fn decode_with_metadata(
|
||||
bytes: &mut &[u8],
|
||||
type_id: u32,
|
||||
metadata: &Metadata,
|
||||
) -> Result<T, scale_decode::Error> {
|
||||
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<u8>,
|
||||
) -> Result<(), scale_encode::Error>;
|
||||
}
|
||||
|
||||
impl<T: scale_encode::EncodeAsType> 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<u8>,
|
||||
) -> Result<(), scale_encode::Error> {
|
||||
self.encode_as_type_to(type_id, metadata.types(), bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -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<subxt_metadata::Metadata>,
|
||||
}
|
||||
|
||||
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<subxt_metadata::PalletMetadata, MetadataError> {
|
||||
self.pallet_by_name(name)
|
||||
.ok_or_else(|| MetadataError::PalletNameNotFound(name.to_owned()))
|
||||
}
|
||||
|
||||
/// Identical to `metadata.pallet_by_index()`, but returns an error if the pallet is not found.
|
||||
pub fn pallet_by_index_err(
|
||||
&self,
|
||||
index: u8,
|
||||
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
|
||||
self.pallet_by_index(index)
|
||||
.ok_or(MetadataError::PalletIndexNotFound(index))
|
||||
}
|
||||
|
||||
/// Identical to `metadata.runtime_api_trait_by_name()`, but returns an error if the trait is not found.
|
||||
pub fn runtime_api_trait_by_name_err(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> Result<subxt_metadata::RuntimeApiMetadata, MetadataError> {
|
||||
self.runtime_api_trait_by_name(name)
|
||||
.ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<subxt_metadata::Metadata> for Metadata {
|
||||
fn from(md: subxt_metadata::Metadata) -> Self {
|
||||
Metadata::new(md)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<frame_metadata::RuntimeMetadataPrefixed> for Metadata {
|
||||
type Error = subxt_metadata::TryFromError;
|
||||
fn try_from(value: frame_metadata::RuntimeMetadataPrefixed) -> Result<Self, Self::Error> {
|
||||
subxt_metadata::Metadata::try_from(value).map(Metadata::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl codec::Decode for Metadata {
|
||||
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
subxt_metadata::Metadata::decode(input).map(Metadata::new)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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),
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod signer;
|
||||
pub mod tx_payload;
|
||||
@@ -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<T: Config> {
|
||||
/// 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<T: Config, Pair> {
|
||||
account_id: T::AccountId,
|
||||
signer: Pair,
|
||||
}
|
||||
|
||||
impl<T, Pair> PairSigner<T, Pair>
|
||||
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.
|
||||
<SpMultiSignature as Verify>::Signer: From<Pair::Public>,
|
||||
T::AccountId: From<SpAccountId32>,
|
||||
{
|
||||
/// Creates a new [`Signer`] from an [`sp_core::Pair`].
|
||||
pub fn new(signer: Pair) -> Self {
|
||||
let account_id =
|
||||
<SpMultiSignature as Verify>::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<T, Pair> Signer<T> for PairSigner<T, Pair>
|
||||
where
|
||||
T: Config,
|
||||
Pair: PairT,
|
||||
Pair::Signature: Into<T::Signature>,
|
||||
{
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<u8>) -> 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<Vec<u8>, 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<ValidationDetails<'_>> {
|
||||
// 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<CallData> {
|
||||
// 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<Arc<dyn EncodeAsFields + Send + Sync + 'static>>;
|
||||
|
||||
// /// The type of a payload typically used for dynamic transaction payloads.
|
||||
// pub type DynamicPayload = Payload<Composite<()>>;
|
||||
|
||||
// impl<CallData> Payload<CallData> {
|
||||
// /// Create a new [`Payload`].
|
||||
// pub fn new(
|
||||
// pallet_name: impl Into<String>,
|
||||
// call_name: impl Into<String>,
|
||||
// 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<Composite<()>> {
|
||||
// /// 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<CallData: EncodeAsFields> TxPayload for Payload<CallData> {
|
||||
// fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> 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<ValidationDetails<'_>> {
|
||||
// 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<String>,
|
||||
// call_name: impl Into<String>,
|
||||
// call_data: impl Into<Composite<()>>,
|
||||
// ) -> DynamicPayload {
|
||||
// Payload::new(pallet_name, call_name, call_data.into())
|
||||
// }
|
||||
@@ -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<Self, FromSs58Error> {
|
||||
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<u8> {
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_ss58check())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for AccountId32 {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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<Self, Self::Err> {
|
||||
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<sp_runtime::AccountId32> for AccountId32 {
|
||||
fn from(value: sp_runtime::AccountId32) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
impl From<sp_core::sr25519::Public> for AccountId32 {
|
||||
fn from(value: sp_core::sr25519::Public) -> Self {
|
||||
let acc: sp_runtime::AccountId32 = value.into();
|
||||
acc.into()
|
||||
}
|
||||
}
|
||||
impl From<sp_core::ed25519::Public> 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Store: BitStore, Order: BitOrder>() -> 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<Store, Order> {
|
||||
bits: Bits,
|
||||
_marker: PhantomData<(Store, Order)>,
|
||||
}
|
||||
|
||||
impl<Store, Order> DecodedBits<Store, Order> {
|
||||
/// 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<Store, Order> core::iter::FromIterator<bool> for DecodedBits<Store, Order> {
|
||||
fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
|
||||
DecodedBits {
|
||||
bits: Bits::from_iter(iter),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Store: BitStore, Order: BitOrder> codec::Decode for DecodedBits<Store, Order> {
|
||||
fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
/// Equivalent of `BitSlice::MAX_BITS` on 32bit machine.
|
||||
const ARCH32BIT_BITSLICE_MAX_BITS: u32 = 0x1fff_ffff;
|
||||
|
||||
let Compact(bits) = <Compact<u32>>::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 <u8, Lsb0>)
|
||||
// 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::<Store, Order>())?;
|
||||
let bits = decoder.collect::<Result<Vec<_>, _>>()?;
|
||||
let bits = Bits::from_iter(bits);
|
||||
|
||||
Ok(DecodedBits {
|
||||
bits,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Store: BitStore, Order: BitOrder> codec::Encode for DecodedBits<Store, Order> {
|
||||
fn size_hint(&self) -> usize {
|
||||
self.bits.size_hint()
|
||||
}
|
||||
|
||||
fn encoded_size(&self) -> usize {
|
||||
self.bits.encoded_size()
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
scale_bits::encode_using_format(self.bits.iter(), bit_format::<Store, Order>())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct DecodedBitsVisitor<S, O>(core::marker::PhantomData<(S, O)>);
|
||||
impl<Store, Order> scale_decode::Visitor for DecodedBitsVisitor<Store, Order> {
|
||||
type Value<'scale, 'info> = DecodedBits<Store, Order>;
|
||||
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::Value<'scale, 'info>, 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<Store, Order> scale_decode::IntoVisitor for DecodedBits<Store, Order> {
|
||||
type Visitor = DecodedBitsVisitor<Store, Order>;
|
||||
fn into_visitor() -> Self::Visitor {
|
||||
DecodedBitsVisitor(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Store, Order> scale_encode::EncodeAsType for DecodedBits<Store, Order> {
|
||||
fn encode_as_type_to(
|
||||
&self,
|
||||
type_id: u32,
|
||||
types: &scale_info::PortableRegistry,
|
||||
out: &mut Vec<u8>,
|
||||
) -> 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<Item = &'a bool>,
|
||||
) where
|
||||
BitVec<Store, <Order as ToBitVec>::Order>: codec::Encode + codec::Decode,
|
||||
{
|
||||
let input: Vec<_> = input.into_iter().copied().collect();
|
||||
|
||||
let decoded_bits = DecodedBits::<Store, Order>::from_iter(input.clone());
|
||||
let bitvec = BitVec::<Store, <Order as ToBitVec>::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::<Store, Order>::decode(&mut &decoded_bits_encoded[..])
|
||||
.expect("SCALE-encoding DecodedBits to roundtrip");
|
||||
let bitvec_decoded =
|
||||
BitVec::<Store, <Order as ToBitVec>::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::<u8, Lsb0>(test_case);
|
||||
scales_like_bitvec_and_roundtrips::<u16, Lsb0>(test_case);
|
||||
scales_like_bitvec_and_roundtrips::<u32, Lsb0>(test_case);
|
||||
scales_like_bitvec_and_roundtrips::<u64, Lsb0>(test_case);
|
||||
scales_like_bitvec_and_roundtrips::<u8, Msb0>(test_case);
|
||||
scales_like_bitvec_and_roundtrips::<u16, Msb0>(test_case);
|
||||
scales_like_bitvec_and_roundtrips::<u32, Msb0>(test_case);
|
||||
scales_like_bitvec_and_roundtrips::<u64, Msb0>(test_case);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<T: codec::Output + ?Sized>(&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<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<u8>);
|
||||
|
||||
impl codec::Encode for Encoded {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
self.0.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Decodes a compact encoded value from the beginning of the provided bytes,
|
||||
/// returning the value and any remaining bytes.
|
||||
pub(crate) fn strip_compact_prefix(bytes: &[u8]) -> Result<(u64, &[u8]), codec::Error> {
|
||||
let cursor = &mut &*bytes;
|
||||
let val = <Compact<u64>>::decode(cursor)?;
|
||||
Ok((val.0, *cursor))
|
||||
}
|
||||
|
||||
/// A URL is considered secure if it uses a secure scheme ("https" or "wss") or is referring to localhost.
|
||||
///
|
||||
/// Returns an error if the the string could not be parsed into a URL.
|
||||
pub fn url_is_secure(url: &str) -> Result<bool, url::ParseError> {
|
||||
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<T>(core::marker::PhantomData<T>);
|
||||
|
||||
impl<T> PhantomDataSendSync<T> {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self(core::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for PhantomDataSendSync<T> {}
|
||||
unsafe impl<T> Sync for PhantomDataSendSync<T> {}
|
||||
|
||||
/// This represents a key-value collection and is SCALE compatible
|
||||
/// with collections like BTreeMap. This has the same type params
|
||||
/// as `BTreeMap` which allows us to easily swap the two during codegen.
|
||||
pub type KeyedVec<K, V> = Vec<(K, V)>;
|
||||
@@ -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<AccountId, AccountIndex> {
|
||||
/// 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<u8>),
|
||||
/// It's a 32 byte representation.
|
||||
Address32([u8; 32]),
|
||||
/// Its a 20 byte representation.
|
||||
Address20([u8; 20]),
|
||||
}
|
||||
|
||||
impl<AccountId, AccountIndex> From<AccountId> for MultiAddress<AccountId, AccountIndex> {
|
||||
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<N> From<sp_runtime::AccountId32> for MultiAddress<AccountId32, N> {
|
||||
fn from(value: sp_runtime::AccountId32) -> Self {
|
||||
let val: AccountId32 = value.into();
|
||||
val.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Id, N> From<sp_runtime::MultiAddress<Id, N>> for MultiAddress<AccountId32, N>
|
||||
where
|
||||
Id: Into<AccountId32>,
|
||||
{
|
||||
fn from(value: sp_runtime::MultiAddress<Id, N>) -> 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<sp_runtime::MultiSignature> 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<sp_core::ed25519::Signature> for MultiSignature {
|
||||
fn from(value: sp_core::ed25519::Signature) -> Self {
|
||||
let sig: sp_runtime::MultiSignature = value.into();
|
||||
sig.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sp_core::sr25519::Signature> for MultiSignature {
|
||||
fn from(value: sp_core::sr25519::Signature) -> Self {
|
||||
let sig: sp_runtime::MultiSignature = value.into();
|
||||
sig.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sp_core::ecdsa::Signature> for MultiSignature {
|
||||
fn from(value: sp_core::ecdsa::Signature) -> Self {
|
||||
let sig: sp_runtime::MultiSignature = value.into();
|
||||
sig.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<T>(pub T);
|
||||
|
||||
impl<T: Encode> EncodeAsType for Static<T> {
|
||||
fn encode_as_type_to(
|
||||
&self,
|
||||
_type_id: u32,
|
||||
_types: &scale_decode::PortableRegistry,
|
||||
out: &mut Vec<u8>,
|
||||
) -> Result<(), scale_encode::Error> {
|
||||
self.0.encode_to(out);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StaticDecodeAsTypeVisitor<T>(core::marker::PhantomData<T>);
|
||||
|
||||
impl<T: Decode> Visitor for StaticDecodeAsTypeVisitor<T> {
|
||||
type Value<'scale, 'info> = Static<T>;
|
||||
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, Result<Self::Value<'scale, 'info>, 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<T: Decode> IntoVisitor for Static<T> {
|
||||
type Visitor = StaticDecodeAsTypeVisitor<T>;
|
||||
fn into_visitor() -> Self::Visitor {
|
||||
StaticDecodeAsTypeVisitor(core::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
// Make it easy to convert types into Static where required.
|
||||
impl<T> From<T> for Static<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Static(value)
|
||||
}
|
||||
}
|
||||
|
||||
// Static<T> is just a marker type and should be as transparent as possible:
|
||||
impl<T> core::ops::Deref for Static<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::DerefMut for Static<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
@@ -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<Address, Call, Signature, Extra>(
|
||||
Static<Encoded>,
|
||||
#[codec(skip)] PhantomData<(Address, Call, Signature, Extra)>,
|
||||
);
|
||||
|
||||
impl<Address, Call, Signature, Extra> UncheckedExtrinsic<Address, Call, Signature, Extra> {
|
||||
/// Construct a new [`UncheckedExtrinsic`].
|
||||
pub fn new(bytes: Vec<u8>) -> 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<Address, Call, Signature, Extra> Decode
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
{
|
||||
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
|
||||
// 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<u8> = Decode::decode(input)?;
|
||||
Ok(UncheckedExtrinsic::new(xt_vec))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra> scale_encode::EncodeAsType
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
{
|
||||
fn encode_as_type_to(
|
||||
&self,
|
||||
type_id: u32,
|
||||
types: &scale_info::PortableRegistry,
|
||||
out: &mut Vec<u8>,
|
||||
) -> Result<(), scale_encode::Error> {
|
||||
self.0.encode_as_type_to(type_id, types, out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra> From<Vec<u8>>
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
{
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
UncheckedExtrinsic::new(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra> From<UncheckedExtrinsic<Address, Call, Signature, Extra>>
|
||||
for Vec<u8>
|
||||
{
|
||||
fn from(bytes: UncheckedExtrinsic<Address, Call, Signature, Extra>) -> Self {
|
||||
bytes.0 .0 .0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra>(
|
||||
PhantomData<(Address, Call, Signature, Extra)>,
|
||||
);
|
||||
|
||||
impl<Address, Call, Signature, Extra> Visitor
|
||||
for UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra>
|
||||
{
|
||||
type Value<'scale, 'info> = UncheckedExtrinsic<Address, Call, Signature, Extra>;
|
||||
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, Result<Self::Value<'scale, 'info>, Self::Error>> {
|
||||
DecodeAsTypeResult::Decoded(Self::Value::decode_as_type(input, type_id.0, types))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra> IntoVisitor
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
{
|
||||
type Visitor = UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra>;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<u8>`.
|
||||
/// [`WrapperKeepOpaque`] stores the type only in its opaque format, aka as a `Vec<u8>`. To
|
||||
/// access the real type `T` [`Self::try_decode`] needs to be used.
|
||||
// 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<u8>`, but we need a custom [`EncodeAsType`]
|
||||
// and [`Visitor`] implementation to encode and decode based on TypeInfo.
|
||||
#[derive(Derivative, Encode, Decode)]
|
||||
#[derivative(
|
||||
Debug(bound = ""),
|
||||
Clone(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Eq(bound = ""),
|
||||
Default(bound = ""),
|
||||
Hash(bound = "")
|
||||
)]
|
||||
pub struct WrapperKeepOpaque<T> {
|
||||
data: Vec<u8>,
|
||||
_phantom: PhantomDataSendSync<T>,
|
||||
}
|
||||
|
||||
impl<T> WrapperKeepOpaque<T> {
|
||||
/// Try to decode the wrapped type from the inner `data`.
|
||||
///
|
||||
/// Returns `None` if the decoding failed.
|
||||
pub fn try_decode(&self) -> Option<T>
|
||||
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<u8>) -> 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<T> EncodeAsType for WrapperKeepOpaque<T> {
|
||||
fn encode_as_type_to(
|
||||
&self,
|
||||
type_id: u32,
|
||||
types: &scale_info::PortableRegistry,
|
||||
out: &mut Vec<u8>,
|
||||
) -> 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<T>(core::marker::PhantomData<T>);
|
||||
impl<T> Visitor for WrapperKeepOpaqueVisitor<T> {
|
||||
type Value<'scale, 'info> = WrapperKeepOpaque<T>;
|
||||
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::Value<'scale, 'info>, 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::<u32>::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<T> IntoVisitor for WrapperKeepOpaque<T> {
|
||||
type Visitor = WrapperKeepOpaqueVisitor<T>;
|
||||
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<T: scale_info::TypeInfo + 'static> scale_info::TypeInfo for WrapperKeepOpaque<T> {
|
||||
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::<T>()))])
|
||||
.composite(
|
||||
Fields::unnamed()
|
||||
.field(|f| f.compact::<u32>())
|
||||
.field(|f| f.ty::<T>().type_name("T")),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a type definition, return type ID and registry representing it.
|
||||
fn make_type<T: scale_info::TypeInfo + 'static>() -> (u32, scale_info::PortableRegistry) {
|
||||
let m = scale_info::MetaType::new::<T>();
|
||||
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: T)
|
||||
where
|
||||
T: EncodeAsType
|
||||
+ DecodeAsType
|
||||
+ Encode
|
||||
+ Decode
|
||||
+ PartialEq
|
||||
+ core::fmt::Debug
|
||||
+ scale_info::TypeInfo
|
||||
+ 'static,
|
||||
{
|
||||
let (type_id, types) = make_type::<T>();
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
Generated
+499
-1
@@ -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"
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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) (*)
|
||||
Reference in New Issue
Block a user