Metadata V16: Be more dynamic over which hasher is used. (#1974)

* Use DynamicHasher256 to support Blake2 or Keccack depending on chain

* remove Config::Hash associated type, replace with HashFor<Config> alias

* Fix doc links

* fix wasm tests

* Don't strip system pallet associated types. check System.Hashing, not Hash. Rename BlockHash trait to Hash

* Tweak comment

* fmt

* fix merge

* Fix typo
This commit is contained in:
James Wilson
2025-04-23 10:12:48 +01:00
committed by GitHub
parent a8ae55a61b
commit 21b3f52191
43 changed files with 573 additions and 371 deletions
+8 -3
View File
@@ -7,7 +7,11 @@
//! [`crate::config::DefaultExtrinsicParams`] provides a general-purpose
//! implementation of this that will work in many cases.
use crate::{client::ClientState, error::ExtrinsicParamsError, Config};
use crate::{
client::ClientState,
config::{Config, HashFor},
error::ExtrinsicParamsError,
};
use alloc::vec::Vec;
use core::any::Any;
@@ -74,7 +78,7 @@ pub trait Params<T: Config> {
/// Set the account nonce.
fn inject_account_nonce(&mut self, _nonce: u64) {}
/// Set the current block.
fn inject_block(&mut self, _number: u64, _hash: T::Hash) {}
fn inject_block(&mut self, _number: u64, _hash: HashFor<T>) {}
}
impl<T: Config> Params<T> for () {}
@@ -85,7 +89,8 @@ macro_rules! impl_tuples {
fn inject_account_nonce(&mut self, nonce: u64) {
$(self.$index.inject_account_nonce(nonce);)+
}
fn inject_block(&mut self, number: u64, hash: Conf::Hash) {
fn inject_block(&mut self, number: u64, hash: HashFor<Conf>) {
$(self.$index.inject_block(number, hash);)+
}
}
+16 -12
View File
@@ -20,6 +20,7 @@ use core::fmt::Debug;
use scale_decode::DecodeAsType;
use scale_encode::EncodeAsType;
use serde::{de::DeserializeOwned, Serialize};
use subxt_metadata::Metadata;
pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder};
pub use extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder};
@@ -33,9 +34,6 @@ pub use transaction_extensions::TransactionExtension;
// 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 + Decode + Serialize + Send;
@@ -46,7 +44,7 @@ pub trait Config: Sized + Send + Sync + 'static {
type Signature: Debug + Clone + Encode + Decode + Send;
/// The hashing system (algorithm) being used in the runtime (e.g. Blake2).
type Hasher: Debug + Hasher<Output = Self::Hash>;
type Hasher: Debug + Clone + Copy + Hasher + Send + Sync;
/// The block header.
type Header: Debug + Header<Hasher = Self::Hasher> + Sync + Send + DeserializeOwned;
@@ -58,11 +56,14 @@ pub trait Config: Sized + Send + Sync + 'static {
type AssetId: Debug + Clone + Encode + DecodeAsType + EncodeAsType + Send;
}
/// Given some [`Config`], this returns the type of hash used.
pub type HashFor<T> = <<T as Config>::Hasher as Hasher>::Output;
/// given some [`Config`], this return the other params needed for its `ExtrinsicParams`.
pub type ParamsFor<T> = <<T as Config>::ExtrinsicParams as ExtrinsicParams<T>>::Params;
/// Block hashes must conform to a bunch of things to be used in Subxt.
pub trait BlockHash:
pub trait Hash:
Debug
+ Copy
+ Send
@@ -77,7 +78,7 @@ pub trait BlockHash:
+ core::hash::Hash
{
}
impl<T> BlockHash for T where
impl<T> Hash for T where
T: Debug
+ Copy
+ Send
@@ -97,15 +98,18 @@ impl<T> BlockHash for T where
/// and extrinsics.
pub trait Hasher {
/// The type given back from the hash operation
type Output;
type Output: Hash;
/// Construct a new hasher.
fn new(metadata: &Metadata) -> Self;
/// Hash some bytes to the given output type.
fn hash(s: &[u8]) -> Self::Output;
fn hash(&self, s: &[u8]) -> Self::Output;
/// Hash some SCALE encodable type to the given output type.
fn hash_of<S: Encode>(s: &S) -> Self::Output {
fn hash_of<S: Encode>(&self, s: &S) -> Self::Output {
let out = s.encode();
Self::hash(&out)
self.hash(&out)
}
}
@@ -120,7 +124,7 @@ pub trait Header: Sized + Encode + Decode {
fn number(&self) -> Self::Number;
/// Hash this header.
fn hash(&self) -> <Self::Hasher as Hasher>::Output {
Self::Hasher::hash_of(self)
fn hash_with(&self, hasher: Self::Hasher) -> <Self::Hasher as Hasher>::Output {
hasher.hash_of(self)
}
}
-1
View File
@@ -17,7 +17,6 @@ pub use primitive_types::{H256, U256};
pub enum PolkadotConfig {}
impl Config for PolkadotConfig {
type Hash = <SubstrateConfig as Config>::Hash;
type AccountId = <SubstrateConfig as Config>::AccountId;
type Signature = <SubstrateConfig as Config>::Signature;
type Hasher = <SubstrateConfig as Config>::Hasher;
+64 -7
View File
@@ -11,6 +11,7 @@ use alloc::vec::Vec;
use codec::{Decode, Encode};
pub use primitive_types::{H256, U256};
use serde::{Deserialize, Serialize};
use subxt_metadata::Metadata;
/// Default set of commonly used types by Substrate runtimes.
// Note: We only use this at the type level, so it should be impossible to
@@ -21,12 +22,11 @@ use serde::{Deserialize, Serialize};
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 Hasher = DynamicHasher256;
type Header = SubstrateHeader<u32, DynamicHasher256>;
type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
type AssetId = u32;
}
@@ -39,17 +39,73 @@ pub type SubstrateExtrinsicParams<T> = DefaultExtrinsicParams<T>;
/// 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)]
/// A hasher (ie implements [`Hasher`]) which hashes values using the blaks2_256 algorithm.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlakeTwo256;
impl Hasher for BlakeTwo256 {
type Output = H256;
fn hash(s: &[u8]) -> Self::Output {
fn new(_metadata: &Metadata) -> Self {
Self
}
fn hash(&self, s: &[u8]) -> Self::Output {
sp_crypto_hashing::blake2_256(s).into()
}
}
/// A hasher (ie implements [`Hasher`]) which inspects the runtime metadata to decide how to
/// hash types, falling back to blake2_256 if the hasher information is not available.
///
/// Currently this hasher supports only `BlakeTwo256` and `Keccak256` hashing methods.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DynamicHasher256(HashType);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum HashType {
// Most chains use this:
BlakeTwo256,
// Chains like Hyperbridge use this (tends to be eth compatible chains)
Keccak256,
// If we don't have V16 metadata, we'll emit this and default to BlakeTwo256.
Unknown,
}
impl Hasher for DynamicHasher256 {
type Output = H256;
fn new(metadata: &Metadata) -> Self {
// Determine the Hash associated type used for the current chain, if possible.
let Some(system_pallet) = metadata.pallet_by_name("System") else {
return Self(HashType::Unknown);
};
let Some(hash_ty_id) = system_pallet.associated_type_id("Hashing") else {
return Self(HashType::Unknown);
};
let ty = metadata
.types()
.resolve(hash_ty_id)
.expect("Type information for 'Hashing' associated type should be in metadata");
let hash_type = match ty.path.ident().as_deref().unwrap_or("") {
"BlakeTwo256" => HashType::BlakeTwo256,
"Keccak256" => HashType::Keccak256,
_ => HashType::Unknown,
};
Self(hash_type)
}
fn hash(&self, s: &[u8]) -> Self::Output {
match self.0 {
HashType::BlakeTwo256 | HashType::Unknown => sp_crypto_hashing::blake2_256(s).into(),
HashType::Keccak256 => sp_crypto_hashing::keccak_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)]
@@ -75,11 +131,12 @@ pub struct SubstrateHeader<N: Copy + Into<U256> + TryFrom<U256>, H: Hasher> {
impl<N, H> Header for SubstrateHeader<N, H>
where
N: Copy + Into<u64> + Into<U256> + TryFrom<U256> + Encode,
H: Hasher + Encode,
H: Hasher,
SubstrateHeader<N, H>: Encode + Decode,
{
type Number = N;
type Hasher = H;
fn number(&self) -> Self::Number {
self.number
}
+9 -18
View File
@@ -9,10 +9,10 @@
use super::extrinsic_params::ExtrinsicParams;
use crate::client::ClientState;
use crate::config::{ExtrinsicParamsEncoder, Header};
use crate::config::ExtrinsicParamsEncoder;
use crate::config::{Config, HashFor};
use crate::error::ExtrinsicParamsError;
use crate::utils::{Era, Static};
use crate::Config;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::vec::Vec;
@@ -262,7 +262,7 @@ impl<T: Config> TransactionExtension<T> for CheckTxVersion {
}
/// The [`CheckGenesis`] transaction extension.
pub struct CheckGenesis<T: Config>(T::Hash);
pub struct CheckGenesis<T: Config>(HashFor<T>);
impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
type Params = ();
@@ -288,7 +288,7 @@ impl<T: Config> TransactionExtension<T> for CheckGenesis<T> {
/// The [`CheckMortality`] transaction extension.
pub struct CheckMortality<T: Config> {
params: CheckMortalityParamsInner<T>,
genesis_hash: T::Hash,
genesis_hash: HashFor<T>,
}
impl<T: Config> ExtrinsicParams<T> for CheckMortality<T> {
@@ -352,7 +352,7 @@ enum CheckMortalityParamsInner<T: Config> {
MortalFromBlock {
for_n_blocks: u64,
from_block_n: u64,
from_block_hash: T::Hash,
from_block_hash: HashFor<T>,
},
}
@@ -368,23 +368,14 @@ impl<T: Config> CheckMortalityParams<T> {
pub fn mortal(for_n_blocks: u64) -> Self {
Self(CheckMortalityParamsInner::MortalForBlocks(for_n_blocks))
}
/// Configure a transaction that will be mortal for the number of blocks given,
/// and from the block header provided.
pub fn mortal_from(for_n_blocks: u64, from_block: T::Header) -> Self {
Self(CheckMortalityParamsInner::MortalFromBlock {
for_n_blocks,
from_block_n: from_block.number().into(),
from_block_hash: from_block.hash(),
})
}
/// Configure a transaction that will be mortal for the number of blocks given,
/// and from the block details provided. Prefer to use [`CheckMortalityParams::mortal()`]
/// or [`CheckMortalityParams::mortal_from()`] which both avoid the block number and hash
/// from being misaligned.
/// where possible, which prevents the block number and hash from being misaligned.
pub fn mortal_from_unchecked(
for_n_blocks: u64,
from_block_n: u64,
from_block_hash: T::Hash,
from_block_hash: HashFor<T>,
) -> Self {
Self(CheckMortalityParamsInner::MortalFromBlock {
for_n_blocks,
@@ -399,7 +390,7 @@ impl<T: Config> CheckMortalityParams<T> {
}
impl<T: Config> Params<T> for CheckMortalityParams<T> {
fn inject_block(&mut self, from_block_n: u64, from_block_hash: <T as Config>::Hash) {
fn inject_block(&mut self, from_block_n: u64, from_block_hash: HashFor<T>) {
match &self.0 {
CheckMortalityParamsInner::MortalForBlocks(n) => {
self.0 = CheckMortalityParamsInner::MortalFromBlock {