Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New trie version #478

Merged
merged 10 commits into from
Mar 4, 2022
84 changes: 42 additions & 42 deletions 01_host/02_state/state_storage_trie.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ the following concatenated data:

[stem]
++++
"Node Header|Partial Key|Node Subvalue"
"Node Header"||"Partial Key"||"Node Subvalue"
++++

Formally noted as:
Expand All @@ -229,59 +229,47 @@ defined in <<defn-node-header>>, <<defn-node-key>>, <<defn-hex-encoding>> and
[#defn-node-header]
.<<defn-node-header, Node Header>>
====
The *node header* of node stem:[N], stem:["Head"_N], consists of stem:[l + 1 >= 1]
bytes stem:["Head"_(N,1),...,"Head"_(N,l+1)] such that:

[cols="2,2,4,4,.^1,4"]
|===
|Node Type |pk length | pk length extra byte 1 |pk key length extra byte 2 1.2+|...| pk length extra byte stem:[l]
|stem:["Head"_(N,1)^(6-7)] | stem:["Head"_(N,1)^(0-5)] | stem:["Head"_(N,2)] |... | stem:["Head"_(N,l+1)]
|===

In which stem:["Head"_(N,1)^(6-7)], the two most significant bits of the first
byte of stem:["Head"_N] are determined as follows:
The *node header*, consisting of stem:[>= 1] bytes, stem:[N_1...N_n], specifies
the node variant and the length of the partial key length (<<defn-node-key>>) in
4-bit nibbles. Both pieces of information can be represented in bits within a
single byte, stem:[N_1], where the amount of bits of the variant, stem:[v], and
the bits of the partial key length, stem:[p_l] varies.

[stem]
++++
"Head"_(N,1)^(6-7) := {(00,"Special case"),(01,"Leaf Node"),(10,"Branch Node with " k_N !in cc K),(11,"Branch Node with " k_N in cc K):}
v = {
(01,"Leaf",p_l = 2^6),
(10,"Branch Node with" k_N !in cc "K", p_l = 2^6),
(11, "Branch Node with" k_N in cc "K", p_l = 2^6),
(001,"Leaf containing hashes", p_l = 2^5),
(0001,"Branch containing hashes", p_l = 2^4),
(0000 0000,"Empty",),
(0001 0000,"Reserved for compact encoding",)
:}
++++

where stem:[cc K] is defined <<defn-stored-value>>.
If the value of stem:[p_l] is equal to the maximum possible value the bits can
hold, such as 63 (stem:[2^6-1]) in case of the stem:[01] variant, then the value
of the next 8 bits (stem:[N_2]) are added the the length. This process is
repeated for every stem:[N_n] where stem:[N_n = 2^8-1]. Any value smaller than
the maximum possible value of stem:[N_n] implies that the next value of
stem:[N_(n+1)] should not be added to the length.

The variant stem:[0001] can be distinguished from stem:[0001 0000] due to the
fact that the following 4 bits of the first variant never equal zero.

stem:["Head"_(N,1)^(0-5)], the 6 least significant bits of teh first byte of stem:["Head"_N] are defined to be:
Formally, the lenght of the parital key, stem:["pk"_N^l], is defined as:

[stem]
++++
"Head"_(N,1)^(0-5) := {(||pk_N||_(nib),||pk_N||_(nib) < 63),(63,||pk_N||_(nib) >= 63):}
"pk"_N^l = p_l + N_n + N_(n+x) + ... + N_(n+x+y)
++++

In which stem:[||"pk"_N||_(nib)] is the length of stem:["pk"_N] in number nibbles.
stem:["Head"_(N,2),...,"Head"_(N,l+1)] bytes are determined by the algorithm as
defined in <<algo-pk-length>>.
as long as stem:[p_l = m], stem:[N_(n+x) = 2^8-1] and
stem:[N_(n+x+y) < 2^8-1], where stem:[m] is the maximum possible value
that stem:[p_l] can hold.
====

[#algo-pk-length]
===== Partial Key Length Encoding
****
Algorithm: stem:["Partial-Key-Length-Encoding"("Head"_(N,1)^(6-7),pk_N)]

. stem:["if " ||pk_N||_(nib) >= 2^16]
. stem:[" " "return Error"]
. stem:["Head"_(N,1) larr 64 xx "Head"_(N,1)^(6-7)]
. stem:["if " ||pk_N||_(nib) < 63]
. stem:[" " "Head"_(N,1) larr "Head"_(N,1) + ||pk_N||_(nib)]
. stem:[" " "return Head"_N]
. stem:["Head"_(N,1) larr "Head"_(N,1) + 63]
. stem:[l larr ||pk_N||_(nib) - 63]
. stem:[i larr 2]
. stem:["while " (l > 255)]
. stem:[" " "Head"_(N,i) larr 255]
. stem:[" " l larr l - 255]
. stem:[" " i larr i + 1]
. stem:["Head"_(N,1) larr l]
. stem:["return Head"_N]
****

[#sect-merkl-proof]
==== Merkle Proof

Expand Down Expand Up @@ -340,7 +328,6 @@ where the first variant is a leaf node and the second variant is a branch node.
++++
"StoredValue"_("SC") := {("Enc"_("SC")("StoredValue"(k_N)),"if StoredValue"(k_N) = v),(phi,"if StoredValue"(k_N) = phi):}
++++
====

stem:[N_(C_1) ... N_(C_n)] with stem:[n <= 16] are the children nodes of the
branch node stem:[N] and stem:["Enc"_("SC")], stem:["StoredValue"], stem:[H], and
Expand All @@ -351,6 +338,19 @@ respectively.
The Trie deviates from a traditional Merkle tree where node value
(<<defn-node-value>>), stem:[v_N], is presented instead of its hash if it
occupies less space than its hash.
====

[#defn-node-hashes]
.<<defn-node-hashes, Node Hashes>>
====
To increate performance, a merkle proof can be generated by inserting the hash of
a value into the trie rather than the value itself (which can be quite
large). If merkle proof computation with node hashing is explicitly executed via
the Host API (<<sect-ext-storage-root-version-2>>), then any value larger than
32 bytes is hashed, resulting in that hash being used as the subvalue
(<<defn-node-subvalue>>) under the corresponding key. The node header must
specify the variant stem:[001] respectively stem:[0001] (<<defn-node-header>>).
====

[#defn-merkle-value]
.<<defn-merkle-value, Merkle Value>>
Expand Down
21 changes: 21 additions & 0 deletions ab_host-api/storage.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,27 @@ value to be appended.

Compute the storage root.

[#sect-ext-storage-root-version-2]
===== Version 2 - Prototype
----
(func $ext_storage_root_version_2
(param $version i32) (return i32))
----

Arguments::
* `version`: a 32-bit pointer to the state version of the following format:
+
[stem]
++++
v = {(0, "state version 1"),(1, "node hashes"):}
++++
where stem:[0] behaves like version 1 (<<sect-ext-storage-root-version-1>>) of
this function and stem:[1] makes use of "node hashes" when calculating the
merkle proof (<<defn-node-hashes>>).
* `return`: a 32-bit pointer to the buffer containing the 256-bit Blake2 storage
root.

[#sect-ext-storage-root-version-1]
===== Version 1 - Prototype
----
(func $ext_storage_root_version_1
Expand Down
7 changes: 1 addition & 6 deletions ac_runtime-api/modules/core.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,13 @@ Return::
|Name |Type |Description

| `spec_name` | String | Runtime identifier

| `impl_name` | String | Name of the implementation (e.g. C++)

| `authoring_version` | Unsigned 32-bit integer | Version of the authorship interface

| `spec_version` | Unsigned 32-bit integer | Version of the Runtime specification

| `impl_version` | Unsigned 32-bit integer | Version of the Runtime implementation

| `apis` | ApiVersions (<<defn-rt-apisvec>>) | List of supported APIs along with their version

| `transaction_version` | Unsigned 32-bit integer | Version of the transaction format
| `state_version` | Unsigned 32-bit integer | Version of the trie format
|===

.ApiVersions
Expand Down