Replies: 6 comments 9 replies
-
Hi Quentin,
Right the point is for all value with size >= 33 the value is its own node and we write its hash in the parent node. (it allows not having value in PoV for operation such as value removal and other more specific).
Yes new implementation accept both V0 nodes and V1. substrate/primitives/io/src/lib.rs Line 271 in 3ca525d So the choice is driven by the runtime. V0 decoding not being compatible with V1 encoding is not really an issue (using a single decoder that works with both V0 and V1 is what we did, meaning that some stuff that would historically be rejected are now accepted, but not an issue).
Tip of the chain after state migration happens will only contains V1. But there will be a time where the state is V0 and new values are written in V1 (let's call it hybrid state). This hybrid state (containing values of size > 33 byte in some trie nodes and value nodes at the same time) is not a good thing (break warp sync for instance), and migrating should be done quickly.
a single codec is used doing both V0 or V1 decoding, it maybe be doable to remove the V0 variant from the V1 codec, but I think it is easier to keep both version decoding capability even after migrating, since a runtime is still allowed to call the
Yes, that's it, no need to distinguish at the codec level. Also, worth mentioning, there is still a subtlety for compact proof: a special header can indicate that the value is not included in the proof (https://github.com/paritytech/trie/blob/aa3168d6de01793e71ebd906d3a82ae4b363db59/trie-db/src/trie_codec.rs#L140). |
Beta Was this translation helpful? Give feedback.
-
In the leaf or branch the 'value part' of the encoded node is the value hash, but from our implementation point of view (and the proof) the value bytes content can be seen as a single node (without header or additional encoding) and is stored as a key value (with key being hash of value bytes).
yes 👍 . And the hash points to the actual byte content of the value (which can be seen as a node, just without header as we already have all needed info).
yes, thus the need for a migration: warning it also mean that child trie root will change after migration (can be an issue for some parachains). but as long as runtime did not change to V1, no.
yes 👍 , but using v1 is driven by the parameter passed to the root host function.
asap, but it is been a while since things are ready (so not sure). There will be first the test networks migrating (devops team is on the starting blocks but we need to write and configure the new runtime, this task was postponed a few time due to other more urgent runtime problematic but I have good hope we can proceed to do soon). but the switch is a chain choice (for instance currently our testnet for smart contract is already using V1 as it was restarted recently).
It should not have (except maybe on the smart contract testnet), I mean I did not hear of user facing state root issue if they did not upgrade to 9.20, trie-db version seems to be 0.23.1 in both 0.9.19 and 0.9.20. New format was added in 0.23.0, switch to 0.23.1 in polkadot 0.9.17 and to 0.23.0 in polkadot 0.9.16. What kind of the root hash difference do you see (is it for a specific chain, a specific block)?
yes since polkadot 0:9.16. |
Beta Was this translation helpful? Give feedback.
-
I am not sure I get your description.
I don't really understand why differentiating is needed at trie node level. (one only need the trie version when writing change in trie and the state version can be pass as parameter). In the rust trie crate implementation, nodes are not versioned, and the version is passed as paremeter of the codec when committing the changes (and then depending on version we use a subvalue node or not).
Using a reference counter is fine, that is what we do when using paritydb.
That is how things work in our case (we do the same for every encoded node of the trie).
The value node is just it's bytes. The proof is of type Vec< Vec > with the Vec being either value node or a trie encoded node. Then the proof is scale encoded so yes the subvalue will be scale encoded.
For a leaf (or branch) containing a hash pointing to a subvalue node, there is two case:
We could have kept the |
Beta Was this translation helpful? Give feedback.
-
About the hybrid state, the way we handle it is that we switch/choose trie implementation when calling storage root only (that is the only time we do insert operation on trie).
yes, that is why both implementation (in our case we share a lot of code), need to be compatible (V1 can read V0 node so it is safe to use it for any read only operation).
yes and sometime leaf are included in proof (to read the partial key) without reading value. Also, on this trie format, get_hash host function do not access the value anymore (before it was get_value then hash).
Without value is not permitted but 0 length value can be.
Yes you are right, could only help if there was parasitic writes of value (like some V0 are loaded and written without actual changes happening). |
Beta Was this translation helpful? Give feedback.
-
Note that changing trie compact format is something doable but would require client upgrade (it is compiled in wasm in cumulus PoV and I think is not yet used in rpc), still some work to deploy/do |
Beta Was this translation helpful? Give feedback.
-
@cheme I'm finishing Quentin's implementation of trie V1 for gossamer and I have a question. Is there any chance where we have to encode a trie built on V1 using V0 encoding? I've been looking into all substrate code and I think you are always assuming that we cannot go back from V1 to V0, is that correct? What happen if we apply a runtime change that's start using V1 (for example in a fork) and then we discard that fork and have to re apply a previous runtime that is using V0? what is the expected behavior for that scenario? Thanks! |
Beta Was this translation helpful? Give feedback.
-
Hi there!
I'm Quentin working on the Polkadot implementation in Go Gossamer.
I have a few questions regarding the state trie upgrade from v0 to v1.
From reading around the Polkadot specification, substrate and the trie crate pull requests, I have understood so far the only difference between v0 and v1 is that trie proofs can be encoded nodes with their subvalue hash as their 'subvalue field'.
Now it seems the v1 decoding of proofs is retro-compatible with the v0 generated proofs, since new node headers were added and they don't conflict. On the other hand, v1 encoding is not compatible with v0 decoding.
My understanding is that the tip of the chain will have only v1 state trie runtimes, and that we only need to support decoding proof nodes when syncing the chain together with the v0 state trie runtime. So does that mean we can only implement the v1 state trie codec and have it run for both v0 and v1 encoded proof nodes? Or do we still need to support both versions?
Feel free to correct me or clarify on any of the points mentioned above! I might had missed some differences between v0 and v1.
Thank you in advance!
Beta Was this translation helpful? Give feedback.
All reactions