From 609b701d4d1e60e4f6e85e2082e0c1a7d51e2306 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 8 May 2024 08:34:52 +0000 Subject: [PATCH] config: Handle hasher and header Signed-off-by: Alexandru Vasile --- codegen/src/api/custom_values.rs | 108 ++++++++++++++++++++++++++++-- core/src/storage/mod.rs | 2 +- core/src/storage/utils.rs | 3 +- subxt/examples/metadata_config.rs | 20 +++++- 4 files changed, 125 insertions(+), 8 deletions(-) diff --git a/codegen/src/api/custom_values.rs b/codegen/src/api/custom_values.rs index ef6929a454..315a008f4c 100644 --- a/codegen/src/api/custom_values.rs +++ b/codegen/src/api/custom_values.rs @@ -3,9 +3,10 @@ // see LICENSE for license details. use heck::ToSnakeCase as _; +use scale_info::{TypeDef, TypeDefComposite}; use scale_typegen::typegen::ir::ToTokensWithSettings; use scale_typegen::TypeGenerator; -use std::collections::HashSet; +use std::{any::Any, collections::HashSet}; use subxt_metadata::{CustomValueMetadata, Metadata}; use proc_macro2::TokenStream as TokenStream2; @@ -20,14 +21,113 @@ pub fn generate_custom_values( let mut fn_names_taken = HashSet::new(); let custom = metadata.custom(); let custom_types = custom.iter().map(|custom| { - let name = format_ident!("{}", custom.name()); + let name_str = custom.name(); - let Ok(ty) = type_gen.resolve_type_path(custom.type_id()) else { + let name = format_ident!("{}", name_str); + + let Ok(ty_path) = type_gen.resolve_type_path(custom.type_id()) else { return quote! {}; }; - let ty = ty.to_token_stream(type_gen.settings()); + let ty = ty_path.to_token_stream(type_gen.settings()); + + let mut maybe_impl = None; + let mut extra = None; + if name_str == "Hashing" { + // Extract hasher name + let ty_path_str = ty.to_string(); + if ty_path_str.contains("BlakeTwo256") { + maybe_impl = Some(quote! { + impl #crate_path::config::Hasher for #ty { + type Output = #crate_path::utils::H256; + + fn hash(s: &[u8]) -> Self::Output { + let mut bytes = Vec::new(); + #crate_path::storage::utils::hash_bytes(s, #crate_path::storage::utils::StorageHasher::Blake2_256, &mut bytes); + let arr: [u8; 32] = bytes.try_into().expect("Invalid hashing output provided"); + arr.into() + } + } + }); + } + } + + if name_str == "Header" { + // Extract header number from the provided type. + let Ok(ty_res) = type_gen.resolve_type(custom.type_id()) else { + return quote! {}; + }; + + let TypeDef::Composite(composite) = &ty_res.type_def else { + return quote! {}; + }; + + // Sanity check for the number type. + let number_ty = composite.fields.iter().find_map( + |field| if let Some(n) = &field.name { + if n == "number" { + Some(field.ty.id) + } else { + None + } + } else { + None + } + ); + + if let Some(num) = number_ty { + + let Ok(ty_path) = type_gen.resolve_type_path(num) else { + return quote! {}; + }; + + if !ty_path.is_compact() { + let ty = ty_path.to_token_stream(type_gen.settings()); + + extra = Some(quote! { + pub type HeaderNumber = #ty; + }); + } else { + // Ty is compact. + let Ok(ty_res) = type_gen.resolve_type(num) else { + return quote! {}; + }; + + let TypeDef::Compact(compact) = &ty_res.type_def else { + return quote! {}; + }; + let compact_ty_id = compact.type_param.id; + + let Ok(ty_path) = type_gen.resolve_type_path(compact_ty_id) else { + return quote! {}; + }; + let ty = ty_path.to_token_stream(type_gen.settings()); + + extra = Some(quote! { + pub type HeaderNumber = #ty; + }); + } + + + maybe_impl = Some(quote! { + impl #crate_path::config::Header for #ty { + type Number = HeaderNumber; + type Hasher = Hashing; + + // If we got to this point, the `number` field exists on the header + // structure. + fn number(&self) -> Self::Number { + self.number + } + } + }); + } + } + quote! { pub type #name = #ty; + + #maybe_impl + #extra } }); diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index c11957a665..a019ce82ab 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -42,7 +42,7 @@ //! ``` mod storage_key; -mod utils; +pub mod utils; pub mod address; diff --git a/core/src/storage/utils.rs b/core/src/storage/utils.rs index dc5d10ace4..6a891c455e 100644 --- a/core/src/storage/utils.rs +++ b/core/src/storage/utils.rs @@ -11,7 +11,8 @@ use crate::error::{Error, MetadataError}; use crate::metadata::Metadata; use alloc::borrow::ToOwned; use alloc::vec::Vec; -use subxt_metadata::{PalletMetadata, StorageEntryMetadata, StorageHasher}; +pub use subxt_metadata::StorageHasher; +use subxt_metadata::{PalletMetadata, StorageEntryMetadata}; /// Return the root of a given [`Address`]: hash the pallet name and entry name /// and append those bytes to the output. diff --git a/subxt/examples/metadata_config.rs b/subxt/examples/metadata_config.rs index 2d1dc9ec98..ca3b3850b2 100644 --- a/subxt/examples/metadata_config.rs +++ b/subxt/examples/metadata_config.rs @@ -1,5 +1,6 @@ #![allow(missing_docs)] use subxt::{OnlineClient, SubstrateConfig}; +use subxt_core::config::substrate::SubstrateHeader; use subxt_core::config::{Config, DefaultExtrinsicParams}; use subxt_signer::sr25519::dev; @@ -20,10 +21,25 @@ impl Config for MetadataConfig { // Present in metadata but this PoC needs to add // fn specific per name of type to impl the hashing fn. - type Hasher = ::Hasher; + // type Hasher = ::Hasher; + // + // TODO: Eventually extend this to a DynamicHasher object instead. + // Similar logic already exists for StorageHasher. + type Hasher = polkadot::custom_types::Hashing; + // Present in metadata but this PoC needs to impl the header // trait to make use of this. - type Header = ::Header; + // type Header = ::Header; + + // The metadata header type must implement `Deserialize`, which + // must be manually implemented for the Digest logs of the header. + // An alternative is to extract the header number and use the + // generated hasher to expose the same information, this is not + // as robust and codgen can be used instead. + // type Header = polkadot::custom_types::Header; + type Header = + SubstrateHeader; + // Same story, present in md but needs subxt::tx::Signer. // type Signature = polkadot::custom_types::Signature; type Signature = ::Signature;