mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 17:31:05 +00:00
Bugfix: trimmed metadata hash comparison fails in is_codegen_valid_for (#1306)
* cargo: Switch to workspace lints (#1299) * subxt: Remove unstable lints that cause compile warnings Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cargo: Switch to workspace lints Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cargo: Fix codec package at root level Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cargo: Move profiles to the root level Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix lightclient and metadata crates Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Revert "cargo: Fix codec package at root level" This reverts commit cdf9e1628d708a972673eb3a9e967b6896edbd73. * Fix complexity clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cargo: Remove lints to be replaced by `cargo machete` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cargo: Remove unused dependencies (detected by machete) Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Add machete step Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cargo: Bump rust version Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Rename machete step Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Rename cargo machete step Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * test and outer enum pre compute * explicit OuterEnumHashes type * clippy * move outer enums to their own module --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
+5
-6
@@ -22,8 +22,8 @@ mod utils;
|
|||||||
use scale_info::{form::PortableForm, PortableRegistry, Variant};
|
use scale_info::{form::PortableForm, PortableRegistry, Variant};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use utils::ordered_map::OrderedMap;
|
|
||||||
use utils::variant_index::VariantIndex;
|
use utils::variant_index::VariantIndex;
|
||||||
|
use utils::{ordered_map::OrderedMap, validation::outer_enum_hashes::OuterEnumHashes};
|
||||||
|
|
||||||
type ArcStr = Arc<str>;
|
type ArcStr = Arc<str>;
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ impl Metadata {
|
|||||||
Some(crate::utils::validation::get_type_hash(
|
Some(crate::utils::validation::get_type_hash(
|
||||||
&self.types,
|
&self.types,
|
||||||
id,
|
id,
|
||||||
&mut HashMap::new(),
|
&OuterEnumHashes::empty(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +324,7 @@ impl<'a> PalletMetadata<'a> {
|
|||||||
|
|
||||||
/// Return a hash for the entire pallet.
|
/// Return a hash for the entire pallet.
|
||||||
pub fn hash(&self) -> [u8; HASH_LEN] {
|
pub fn hash(&self) -> [u8; HASH_LEN] {
|
||||||
crate::utils::validation::get_pallet_hash(*self)
|
crate::utils::validation::get_pallet_hash(*self, &OuterEnumHashes::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,7 +643,7 @@ impl<'a> RuntimeApiMetadata<'a> {
|
|||||||
|
|
||||||
/// Return a hash for the runtime API trait.
|
/// Return a hash for the runtime API trait.
|
||||||
pub fn hash(&self) -> [u8; HASH_LEN] {
|
pub fn hash(&self) -> [u8; HASH_LEN] {
|
||||||
crate::utils::validation::get_runtime_trait_hash(*self)
|
crate::utils::validation::get_runtime_trait_hash(*self, &OuterEnumHashes::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -766,8 +766,7 @@ impl<'a> CustomValueMetadata<'a> {
|
|||||||
|
|
||||||
/// Calculates the hash for the CustomValueMetadata.
|
/// Calculates the hash for the CustomValueMetadata.
|
||||||
pub fn hash(&self) -> [u8; HASH_LEN] {
|
pub fn hash(&self) -> [u8; HASH_LEN] {
|
||||||
let mut cache = HashMap::new();
|
get_custom_value_hash(self, &OuterEnumHashes::empty())
|
||||||
get_custom_value_hash(self, &mut cache)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+283
-121
@@ -5,15 +5,18 @@
|
|||||||
//! Utility functions for metadata validation.
|
//! Utility functions for metadata validation.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CustomMetadata, CustomValueMetadata, ExtrinsicMetadata, Metadata, OuterEnumsMetadata,
|
CustomMetadata, CustomValueMetadata, ExtrinsicMetadata, Metadata, PalletMetadata,
|
||||||
PalletMetadata, RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata,
|
RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata, StorageEntryType,
|
||||||
StorageEntryType,
|
|
||||||
};
|
};
|
||||||
|
use outer_enum_hashes::OuterEnumHashes;
|
||||||
use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefVariant, Variant};
|
use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefVariant, Variant};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub mod outer_enum_hashes;
|
||||||
|
|
||||||
// The number of bytes our `hash` function produces.
|
// The number of bytes our `hash` function produces.
|
||||||
pub(crate) const HASH_LEN: usize = 32;
|
pub(crate) const HASH_LEN: usize = 32;
|
||||||
|
pub type Hash = [u8; HASH_LEN];
|
||||||
|
|
||||||
/// Internal byte representation for various metadata types utilized for
|
/// Internal byte representation for various metadata types utilized for
|
||||||
/// generating deterministic hashes between different rust versions.
|
/// generating deterministic hashes between different rust versions.
|
||||||
@@ -30,13 +33,13 @@ enum TypeBeingHashed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Hashing function utilized internally.
|
/// Hashing function utilized internally.
|
||||||
fn hash(data: &[u8]) -> [u8; HASH_LEN] {
|
fn hash(data: &[u8]) -> Hash {
|
||||||
sp_core_hashing::twox_256(data)
|
sp_core_hashing::twox_256(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// XOR two hashes together. Only use this when you don't care about the order
|
/// XOR two hashes together. Only use this when you don't care about the order
|
||||||
/// of the things you're hashing together.
|
/// of the things you're hashing together.
|
||||||
fn xor(a: [u8; HASH_LEN], b: [u8; HASH_LEN]) -> [u8; HASH_LEN] {
|
fn xor(a: Hash, b: Hash) -> Hash {
|
||||||
let mut out = [0u8; HASH_LEN];
|
let mut out = [0u8; HASH_LEN];
|
||||||
for (idx, (a, b)) in a.into_iter().zip(b).enumerate() {
|
for (idx, (a, b)) in a.into_iter().zip(b).enumerate() {
|
||||||
out[idx] = a ^ b;
|
out[idx] = a ^ b;
|
||||||
@@ -52,7 +55,7 @@ macro_rules! count_idents {
|
|||||||
}
|
}
|
||||||
macro_rules! concat_and_hash_n {
|
macro_rules! concat_and_hash_n {
|
||||||
($name:ident($($arg:ident)+)) => {
|
($name:ident($($arg:ident)+)) => {
|
||||||
fn $name($($arg: &[u8; HASH_LEN]),+) -> [u8; HASH_LEN] {
|
fn $name($($arg: &Hash),+) -> Hash {
|
||||||
let mut out = [0u8; HASH_LEN * count_idents!($($arg)+)];
|
let mut out = [0u8; HASH_LEN * count_idents!($($arg)+)];
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
$(
|
$(
|
||||||
@@ -75,7 +78,8 @@ fn get_field_hash(
|
|||||||
registry: &PortableRegistry,
|
registry: &PortableRegistry,
|
||||||
field: &Field<PortableForm>,
|
field: &Field<PortableForm>,
|
||||||
cache: &mut HashMap<u32, CachedHash>,
|
cache: &mut HashMap<u32, CachedHash>,
|
||||||
) -> [u8; HASH_LEN] {
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
|
) -> Hash {
|
||||||
let field_name_bytes = match &field.name {
|
let field_name_bytes = match &field.name {
|
||||||
Some(name) => hash(name.as_bytes()),
|
Some(name) => hash(name.as_bytes()),
|
||||||
None => [0u8; HASH_LEN],
|
None => [0u8; HASH_LEN],
|
||||||
@@ -83,7 +87,7 @@ fn get_field_hash(
|
|||||||
|
|
||||||
concat_and_hash2(
|
concat_and_hash2(
|
||||||
&field_name_bytes,
|
&field_name_bytes,
|
||||||
&get_type_hash(registry, field.ty.id, cache),
|
&get_type_hash_recurse(registry, field.ty.id, cache, outer_enum_hashes),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,12 +96,16 @@ fn get_variant_hash(
|
|||||||
registry: &PortableRegistry,
|
registry: &PortableRegistry,
|
||||||
var: &Variant<PortableForm>,
|
var: &Variant<PortableForm>,
|
||||||
cache: &mut HashMap<u32, CachedHash>,
|
cache: &mut HashMap<u32, CachedHash>,
|
||||||
) -> [u8; HASH_LEN] {
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
|
) -> Hash {
|
||||||
let variant_name_bytes = hash(var.name.as_bytes());
|
let variant_name_bytes = hash(var.name.as_bytes());
|
||||||
let variant_field_bytes = var.fields.iter().fold([0u8; HASH_LEN], |bytes, field| {
|
let variant_field_bytes = var.fields.iter().fold([0u8; HASH_LEN], |bytes, field| {
|
||||||
// EncodeAsType and DecodeAsType don't care about variant field ordering,
|
// EncodeAsType and DecodeAsType don't care about variant field ordering,
|
||||||
// so XOR the fields to ensure that it doesn't matter.
|
// so XOR the fields to ensure that it doesn't matter.
|
||||||
xor(bytes, get_field_hash(registry, field, cache))
|
xor(
|
||||||
|
bytes,
|
||||||
|
get_field_hash(registry, field, cache, outer_enum_hashes),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
concat_and_hash2(&variant_name_bytes, &variant_field_bytes)
|
concat_and_hash2(&variant_name_bytes, &variant_field_bytes)
|
||||||
@@ -108,7 +116,8 @@ fn get_type_def_variant_hash(
|
|||||||
variant: &TypeDefVariant<PortableForm>,
|
variant: &TypeDefVariant<PortableForm>,
|
||||||
only_these_variants: Option<&[&str]>,
|
only_these_variants: Option<&[&str]>,
|
||||||
cache: &mut HashMap<u32, CachedHash>,
|
cache: &mut HashMap<u32, CachedHash>,
|
||||||
) -> [u8; HASH_LEN] {
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
|
) -> Hash {
|
||||||
let variant_id_bytes = [TypeBeingHashed::Variant as u8; HASH_LEN];
|
let variant_id_bytes = [TypeBeingHashed::Variant as u8; HASH_LEN];
|
||||||
let variant_field_bytes = variant.variants.iter().fold([0u8; HASH_LEN], |bytes, var| {
|
let variant_field_bytes = variant.variants.iter().fold([0u8; HASH_LEN], |bytes, var| {
|
||||||
// With EncodeAsType and DecodeAsType we no longer care which order the variants are in,
|
// With EncodeAsType and DecodeAsType we no longer care which order the variants are in,
|
||||||
@@ -117,9 +126,11 @@ fn get_type_def_variant_hash(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|only_these_variants| only_these_variants.contains(&var.name.as_str()))
|
.map(|only_these_variants| only_these_variants.contains(&var.name.as_str()))
|
||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
|
|
||||||
if should_hash {
|
if should_hash {
|
||||||
xor(bytes, get_variant_hash(registry, var, cache))
|
xor(
|
||||||
|
bytes,
|
||||||
|
get_variant_hash(registry, var, cache, outer_enum_hashes),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -132,7 +143,8 @@ fn get_type_def_hash(
|
|||||||
registry: &PortableRegistry,
|
registry: &PortableRegistry,
|
||||||
ty_def: &TypeDef<PortableForm>,
|
ty_def: &TypeDef<PortableForm>,
|
||||||
cache: &mut HashMap<u32, CachedHash>,
|
cache: &mut HashMap<u32, CachedHash>,
|
||||||
) -> [u8; HASH_LEN] {
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
|
) -> Hash {
|
||||||
match ty_def {
|
match ty_def {
|
||||||
TypeDef::Composite(composite) => {
|
TypeDef::Composite(composite) => {
|
||||||
let composite_id_bytes = [TypeBeingHashed::Composite as u8; HASH_LEN];
|
let composite_id_bytes = [TypeBeingHashed::Composite as u8; HASH_LEN];
|
||||||
@@ -143,14 +155,19 @@ fn get_type_def_hash(
|
|||||||
.fold([0u8; HASH_LEN], |bytes, field| {
|
.fold([0u8; HASH_LEN], |bytes, field| {
|
||||||
// With EncodeAsType and DecodeAsType we no longer care which order the fields are in,
|
// With EncodeAsType and DecodeAsType we no longer care which order the fields are in,
|
||||||
// as long as all of the names+types are there. XOR to not care about ordering.
|
// as long as all of the names+types are there. XOR to not care about ordering.
|
||||||
xor(bytes, get_field_hash(registry, field, cache))
|
xor(
|
||||||
|
bytes,
|
||||||
|
get_field_hash(registry, field, cache, outer_enum_hashes),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
concat_and_hash2(&composite_id_bytes, &composite_field_bytes)
|
concat_and_hash2(&composite_id_bytes, &composite_field_bytes)
|
||||||
}
|
}
|
||||||
TypeDef::Variant(variant) => get_type_def_variant_hash(registry, variant, None, cache),
|
TypeDef::Variant(variant) => {
|
||||||
|
get_type_def_variant_hash(registry, variant, None, cache, outer_enum_hashes)
|
||||||
|
}
|
||||||
TypeDef::Sequence(sequence) => concat_and_hash2(
|
TypeDef::Sequence(sequence) => concat_and_hash2(
|
||||||
&[TypeBeingHashed::Sequence as u8; HASH_LEN],
|
&[TypeBeingHashed::Sequence as u8; HASH_LEN],
|
||||||
&get_type_hash(registry, sequence.type_param.id, cache),
|
&get_type_hash_recurse(registry, sequence.type_param.id, cache, outer_enum_hashes),
|
||||||
),
|
),
|
||||||
TypeDef::Array(array) => {
|
TypeDef::Array(array) => {
|
||||||
// Take length into account too; different length must lead to different hash.
|
// Take length into account too; different length must lead to different hash.
|
||||||
@@ -162,13 +179,16 @@ fn get_type_def_hash(
|
|||||||
};
|
};
|
||||||
concat_and_hash2(
|
concat_and_hash2(
|
||||||
&array_id_bytes,
|
&array_id_bytes,
|
||||||
&get_type_hash(registry, array.type_param.id, cache),
|
&get_type_hash_recurse(registry, array.type_param.id, cache, outer_enum_hashes),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TypeDef::Tuple(tuple) => {
|
TypeDef::Tuple(tuple) => {
|
||||||
let mut bytes = hash(&[TypeBeingHashed::Tuple as u8]);
|
let mut bytes = hash(&[TypeBeingHashed::Tuple as u8]);
|
||||||
for field in &tuple.fields {
|
for field in &tuple.fields {
|
||||||
bytes = concat_and_hash2(&bytes, &get_type_hash(registry, field.id, cache));
|
bytes = concat_and_hash2(
|
||||||
|
&bytes,
|
||||||
|
&get_type_hash_recurse(registry, field.id, cache, outer_enum_hashes),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -178,12 +198,12 @@ fn get_type_def_hash(
|
|||||||
}
|
}
|
||||||
TypeDef::Compact(compact) => concat_and_hash2(
|
TypeDef::Compact(compact) => concat_and_hash2(
|
||||||
&[TypeBeingHashed::Compact as u8; HASH_LEN],
|
&[TypeBeingHashed::Compact as u8; HASH_LEN],
|
||||||
&get_type_hash(registry, compact.type_param.id, cache),
|
&get_type_hash_recurse(registry, compact.type_param.id, cache, outer_enum_hashes),
|
||||||
),
|
),
|
||||||
TypeDef::BitSequence(bitseq) => concat_and_hash3(
|
TypeDef::BitSequence(bitseq) => concat_and_hash3(
|
||||||
&[TypeBeingHashed::BitSequence as u8; HASH_LEN],
|
&[TypeBeingHashed::BitSequence as u8; HASH_LEN],
|
||||||
&get_type_hash(registry, bitseq.bit_order_type.id, cache),
|
&get_type_hash_recurse(registry, bitseq.bit_order_type.id, cache, outer_enum_hashes),
|
||||||
&get_type_hash(registry, bitseq.bit_store_type.id, cache),
|
&get_type_hash_recurse(registry, bitseq.bit_store_type.id, cache, outer_enum_hashes),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,11 +214,11 @@ pub enum CachedHash {
|
|||||||
/// hash not known yet, but computation has already started
|
/// hash not known yet, but computation has already started
|
||||||
Recursive,
|
Recursive,
|
||||||
/// hash of the type, computation was finished
|
/// hash of the type, computation was finished
|
||||||
Hash([u8; HASH_LEN]),
|
Hash(Hash),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CachedHash {
|
impl CachedHash {
|
||||||
fn hash(&self) -> [u8; HASH_LEN] {
|
fn hash(&self) -> Hash {
|
||||||
match &self {
|
match &self {
|
||||||
CachedHash::Hash(hash) => *hash,
|
CachedHash::Hash(hash) => *hash,
|
||||||
CachedHash::Recursive => [123; HASH_LEN], // some magical value
|
CachedHash::Recursive => [123; HASH_LEN], // some magical value
|
||||||
@@ -207,11 +227,33 @@ impl CachedHash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the hash representation of a `scale_info::Type` identified by id.
|
/// Obtain the hash representation of a `scale_info::Type` identified by id.
|
||||||
|
///
|
||||||
|
/// Hashes of the outer enums (call, event, error) should be computed prior to this
|
||||||
|
/// and passed in as the `outer_enum_hashes` argument. Whenever a type is encountered that
|
||||||
|
/// is one of the outer enums, the procomputed hash is used instead of computing a new one.
|
||||||
|
///
|
||||||
|
/// The reason for this unintuitive behavior is that we sometimes want to trim the outer enum types
|
||||||
|
/// beforehand to only include certain pallets, which affects their hash values.
|
||||||
pub fn get_type_hash(
|
pub fn get_type_hash(
|
||||||
|
registry: &PortableRegistry,
|
||||||
|
id: u32,
|
||||||
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
|
) -> Hash {
|
||||||
|
get_type_hash_recurse(registry, id, &mut HashMap::new(), outer_enum_hashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain the hash representation of a `scale_info::Type` identified by id.
|
||||||
|
fn get_type_hash_recurse(
|
||||||
registry: &PortableRegistry,
|
registry: &PortableRegistry,
|
||||||
id: u32,
|
id: u32,
|
||||||
cache: &mut HashMap<u32, CachedHash>,
|
cache: &mut HashMap<u32, CachedHash>,
|
||||||
) -> [u8; HASH_LEN] {
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
|
) -> Hash {
|
||||||
|
// If the type is part of precomputed outer enum hashes, the respective hash is used instead:
|
||||||
|
if let Some(hash) = outer_enum_hashes.resolve(id) {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
// Guard against recursive types, with a 2 step caching approach:
|
// Guard against recursive types, with a 2 step caching approach:
|
||||||
// if the cache has an entry for the id, just return a hash derived from it.
|
// if the cache has an entry for the id, just return a hash derived from it.
|
||||||
// if the type has not been seen yet, mark it with `CachedHash::Recursive` in the cache and proceed to `get_type_def_hash()`.
|
// if the type has not been seen yet, mark it with `CachedHash::Recursive` in the cache and proceed to `get_type_def_hash()`.
|
||||||
@@ -225,7 +267,6 @@ pub fn get_type_hash(
|
|||||||
// type_id -> not contained = We haven't seen the type yet.
|
// type_id -> not contained = We haven't seen the type yet.
|
||||||
// -> `CachedHash::Recursive` = We have seen the type but hash calculation for it hasn't finished yet.
|
// -> `CachedHash::Recursive` = We have seen the type but hash calculation for it hasn't finished yet.
|
||||||
// -> `CachedHash::Hash(hash)` = Hash calculation for the type was completed.
|
// -> `CachedHash::Hash(hash)` = Hash calculation for the type was completed.
|
||||||
|
|
||||||
if let Some(cached_hash) = cache.get(&id) {
|
if let Some(cached_hash) = cache.get(&id) {
|
||||||
return cached_hash.hash();
|
return cached_hash.hash();
|
||||||
}
|
}
|
||||||
@@ -233,7 +274,7 @@ pub fn get_type_hash(
|
|||||||
let ty = registry
|
let ty = registry
|
||||||
.resolve(id)
|
.resolve(id)
|
||||||
.expect("Type ID provided by the metadata is registered; qed");
|
.expect("Type ID provided by the metadata is registered; qed");
|
||||||
let type_hash = get_type_def_hash(registry, &ty.type_def, cache);
|
let type_hash = get_type_def_hash(registry, &ty.type_def, cache, outer_enum_hashes);
|
||||||
cache.insert(id, CachedHash::Hash(type_hash));
|
cache.insert(id, CachedHash::Hash(type_hash));
|
||||||
type_hash
|
type_hash
|
||||||
}
|
}
|
||||||
@@ -242,14 +283,13 @@ pub fn get_type_hash(
|
|||||||
fn get_extrinsic_hash(
|
fn get_extrinsic_hash(
|
||||||
registry: &PortableRegistry,
|
registry: &PortableRegistry,
|
||||||
extrinsic: &ExtrinsicMetadata,
|
extrinsic: &ExtrinsicMetadata,
|
||||||
) -> [u8; HASH_LEN] {
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
let mut cache = HashMap::<u32, CachedHash>::new();
|
) -> Hash {
|
||||||
|
|
||||||
// Get the hashes of the extrinsic type.
|
// Get the hashes of the extrinsic type.
|
||||||
let address_hash = get_type_hash(registry, extrinsic.address_ty, &mut cache);
|
let address_hash = get_type_hash(registry, extrinsic.address_ty, outer_enum_hashes);
|
||||||
// The `RuntimeCall` type is intentionally omitted and hashed by the outer enums instead.
|
// The `RuntimeCall` type is intentionally omitted and hashed by the outer enums instead.
|
||||||
let signature_hash = get_type_hash(registry, extrinsic.signature_ty, &mut cache);
|
let signature_hash = get_type_hash(registry, extrinsic.signature_ty, outer_enum_hashes);
|
||||||
let extra_hash = get_type_hash(registry, extrinsic.extra_ty, &mut cache);
|
let extra_hash = get_type_hash(registry, extrinsic.extra_ty, outer_enum_hashes);
|
||||||
|
|
||||||
let mut bytes = concat_and_hash4(
|
let mut bytes = concat_and_hash4(
|
||||||
&address_hash,
|
&address_hash,
|
||||||
@@ -262,53 +302,20 @@ fn get_extrinsic_hash(
|
|||||||
bytes = concat_and_hash4(
|
bytes = concat_and_hash4(
|
||||||
&bytes,
|
&bytes,
|
||||||
&hash(signed_extension.identifier.as_bytes()),
|
&hash(signed_extension.identifier.as_bytes()),
|
||||||
&get_type_hash(registry, signed_extension.extra_ty, &mut cache),
|
&get_type_hash(registry, signed_extension.extra_ty, outer_enum_hashes),
|
||||||
&get_type_hash(registry, signed_extension.additional_ty, &mut cache),
|
&get_type_hash(registry, signed_extension.additional_ty, outer_enum_hashes),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the hash representation of the `frame_metadata::v15::OuterEnums`.
|
|
||||||
fn get_outer_enums_hash(
|
|
||||||
registry: &PortableRegistry,
|
|
||||||
enums: &OuterEnumsMetadata,
|
|
||||||
only_these_variants: Option<&[&str]>,
|
|
||||||
) -> [u8; HASH_LEN] {
|
|
||||||
/// Hash the provided enum type.
|
|
||||||
fn get_enum_hash(
|
|
||||||
registry: &PortableRegistry,
|
|
||||||
id: u32,
|
|
||||||
only_these_variants: Option<&[&str]>,
|
|
||||||
) -> [u8; HASH_LEN] {
|
|
||||||
let ty = registry
|
|
||||||
.types
|
|
||||||
.get(id as usize)
|
|
||||||
.expect("Metadata should contain enum type in registry");
|
|
||||||
|
|
||||||
if let TypeDef::Variant(variant) = &ty.ty.type_def {
|
|
||||||
get_type_def_variant_hash(registry, variant, only_these_variants, &mut HashMap::new())
|
|
||||||
} else {
|
|
||||||
get_type_hash(registry, id, &mut HashMap::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let call_hash = get_enum_hash(registry, enums.call_enum_ty, only_these_variants);
|
|
||||||
|
|
||||||
let event_hash = get_enum_hash(registry, enums.event_enum_ty, only_these_variants);
|
|
||||||
|
|
||||||
let error_hash = get_enum_hash(registry, enums.error_enum_ty, only_these_variants);
|
|
||||||
|
|
||||||
concat_and_hash3(&call_hash, &event_hash, &error_hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the hash corresponding to a single storage entry.
|
/// Get the hash corresponding to a single storage entry.
|
||||||
fn get_storage_entry_hash(
|
fn get_storage_entry_hash(
|
||||||
registry: &PortableRegistry,
|
registry: &PortableRegistry,
|
||||||
entry: &StorageEntryMetadata,
|
entry: &StorageEntryMetadata,
|
||||||
cache: &mut HashMap<u32, CachedHash>,
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
) -> [u8; HASH_LEN] {
|
) -> Hash {
|
||||||
let mut bytes = concat_and_hash3(
|
let mut bytes = concat_and_hash3(
|
||||||
&hash(entry.name.as_bytes()),
|
&hash(entry.name.as_bytes()),
|
||||||
// Cloning 'entry.modifier' should essentially be a copy.
|
// Cloning 'entry.modifier' should essentially be a copy.
|
||||||
@@ -318,7 +325,7 @@ fn get_storage_entry_hash(
|
|||||||
|
|
||||||
match &entry.entry_type {
|
match &entry.entry_type {
|
||||||
StorageEntryType::Plain(ty) => {
|
StorageEntryType::Plain(ty) => {
|
||||||
concat_and_hash2(&bytes, &get_type_hash(registry, *ty, cache))
|
concat_and_hash2(&bytes, &get_type_hash(registry, *ty, outer_enum_hashes))
|
||||||
}
|
}
|
||||||
StorageEntryType::Map {
|
StorageEntryType::Map {
|
||||||
hashers,
|
hashers,
|
||||||
@@ -331,8 +338,8 @@ fn get_storage_entry_hash(
|
|||||||
}
|
}
|
||||||
concat_and_hash3(
|
concat_and_hash3(
|
||||||
&bytes,
|
&bytes,
|
||||||
&get_type_hash(registry, *key_ty, cache),
|
&get_type_hash(registry, *key_ty, outer_enum_hashes),
|
||||||
&get_type_hash(registry, *value_ty, cache),
|
&get_type_hash(registry, *value_ty, outer_enum_hashes),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,8 +350,8 @@ fn get_runtime_method_hash(
|
|||||||
registry: &PortableRegistry,
|
registry: &PortableRegistry,
|
||||||
trait_name: &str,
|
trait_name: &str,
|
||||||
method_metadata: &RuntimeApiMethodMetadata,
|
method_metadata: &RuntimeApiMethodMetadata,
|
||||||
cache: &mut HashMap<u32, CachedHash>,
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
) -> [u8; HASH_LEN] {
|
) -> Hash {
|
||||||
// The trait name is part of the runtime API call that is being
|
// The trait name is part of the runtime API call that is being
|
||||||
// generated for this method. Therefore the trait name is strongly
|
// generated for this method. Therefore the trait name is strongly
|
||||||
// connected to the method in the same way as a parameter is
|
// connected to the method in the same way as a parameter is
|
||||||
@@ -358,21 +365,23 @@ fn get_runtime_method_hash(
|
|||||||
bytes = concat_and_hash3(
|
bytes = concat_and_hash3(
|
||||||
&bytes,
|
&bytes,
|
||||||
&hash(input.name.as_bytes()),
|
&hash(input.name.as_bytes()),
|
||||||
&get_type_hash(registry, input.ty, cache),
|
&get_type_hash(registry, input.ty, outer_enum_hashes),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes = concat_and_hash2(
|
bytes = concat_and_hash2(
|
||||||
&bytes,
|
&bytes,
|
||||||
&get_type_hash(registry, method_metadata.output_ty, cache),
|
&get_type_hash(registry, method_metadata.output_ty, outer_enum_hashes),
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the hash of all of a runtime API trait, including all of its methods.
|
/// Obtain the hash of all of a runtime API trait, including all of its methods.
|
||||||
pub fn get_runtime_trait_hash(trait_metadata: RuntimeApiMetadata) -> [u8; HASH_LEN] {
|
pub fn get_runtime_trait_hash(
|
||||||
let mut cache = HashMap::new();
|
trait_metadata: RuntimeApiMetadata,
|
||||||
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
|
) -> Hash {
|
||||||
let trait_name = &*trait_metadata.inner.name;
|
let trait_name = &*trait_metadata.inner.name;
|
||||||
let method_bytes = trait_metadata
|
let method_bytes = trait_metadata
|
||||||
.methods()
|
.methods()
|
||||||
@@ -387,7 +396,7 @@ pub fn get_runtime_trait_hash(trait_metadata: RuntimeApiMetadata) -> [u8; HASH_L
|
|||||||
trait_metadata.types,
|
trait_metadata.types,
|
||||||
trait_name,
|
trait_name,
|
||||||
method_metadata,
|
method_metadata,
|
||||||
&mut cache,
|
outer_enum_hashes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@@ -395,12 +404,17 @@ pub fn get_runtime_trait_hash(trait_metadata: RuntimeApiMetadata) -> [u8; HASH_L
|
|||||||
concat_and_hash2(&hash(trait_name.as_bytes()), &method_bytes)
|
concat_and_hash2(&hash(trait_name.as_bytes()), &method_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_custom_metadata_hash(custom_metadata: &CustomMetadata) -> [u8; HASH_LEN] {
|
fn get_custom_metadata_hash(
|
||||||
let mut cache = HashMap::new();
|
custom_metadata: &CustomMetadata,
|
||||||
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
|
) -> Hash {
|
||||||
custom_metadata
|
custom_metadata
|
||||||
.iter()
|
.iter()
|
||||||
.fold([0u8; HASH_LEN], |bytes, custom_value| {
|
.fold([0u8; HASH_LEN], |bytes, custom_value| {
|
||||||
xor(bytes, get_custom_value_hash(&custom_value, &mut cache))
|
xor(
|
||||||
|
bytes,
|
||||||
|
get_custom_value_hash(&custom_value, outer_enum_hashes),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,50 +424,56 @@ pub fn get_custom_metadata_hash(custom_metadata: &CustomMetadata) -> [u8; HASH_L
|
|||||||
/// only the name and bytes are used for hashing.
|
/// only the name and bytes are used for hashing.
|
||||||
pub fn get_custom_value_hash(
|
pub fn get_custom_value_hash(
|
||||||
custom_value: &CustomValueMetadata,
|
custom_value: &CustomValueMetadata,
|
||||||
cache: &mut HashMap<u32, CachedHash>,
|
outer_enum_hashes: &OuterEnumHashes,
|
||||||
) -> [u8; HASH_LEN] {
|
) -> Hash {
|
||||||
let name_hash = hash(custom_value.name.as_bytes());
|
let name_hash = hash(custom_value.name.as_bytes());
|
||||||
if custom_value.types.resolve(custom_value.type_id()).is_none() {
|
if custom_value.types.resolve(custom_value.type_id()).is_none() {
|
||||||
hash(&name_hash)
|
hash(&name_hash)
|
||||||
} else {
|
} else {
|
||||||
concat_and_hash2(
|
concat_and_hash2(
|
||||||
&name_hash,
|
&name_hash,
|
||||||
&get_type_hash(custom_value.types, custom_value.type_id(), cache),
|
&get_type_hash(
|
||||||
|
custom_value.types,
|
||||||
|
custom_value.type_id(),
|
||||||
|
outer_enum_hashes,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the hash for a specific storage item, or an error if it's not found.
|
/// Obtain the hash for a specific storage item, or an error if it's not found.
|
||||||
pub fn get_storage_hash(pallet: &PalletMetadata, entry_name: &str) -> Option<[u8; HASH_LEN]> {
|
pub fn get_storage_hash(pallet: &PalletMetadata, entry_name: &str) -> Option<Hash> {
|
||||||
let storage = pallet.storage()?;
|
let storage = pallet.storage()?;
|
||||||
let entry = storage.entry_by_name(entry_name)?;
|
let entry = storage.entry_by_name(entry_name)?;
|
||||||
let hash = get_storage_entry_hash(pallet.types, entry, &mut HashMap::new());
|
let hash = get_storage_entry_hash(pallet.types, entry, &OuterEnumHashes::empty());
|
||||||
Some(hash)
|
Some(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the hash for a specific constant, or an error if it's not found.
|
/// Obtain the hash for a specific constant, or an error if it's not found.
|
||||||
pub fn get_constant_hash(pallet: &PalletMetadata, constant_name: &str) -> Option<[u8; HASH_LEN]> {
|
pub fn get_constant_hash(pallet: &PalletMetadata, constant_name: &str) -> Option<Hash> {
|
||||||
let constant = pallet.constant_by_name(constant_name)?;
|
let constant = pallet.constant_by_name(constant_name)?;
|
||||||
|
|
||||||
// We only need to check that the type of the constant asked for matches.
|
// We only need to check that the type of the constant asked for matches.
|
||||||
let bytes = get_type_hash(pallet.types, constant.ty, &mut HashMap::new());
|
let bytes = get_type_hash(pallet.types, constant.ty, &OuterEnumHashes::empty());
|
||||||
Some(bytes)
|
Some(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the hash for a specific call, or an error if it's not found.
|
/// Obtain the hash for a specific call, or an error if it's not found.
|
||||||
pub fn get_call_hash(pallet: &PalletMetadata, call_name: &str) -> Option<[u8; HASH_LEN]> {
|
pub fn get_call_hash(pallet: &PalletMetadata, call_name: &str) -> Option<Hash> {
|
||||||
let call_variant = pallet.call_variant_by_name(call_name)?;
|
let call_variant = pallet.call_variant_by_name(call_name)?;
|
||||||
|
|
||||||
// hash the specific variant representing the call we are interested in.
|
// hash the specific variant representing the call we are interested in.
|
||||||
let hash = get_variant_hash(pallet.types, call_variant, &mut HashMap::new());
|
let hash = get_variant_hash(
|
||||||
|
pallet.types,
|
||||||
|
call_variant,
|
||||||
|
&mut HashMap::new(),
|
||||||
|
&OuterEnumHashes::empty(),
|
||||||
|
);
|
||||||
Some(hash)
|
Some(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the hash of a specific runtime API function, or an error if it's not found.
|
/// Obtain the hash of a specific runtime API function, or an error if it's not found.
|
||||||
pub fn get_runtime_api_hash(
|
pub fn get_runtime_api_hash(runtime_apis: &RuntimeApiMetadata, method_name: &str) -> Option<Hash> {
|
||||||
runtime_apis: &RuntimeApiMetadata,
|
|
||||||
method_name: &str,
|
|
||||||
) -> Option<[u8; HASH_LEN]> {
|
|
||||||
let trait_name = &*runtime_apis.inner.name;
|
let trait_name = &*runtime_apis.inner.name;
|
||||||
let method_metadata = runtime_apis.method_by_name(method_name)?;
|
let method_metadata = runtime_apis.method_by_name(method_name)?;
|
||||||
|
|
||||||
@@ -461,25 +481,24 @@ pub fn get_runtime_api_hash(
|
|||||||
runtime_apis.types,
|
runtime_apis.types,
|
||||||
trait_name,
|
trait_name,
|
||||||
method_metadata,
|
method_metadata,
|
||||||
&mut HashMap::new(),
|
&OuterEnumHashes::empty(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the hash representation of a `frame_metadata::v15::PalletMetadata`.
|
/// Obtain the hash representation of a `frame_metadata::v15::PalletMetadata`.
|
||||||
pub fn get_pallet_hash(pallet: PalletMetadata) -> [u8; HASH_LEN] {
|
pub fn get_pallet_hash(pallet: PalletMetadata, outer_enum_hashes: &OuterEnumHashes) -> Hash {
|
||||||
let mut cache = HashMap::<u32, CachedHash>::new();
|
|
||||||
let registry = pallet.types;
|
let registry = pallet.types;
|
||||||
|
|
||||||
let call_bytes = match pallet.call_ty_id() {
|
let call_bytes = match pallet.call_ty_id() {
|
||||||
Some(calls) => get_type_hash(registry, calls, &mut cache),
|
Some(calls) => get_type_hash(registry, calls, outer_enum_hashes),
|
||||||
None => [0u8; HASH_LEN],
|
None => [0u8; HASH_LEN],
|
||||||
};
|
};
|
||||||
let event_bytes = match pallet.event_ty_id() {
|
let event_bytes = match pallet.event_ty_id() {
|
||||||
Some(event) => get_type_hash(registry, event, &mut cache),
|
Some(event) => get_type_hash(registry, event, outer_enum_hashes),
|
||||||
None => [0u8; HASH_LEN],
|
None => [0u8; HASH_LEN],
|
||||||
};
|
};
|
||||||
let error_bytes = match pallet.error_ty_id() {
|
let error_bytes = match pallet.error_ty_id() {
|
||||||
Some(error) => get_type_hash(registry, error, &mut cache),
|
Some(error) => get_type_hash(registry, error, outer_enum_hashes),
|
||||||
None => [0u8; HASH_LEN],
|
None => [0u8; HASH_LEN],
|
||||||
};
|
};
|
||||||
let constant_bytes = pallet.constants().fold([0u8; HASH_LEN], |bytes, constant| {
|
let constant_bytes = pallet.constants().fold([0u8; HASH_LEN], |bytes, constant| {
|
||||||
@@ -487,7 +506,7 @@ pub fn get_pallet_hash(pallet: PalletMetadata) -> [u8; HASH_LEN] {
|
|||||||
// of (constantName, constantType) to make the order we see them irrelevant.
|
// of (constantName, constantType) to make the order we see them irrelevant.
|
||||||
let constant_hash = concat_and_hash2(
|
let constant_hash = concat_and_hash2(
|
||||||
&hash(constant.name.as_bytes()),
|
&hash(constant.name.as_bytes()),
|
||||||
&get_type_hash(registry, constant.ty(), &mut cache),
|
&get_type_hash(registry, constant.ty(), outer_enum_hashes),
|
||||||
);
|
);
|
||||||
xor(bytes, constant_hash)
|
xor(bytes, constant_hash)
|
||||||
});
|
});
|
||||||
@@ -500,7 +519,10 @@ pub fn get_pallet_hash(pallet: PalletMetadata) -> [u8; HASH_LEN] {
|
|||||||
.fold([0u8; HASH_LEN], |bytes, entry| {
|
.fold([0u8; HASH_LEN], |bytes, entry| {
|
||||||
// We don't care what order the storage entries occur in, so XOR them together
|
// We don't care what order the storage entries occur in, so XOR them together
|
||||||
// to make the order irrelevant.
|
// to make the order irrelevant.
|
||||||
xor(bytes, get_storage_entry_hash(registry, entry, &mut cache))
|
xor(
|
||||||
|
bytes,
|
||||||
|
get_storage_entry_hash(registry, entry, outer_enum_hashes),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
concat_and_hash2(&prefix_hash, &entries_hash)
|
concat_and_hash2(&prefix_hash, &entries_hash)
|
||||||
}
|
}
|
||||||
@@ -560,9 +582,13 @@ impl<'a> MetadataHasher<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Hash the given metadata.
|
/// Hash the given metadata.
|
||||||
pub fn hash(&self) -> [u8; HASH_LEN] {
|
pub fn hash(&self) -> Hash {
|
||||||
let metadata = self.metadata;
|
let metadata = self.metadata;
|
||||||
|
|
||||||
|
// Get the hashes of outer enums, considering only `specific_pallets` (if any are set).
|
||||||
|
// If any of the typed that represent outer enums are encountered later, hashes from `top_level_enum_hashes` can be substituted.
|
||||||
|
let outer_enum_hashes = OuterEnumHashes::new(metadata, self.specific_pallets.as_deref());
|
||||||
|
|
||||||
let pallet_hash = metadata.pallets().fold([0u8; HASH_LEN], |bytes, pallet| {
|
let pallet_hash = metadata.pallets().fold([0u8; HASH_LEN], |bytes, pallet| {
|
||||||
// If specific pallets are given, only include this pallet if it is in the specific pallets.
|
// If specific pallets are given, only include this pallet if it is in the specific pallets.
|
||||||
let should_hash = self
|
let should_hash = self
|
||||||
@@ -573,7 +599,7 @@ impl<'a> MetadataHasher<'a> {
|
|||||||
// We don't care what order the pallets are seen in, so XOR their
|
// We don't care what order the pallets are seen in, so XOR their
|
||||||
// hashes together to be order independent.
|
// hashes together to be order independent.
|
||||||
if should_hash {
|
if should_hash {
|
||||||
xor(bytes, get_pallet_hash(pallet))
|
xor(bytes, get_pallet_hash(pallet, &outer_enum_hashes))
|
||||||
} else {
|
} else {
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -591,24 +617,19 @@ impl<'a> MetadataHasher<'a> {
|
|||||||
// We don't care what order the runtime APIs are seen in, so XOR their
|
// We don't care what order the runtime APIs are seen in, so XOR their
|
||||||
// hashes together to be order independent.
|
// hashes together to be order independent.
|
||||||
if should_hash {
|
if should_hash {
|
||||||
xor(bytes, get_runtime_trait_hash(api))
|
xor(bytes, get_runtime_trait_hash(api, &outer_enum_hashes))
|
||||||
} else {
|
} else {
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let extrinsic_hash = get_extrinsic_hash(&metadata.types, &metadata.extrinsic);
|
let extrinsic_hash =
|
||||||
|
get_extrinsic_hash(&metadata.types, &metadata.extrinsic, &outer_enum_hashes);
|
||||||
let runtime_hash =
|
let runtime_hash =
|
||||||
get_type_hash(&metadata.types, metadata.runtime_ty(), &mut HashMap::new());
|
get_type_hash(&metadata.types, metadata.runtime_ty(), &outer_enum_hashes);
|
||||||
let outer_enums_hash = get_outer_enums_hash(
|
|
||||||
&metadata.types,
|
|
||||||
&metadata.outer_enums(),
|
|
||||||
self.specific_pallets.as_deref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let custom_values_hash = self
|
let custom_values_hash = self
|
||||||
.include_custom_values
|
.include_custom_values
|
||||||
.then(|| get_custom_metadata_hash(&metadata.custom()))
|
.then(|| get_custom_metadata_hash(&metadata.custom(), &outer_enum_hashes))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
concat_and_hash6(
|
concat_and_hash6(
|
||||||
@@ -616,7 +637,7 @@ impl<'a> MetadataHasher<'a> {
|
|||||||
&apis_hash,
|
&apis_hash,
|
||||||
&extrinsic_hash,
|
&extrinsic_hash,
|
||||||
&runtime_hash,
|
&runtime_hash,
|
||||||
&outer_enums_hash,
|
&outer_enum_hashes.combined_hash(),
|
||||||
&custom_values_hash,
|
&custom_values_hash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -646,7 +667,7 @@ mod tests {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(scale_info::TypeInfo)]
|
#[derive(scale_info::TypeInfo)]
|
||||||
// TypeDef::Composite with TypeDef::Array with Typedef::Primitive.
|
// TypeDef::Composite with TypeDef::Array with Typedef::Primitive.
|
||||||
struct AccountId32([u8; HASH_LEN]);
|
struct AccountId32(Hash);
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(scale_info::TypeInfo)]
|
#[derive(scale_info::TypeInfo)]
|
||||||
@@ -849,10 +870,11 @@ mod tests {
|
|||||||
let registry: PortableRegistry = registry.into();
|
let registry: PortableRegistry = registry.into();
|
||||||
|
|
||||||
let mut cache = HashMap::new();
|
let mut cache = HashMap::new();
|
||||||
|
let ignored_enums = &OuterEnumHashes::empty();
|
||||||
|
|
||||||
let a_hash = get_type_hash(®istry, a_type_id, &mut cache);
|
let a_hash = get_type_hash_recurse(®istry, a_type_id, &mut cache, ignored_enums);
|
||||||
let a_hash2 = get_type_hash(®istry, a_type_id, &mut cache);
|
let a_hash2 = get_type_hash_recurse(®istry, a_type_id, &mut cache, ignored_enums);
|
||||||
let b_hash = get_type_hash(®istry, b_type_id, &mut cache);
|
let b_hash = get_type_hash_recurse(®istry, b_type_id, &mut cache, ignored_enums);
|
||||||
|
|
||||||
let CachedHash::Hash(a_cache_hash) = cache[&a_type_id] else {
|
let CachedHash::Hash(a_cache_hash) = cache[&a_type_id] else {
|
||||||
panic!()
|
panic!()
|
||||||
@@ -1080,4 +1102,144 @@ mod tests {
|
|||||||
to_hash(meta_type::<StructE2>())
|
to_hash(meta_type::<StructE2>())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use frame_metadata::v15::{
|
||||||
|
PalletEventMetadata, PalletStorageMetadata, StorageEntryMetadata, StorageEntryModifier,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn metadata_with_pallet_events() -> Metadata {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(scale_info::TypeInfo)]
|
||||||
|
struct FirstEvent {
|
||||||
|
s: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(scale_info::TypeInfo)]
|
||||||
|
struct SecondEvent {
|
||||||
|
n: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(scale_info::TypeInfo)]
|
||||||
|
enum Events {
|
||||||
|
First(FirstEvent),
|
||||||
|
Second(SecondEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(scale_info::TypeInfo)]
|
||||||
|
enum Errors {
|
||||||
|
First(DispatchError),
|
||||||
|
Second(DispatchError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(scale_info::TypeInfo)]
|
||||||
|
enum Calls {
|
||||||
|
First(u8),
|
||||||
|
Second(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum DispatchError {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl scale_info::TypeInfo for DispatchError {
|
||||||
|
type Identity = DispatchError;
|
||||||
|
|
||||||
|
fn type_info() -> scale_info::Type {
|
||||||
|
scale_info::Type {
|
||||||
|
path: scale_info::Path {
|
||||||
|
segments: vec!["sp_runtime", "DispatchError"],
|
||||||
|
},
|
||||||
|
type_params: vec![],
|
||||||
|
type_def: TypeDef::Variant(TypeDefVariant { variants: vec![] }),
|
||||||
|
docs: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pallets = vec![
|
||||||
|
v15::PalletMetadata {
|
||||||
|
name: "First",
|
||||||
|
index: 0,
|
||||||
|
calls: Some(v15::PalletCallMetadata {
|
||||||
|
ty: meta_type::<u8>(),
|
||||||
|
}),
|
||||||
|
storage: Some(PalletStorageMetadata {
|
||||||
|
prefix: "___",
|
||||||
|
entries: vec![StorageEntryMetadata {
|
||||||
|
name: "Hello",
|
||||||
|
modifier: StorageEntryModifier::Optional,
|
||||||
|
// Note: This is the important part here:
|
||||||
|
// The Events type will be trimmed down and this trimming needs to be reflected
|
||||||
|
// when the hash of this storage item is computed.
|
||||||
|
ty: frame_metadata::v14::StorageEntryType::Plain(meta_type::<Vec<Events>>()),
|
||||||
|
default: vec![],
|
||||||
|
docs: vec![],
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
event: Some(PalletEventMetadata {
|
||||||
|
ty: meta_type::<FirstEvent>(),
|
||||||
|
}),
|
||||||
|
constants: vec![],
|
||||||
|
error: None,
|
||||||
|
docs: vec![],
|
||||||
|
},
|
||||||
|
v15::PalletMetadata {
|
||||||
|
name: "Second",
|
||||||
|
index: 1,
|
||||||
|
calls: Some(v15::PalletCallMetadata {
|
||||||
|
ty: meta_type::<u64>(),
|
||||||
|
}),
|
||||||
|
storage: None,
|
||||||
|
event: Some(PalletEventMetadata {
|
||||||
|
ty: meta_type::<SecondEvent>(),
|
||||||
|
}),
|
||||||
|
constants: vec![],
|
||||||
|
error: None,
|
||||||
|
docs: vec![],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
v15::RuntimeMetadataV15::new(
|
||||||
|
pallets,
|
||||||
|
build_default_extrinsic(),
|
||||||
|
meta_type::<()>(),
|
||||||
|
vec![],
|
||||||
|
v15::OuterEnums {
|
||||||
|
call_enum_ty: meta_type::<Calls>(),
|
||||||
|
event_enum_ty: meta_type::<Events>(),
|
||||||
|
error_enum_ty: meta_type::<Errors>(),
|
||||||
|
},
|
||||||
|
v15::CustomMetadata {
|
||||||
|
map: Default::default(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.try_into()
|
||||||
|
.expect("can build valid metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hash_comparison_trimmed_metadata() {
|
||||||
|
// trim the metadata:
|
||||||
|
let metadata = metadata_with_pallet_events();
|
||||||
|
let trimmed_metadata = {
|
||||||
|
let mut m = metadata.clone();
|
||||||
|
m.retain(|e| e == "First", |_| true);
|
||||||
|
m
|
||||||
|
};
|
||||||
|
|
||||||
|
// test that the hashes are the same:
|
||||||
|
let hash = MetadataHasher::new(&metadata)
|
||||||
|
.only_these_pallets(&["First"])
|
||||||
|
.hash();
|
||||||
|
let hash_trimmed = MetadataHasher::new(&trimmed_metadata).hash();
|
||||||
|
|
||||||
|
assert_eq!(hash, hash_trimmed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//! Hash representations of the `frame_metadata::v15::OuterEnums`.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use scale_info::{PortableRegistry, TypeDef};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
utils::validation::{get_type_def_variant_hash, get_type_hash},
|
||||||
|
Metadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{concat_and_hash3, Hash, HASH_LEN};
|
||||||
|
|
||||||
|
/// Hash representations of the `frame_metadata::v15::OuterEnums`.
|
||||||
|
pub struct OuterEnumHashes {
|
||||||
|
call_hash: (u32, Hash),
|
||||||
|
error_hash: (u32, Hash),
|
||||||
|
event_hash: (u32, Hash),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OuterEnumHashes {
|
||||||
|
/// Constructs new `OuterEnumHashes` from metadata. If `only_these_variants` is set, the enums are stripped down to only these variants, before their hashes are calculated.
|
||||||
|
pub fn new(metadata: &Metadata, only_these_variants: Option<&[&str]>) -> Self {
|
||||||
|
fn get_enum_hash(
|
||||||
|
registry: &PortableRegistry,
|
||||||
|
id: u32,
|
||||||
|
only_these_variants: Option<&[&str]>,
|
||||||
|
) -> Hash {
|
||||||
|
let ty = registry
|
||||||
|
.types
|
||||||
|
.get(id as usize)
|
||||||
|
.expect("Metadata should contain enum type in registry");
|
||||||
|
|
||||||
|
if let TypeDef::Variant(variant) = &ty.ty.type_def {
|
||||||
|
get_type_def_variant_hash(
|
||||||
|
registry,
|
||||||
|
variant,
|
||||||
|
only_these_variants,
|
||||||
|
&mut HashMap::new(),
|
||||||
|
// ignored, because not computed yet...
|
||||||
|
&OuterEnumHashes::empty(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
get_type_hash(registry, id, &OuterEnumHashes::empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let enums = &metadata.outer_enums;
|
||||||
|
|
||||||
|
let call_hash = get_enum_hash(metadata.types(), enums.call_enum_ty, only_these_variants);
|
||||||
|
let event_hash = get_enum_hash(metadata.types(), enums.event_enum_ty, only_these_variants);
|
||||||
|
let error_hash = get_enum_hash(metadata.types(), enums.error_enum_ty, only_these_variants);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
call_hash: (enums.call_enum_ty, call_hash),
|
||||||
|
error_hash: (enums.error_enum_ty, error_hash),
|
||||||
|
event_hash: (enums.event_enum_ty, event_hash),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs empty `OuterEnumHashes` with type ids that are never a real type id.
|
||||||
|
/// Can be used as a placeholder when outer enum hashes are required but should be ignored.
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
call_hash: (u32::MAX, [0; HASH_LEN]),
|
||||||
|
error_hash: (u32::MAX, [0; HASH_LEN]),
|
||||||
|
event_hash: (u32::MAX, [0; HASH_LEN]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a combined hash of the top level enums.
|
||||||
|
pub fn combined_hash(&self) -> Hash {
|
||||||
|
concat_and_hash3(&self.call_hash.1, &self.error_hash.1, &self.event_hash.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a type is one of the 3 top level enum types. If so, returns Some(hash).
|
||||||
|
///
|
||||||
|
/// This is useful, because top level enums are sometimes stripped down to only certain pallets.
|
||||||
|
/// The hashes of these stripped down types are stored in this struct.
|
||||||
|
pub fn resolve(&self, id: u32) -> Option<[u8; HASH_LEN]> {
|
||||||
|
match id {
|
||||||
|
e if e == self.error_hash.0 => Some(self.error_hash.1),
|
||||||
|
e if e == self.event_hash.0 => Some(self.event_hash.1),
|
||||||
|
e if e == self.call_hash.0 => Some(self.call_hash.1),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user