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

Consolidate APIs given common EncodeAsType/DecodeAsType for static/dynamic values #862

Merged
merged 7 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ pub fn generate_calls(
pub fn #fn_name(
&self,
#( #call_fn_args, )*
) -> #crate_path::tx::StaticTxPayload<#struct_name> {
#crate_path::tx::StaticTxPayload::new(
) -> #crate_path::tx::Payload<#struct_name> {
#crate_path::tx::Payload::new_static(
#pallet_name,
#call_name,
#struct_name { #( #call_args, )* },
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/api/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ pub fn generate_constants(

Ok(quote! {
#docs
pub fn #fn_name(&self) -> #crate_path::constants::StaticConstantAddress<#return_ty> {
#crate_path::constants::StaticConstantAddress::new(
pub fn #fn_name(&self) -> #crate_path::constants::Address<#return_ty> {
#crate_path::constants::Address::new_static(
#pallet_name,
#constant_name,
[#(#constant_hash,)*]
Expand Down
87 changes: 41 additions & 46 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ use syn::parse_quote;
/// Error returned when the Codegen cannot generate the runtime API.
#[derive(Debug, thiserror::Error)]
pub enum CodegenError {
/// The given metadata type could not be found.
#[error("Could not find type with ID {0} in the type registry; please raise a support issue.")]
TypeNotFound(u32),
/// Cannot fetch the metadata bytes.
#[error("Failed to fetch metadata, make sure that you're pointing at a node which is providing V14 metadata: {0}")]
Fetch(#[from] FetchMetadataError),
Expand Down Expand Up @@ -80,12 +83,6 @@ pub enum CodegenError {
/// Metadata for storage could not be found.
#[error("Metadata for storage entry {0}_{1} could not be found. Make sure you are providing a valid metadata V14")]
MissingStorageMetadata(String, String),
/// StorageNMap should have N hashers.
#[error("Number of hashers ({0}) does not equal 1 for StorageMap, or match number of fields ({1}) for StorageNMap. Make sure you are providing a valid metadata V14")]
MismatchHashers(usize, usize),
/// Expected to find one hasher for StorageMap.
#[error("No hasher found for single key. Make sure you are providing a valid metadata V14")]
MissingHasher,
/// Metadata for call could not be found.
#[error("Metadata for call entry {0}_{1} could not be found. Make sure you are providing a valid metadata V14")]
MissingCallMetadata(String, String),
Expand Down Expand Up @@ -250,7 +247,23 @@ impl RuntimeGenerator {
) -> Result<TokenStream2, CodegenError> {
let item_mod_attrs = item_mod.attrs.clone();
let item_mod_ir = ir::ItemMod::try_from(item_mod)?;
let default_derives = derives.default_derives();

// Get the path to the `Runtime` struct. We assume that the same path contains
// RuntimeCall and RuntimeEvent.
let runtime_type_id = self.metadata.ty.id();
let runtime_path_segments = self
.metadata
.types
.resolve(runtime_type_id)
.ok_or(CodegenError::TypeNotFound(runtime_type_id))?
.path()
.namespace()
.iter()
.map(|part| syn::PathSegment::from(format_ident!("{}", part)));
let runtime_path = syn::Path {
leading_colon: None,
segments: syn::punctuated::Punctuated::from_iter(runtime_path_segments),
};

let type_gen = TypeGenerator::new(
&self.metadata.types,
Expand Down Expand Up @@ -338,53 +351,23 @@ impl RuntimeGenerator {
})
.collect::<Result<Vec<_>, CodegenError>>()?;

let outer_event_variants_and_match_arms = self.metadata.pallets.iter().filter_map(|p| {
let root_event_if_arms = self.metadata.pallets.iter().filter_map(|p| {
let variant_name_str = &p.name;
let variant_name = format_ident!("{}", variant_name_str);
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
let index = proc_macro2::Literal::u8_unsuffixed(p.index);

p.event.as_ref().map(|_| {
// The variant name to go into the event enum:
let outer_event_variant = quote! {
#[codec(index = #index)]
#variant_name(#mod_name::Event),
};

// An 'if' arm for the RootEvent impl to match this variant name:
let outer_event_match_arm = quote! {
quote! {
if pallet_name == #variant_name_str {
return Ok(Event::#variant_name(#mod_name::Event::decode_with_metadata(
&mut &*pallet_bytes,
pallet_ty,
metadata
)?));
}
};

(outer_event_variant, outer_event_match_arm)
})
}).collect::<Vec<_>>();

let outer_event_variants =
outer_event_variants_and_match_arms.iter().map(|v| &v.0);
let outer_event_match_arms =
outer_event_variants_and_match_arms.iter().map(|v| &v.1);

let outer_event = quote! {
#default_derives
pub enum Event {
#( #outer_event_variants )*
}

impl #crate_path::events::RootEvent for Event {
fn root_event(pallet_bytes: &[u8], pallet_name: &str, pallet_ty: u32, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
use #crate_path::metadata::DecodeWithMetadata;
#( #outer_event_match_arms )*
Err(#crate_path::ext::scale_decode::Error::custom(format!("Pallet name '{}' not found in root Event enum", pallet_name)).into())
}
}
};
})
});

let mod_ident = &item_mod_ir.ident;
let pallets_with_constants: Vec<_> = pallets_with_mod_names
Expand Down Expand Up @@ -423,14 +406,23 @@ impl RuntimeGenerator {
// Identify the pallets composing the static metadata by name.
pub static PALLETS: [&str; #pallet_names_len] = [ #(#pallet_names,)* ];

#outer_event
#( #modules )*
#types_mod
/// The statically generated runtime call type.
pub type Call = #types_mod_ident::#runtime_path::RuntimeCall;

/// The default error type returned when there is a runtime issue,
/// exposed here for ease of use.
/// The error type returned when there is a runtime issue.
pub type DispatchError = #types_mod_ident::sp_runtime::DispatchError;

// Make the runtime event type easily accessible, and impl RootEvent to help decode into it.
pub type Event = #types_mod_ident::#runtime_path::RuntimeEvent;

impl #crate_path::events::RootEvent for Event {
fn root_event(pallet_bytes: &[u8], pallet_name: &str, pallet_ty: u32, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
use #crate_path::metadata::DecodeWithMetadata;
#( #root_event_if_arms )*
Err(#crate_path::ext::scale_decode::Error::custom(format!("Pallet name '{}' not found in root Event enum", pallet_name)).into())
}
}

pub fn constants() -> ConstantsApi {
ConstantsApi
}
Expand Down Expand Up @@ -479,6 +471,9 @@ impl RuntimeGenerator {
Ok(())
}
}

#( #modules )*
#types_mod
}
})
}
Expand Down
87 changes: 28 additions & 59 deletions codegen/src/api/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use frame_metadata::{
StorageEntryMetadata,
StorageEntryModifier,
StorageEntryType,
StorageHasher,
};
use heck::ToSnakeCase as _;
use proc_macro2::TokenStream as TokenStream2;
Expand Down Expand Up @@ -84,31 +83,12 @@ fn generate_storage_entry_fns(
crate_path: &CratePath,
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let (fields, key_impl) = match storage_entry.ty {
let (fields, key_impl) = match &storage_entry.ty {
StorageEntryType::Plain(_) => (vec![], quote!(vec![])),
StorageEntryType::Map {
ref key,
ref hashers,
..
} => {
StorageEntryType::Map { key, .. } => {
let key_ty = type_gen.resolve_type(key.id());
let hashers = hashers
.iter()
.map(|hasher| {
let hasher = match hasher {
StorageHasher::Blake2_128 => "Blake2_128",
StorageHasher::Blake2_256 => "Blake2_256",
StorageHasher::Blake2_128Concat => "Blake2_128Concat",
StorageHasher::Twox128 => "Twox128",
StorageHasher::Twox256 => "Twox256",
StorageHasher::Twox64Concat => "Twox64Concat",
StorageHasher::Identity => "Identity",
};
let hasher = format_ident!("{}", hasher);
quote!( #crate_path::storage::address::StorageHasher::#hasher )
})
.collect::<Vec<_>>();
match key_ty.type_def() {
// An N-map; return each of the keys separately.
TypeDef::Tuple(tuple) => {
let fields = tuple
.fields()
Expand All @@ -121,46 +101,23 @@ fn generate_storage_entry_fns(
})
.collect::<Vec<_>>();

let key_impl = if hashers.len() == fields.len() {
// If the number of hashers matches the number of fields, we're dealing with
// something shaped like a StorageNMap, and each field should be hashed separately
// according to the corresponding hasher.
let keys = hashers
.into_iter()
.zip(&fields)
.map(|(hasher, (field_name, _))| {
quote!( #crate_path::storage::address::StorageMapKey::new(#field_name.borrow(), #hasher) )
});
quote! {
vec![ #( #keys ),* ]
}
} else if hashers.len() == 1 {
// If there is one hasher, then however many fields we have, we want to hash a
// tuple of them using the one hasher we're told about. This corresponds to a
// StorageMap.
let hasher = hashers.get(0).expect("checked for 1 hasher");
let items =
fields.iter().map(|(field_name, _)| quote!( #field_name ));
quote! {
vec![ #crate_path::storage::address::StorageMapKey::new(&(#( #items.borrow() ),*), #hasher) ]
}
} else {
return Err(CodegenError::MismatchHashers(
hashers.len(),
fields.len(),
))
let keys = fields
.iter()
.map(|(field_name, _)| {
quote!( #crate_path::storage::address::StaticStorageMapKey::new(#field_name.borrow()) )
});
let key_impl = quote! {
vec![ #( #keys ),* ]
};

(fields, key_impl)
}
// A map with a single key; return the single key.
_ => {
let ty_path = type_gen.resolve_type_path(key.id());
let fields = vec![(format_ident!("_0"), ty_path)];
let Some(hasher) = hashers.get(0) else {
return Err(CodegenError::MissingHasher)
};
let key_impl = quote! {
vec![ #crate_path::storage::address::StorageMapKey::new(_0.borrow(), #hasher) ]
vec![ #crate_path::storage::address::StaticStorageMapKey::new(_0.borrow()) ]
};
(fields, key_impl)
}
Expand Down Expand Up @@ -233,8 +190,14 @@ fn generate_storage_entry_fns(
#docs
pub fn #fn_name_root(
&self,
) -> #crate_path::storage::address::StaticStorageAddress::<#storage_entry_value_ty, (), #is_defaultable_type, #is_iterable_type> {
#crate_path::storage::address::StaticStorageAddress::new(
) -> #crate_path::storage::address::Address::<
#crate_path::storage::address::StaticStorageMapKey,
#storage_entry_value_ty,
(),
#is_defaultable_type,
#is_iterable_type
> {
#crate_path::storage::address::Address::new_static(
#pallet_name,
#storage_name,
Vec::new(),
Expand All @@ -252,8 +215,14 @@ fn generate_storage_entry_fns(
pub fn #fn_name(
&self,
#( #key_args, )*
) -> #crate_path::storage::address::StaticStorageAddress::<#storage_entry_value_ty, #crate_path::storage::address::Yes, #is_defaultable_type, #is_iterable_type> {
#crate_path::storage::address::StaticStorageAddress::new(
) -> #crate_path::storage::address::Address::<
#crate_path::storage::address::StaticStorageMapKey,
#storage_entry_value_ty,
#crate_path::storage::address::Yes,
#is_defaultable_type,
#is_iterable_type
> {
#crate_path::storage::address::Address::new_static(
#pallet_name,
#storage_name,
#key_impl,
Expand Down
5 changes: 4 additions & 1 deletion codegen/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ pub use self::{
Derives,
DerivesRegistry,
},
substitutes::TypeSubstitutes,
substitutes::{
AbsolutePath,
TypeSubstitutes,
},
type_def::TypeDefGen,
type_def_params::TypeDefParameters,
type_path::{
Expand Down
Loading