Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Makes storage hashers optional in dev mode (#13815)
Browse files Browse the repository at this point in the history
* Initial changes

* Adds UI test for error when _ is used without dev_mode

* Minor

* ".git/.scripts/commands/fmt/fmt.sh"

* Adds test to verify hasher

---------

Co-authored-by: command-bot <>
  • Loading branch information
gupnik committed Apr 11, 2023
1 parent ca6af5d commit da9f88d
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 12 deletions.
13 changes: 13 additions & 0 deletions frame/support/procedural/src/pallet/expand/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,19 @@ pub fn process_generics(def: &mut Def) -> syn::Result<Vec<ResultOnEmptyStructMet
Metadata::DoubleMap { .. } => (5, 6, 7),
};

if storage_def.use_default_hasher {
let hasher_indices: Vec<usize> = match storage_def.metadata {
Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1],
Metadata::DoubleMap { .. } => vec![1, 3],
_ => vec![],
};
for hasher_idx in hasher_indices {
args.args[hasher_idx] = syn::GenericArgument::Type(
syn::parse_quote!(#frame_support::Blake2_128Concat),
);
}
}

if query_idx < args.args.len() {
if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) {
set_result_query_type_parameter(query_kind)?;
Expand Down
44 changes: 33 additions & 11 deletions frame/support/procedural/src/pallet/parse/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ pub struct StorageDef {
pub unbounded: bool,
/// Whether or not reads to this storage key will be ignored by benchmarking
pub whitelisted: bool,
/// Whether or not a default hasher is allowed to replace `_`
pub use_default_hasher: bool,
}

/// The parsed generic from the
Expand Down Expand Up @@ -325,12 +327,12 @@ fn check_generics(
}
}

/// Returns `(named generics, metadata, query kind)`
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
fn process_named_generics(
storage: &StorageKind,
args_span: proc_macro2::Span,
args: &[syn::Binding],
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>)> {
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
let mut parsed = HashMap::<String, syn::Binding>::new();

// Ensure no duplicate.
Expand Down Expand Up @@ -480,15 +482,16 @@ fn process_named_generics(
let metadata = generics.metadata()?;
let query_kind = generics.query_kind();

Ok((Some(generics), metadata, query_kind))
Ok((Some(generics), metadata, query_kind, false))
}

/// Returns `(named generics, metadata, query kind)`
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
fn process_unnamed_generics(
storage: &StorageKind,
args_span: proc_macro2::Span,
args: &[syn::Type],
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>)> {
dev_mode: bool,
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
let retrieve_arg = |arg_pos| {
args.get(arg_pos).cloned().ok_or_else(|| {
let msg = format!(
Expand All @@ -510,18 +513,28 @@ fn process_unnamed_generics(
err
})?;

let use_default_hasher = |arg_pos| {
if let Some(arg) = retrieve_arg(arg_pos).ok() {
dev_mode && syn::parse2::<syn::Token![_]>(arg.to_token_stream()).is_ok()
} else {
false
}
};

let res = match storage {
StorageKind::Value =>
(None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok()),
(None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok(), false),
StorageKind::Map => (
None,
Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? },
retrieve_arg(4).ok(),
use_default_hasher(1),
),
StorageKind::CountedMap => (
None,
Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? },
retrieve_arg(4).ok(),
use_default_hasher(1),
),
StorageKind::DoubleMap => (
None,
Expand All @@ -531,21 +544,28 @@ fn process_unnamed_generics(
value: retrieve_arg(5)?,
},
retrieve_arg(6).ok(),
use_default_hasher(1) && use_default_hasher(3),
),
StorageKind::NMap => {
let keygen = retrieve_arg(1)?;
let keys = collect_keys(&keygen)?;
(None, Metadata::NMap { keys, keygen, value: retrieve_arg(2)? }, retrieve_arg(3).ok())
(
None,
Metadata::NMap { keys, keygen, value: retrieve_arg(2)? },
retrieve_arg(3).ok(),
false,
)
},
};

Ok(res)
}

/// Returns `(named generics, metadata, query kind)`
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
fn process_generics(
segment: &syn::PathSegment,
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>)> {
dev_mode: bool,
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
let storage_kind = match &*segment.ident.to_string() {
"StorageValue" => StorageKind::Value,
"StorageMap" => StorageKind::Map,
Expand Down Expand Up @@ -583,7 +603,7 @@ fn process_generics(
_ => unreachable!("It is asserted above that all generics are types"),
})
.collect::<Vec<_>>();
process_unnamed_generics(&storage_kind, args_span, &args)
process_unnamed_generics(&storage_kind, args_span, &args, dev_mode)
} else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Binding(_))) {
let args = args
.args
Expand Down Expand Up @@ -711,7 +731,8 @@ impl StorageDef {
return Err(syn::Error::new(item.ty.span(), msg))
}

let (named_generics, metadata, query_kind) = process_generics(&typ.path.segments[0])?;
let (named_generics, metadata, query_kind, use_default_hasher) =
process_generics(&typ.path.segments[0], dev_mode)?;

let query_kind = query_kind
.map(|query_kind| {
Expand Down Expand Up @@ -832,6 +853,7 @@ impl StorageDef {
named_generics,
unbounded,
whitelisted,
use_default_hasher,
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;

// The struct on which we build all of our Pallet logic.
#[pallet::pallet]
pub struct Pallet<T>(_);

// Your Pallet's configuration trait, representing custom external types and interfaces.
#[pallet::config]
pub trait Config: frame_system::Config {}

#[pallet::storage]
type MyStorage<T: Config> = StorageValue<_, Vec<u8>>;

#[pallet::storage]
type MyStorageMap<T: Config> = StorageMap<_, _, u32, u64>;

#[pallet::storage]
type MyStorageDoubleMap<T: Config> = StorageDoubleMap<_, _, u32, _, u64, u64>;

#[pallet::storage]
type MyCountedStorageMap<T: Config> = CountedStorageMap<_, _, u32, u64>;

// Your Pallet's internal functions.
impl<T: Config> Pallet<T> {}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases
--> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:21:47
|
21 | type MyStorageMap<T: Config> = StorageMap<_, _, u32, u64>;
| ^ not allowed in type signatures

error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases
--> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:24:59
|
24 | type MyStorageDoubleMap<T: Config> = StorageDoubleMap<_, _, u32, _, u64, u64>;
| ^ ^ not allowed in type signatures
| |
| not allowed in type signatures

error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases
--> tests/pallet_ui/dev_mode_without_arg_default_hasher.rs:27:61
|
27 | type MyCountedStorageMap<T: Config> = CountedStorageMap<_, _, u32, u64>;
| ^ not allowed in type signatures
85 changes: 84 additions & 1 deletion frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{
traits::{
ConstU32,
},
};

pub use pallet::*;

#[frame_support::pallet(dev_mode)]
Expand All @@ -19,6 +25,16 @@ pub mod pallet {
#[pallet::storage]
type MyStorage<T: Config> = StorageValue<_, Vec<u8>>;

// The Hasher requirement skipped by `dev_mode`.
#[pallet::storage]
pub type MyStorageMap<T: Config> = StorageMap<_, _, u32, u64>;

#[pallet::storage]
type MyStorageDoubleMap<T: Config> = StorageDoubleMap<_, _, u32, _, u64, u64>;

#[pallet::storage]
type MyCountedStorageMap<T: Config> = CountedStorageMap<_, _, u32, u64>;

// Your Pallet's callable functions.
#[pallet::call]
impl<T: Config> Pallet<T> {
Expand All @@ -32,4 +48,71 @@ pub mod pallet {
impl<T: Config> Pallet<T> {}
}

fn main() {}
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type RuntimeOrigin = RuntimeOrigin;
type Index = u64;
type BlockNumber = u32;
type RuntimeCall = RuntimeCall;
type Hash = sp_runtime::testing::H256;
type Hashing = sp_runtime::traits::BlakeTwo256;
type AccountId = u64;
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
type Header = Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU32<250>;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}

pub type Header = sp_runtime::generic::Header<u32, sp_runtime::traits::BlakeTwo256>;
pub type Block = sp_runtime::generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;

frame_support::construct_runtime!(
pub struct Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// Exclude part `Storage` in order not to check its metadata in tests.
System: frame_system exclude_parts { Pallet, Storage },
Example: pallet,
}
);

impl pallet::Config for Runtime {

}

fn main() {
use frame_support::{pallet_prelude::*};
use storage::unhashed;
use sp_io::{
hashing::{blake2_128, twox_128},
TestExternalities,
};

fn blake2_128_concat(d: &[u8]) -> Vec<u8> {
let mut v = blake2_128(d).to_vec();
v.extend_from_slice(d);
v
}

TestExternalities::default().execute_with(|| {
pallet::MyStorageMap::<Runtime>::insert(1, 2);
let mut k = [twox_128(b"Example"), twox_128(b"MyStorageMap")].concat();
k.extend(1u32.using_encoded(blake2_128_concat));
assert_eq!(unhashed::get::<u64>(&k), Some(2u64));
});
}

0 comments on commit da9f88d

Please sign in to comment.