Creating keys and paths
Let us first look at the zkProver's storage parameters. In the Storage SM, keys and values are strings of 256 bits. Keys are henceforth represented as 256-bit u
Let us first look at the zkProver's storage parameters.
In the Storage SM, keys and values are strings of 256 bits. Keys are henceforth represented as 256-bit unsigned integers that are quadruples of 64-bit field elements. For example:
where each , where .
Although hashed-values are also 256-bit long and are used in quadruple form, committed values are 256-bit long and are often expressed as octets. It is mainly due to the Poseidon SM convention, where 256-bit committed values are input as
and each 32-bit chunk of bits.
In fact, almost every other 256-bit value in storage is expressed in the form of a quadruple of 64-bit field elements.
How keys are created
In the key-value pair SMT context of our storage design, a key uniquely identifies a leaf. And it is because, although values can change, keys do not.
Keys must consequently be generated deterministically, and in such a way that there are no collisions. That is, there must be a one-to-one correspondence between keys and leaves.
A collision-resistant hash function is therefore the best tool for generating keys. And the most convenient way to generate keys is by hashing some specific information so that the resultant hash uniquely identifies the leaf.
The specific information used for generating keys is the Ethereum address and some constant. The Poseidon hash function is again used for this purpose.
Constructing navigation paths
A path refers to the edges traversed from the root to a leaf. Since the SMTs are binary, all edges can be thought of as labeled with either a bit 0 or 1.
Edges to the left are labeled with a bit 0, while edges to the right are labeled with a bit 1.
Paths are therefore strings of bits, and are derived from keys in a very specific way.
First of all, every key can be thought of as a quadruple,
Denote each 64-bit key part bit-wise as;
where the most-significant bit and the least-significant bit , for each .
The navigation path to the leaf corresponding to the key is defined as the following string of shuffled key-bits;
That is, the navigation path to the leaf corresponding to is the string of bits composed of:
- The least-significant bits of the four 64-bit key parts, , , , , appearing in the order of the key parts as:
- Followed by the second least-significant bits of the four 64-bit key parts, , , , , appearing in the order of the key parts as:
- Then the third least-significant bits of the four key parts, , , , , appearing in the order of the key parts as:
- Up until the most-significant bits of the four key parts, , , , , appearing in the order of the key parts as:

This construction ensures that in every quadruplet of consecutive path-bits, there is a one-to-one correspondence between the bits and the four parts of the key, , , , .
Reconstructing the key from path-bits
When executing a basic operation such as an UPDATE of a value at a leaf, one has to reconstruct the key from the remaining key found at the leaf and the path-bits spent in navigating to the leaf.
Denote the remaining key as a quadruple,
Since the Path was constructed by shuffling key-bits from the four parts, , , , , one would expect the reverse-process (going from the path-bits to the original key) to work just as easily.
Perhaps taking the level of a leaf and reducing it modulo 4, should be sufficient to tell which part of the Remaining Key, , must the Path key-bit be appended to.
Example of key reconstruction
Suppose the leaf storing the key-value pair is reached at level 7, the path-bits used are , and the remaining key is
That is, the path-bits are
So, in order to place , one first computes to get . Hence, the must be appended to the third key part, .
Next, one climbs the tree to level , where . One then computes and gets . The must then be appended to the second key part, .
Again, one climbs the tree to level , where . Computing yields . The is thence appended to the first key part, .
One then continues in the same fashion:
The next climb is to the root. The navigation path-bits have been exhausted, and the last append has actually completed reconstruction of the key.
Leaf levels and integers modulo 4
It is clear, from the above example, that there is a one-to-one correspondence between the integers modulo 4 (i.e., elements of the group ) and remaining key parts , , , .
That is, there is a mapping:
The quadruple structure of the path bits and the level of leaves therefore have a homomorphic relationship that can be described in terms of the cyclic group of integers modulo 4, .
Since addition modulo n is an expensive computation in the state machine context, it is important to find a more efficient algorithm to achieve the same result.
Alternate cyclic group of order 4
In order to explore cyclic groups of order 4, take the vector , and rotate the components of one position to the left.
Note that, rotating
- once, yields
- twice, one obtains
- thrice, one gets
- four times, and the result is
Continuously rotating does not result in any other vector but the four vectors
This set of four vectors together with the described rotation, forms a group.
In fact, is isomorphic (or homomorphically equivalent) to under addition modulo 4.
That is, there is a natural one-to-one correspondence between the elements of and those of , as follows:
Note that the four numbers , , and can be expressed in their binary form with just two bits, and the same one-to-one correspondence holds as:
Special cyclic register for leaf levels
Define a register called LEVEL which is a vector of four bits; three 0 bits and one 1 bit. And the operation ROTATE_LEVEL which is the left rotation of LEVEL's bits by one position.
If LEVEL is initialised as , observe that applying ROTATE_LEVEL four times brings LEVEL back to . That is,
Therefore, LEVEL is cyclic under ROTATE_LEVEL, and is in fact algebraically the same as the cyclic group described above.
Using LEVEL register in key reconstruction
First note that, when navigating the tree, the leaf level can be indicated by one of the four possible states of the LEVEL register. And this works for all possible leaf levels because, for any positive integer :
Second, the two least-significant bits of each of these number, when written in binary, are as follows:
It suffices therefore to only read the two least-significant bits of the leaf level in order to determine the position of the bit 1 in the LEVEL register.
Third, the position of the bit 1 in the LEVEL register tallies precisely with the part of the remaining key, , to which the last used path-bit came from.
So then, when reconstructing the key, one needs only to check where the bit 1 is in the LEVEL register, because:
Since things are rather mechanical in state machines, one or two more functions are needed. For instance, one for initialising the LEVEL register, and another for reading the position of the bit 1.
Last updated on
Storage state machine
A standard state machine is characterized by sets of states (as inputs) stored in registers, instructions on how the states should transition, and the resultant
Storage SM mechanism
The Storage SM is one of zkProver's secondary state machines, and thus receives instructions from the Main SM called Storage Actions. The Storage Actions basica