Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent bug when reusing type ids in hashing #1075

Merged
51 changes: 35 additions & 16 deletions metadata/src/utils/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ fn get_type_def_hash(
}

/// indicates whether a hash has been fully computed for a type or not
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum CachedHash {
/// hash not known yet, but computation has already started
Recursive,
Expand Down Expand Up @@ -584,7 +584,7 @@ mod tests {
use super::*;
use bitvec::{order::Lsb0, vec::BitVec};
use frame_metadata::v15;
use scale_info::meta_type;
use scale_info::{meta_type, Registry};

// Define recursive types.
#[allow(dead_code)]
Expand Down Expand Up @@ -768,23 +768,23 @@ mod tests {
assert_eq!(hash, hash_swap);
}

#[allow(dead_code)]
#[derive(scale_info::TypeInfo)]
struct Aba {
ab: (A, B),
other: A,
}

#[allow(dead_code)]
#[derive(scale_info::TypeInfo)]
struct Abb {
ab: (A, B),
other: B,
}

#[test]
jsdw marked this conversation as resolved.
Show resolved Hide resolved
/// Ensure ABB and ABA have a different structure:
fn do_not_reuse_visited_type_ids() {
#[allow(dead_code)]
#[derive(scale_info::TypeInfo)]
struct Aba {
ab: (A, B),
other: A,
}

#[allow(dead_code)]
#[derive(scale_info::TypeInfo)]
struct Abb {
ab: (A, B),
other: B,
}

let metadata_hash_with_type = |ty| {
let mut pallets = build_default_pallets();
pallets[0].calls = Some(v15::PalletCallMetadata { ty });
Expand All @@ -798,6 +798,25 @@ mod tests {
assert_ne!(aba_hash, abb_hash);
}

#[test]
fn hash_cache_gets_filled_with_correct_hashes() {
let mut registry = Registry::new();
let a_type_id = registry.register_type(&meta_type::<A>()).id;
let b_type_id = registry.register_type(&meta_type::<B>()).id;
let registry: PortableRegistry = registry.into();

let mut cache = HashMap::new();

let a_hash = get_type_hash(&registry, a_type_id, &mut cache);
let b_hash = get_type_hash(&registry, b_type_id, &mut cache);

let CachedHash::Hash(a_cache_hash) = cache[&a_type_id] else { panic!() };
let CachedHash::Hash(b_cache_hash) = cache[&b_type_id] else { panic!() };

assert_eq!(a_hash, a_cache_hash);
assert_eq!(b_hash, b_cache_hash);
Comment on lines +814 to +818
Copy link
Collaborator

@jsdw jsdw Jul 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also check that a_hash == b_hash; imo that's more iomportant than looking into the cached stuff :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want the hashes to be the same, because A and B are different structs, with different fields:

struct A { pub b: Box<B> }
struct B { pub a: Box<A> }

Maybe I understood you wrong, what did you mean?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry yeah, I mean, it'd be nice to have a test like:

let a_type_id = registry.register_type(&meta_type::<A>()).id;

let a_hash1 = get_type_hash(&registry, a_type_id, &mut cache);
let a_hash2 = get_type_hash(&registry, a_type_id, &mut cache);

assert_eq!(a_hash1, a_hash2);

ie, we want to check that sharing the cache across different uses doesn't change the hashes that are produced for some type.

}

#[test]
// Redundant clone clippy warning is a lie; https://github.com/rust-lang/rust-clippy/issues/10870
#[allow(clippy::redundant_clone)]
Expand Down