We have few storage value with double map X, Y => Option<()> type to simulate an unordered set.
However I found out the .iter(key1) is not able to iterate all the values for key1.
Using getStorage RPC is able to read that storage did exists. getKeys is not able to iterate as the key wasn't exists.
Seems like the trie db is not able to distinguish between a non-exist key and an exist key with no value in some cases?
Change the type to Option<bool> works around the issue for me.
Example code:
This is not able to iterate.
The interesting thing is it works in unit test so we wasn't able to catch this issue before. Maybe something to do with low level db? The in memory db is able to handle but the real rocks db is not?
cc @thiolliere.
This is not an answer to your question, but a possible workaround. If you want to use Option<()>, which has two variants, Some(()) and None, you might get away with using bool.
true -> Some(())
false -> None
as far as I can understand TrieDbMut doesn't support inserting empty value https://github.com/paritytech/trie/blob/master/trie-db/src/triedbmut.rs#L1537
If I'm correct then I don't know why
@cheme @arkpar
Also I suspect getStorage RPC works because the key is found in the cache, but this is just a guess.
one can easily reproduce by modifying existing test: (the test will fail)
diff --git a/primitives/state-machine/src/trie_backend_essence.rs b/primitives/state-machine/src/trie_backend_essence.rs
index 125a823f5..9ff017d8e 100644
--- a/primitives/state-machine/src/trie_backend_essence.rs
+++ b/primitives/state-machine/src/trie_backend_essence.rs
@@ -445,18 +445,18 @@ mod test {
let mut mdb = PrefixedMemoryDB::<Blake2Hasher>::default();
{
let mut trie = TrieDBMut::new(&mut mdb, &mut root_1);
- trie.insert(b"3", &[1]).expect("insert failed");
- trie.insert(b"4", &[1]).expect("insert failed");
- trie.insert(b"6", &[1]).expect("insert failed");
+ trie.insert(b"3", &[]).expect("insert failed");
+ trie.insert(b"4", &[]).expect("insert failed");
+ trie.insert(b"6", &[]).expect("insert failed");
}
{
let mut mdb = KeySpacedDBMut::new(&mut mdb, child_info.keyspace());
// reuse of root_1 implicitly assert child trie root is same
// as top trie (contents must remain the same).
let mut trie = TrieDBMut::new(&mut mdb, &mut root_1);
- trie.insert(b"3", &[1]).expect("insert failed");
- trie.insert(b"4", &[1]).expect("insert failed");
- trie.insert(b"6", &[1]).expect("insert failed");
+ trie.insert(b"3", &[]).expect("insert failed");
+ trie.insert(b"4", &[]).expect("insert failed");
+ trie.insert(b"6", &[]).expect("insert failed");
}
{
let mut trie = TrieDBMut::new(&mut mdb, &mut root_2);
Ethereum legacy. In EVM there's no distinction between a zeroed and non-existent storage slot. I guess for substrate we could lift that restriction. @gavofyork What do you think?
@arkpar should be fixed AFAIK?
I'd expect so. Just need to be careful maintaining compatibility with existing networks here.
@bkchr Is this fixed?
I'd expect so. Just need to be careful maintaining compatibility with existing networks here.
Definitely not fixed.
I'd expect so. Just need to be careful maintaining compatibility with existing networks here.
This was answering the question if it should be fixed
Okay ;) I meant this as a question if this "should already be fixed".
I鈥檓 not sure it really should be fixed.
then maybe we should
[1])Some(value)), and some other cache also.EDIT: After discussion we will fix it. Storage will support adding empty value