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

Codegen for custom values in metadata #1117

Merged
merged 34 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
09a045f
work in progress
tadeohepperle Aug 2, 2023
e2af2cb
add custom types access
tadeohepperle Aug 3, 2023
72ad54b
nit
tadeohepperle Aug 8, 2023
8a9e76a
Merge branch 'master' into tadeo-hepperle-v15-custom-type-map
tadeohepperle Aug 8, 2023
50da61c
custom values client
tadeohepperle Aug 9, 2023
e8f2140
adjust light client
tadeohepperle Aug 9, 2023
00cbee4
Merge remote-tracking branch 'origin' into tadeo-hepperle-v15-custom-…
tadeohepperle Aug 9, 2023
f286b26
adjust doc comments
tadeohepperle Aug 10, 2023
2f83d36
adjust book for custom values in code gen
tadeohepperle Aug 10, 2023
57be757
format and check docs
tadeohepperle Aug 10, 2023
4a9000a
Merge branch 'master' into tadeo-hepperle-v15-custom-type-map
tadeohepperle Aug 10, 2023
34fa447
work in progress
tadeohepperle Aug 2, 2023
2bb420e
add custom types access
tadeohepperle Aug 3, 2023
4280a8e
nit
tadeohepperle Aug 8, 2023
59ac6cc
custom values client
tadeohepperle Aug 9, 2023
8afa2bf
adjust light client
tadeohepperle Aug 9, 2023
c73ace1
codegen and validation
tadeohepperle Aug 10, 2023
c9e2b5b
adjust docs
tadeohepperle Aug 10, 2023
99bf804
Merge branch 'tadeo-hepperle-v15-custom-type-map' into tadeo-hepperle…
tadeohepperle Aug 10, 2023
922fee2
use ignore in docs in book
tadeohepperle Aug 11, 2023
e1f7889
Merge branch 'tadeo-hepperle-v15-custom-type-map' into tadeo-hepperle…
tadeohepperle Aug 11, 2023
b4c91c4
change iter implementation
tadeohepperle Aug 11, 2023
b131b60
Merge branch 'master' into tadeo-hepperle-custom-values-codegen
tadeohepperle Aug 11, 2023
56bdcd0
Merge branch 'master' into tadeo-hepperle-custom-values-codegen
tadeohepperle Aug 11, 2023
7d871d0
use validation hash and other codegen changes
tadeohepperle Aug 11, 2023
f348cb5
Merge branch 'master' into tadeo-hepperle-custom-values-codegen
tadeohepperle Aug 21, 2023
fba3902
add ui test for custom values codegen
tadeohepperle Aug 21, 2023
959f24e
allow 'latest' metadata to be returned from the fallback code (#1127)
jsdw Aug 21, 2023
41513c0
nits
tadeohepperle Aug 22, 2023
175bbd5
Merge branch 'master' into tadeo-hepperle-custom-values-codegen
tadeohepperle Aug 22, 2023
529fd2f
fix validation check
tadeohepperle Aug 22, 2023
a291c60
Merge branch 'master' into tadeo-hepperle-custom-values-codegen
tadeohepperle Aug 23, 2023
1915ae9
fix comments
tadeohepperle Aug 23, 2023
3a93bda
nits
tadeohepperle Aug 23, 2023
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
61 changes: 61 additions & 0 deletions codegen/src/api/custom_values.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use std::collections::HashSet;

use crate::{types::TypeGenerator, CratePath};
use heck::ToSnakeCase as _;
use subxt_metadata::{CustomValueMetadata, Metadata};

use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};

/// Generate the custom values mod, if there are any custom values in the metadata. Else returns None.
pub fn generate_custom_values<'a>(
metadata: &'a Metadata,
type_gen: &'a TypeGenerator,
crate_path: &'a CratePath,
) -> TokenStream2 {
let mut fn_names_taken = HashSet::new();
let custom = metadata.custom();
let custom_values_fns = custom.iter().filter_map(|custom_value| {
generate_custom_value_fn(custom_value, type_gen, crate_path, &mut fn_names_taken)
});

quote! {
pub struct CustomValuesApi;

impl CustomValuesApi {
#(#custom_values_fns)*
}
}
}

/// Generates runtime functions for the given API metadata.
/// Returns None, if the name would not make for a valid identifier.
fn generate_custom_value_fn(
custom_value: CustomValueMetadata,
type_gen: &TypeGenerator,
crate_path: &CratePath,
fn_names_taken: &mut HashSet<String>,
) -> Option<TokenStream2> {
// names are transformed to snake case to make for good function identifiers.
let name = custom_value.name();
let fn_name = name.to_snake_case();
// Skip elements where the fn name is already occupied. E.g. if you have custom values with names "Foo" and "foo" in the metadata.
if fn_names_taken.contains(&fn_name) {
return None;
}
let fn_name_ident = format_ident!("{fn_name}");
fn_names_taken.insert(fn_name);

let custom_value_hash = custom_value.hash();
let return_ty = type_gen.resolve_type_path(custom_value.type_id());

Some(quote!(
pub fn #fn_name_ident() -> #crate_path::custom_values::StaticAddress<#return_ty> {
#crate_path::custom_values::StaticAddress::new_static(#name, [#(#custom_value_hash,)*])
}
))
}
10 changes: 10 additions & 0 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

mod calls;
mod constants;
mod custom_values;
mod errors;
mod events;
mod runtime_apis;
Expand All @@ -14,6 +15,7 @@ mod storage;
use subxt_metadata::Metadata;

use super::DerivesRegistry;
use crate::api::custom_values::generate_custom_values;
use crate::error::CodegenError;
use crate::{
ir,
Expand Down Expand Up @@ -469,6 +471,8 @@ impl RuntimeGenerator {
let event_path = type_gen.resolve_type_path(self.metadata.outer_enums().event_enum_ty());
let error_path = type_gen.resolve_type_path(self.metadata.outer_enums().error_enum_ty());

let custom_values = generate_custom_values(&self.metadata, &type_gen, &crate_path);

Ok(quote! {
#( #item_mod_attrs )*
#[allow(dead_code, unused_imports, non_camel_case_types)]
Expand Down Expand Up @@ -521,6 +525,12 @@ impl RuntimeGenerator {

#apis_mod

pub fn custom() -> CustomValuesApi {
CustomValuesApi
}

#custom_values

pub struct ConstantsApi;
impl ConstantsApi {
#(
Expand Down
4 changes: 2 additions & 2 deletions codegen/src/types/derives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ impl Derives {

/// Extend this set of `Derives` from another.
pub fn extend_from(&mut self, other: Derives) {
self.derives.extend(other.derives.into_iter());
self.attributes.extend(other.attributes.into_iter());
self.derives.extend(other.derives);
self.attributes.extend(other.attributes);
}

/// Add `#crate_path::ext::codec::CompactAs` to the derives.
Expand Down
10 changes: 5 additions & 5 deletions metadata/src/from_into/v15.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
use super::TryFromError;
use crate::utils::variant_index::VariantIndex;
use crate::{
utils::ordered_map::OrderedMap, ArcStr, ConstantMetadata, CustomMetadata, ExtrinsicMetadata,
Metadata, OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner,
RuntimeApiMethodMetadata, RuntimeApiMethodParamMetadata, SignedExtensionMetadata,
StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata,
utils::ordered_map::OrderedMap, ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata,
OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadata,
RuntimeApiMethodParamMetadata, SignedExtensionMetadata, StorageEntryMetadata,
StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata,
};
use frame_metadata::v15;
use scale_info::form::PortableForm;
Expand Down Expand Up @@ -93,7 +93,7 @@ mod from_v15 {
event_enum_ty: m.outer_enums.event_enum_ty.id,
error_enum_ty: m.outer_enums.error_enum_ty.id,
},
custom: CustomMetadata { map: m.custom.map },
custom: m.custom,
})
}
}
Expand Down
84 changes: 63 additions & 21 deletions metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ mod from_into;
mod utils;

use scale_info::{form::PortableForm, PortableRegistry, Variant};
use std::collections::{BTreeMap, HashMap};
use std::collections::HashMap;
use std::sync::Arc;
use utils::ordered_map::OrderedMap;
use utils::variant_index::VariantIndex;

type ArcStr = Arc<str>;

use crate::utils::validation::{get_custom_value_hash, HASH_LEN};
pub use from_into::TryFromError;
pub use utils::validation::MetadataHasher;

Expand All @@ -52,7 +53,7 @@ pub struct Metadata {
/// Details about each of the runtime API traits.
apis: OrderedMap<ArcStr, RuntimeApiMetadataInner>,
/// Allows users to add custom types to the metadata. A map that associates a string key to a `CustomValueMetadata`.
custom: CustomMetadata,
custom: frame_metadata::v15::CustomMetadata<PortableForm>,
}

impl Metadata {
Expand Down Expand Up @@ -135,8 +136,11 @@ impl Metadata {
}

/// Returns custom user defined types
pub fn custom(&self) -> &CustomMetadata {
&self.custom
pub fn custom(&self) -> CustomMetadata<'_> {
CustomMetadata {
types: self.types(),
inner: &self.custom,
}
}

/// Obtain a unique hash representing this metadata or specific parts of it.
Expand All @@ -154,7 +158,7 @@ impl Metadata {
}

/// Get type hash for a type in the registry
pub fn type_hash(&self, id: u32) -> Option<[u8; 32]> {
pub fn type_hash(&self, id: u32) -> Option<[u8; HASH_LEN]> {
self.types.resolve(id)?;
Some(crate::utils::validation::get_type_hash(
&self.types,
Expand Down Expand Up @@ -265,22 +269,22 @@ impl<'a> PalletMetadata<'a> {
}

/// Return a hash for the storage entry, or None if it was not found.
pub fn storage_hash(&self, entry_name: &str) -> Option<[u8; 32]> {
pub fn storage_hash(&self, entry_name: &str) -> Option<[u8; HASH_LEN]> {
crate::utils::validation::get_storage_hash(self, entry_name)
}

/// Return a hash for the constant, or None if it was not found.
pub fn constant_hash(&self, constant_name: &str) -> Option<[u8; 32]> {
pub fn constant_hash(&self, constant_name: &str) -> Option<[u8; HASH_LEN]> {
crate::utils::validation::get_constant_hash(self, constant_name)
}

/// Return a hash for the call, or None if it was not found.
pub fn call_hash(&self, call_name: &str) -> Option<[u8; 32]> {
pub fn call_hash(&self, call_name: &str) -> Option<[u8; HASH_LEN]> {
crate::utils::validation::get_call_hash(self, call_name)
}

/// Return a hash for the entire pallet.
pub fn hash(&self) -> [u8; 32] {
pub fn hash(&self) -> [u8; HASH_LEN] {
crate::utils::validation::get_pallet_hash(*self)
}
}
Expand Down Expand Up @@ -577,12 +581,12 @@ impl<'a> RuntimeApiMetadata<'a> {
self.inner.methods.get_by_key(name)
}
/// Return a hash for the constant, or None if it was not found.
pub fn method_hash(&self, method_name: &str) -> Option<[u8; 32]> {
pub fn method_hash(&self, method_name: &str) -> Option<[u8; HASH_LEN]> {
crate::utils::validation::get_runtime_api_hash(self, method_name)
}

/// Return a hash for the runtime API trait.
pub fn hash(&self) -> [u8; 32] {
pub fn hash(&self) -> [u8; HASH_LEN] {
crate::utils::validation::get_runtime_trait_hash(*self)
}
}
Expand Down Expand Up @@ -640,36 +644,74 @@ pub struct RuntimeApiMethodParamMetadata {

/// Metadata of custom types with custom values, basically the same as `frame_metadata::v15::CustomMetadata<PortableForm>>`.
#[derive(Debug, Clone)]
pub struct CustomMetadata {
map: BTreeMap<String, frame_metadata::v15::CustomValueMetadata<PortableForm>>,
pub struct CustomMetadata<'a> {
types: &'a PortableRegistry,
inner: &'a frame_metadata::v15::CustomMetadata<PortableForm>,
}

impl CustomMetadata {
/// Get a certain [CustomMetadataValue] by its name.
pub fn get(&self, name: &str) -> Option<CustomMetadataValue<'_>> {
self.map.get(name).map(|e| CustomMetadataValue {
impl<'a> CustomMetadata<'a> {
/// Get a certain [CustomValueMetadata] by its name.
pub fn get(&self, name: &str) -> Option<CustomValueMetadata<'a>> {
self.inner
.map
.get_key_value(name)
.map(|(name, e)| CustomValueMetadata {
types: self.types,
type_id: e.ty.id,
data: &e.value,
name,
})
}

/// Iterates over names (keys) and associated custom values
pub fn iter(&self) -> impl Iterator<Item = CustomValueMetadata> {
self.inner.map.iter().map(|(name, e)| CustomValueMetadata {
types: self.types,
type_id: e.ty.id,
data: &e.value,
name: name.as_ref(),
})
}

/// Access the underlying type registry.
pub fn types(&self) -> &PortableRegistry {
self.types
}
}

/// Basically the same as `frame_metadata::v15::CustomValueMetadata<PortableForm>>`, but borrowed.
pub struct CustomMetadataValue<'a> {
pub struct CustomValueMetadata<'a> {
types: &'a PortableRegistry,
type_id: u32,
data: &'a [u8],
name: &'a str,
}

impl<'a> CustomMetadataValue<'a> {
/// the scale encoded value
impl<'a> CustomValueMetadata<'a> {
/// The scale encoded value
pub fn bytes(&self) -> &'a [u8] {
self.data
}

/// the type id in the TypeRegistry
/// The type id in the TypeRegistry
pub fn type_id(&self) -> u32 {
self.type_id
}

/// The name under which the custom value is registered.
pub fn name(&self) -> &str {
self.name
}

/// Calculates the hash for the CustomValueMetadata.
///
/// # Panics
///
/// Panics if `self.type_id` is not registered in the provided type registry
pub fn hash(&self) -> [u8; HASH_LEN] {
let mut cache = HashMap::new();
get_custom_value_hash(self, &mut cache)
}
}

// Support decoding metadata from the "wire" format directly into this.
Expand Down
Loading