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
+26 -6
View File
@@ -4,8 +4,14 @@
use crate::custom_values::CustomValuesClient;
use crate::{
blocks::BlocksClient, constants::ConstantsClient, events::EventsClient,
runtime_api::RuntimeApiClient, storage::StorageClient, tx::TxClient, Config, Metadata,
blocks::BlocksClient,
config::{Config, HashFor},
constants::ConstantsClient,
events::EventsClient,
runtime_api::RuntimeApiClient,
storage::StorageClient,
tx::TxClient,
Metadata,
};
use derive_where::derive_where;
@@ -19,11 +25,14 @@ pub trait OfflineClientT<T: Config>: Clone + Send + Sync + 'static {
fn metadata(&self) -> Metadata;
/// Return the provided genesis hash.
fn genesis_hash(&self) -> T::Hash;
fn genesis_hash(&self) -> HashFor<T>;
/// Return the provided [`RuntimeVersion`].
fn runtime_version(&self) -> RuntimeVersion;
/// Return the hasher used on the chain.
fn hasher(&self) -> T::Hasher;
/// Return the [subxt_core::client::ClientState] (metadata, runtime version and genesis hash).
fn client_state(&self) -> ClientState<T> {
ClientState {
@@ -74,19 +83,22 @@ pub trait OfflineClientT<T: Config>: Clone + Send + Sync + 'static {
#[derive_where(Debug, Clone)]
pub struct OfflineClient<T: Config> {
inner: Arc<ClientState<T>>,
hasher: T::Hasher,
}
impl<T: Config> OfflineClient<T> {
/// Construct a new [`OfflineClient`], providing
/// the necessary runtime and compile-time arguments.
pub fn new(
genesis_hash: T::Hash,
genesis_hash: HashFor<T>,
runtime_version: RuntimeVersion,
metadata: impl Into<Metadata>,
) -> OfflineClient<T> {
let metadata = metadata.into();
let hasher = <T::Hasher as subxt_core::config::Hasher>::new(&metadata);
OfflineClient {
hasher,
inner: Arc::new(ClientState {
genesis_hash,
runtime_version,
@@ -96,7 +108,7 @@ impl<T: Config> OfflineClient<T> {
}
/// Return the genesis hash.
pub fn genesis_hash(&self) -> T::Hash {
pub fn genesis_hash(&self) -> HashFor<T> {
self.inner.genesis_hash
}
@@ -110,6 +122,11 @@ impl<T: Config> OfflineClient<T> {
self.inner.metadata.clone()
}
/// Return the hasher used for the chain.
pub fn hasher(&self) -> T::Hasher {
self.hasher
}
// Just a copy of the most important trait methods so that people
// don't need to import the trait for most things:
@@ -140,7 +157,7 @@ impl<T: Config> OfflineClient<T> {
}
impl<T: Config> OfflineClientT<T> for OfflineClient<T> {
fn genesis_hash(&self) -> T::Hash {
fn genesis_hash(&self) -> HashFor<T> {
self.genesis_hash()
}
fn runtime_version(&self) -> RuntimeVersion {
@@ -149,6 +166,9 @@ impl<T: Config> OfflineClientT<T> for OfflineClient<T> {
fn metadata(&self) -> Metadata {
self.metadata()
}
fn hasher(&self) -> T::Hasher {
self.hasher()
}
}
// For ergonomics; cloning a client is deliberately fairly cheap (via Arc),
+38 -12
View File
@@ -7,13 +7,14 @@ use crate::custom_values::CustomValuesClient;
use crate::{
backend::{legacy::LegacyBackend, rpc::RpcClient, Backend, BackendExt, StreamOfResults},
blocks::{BlockRef, BlocksClient},
config::{Config, HashFor},
constants::ConstantsClient,
error::Error,
events::EventsClient,
runtime_api::RuntimeApiClient,
storage::StorageClient,
tx::TxClient,
Config, Metadata,
Metadata,
};
use derive_where::derive_where;
use futures::future;
@@ -37,9 +38,10 @@ pub struct OnlineClient<T: Config> {
#[derive_where(Debug)]
struct Inner<T: Config> {
genesis_hash: T::Hash,
genesis_hash: HashFor<T>,
runtime_version: RuntimeVersion,
metadata: Metadata,
hasher: T::Hasher,
}
impl<T: Config> std::fmt::Debug for OnlineClient<T> {
@@ -103,7 +105,7 @@ impl<T: Config> OnlineClient<T> {
/// If you're unsure what you're doing, prefer one of the alternate methods to instantiate
/// a client.
pub fn from_rpc_client_with(
genesis_hash: T::Hash,
genesis_hash: HashFor<T>,
runtime_version: RuntimeVersion,
metadata: impl Into<Metadata>,
rpc_client: impl Into<RpcClient>,
@@ -141,16 +143,22 @@ impl<T: Config> OnlineClient<T> {
/// If you're unsure what you're doing, prefer one of the alternate methods to instantiate
/// a client.
pub fn from_backend_with<B: Backend<T>>(
genesis_hash: T::Hash,
genesis_hash: HashFor<T>,
runtime_version: RuntimeVersion,
metadata: impl Into<Metadata>,
backend: Arc<B>,
) -> Result<OnlineClient<T>, Error> {
use subxt_core::config::Hasher;
let metadata = metadata.into();
let hasher = T::Hasher::new(&metadata);
Ok(OnlineClient {
inner: Arc::new(RwLock::new(Inner {
genesis_hash,
runtime_version,
metadata: metadata.into(),
metadata,
hasher,
})),
backend,
})
@@ -159,7 +167,7 @@ impl<T: Config> OnlineClient<T> {
/// Fetch the metadata from substrate using the runtime API.
async fn fetch_metadata(
backend: &dyn Backend<T>,
block_hash: T::Hash,
block_hash: HashFor<T>,
) -> Result<Metadata, Error> {
#[cfg(feature = "unstable-metadata")]
{
@@ -184,7 +192,7 @@ impl<T: Config> OnlineClient<T> {
/// Fetch the latest stable metadata from the node.
async fn fetch_latest_stable_metadata(
backend: &dyn Backend<T>,
block_hash: T::Hash,
block_hash: HashFor<T>,
) -> Result<Metadata, Error> {
// This is the latest stable metadata that subxt can utilize.
const V15_METADATA_VERSION: u32 = 15;
@@ -246,6 +254,11 @@ impl<T: Config> OnlineClient<T> {
ClientRuntimeUpdater(self.clone())
}
/// Return the hasher configured for hashing blocks and extrinsics.
pub fn hasher(&self) -> T::Hasher {
self.inner.read().expect("shouldn't be poisoned").hasher
}
/// Return the [`Metadata`] used in this client.
pub fn metadata(&self) -> Metadata {
let inner = self.inner.read().expect("shouldn't be poisoned");
@@ -264,7 +277,7 @@ impl<T: Config> OnlineClient<T> {
}
/// Return the genesis hash.
pub fn genesis_hash(&self) -> T::Hash {
pub fn genesis_hash(&self) -> HashFor<T> {
let inner = self.inner.read().expect("shouldn't be poisoned");
inner.genesis_hash
}
@@ -275,7 +288,7 @@ impl<T: Config> OnlineClient<T> {
///
/// Setting a custom genesis hash may leave Subxt unable to
/// submit valid transactions.
pub fn set_genesis_hash(&self, genesis_hash: T::Hash) {
pub fn set_genesis_hash(&self, genesis_hash: HashFor<T>) {
let mut inner = self.inner.write().expect("shouldn't be poisoned");
inner.genesis_hash = genesis_hash;
}
@@ -355,12 +368,15 @@ impl<T: Config> OfflineClientT<T> for OnlineClient<T> {
fn metadata(&self) -> Metadata {
self.metadata()
}
fn genesis_hash(&self) -> T::Hash {
fn genesis_hash(&self) -> HashFor<T> {
self.genesis_hash()
}
fn runtime_version(&self) -> RuntimeVersion {
self.runtime_version()
}
fn hasher(&self) -> T::Hasher {
self.hasher()
}
// This is provided by default, but we can optimise here and only lock once:
fn client_state(&self) -> ClientState<T> {
let inner = self.inner.read().expect("shouldn't be poisoned");
@@ -501,10 +517,20 @@ impl Update {
async fn wait_runtime_upgrade_in_finalized_block<T: Config>(
client: &OnlineClient<T>,
runtime_version: &RuntimeVersion,
) -> Option<Result<BlockRef<T::Hash>, Error>> {
) -> Option<Result<BlockRef<HashFor<T>>, Error>> {
use scale_value::At;
let mut block_sub = match client.backend().stream_finalized_block_headers().await {
let hasher = client
.inner
.read()
.expect("Lock shouldn't be poisoned")
.hasher;
let mut block_sub = match client
.backend()
.stream_finalized_block_headers(hasher)
.await
{
Ok(s) => s,
Err(err) => return Some(Err(err)),
};