Typed Storage Keys (#1419)

* first iteration on storage multi keys

* decoding values from concat style hashers

* move util functions and remove comments

* change codegen for storage keys and fix examples

* trait bounds don't match scale value...

* fix trait bounds and examples

* reconstruct storage keys in iterations

* build(deps): bump js-sys from 0.3.67 to 0.3.68 (#1428)

Bumps [js-sys](https://github.com/rustwasm/wasm-bindgen) from 0.3.67 to 0.3.68.
- [Release notes](https://github.com/rustwasm/wasm-bindgen/releases)
- [Changelog](https://github.com/rustwasm/wasm-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustwasm/wasm-bindgen/commits)

---
updated-dependencies:
- dependency-name: js-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump clap from 4.4.18 to 4.5.0 (#1427)

Bumps [clap](https://github.com/clap-rs/clap) from 4.4.18 to 4.5.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.18...clap_complete-v4.5.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump either from 1.9.0 to 1.10.0 (#1425)

Bumps [either](https://github.com/rayon-rs/either) from 1.9.0 to 1.10.0.
- [Commits](https://github.com/rayon-rs/either/compare/1.9.0...1.10.0)

---
updated-dependencies:
- dependency-name: either
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump thiserror from 1.0.56 to 1.0.57 (#1424)

Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.56 to 1.0.57.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.56...1.0.57)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump jsonrpsee from 0.21.0 to 0.22.0 (#1426)

* build(deps): bump jsonrpsee from 0.21.0 to 0.22.0

Bumps [jsonrpsee](https://github.com/paritytech/jsonrpsee) from 0.21.0 to 0.22.0.
- [Release notes](https://github.com/paritytech/jsonrpsee/releases)
- [Changelog](https://github.com/paritytech/jsonrpsee/blob/master/CHANGELOG.md)
- [Commits](https://github.com/paritytech/jsonrpsee/compare/v0.21.0...v0.22.0)

---
updated-dependencies:
- dependency-name: jsonrpsee
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update Cargo.lock

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Wilson <james@jsdw.me>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>

* subxt: Derive `std::cmp` traits for subxt payloads and addresses (#1429)

* subxt/tx: Derive std::cmp traits

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/runtime_api: Derive std::cmp traits

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/constants: Derive std::cmp traits

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/custom_values: Derive std::cmp traits

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/storage: Derive std::cmp traits

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Fix non_canonical_partial_ord_impl clippy introduced in 1.73

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add comment wrt derivative issue that triggers clippy warning

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update subxt/src/backend/mod.rs

* Update subxt/src/constants/constant_address.rs

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>

* fix clippy

* add integration tests

* fix doc tests

* change hashing logic for hashers=1

* refactor

* clippy and fmt

* regenerate polkadot file which got changed by the automatic PR

* nested design for storage keys

* refactor codegen

* codegen adjustments

* fix storage hasher codegen test

* Suggestions for storage value decoding (#1457)

* Storage decode tweaks

* doc tweak

* more precise error when leftover or not enough bytes

* integrate nits from PR

* add fuzztest for storage keys, fix decoding bug

* clippy and fmt

* clippy

* Niklas Suggestions

* lifetime issues and iterator impls

* fmt and clippy

* regenerate polkadot.rs

* fix storage key encoding for empty keys

* rename trait methods for storage keys

* fix hasher bug...

* impl nits, add iterator struct seperate from `StorageHashers`

* clippy fix

* remove println

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Wilson <james@jsdw.me>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
Tadeo Hepperle
2024-03-06 14:04:51 +01:00
committed by GitHub
parent 55e87c86ba
commit a2ee750365
16 changed files with 1960 additions and 1488 deletions
+124 -25
View File
@@ -8,7 +8,7 @@ use quote::{format_ident, quote};
use scale_info::TypeDef;
use scale_typegen::{typegen::type_path::TypePath, TypeGenerator};
use subxt_metadata::{
PalletMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
PalletMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher,
};
use super::CodegenError;
@@ -75,8 +75,15 @@ fn generate_storage_entry_fns(
let alias_module_name = format_ident!("{snake_case_name}");
let alias_storage_path = quote!( types::#alias_module_name::#alias_name );
let storage_entry_map = |idx, id| {
let ident: Ident = format_ident!("_{}", idx);
struct MapEntryKey {
arg_name: Ident,
alias_type_def: TokenStream,
alias_type_path: TokenStream,
hasher: StorageHasher,
}
let map_entry_key = |idx, id, hasher| -> MapEntryKey {
let arg_name: Ident = format_ident!("_{}", idx);
let ty_path = type_gen
.resolve_type_path(id)
.expect("type is in metadata; qed");
@@ -84,34 +91,67 @@ fn generate_storage_entry_fns(
let alias_name = format_ident!("Param{}", idx);
let alias_type = primitive_type_alias(&ty_path);
let alias_type = quote!( pub type #alias_name = #alias_type; );
let path_to_alias = quote!( types::#alias_module_name::#alias_name );
let alias_type_def = quote!( pub type #alias_name = #alias_type; );
let alias_type_path = quote!( types::#alias_module_name::#alias_name );
(ident, alias_type, path_to_alias)
MapEntryKey {
arg_name,
alias_type_def,
alias_type_path,
hasher,
}
};
let keys: Vec<(Ident, TokenStream, TokenStream)> = match storage_entry.entry_type() {
let keys: Vec<MapEntryKey> = match storage_entry.entry_type() {
StorageEntryType::Plain(_) => vec![],
StorageEntryType::Map { key_ty, .. } => {
StorageEntryType::Map {
key_ty, hashers, ..
} => {
match &type_gen
.resolve_type(*key_ty)
.expect("key type should be present")
.type_def
{
// An N-map; return each of the keys separately.
TypeDef::Tuple(tuple) => tuple
.fields
.iter()
.enumerate()
.map(|(idx, f)| storage_entry_map(idx, f.id))
.collect::<Vec<_>>(),
TypeDef::Tuple(tuple) => {
let key_count = tuple.fields.len();
let hasher_count = hashers.len();
if hasher_count != 1 && hasher_count != key_count {
return Err(CodegenError::InvalidStorageHasherCount {
storage_entry_name: storage_entry.name().to_owned(),
key_count,
hasher_count,
});
}
let mut map_entry_keys: Vec<MapEntryKey> = vec![];
for (idx, field) in tuple.fields.iter().enumerate() {
// Note: these are in bounds because of the checks above, qed;
let hasher = if idx >= hasher_count {
hashers[0]
} else {
hashers[idx]
};
map_entry_keys.push(map_entry_key(idx, field.id, hasher));
}
map_entry_keys
}
// A map with a single key; return the single key.
_ => {
vec![storage_entry_map(0, *key_ty)]
let Some(hasher) = hashers.first() else {
return Err(CodegenError::InvalidStorageHasherCount {
storage_entry_name: storage_entry.name().to_owned(),
key_count: 1,
hasher_count: 0,
});
};
vec![map_entry_key(0, *key_ty, *hasher)]
}
}
}
};
let pallet_name = pallet.name();
let storage_name = storage_entry.name();
let Some(storage_hash) = pallet.storage_hash(storage_name) else {
@@ -133,6 +173,10 @@ fn generate_storage_entry_fns(
StorageEntryModifier::Optional => quote!(()),
};
// Note: putting `#crate_path::storage::address::StaticStorageKey` into this variable is necessary
// to get the line width below a certain limit. If not done, rustfmt will refuse to format the following big expression.
// for more information see [this post](https://users.rust-lang.org/t/rustfmt-silently-fails-to-work/75485/4).
let static_storage_key: TokenStream = quote!(#crate_path::storage::address::StaticStorageKey);
let all_fns = (0..=keys.len()).map(|n_keys| {
let keys_slice = &keys[..n_keys];
let (fn_name, is_fetchable, is_iterable) = if n_keys == keys.len() {
@@ -146,12 +190,65 @@ fn generate_storage_entry_fns(
};
(fn_name, false, true)
};
let is_fetchable_type = is_fetchable.then_some(quote!(#crate_path::storage::address::Yes)).unwrap_or(quote!(()));
let is_iterable_type = is_iterable.then_some(quote!(#crate_path::storage::address::Yes)).unwrap_or(quote!(()));
let key_impls = keys_slice.iter().map(|(field_name, _, _)| quote!( #crate_path::storage::address::make_static_storage_map_key(#field_name.borrow()) ));
let key_args = keys_slice.iter().map(|(field_name, _, path_to_alias )| {
quote!( #field_name: impl ::std::borrow::Borrow<#path_to_alias> )
});
let is_fetchable_type = is_fetchable
.then_some(quote!(#crate_path::storage::address::Yes))
.unwrap_or(quote!(()));
let is_iterable_type = is_iterable
.then_some(quote!(#crate_path::storage::address::Yes))
.unwrap_or(quote!(()));
let (keys, keys_type) = match keys_slice.len() {
0 => (quote!(()), quote!(())),
1 => {
let key = &keys_slice[0];
if key.hasher.ends_with_key() {
let arg = &key.arg_name;
let keys = quote!(#static_storage_key::new(#arg.borrow()));
let path = &key.alias_type_path;
let path = quote!(#static_storage_key<#path>);
(keys, path)
} else {
(quote!(()), quote!(()))
}
}
_ => {
let keys_iter = keys_slice.iter().map(
|MapEntryKey {
arg_name, hasher, ..
}| {
if hasher.ends_with_key() {
quote!( #static_storage_key::new(#arg_name.borrow()) )
} else {
quote!(())
}
},
);
let keys = quote!( (#(#keys_iter,)*) );
let paths_iter = keys_slice.iter().map(
|MapEntryKey {
alias_type_path,
hasher,
..
}| {
if hasher.ends_with_key() {
quote!( #static_storage_key<#alias_type_path> )
} else {
quote!(())
}
},
);
let paths = quote!( (#(#paths_iter,)*) );
(keys, paths)
}
};
let key_args = keys_slice.iter().map(
|MapEntryKey {
arg_name,
alias_type_path,
..
}| quote!( #arg_name: impl ::std::borrow::Borrow<#alias_type_path> ),
);
quote!(
#docs
@@ -159,7 +256,7 @@ fn generate_storage_entry_fns(
&self,
#(#key_args,)*
) -> #crate_path::storage::address::Address::<
#crate_path::storage::address::StaticStorageMapKey,
#keys_type,
#alias_storage_path,
#is_fetchable_type,
#is_defaultable_type,
@@ -168,14 +265,16 @@ fn generate_storage_entry_fns(
#crate_path::storage::address::Address::new_static(
#pallet_name,
#storage_name,
vec![#(#key_impls,)*],
#keys,
[#(#storage_hash,)*]
)
}
)
});
let alias_types = keys.iter().map(|(_, alias_type, _)| alias_type);
let alias_types = keys
.iter()
.map(|MapEntryKey { alias_type_def, .. }| alias_type_def);
let types_mod_ident = type_gen.types_mod_ident();
// Generate type alias for the return type only, since
@@ -231,7 +330,7 @@ mod tests {
name,
modifier: v15::StorageEntryModifier::Optional,
ty: v15::StorageEntryType::Map {
hashers: vec![],
hashers: vec![v15::StorageHasher::Blake2_128Concat],
key,
value: meta_type::<bool>(),
},