use super::{Config, DefaultExtrinsicParamsBuilder, DefaultTransactionExtensions, Hasher, Header};
use crate::config::Hash;
use crate::metadata::{ArcMetadata, Metadata};
use crate::utils::RangeMap;
pub use crate::utils::{AccountId32, MultiAddress, MultiSignature};
use codec::{Decode, Encode};
pub use primitive_types::{H256, U256};
use scale_info_legacy::{ChainTypeRegistry, TypeRegistrySet};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::RwLock;
pub struct SubstrateConfigBuilder {
legacy_types: Option<ChainTypeRegistry>,
spec_and_transaction_version_for_block_number: RangeMap<u64, (u32, u32)>,
genesis_hash: Option<H256>,
metadata_for_spec_version: RwLock<HashMap<u32, ArcMetadata>>,
use_old_v9_hashers_before_spec_version: u32,
}
impl Default for SubstrateConfigBuilder {
fn default() -> Self {
Self::new()
}
}
impl SubstrateConfigBuilder {
pub fn new() -> Self {
SubstrateConfigBuilder {
legacy_types: None,
genesis_hash: None,
spec_and_transaction_version_for_block_number: RangeMap::empty(),
metadata_for_spec_version: RwLock::new(HashMap::new()),
use_old_v9_hashers_before_spec_version: 0,
}
}
pub fn set_genesis_hash(mut self, genesis_hash: H256) -> Self {
self.genesis_hash = Some(genesis_hash);
self
}
pub fn set_legacy_types(mut self, legacy_types: ChainTypeRegistry) -> Self {
self.legacy_types = Some(legacy_types);
self
}
pub fn set_metadata_for_spec_versions(
self,
ranges: impl IntoIterator<Item = (u32, ArcMetadata)>,
) -> Self {
let mut map = self.metadata_for_spec_version.write().unwrap();
for (spec_version, metadata) in ranges.into_iter() {
map.insert(spec_version, metadata);
}
drop(map);
self
}
pub fn set_spec_version_for_block_ranges(
mut self,
ranges: impl IntoIterator<Item = SpecVersionForRange>,
) -> Self {
let mut m = RangeMap::builder();
for version_for_range in ranges.into_iter() {
let start = version_for_range.block_range.start;
let end = version_for_range.block_range.end;
let spec_version = version_for_range.spec_version;
let transaction_version = version_for_range.transaction_version;
m = m.add_range(start, end, (spec_version, transaction_version));
}
self.spec_and_transaction_version_for_block_number = m.build();
self
}
pub fn use_old_v9_hashers_before_spec_version(mut self, spec_version: u32) -> Self {
self.use_old_v9_hashers_before_spec_version = spec_version;
self
}
pub fn build(self) -> SubstrateConfig {
SubstrateConfig {
inner: Arc::new(SubstrateConfigInner {
legacy_types: self.legacy_types,
spec_and_transaction_version_for_block_number: self
.spec_and_transaction_version_for_block_number,
metadata_for_spec_version: self.metadata_for_spec_version,
}),
}
}
}
pub struct SpecVersionForRange {
pub block_range: std::ops::Range<u64>,
pub spec_version: u32,
pub transaction_version: u32,
}
#[derive(Debug, Clone)]
pub struct SubstrateConfig {
inner: Arc<SubstrateConfigInner>,
}
#[derive(Debug)]
struct SubstrateConfigInner {
legacy_types: Option<ChainTypeRegistry>,
spec_and_transaction_version_for_block_number: RangeMap<u64, (u32, u32)>,
metadata_for_spec_version: RwLock<HashMap<u32, ArcMetadata>>,
}
impl Default for SubstrateConfig {
fn default() -> Self {
Self::new()
}
}
impl SubstrateConfig {
pub fn new() -> Self {
Self::builder().build()
}
pub fn builder() -> SubstrateConfigBuilder {
SubstrateConfigBuilder::new()
}
}
impl Config for SubstrateConfig {
type AccountId = AccountId32;
type Address = MultiAddress<Self::AccountId, u32>;
type Signature = MultiSignature;
type Hasher = DynamicHasher256;
type Header = SubstrateHeader<<Self::Hasher as Hasher>::Hash>;
type TransactionExtensions = SubstrateExtrinsicParams<Self>;
type AssetId = u32;
fn legacy_types_for_spec_version(&'_ self, spec_version: u32) -> Option<TypeRegistrySet<'_>> {
self.inner
.legacy_types
.as_ref()
.map(|types| types.for_spec_version(spec_version as u64))
}
fn spec_and_transaction_version_for_block_number(
&self,
block_number: u64,
) -> Option<(u32, u32)> {
self.inner
.spec_and_transaction_version_for_block_number
.get(block_number)
.copied()
}
fn metadata_for_spec_version(&self, spec_version: u32) -> Option<ArcMetadata> {
self.inner
.metadata_for_spec_version
.read()
.unwrap()
.get(&spec_version)
.cloned()
}
fn set_metadata_for_spec_version(&self, spec_version: u32, metadata: ArcMetadata) {
self.inner
.metadata_for_spec_version
.write()
.unwrap()
.insert(spec_version, metadata);
}
}
pub type SubstrateExtrinsicParams<T> = DefaultTransactionExtensions<T>;
pub type SubstrateExtrinsicParamsBuilder<T> = DefaultExtrinsicParamsBuilder<T>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlakeTwo256;
impl Hasher for BlakeTwo256 {
type Hash = H256;
fn new(_metadata: &Metadata) -> Self {
Self
}
fn hash(&self, s: &[u8]) -> Self::Hash {
sp_crypto_hashing::blake2_256(s).into()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DynamicHasher256(HashType);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum HashType {
BlakeTwo256,
Keccak256,
Unknown,
}
impl Hasher for DynamicHasher256 {
type Hash = H256;
fn new(metadata: &Metadata) -> Self {
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::Hash {
match self.0 {
HashType::BlakeTwo256 | HashType::Unknown => sp_crypto_hashing::blake2_256(s).into(),
HashType::Keccak256 => sp_crypto_hashing::keccak_256(s).into(),
}
}
}
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubstrateHeader<Hash> {
pub parent_hash: Hash,
#[serde(
serialize_with = "serialize_number",
deserialize_with = "deserialize_number"
)]
#[codec(compact)]
pub number: u64,
pub state_root: Hash,
pub extrinsics_root: Hash,
pub digest: Digest,
}
impl<H> Header for SubstrateHeader<H>
where
H: Hash,
SubstrateHeader<H>: Encode + Decode,
{
fn number(&self) -> u64 {
self.number
}
}
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
pub struct Digest {
pub logs: Vec<DigestItem>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum DigestItem {
PreRuntime(ConsensusEngineId, Vec<u8>),
Consensus(ConsensusEngineId, Vec<u8>),
Seal(ConsensusEngineId, Vec<u8>),
Other(Vec<u8>),
RuntimeEnvironmentUpdated,
}
#[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),
}
}
}
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>,
{
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"))
}
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum NumberOrHex {
Number(u64),
Hex(U256),
}
impl NumberOrHex {
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)
}
}
#[cfg(test)]
mod test {
use super::*;
#[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<H256> =
serde_json::from_str(numeric_block_number_json).expect("valid block header");
assert_eq!(header.number(), 4);
}
#[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<H256> =
serde_json::from_str(numeric_block_number_json).expect("valid block header");
assert_eq!(header.number(), 4);
}
}