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

Implement StorageNMap #8635

Merged
37 commits merged into from
May 14, 2021
Merged

Implement StorageNMap #8635

37 commits merged into from
May 14, 2021

Conversation

KiChjang
Copy link
Contributor

@KiChjang KiChjang commented Apr 17, 2021

Partial #8291.

Things left to be done:

  • Support StorageNMap in #[pallet::storage] and decl_storage! macros
  • Make use of it in pallets that wanted a StorageTripleMap
  • (Optional, needs discussion) Deprecate StorageMap and StorageDoubleMap and make them use StorageNMap underneath

@shawntabrizi
Copy link
Member

@KiChjang I see this is a draft. If there are outstanding things that need to be done, make a checklist of those things?

@KiChjang
Copy link
Contributor Author

@shawntabrizi There are some really tough problems with regards to supporting iteration of prefixes in the StorageNMap. Specifically, I'm really not sure how to tell Rust to return the suffix key types, so I think I'd probably not support the iter_prefix and the drain_prefix methods.

Other than that, what's mostly left to do is trying to get it to actually work in pallets, so that people can start using it.

@shawntabrizi shawntabrizi requested a review from bkchr April 18, 2021 23:53
@shawntabrizi
Copy link
Member

shawntabrizi commented Apr 19, 2021

It feels to me without the ability to do iteration and the lingering (), this is probably no the correct approach.

@bkchr are there better ways to approach this?

@KiChjang
Copy link
Contributor Author

KiChjang commented Apr 19, 2021

@shawntabrizi Just to clarify, iteration itself is supported, the only problem is supporting the iteration of prefixes.

EDIT: That means there is a very crude way of supporting the iteration of prefixes using filter or filter_map and filtering out keys that matches a certain prefix.

I'm pausing development for now until there's a consensus on what to do next.

@bkchr
Copy link
Member

bkchr commented Apr 19, 2021

If you use tuples as keys, you should be able to remove this extra ().

What is the exact problem with prefix iteration? That it is hard to declare a prefix of v for a key (v, x)?

@KiChjang
Copy link
Contributor Author

@bkchr With the current design, this is how a iter_prefix method declaration can be written as follows:

fn iter_prefix<KG, KArg>(key: (KArg, KG::NextKey)) -> ??? // what should even go in here?

The problem I see here is two-fold:

  1. How do I make sure that the KG parameter is a proper prefix of K in the StorageNMap?
  2. How do I write the rest of the key suffixes out as the return type?

@gui1117
Copy link
Contributor

gui1117 commented Apr 19, 2021

for prefix iterator maybe we can create a new trait which enforce the correctness of the prefix. an example can be:

trait KeyPrefix<T>: KeyGenerator {}

// To be implemented with macro
impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator> KeyPrefix<(A, B, C)> for (A, B) {}
impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator, D: KeyGenerator> KeyPrefix<(A, B, C, D)> for (A, B) {}
impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator, D: KeyGenerator, E: KeyGenerator> KeyPrefix<(A, B, C, D, E)> for (A, B) {}

impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator, D: KeyGenerator> KeyPrefix<(A, B, C, D)> for (A, B, C) {}
impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator, D: KeyGenerator, E: KeyGenerator> KeyPrefix<(A, B, C, D, E)> for (A, B, C) {}

@bkchr
Copy link
Member

bkchr commented Apr 19, 2021

for prefix iterator maybe we can create a new trait which enforce the correctness of the prefix. an example can be:

trait KeyPrefix<T>: KeyGenerator {}

// To be implemented with macro
impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator> KeyPrefix<(A, B, C)> for (A, B) {}
impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator, D: KeyGenerator> KeyPrefix<(A, B, C, D)> for (A, B) {}
impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator, D: KeyGenerator, E: KeyGenerator> KeyPrefix<(A, B, C, D, E)> for (A, B) {}

impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator, D: KeyGenerator> KeyPrefix<(A, B, C, D)> for (A, B, C) {}
impl<A: KeyGenerator, B: KeyGenerator, C: KeyGenerator, D: KeyGenerator, E: KeyGenerator> KeyPrefix<(A, B, C, D, E)> for (A, B, C) {}

Yeah nice! I had the same idea, basically it should be done in a similar way as it is done with Borrow.

So,

fn iterate_prefix<P>(prefix: P) where Key: KeyPrefix<P>

@bkchr
Copy link
Member

bkchr commented Apr 19, 2021

So, I would actually do it the other way around:

impl<A, B, C> KeyPrefix<(A, B)> for (A, B, C) {}

And we will probably require some methods on KeyPrefix (maybe we should use IsKeyPrefix as name), but you should see this when implementing.

@KiChjang
Copy link
Contributor Author

KiChjang commented Apr 20, 2021

So the suggestions above would only solve (1), but I suppose I can easily extend it to contain an associated type KeySuffix, which will solve (2) as such:

fn iter_prefix<P>(prefix: P) -> PrefixIterator<(<Key as KeyPrefix<P>>::KeySuffix, V)> where Key: KeyPrefix<P>

Now here comes the fun part: what do I do about final_hash? This function is used only in one place, and that's in migrate_keys where we need to migrate the keys from an old hasher to a new hasher. Using impl_for_tuple causes us to lose the ability to extract each individual key out to be paired with the corresponding new hasher, so I don't have a good idea about what to do there.

@bkchr
Copy link
Member

bkchr commented Apr 20, 2021

migeate_keys can be more manual.

Meaning you take the old hashers as tuple as you take them for the struct itself. The user would need to make sure that they pass the same number of arguments of hashers as the struct itself.

@KiChjang
Copy link
Contributor Author

KiChjang commented Apr 20, 2021

migeate_keys can be more manual.

Meaning you take the old hashers as tuple as you take them for the struct itself. The user would need to make sure that they pass the same number of arguments of hashers as the struct itself.

I thought about this before, but StorageHasher functions are defined as "class functions" on the hasher structs, so I can't just hasher.hash() something, I need to have its type and do Hasher::hash().

I of course could add a hash(&self, x: &[u8]) method for these structs, inside of which would just contain Self::hash(x), but I'm not sure if that's a good idea.

@bkchr
Copy link
Member

bkchr commented Apr 20, 2021

If migrate keys is a problem, you could obmit it for now and make the rest working for now.

@KiChjang KiChjang force-pushed the storage-n-map branch 2 times, most recently from f3d539f to ad8f391 Compare April 23, 2021 07:28
Copy link
Contributor

@gui1117 gui1117 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me, polkadot js seems to support it polkadot-js/api#3465
but I don't know how much we should advertise the metadata change.
But at some point as long as we don't use any storage n map in polkadot the metadata are entirely compatible, except the version has bump to v13.

note the metadata will also change in #8615, I don't know if worth doing both at the same time.

maybe @joepetrowski can say more ?

@joepetrowski
Copy link
Contributor

Might be a breaking change to some of the paths in Sidecar and tx construction in txwrapper-core. Deferring to @TarikGul if those will need a major version change.

Will this or #8615 change transaction encoding? Then we would need to bump the version.

@kianenigma
Copy link
Contributor

Will this or #8615 change transaction encoding? Then we would need to bump the version.

Will not affect tx-version.

Copy link
Contributor

@kianenigma kianenigma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see some leftover discussions, but probably all can be done separately.

As sanity, I'd be interested in seeing a a test that demonstrates a double map and an NMap(2) which same key types and hashers, generate exactly identical keys, and are thus exactly identical.

And for all the rest looks broadly good to me! Welcome :)

Copy link
Member

@bkchr bkchr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nitpicks.

I also did not review anything, but I also don't want to block anything here.

frame/support/procedural/src/pallet/parse/storage.rs Outdated Show resolved Hide resolved
frame/support/procedural/src/pallet/parse/storage.rs Outdated Show resolved Hide resolved
frame/support/procedural/src/pallet/parse/storage.rs Outdated Show resolved Hide resolved
frame/support/procedural/src/pallet/parse/storage.rs Outdated Show resolved Hide resolved
frame/support/procedural/src/pallet/parse/storage.rs Outdated Show resolved Hide resolved
frame/support/procedural/src/storage/mod.rs Outdated Show resolved Hide resolved
frame/support/src/storage/generator/nmap.rs Outdated Show resolved Hide resolved
frame/support/src/storage/types/key.rs Outdated Show resolved Hide resolved
frame/support/src/storage/types/key.rs Outdated Show resolved Hide resolved
frame/support/src/storage/types/key.rs Show resolved Hide resolved
@gui1117
Copy link
Contributor

gui1117 commented May 14, 2021

it seems follow up pieces are:

  • As sanity, I'd be interested in seeing a a test that demonstrates a double map and an NMap(2) which same key types and hashers, generate exactly identical keys, and are thus exactly identical.

  • have a procedural macro for impl_key_prefix_for!((A, B, C), (A, B), C); ....

  • update generate_storage_alias to support nmpa as well

  • maybe making StorageMap and StorageDoubleMap using StorageNMap under the hood.

  • maybe consider using storage n map everywhere instead of storage map and storage double map. or the other way around: Implement StorageNMap #8635 (review)

@gui1117
Copy link
Contributor

gui1117 commented May 14, 2021

bot merge

@ghost
Copy link

ghost commented May 14, 2021

Trying merge.

@ghost ghost merged commit bcd649f into paritytech:master May 14, 2021
@KiChjang KiChjang deleted the storage-n-map branch May 17, 2021 03:15
@viniul viniul added D1-audited 👍 PR contains changes to fund-managing logic that has been properly reviewed and externally audited and removed D5-nicetohaveaudit ⚠️ PR contains trivial changes to logic that should be properly reviewed. labels May 28, 2021
nazar-pc pushed a commit to autonomys/substrate that referenced this pull request Aug 8, 2021
* Implement StorageNMap

* Change copyright date to 2021

* Rewrite keys to use impl_for_tuples instead of recursion

* Implement prefix iteration on StorageNMap

* Implement EncodeLike for key arguments

* Rename KeyGenerator::Arg to KeyGenerator::KArg

* Support StorageNMap in decl_storage and #[pallet::storage] macros

* Use StorageNMap in assets pallet

* Support migrate_keys in StorageNMap

* Reduce line characters on select files

* Refactor crate imports in decl_storage macros

* Some more line char reductions and doc comment update

* Update UI test expectations

* Revert whitespace changes to untouched files

* Generate Key struct instead of a 1-tuple when only 1 pair of key and hasher is provided

* Revert formatting changes to unrelated files

* Introduce KeyGeneratorInner

* Add tests for StorageNMap in FRAMEv2 pallet macro

* Small fixes to unit tests for StorageNMap

* Bump runtime metadata version

* Remove unused import

* Update tests to use runtime metadata v13

* Introduce and use EncodeLikeTuple as a trait bound for KArg

* Add some rustdocs

* Revert usage of StorageNMap in assets pallet

* Make use of ext::PunctuatedTrailing

* Add rustdoc for final_hash

* Fix StorageNMap proc macro expansions for single key cases

* Create associated const in KeyGenerator for hasher metadata

* Refactor code according to comments from Basti

* Add module docs for generator/nmap.rs

* Re-export storage::Key as NMapKey in pallet prelude

* Seal the EncodeLikeTuple trait

* Extract sealing code out of key.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. C1-low PR touches the given topic and has a low impact on builders. D1-audited 👍 PR contains changes to fund-managing logic that has been properly reviewed and externally audited
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants