use super::Config; use crate::utils::RangeMap; use primitive_types::H256; use scale_info_legacy::{ChainTypeRegistry, TypeRegistrySet}; use std::collections::HashMap; use std::sync::Arc; use std::sync::Mutex; /// Configuration that's suitable for standard Substrate chains (ie those /// that have not customized the block hash type). pub struct SubstrateConfig { legacy_types: ChainTypeRegistry, spec_version_for_block_number: RangeMap, metadata_for_spec_version: Mutex>>, } impl SubstrateConfig { /// Create a new SubstrateConfig with no legacy types. /// /// Without any further configuration, this will only work with /// the [`crate::client::OnlineClient`] for blocks that were produced by Runtimes /// that emit metadata V14 or later. /// /// To support working at any block with the [`crate::client::OnlineClient`], you /// must call [`SubstrateConfig::set_legacy_types`] with appropriate legacy type /// definitions. /// /// To support working with the [`crate::client::OfflineClient`] at any block, /// you must also call: /// - [`SubstrateConfig::set_metadata_for_spec_versions`] to set the metadata to /// use at each spec version we might encounter. /// - [`SubstrateConfig::set_spec_version_for_block_ranges`] to set the spec version /// to use for each range of blocks we might encounter. pub fn new() -> Self { Self { legacy_types: ChainTypeRegistry::empty(), spec_version_for_block_number: RangeMap::empty(), metadata_for_spec_version: Mutex::new(HashMap::new()), } } /// Set the legacy types to use for this configuration. This enables support for /// blocks produced by Runtimes that emit metadata older than V14. pub fn set_legacy_types(mut self, legacy_types: ChainTypeRegistry) -> Self { self.legacy_types = legacy_types; self } /// Set the metadata to be used for decoding blocks at the given spec versions. pub fn set_metadata_for_spec_versions( self, ranges: impl Iterator, ) -> Self { let mut map = self.metadata_for_spec_version.lock().unwrap(); for (spec_version, metadata) in ranges { map.insert(spec_version, Arc::new(metadata)); } drop(map); self } /// Given an iterator of block ranges to spec version of the form `(start, end, spec_version)`, add them /// to this configuration. pub fn set_spec_version_for_block_ranges( mut self, ranges: impl Iterator, ) -> Self { let mut m = RangeMap::builder(); for (start, end, spec_version) in ranges { m = m.add_range(start, end, spec_version); } self.spec_version_for_block_number = m.build(); self } } impl Default for SubstrateConfig { fn default() -> Self { Self::new() } } impl Config for SubstrateConfig { type Hash = H256; fn legacy_types_for_spec_version(&'_ self, spec_version: u32) -> TypeRegistrySet<'_> { self.legacy_types.for_spec_version(spec_version as u64) } fn spec_version_for_block_number(&self, block_number: u64) -> Option { self.spec_version_for_block_number .get(block_number) .copied() } fn metadata_for_spec_version( &self, spec_version: u32, ) -> Option> { self.metadata_for_spec_version .lock() .unwrap() .get(&spec_version) .cloned() } fn set_metadata_for_spec_version( &self, spec_version: u32, metadata: Arc, ) { self.metadata_for_spec_version .lock() .unwrap() .insert(spec_version, metadata); } fn hash(s: &[u8]) -> ::Hash { sp_crypto_hashing::blake2_256(s).into() } } impl subxt_rpcs::RpcConfig for SubstrateConfig { type Hash = ::Hash; // We don't use these types in any of the RPC methods we call, // so don't bother setting them up: type Header = (); type AccountId = (); }