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

Fix codegen validation when Runtime APIs are stripped #1000

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,14 @@ impl RuntimeGenerator {
.collect();
let pallet_names_len = pallet_names.len();

let metadata_hash = self
let runtime_api_names: Vec<_> = self
.metadata
.hasher()
.only_these_pallets(&pallet_names)
jsdw marked this conversation as resolved.
Show resolved Hide resolved
.hash();
.runtime_api_traits()
.map(|api| api.name().to_string())
.collect();
let runtime_api_names_len = runtime_api_names.len();

let metadata_hash = self.metadata.hasher().hash();

let modules = pallets_with_mod_names
.iter()
Expand Down Expand Up @@ -621,7 +624,12 @@ impl RuntimeGenerator {

/// check whether the Client you are using is aligned with the statically generated codegen.
pub fn validate_codegen<T: #crate_path::Config, C: #crate_path::client::OfflineClientT<T>>(client: &C) -> Result<(), #crate_path::error::MetadataError> {
let runtime_metadata_hash = client.metadata().hasher().only_these_pallets(&PALLETS).hash();
static RUNTIME_APIS: [&str; #runtime_api_names_len] = [ #(#runtime_api_names,)* ];
jsdw marked this conversation as resolved.
Show resolved Hide resolved
let runtime_metadata_hash = client
.metadata()
.hasher()
.only_these_pallets(&PALLETS)
.only_these_runtime_apis(&RUNTIME_APIS).hash();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it just GitHub or are these lines super indented? :D

if runtime_metadata_hash != [ #(#metadata_hash,)* ] {
Err(#crate_path::error::MetadataError::IncompatibleCodegen)
} else {
Expand Down
46 changes: 36 additions & 10 deletions metadata/src/utils/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ pub fn get_pallet_hash(pallet: PalletMetadata) -> [u8; HASH_LEN] {
pub struct MetadataHasher<'a> {
metadata: &'a Metadata,
specific_pallets: Option<Vec<&'a str>>,
specific_runtime_apis: Option<Vec<&'a str>>,
}

impl<'a> MetadataHasher<'a> {
Expand All @@ -415,6 +416,7 @@ impl<'a> MetadataHasher<'a> {
Self {
metadata,
specific_pallets: None,
specific_runtime_apis: None,
}
}

Expand All @@ -424,30 +426,54 @@ impl<'a> MetadataHasher<'a> {
self
}

/// only hash the provided runtime APIs instead of hashing every runtime API
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
/// only hash the provided runtime APIs instead of hashing every runtime API
/// Only hash the provided runtime APIs instead of hashing every runtime API.

pub fn only_these_runtime_apis<S: AsRef<str>>(
&mut self,
specific_runtime_apis: &'a [S],
) -> &mut Self {
self.specific_runtime_apis =
Some(specific_runtime_apis.iter().map(|n| n.as_ref()).collect());
self
}

/// Hash the given metadata.
pub fn hash(&self) -> [u8; HASH_LEN] {
let mut visited_ids = HashSet::<u32>::new();

let metadata = self.metadata;

let pallet_hash = metadata.pallets().fold([0u8; HASH_LEN], |bytes, pallet| {
// If specific pallets are given, only include this pallet if it's
// in the list.
if let Some(specific_pallets) = &self.specific_pallets {
if specific_pallets.iter().all(|&p| p != pallet.name()) {
return bytes;
}
}
// If specific pallets are given, only include this pallet if it is in the specific pallets.
let should_hash = self
.specific_pallets
.as_ref()
.map(|specific_pallets| specific_pallets.contains(&pallet.name()))
.unwrap_or(true);
// We don't care what order the pallets are seen in, so XOR their
// hashes together to be order independent.
xor(bytes, get_pallet_hash(pallet))
if should_hash {
xor(bytes, get_pallet_hash(pallet))
} else {
bytes
}
});

let apis_hash = metadata
.runtime_api_traits()
.fold([0u8; HASH_LEN], |bytes, api| {
// We don't care what order the runtime APIs are seen in, so XOR
xor(bytes, get_runtime_trait_hash(api))
// If specific runtime APIs are given, only include this pallet if it is in the specific runtime APIs.
let should_hash = self
.specific_runtime_apis
.as_ref()
.map(|specific_runtime_apis| specific_runtime_apis.contains(&api.name()))
.unwrap_or(true);
// We don't care what order the runtime APIs are seen in, so XOR their
// hashes together to be order independent.
if should_hash {
xor(bytes, xor(bytes, get_runtime_trait_hash(api)))
} else {
bytes
}
});

let extrinsic_hash = get_extrinsic_hash(&metadata.types, &metadata.extrinsic);
Expand Down
26 changes: 23 additions & 3 deletions testing/integration-tests/src/codegen/polkadot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4424,16 +4424,36 @@ pub mod api {
pub fn validate_codegen<T: ::subxt::Config, C: ::subxt::client::OfflineClientT<T>>(
client: &C,
) -> Result<(), ::subxt::error::MetadataError> {
static RUNTIME_APIS: [&str; 17usize] = [
"Core",
"Metadata",
"BlockBuilder",
"NominationPoolsApi",
"StakingApi",
"TaggedTransactionQueue",
"OffchainWorkerApi",
"ParachainHost",
"BeefyApi",
"MmrApi",
"GrandpaApi",
"BabeApi",
"AuthorityDiscoveryApi",
"SessionKeys",
"AccountNonceApi",
"TransactionPaymentApi",
"TransactionPaymentCallApi",
];
let runtime_metadata_hash = client
.metadata()
.hasher()
.only_these_pallets(&PALLETS)
.only_these_runtime_apis(&RUNTIME_APIS)
.hash();
if runtime_metadata_hash
!= [
151u8, 83u8, 251u8, 44u8, 149u8, 59u8, 20u8, 183u8, 19u8, 173u8, 234u8, 48u8,
114u8, 104u8, 69u8, 102u8, 189u8, 208u8, 10u8, 87u8, 154u8, 252u8, 54u8, 185u8,
248u8, 199u8, 45u8, 173u8, 199u8, 95u8, 189u8, 253u8,
48u8, 175u8, 255u8, 171u8, 180u8, 123u8, 181u8, 54u8, 125u8, 74u8, 109u8, 140u8,
192u8, 208u8, 131u8, 194u8, 195u8, 232u8, 33u8, 229u8, 178u8, 181u8, 236u8, 230u8,
37u8, 97u8, 134u8, 144u8, 187u8, 127u8, 47u8, 237u8,
]
{
Err(::subxt::error::MetadataError::IncompatibleCodegen)
Expand Down