mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 22:56:43 +00:00
unify byte order in zktrie node's fields being stored (#133)
This commit is contained in:
parent
fefa8b99c7
commit
f9952a396f
2 changed files with 149 additions and 8 deletions
143
core/types/zktrie/README.md
Normal file
143
core/types/zktrie/README.md
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# Type for zktrie
|
||||
|
||||
## Data Format in stateDb
|
||||
|
||||
All data node being stored via stateDb are encoded by following syntax:
|
||||
|
||||
``` EBNF
|
||||
node = magic string | node data ;
|
||||
|
||||
magic string = "THIS IS SOME MAGIC BYTES FOR SMT m1rRXgP2xpDI" ;
|
||||
|
||||
node data = middle node | leaf node | empty node ;
|
||||
|
||||
empty node = '0x2' ;
|
||||
|
||||
middle node = '0x0', left hash, right hash ;
|
||||
|
||||
field = 32 * hex char ;
|
||||
|
||||
left hash = field ;
|
||||
|
||||
right hash = field ;
|
||||
|
||||
leaf node = node key , value len , compress flag , <value len> * value field, key preimage ;
|
||||
|
||||
node key = field ;
|
||||
|
||||
compress flag = 3 * byte ;
|
||||
|
||||
value len = byte ;
|
||||
|
||||
value field = field | compressed field, compressed field ;
|
||||
|
||||
compressed field = 16 * hex char ;
|
||||
|
||||
key preimage = '0x0' | preimage bytes ;
|
||||
|
||||
preimage bytes = len, <len> * byte ;
|
||||
|
||||
len = byte ;
|
||||
```
|
||||
|
||||
A `field` is an element in prime field of BN256 represented by **big endian** integer and contained in fixed length (32) bytes;
|
||||
|
||||
A `compressed field` is a field represented by **big endian** integer which could be contained in 16 bytes;
|
||||
|
||||
For the total `value len` items of `value field` (maximum 255), the first 24 `value field`s can be recorded as `field` or 2x `compressed field` (i.e. a byte32). The corresonpdoing bit in `compress flag` is set to 1 if it was recorded as byte32, or 0 for a field.
|
||||
|
||||
## Key scheme
|
||||
|
||||
The key of data node is obtained from one or more poseidon hash calculation: `poseidon := (field, field) => field`.
|
||||
|
||||
For middle node:
|
||||
|
||||
```
|
||||
key = poseidon(<left hash>, <right hash>)
|
||||
```
|
||||
|
||||
For leaf node:
|
||||
|
||||
```
|
||||
key = poseidon(<pre key>, <value hash>)
|
||||
|
||||
pre key = poseidon(field(1), <node key>)
|
||||
|
||||
value hash = poseidon(<leaf element>, <leaf element>) | poseidon(<value hash>, <value hash>)
|
||||
|
||||
leaf element = <value field as field> | poseidon(<compressed field as field>, <compressed field as field>) | field(0)
|
||||
|
||||
```
|
||||
|
||||
That is, to calculate the key of a leaf node:
|
||||
|
||||
1. In the sequence of `value field`s, take which is recorded as 'compressed' and calculate the 2x `compressed field` for its poseidon hash, replace the corresponding `value field` ad-hoc in the sequence;
|
||||
|
||||
2. Consider the sequence from 1 as the leafs of a binary merkle tree (append a 0 field for odd leafs) and calculate its root by poseidon hash;
|
||||
|
||||
For empty node:
|
||||
|
||||
```
|
||||
key = field(0)
|
||||
```
|
||||
|
||||
## Account data
|
||||
|
||||
Each account data is saved in one leaf node of account zktrie as 4 `value field`s:
|
||||
|
||||
1. Nonce as `field`
|
||||
2. Balance as `field`
|
||||
3. CodeHash as `compressed field` (byte32)
|
||||
4. Storage root as `field`
|
||||
|
||||
The key for an account data is calculated from the 20-bit account address as following:
|
||||
|
||||
```
|
||||
|
||||
32-byte-zero-end-padding-addr := address, 16 * bytes (0)
|
||||
|
||||
key = poseidon(<first 16 byte of 32-byte-zero-end-padding-addr as field>, <last 16 byte of 32-byte-zero-end-padding-addr as field>)
|
||||
|
||||
```
|
||||
|
||||
## Data examples
|
||||
|
||||
### A leaf node in account trie:
|
||||
|
||||
> 0x017f9d3bbc51d12566ecc6049ca6bf76e32828c22b197405f63a833b566fe7da0a040400000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000029b74e075daad9f17eb39cd893c2dd32f52ecd99084d63964842defd00ebcbe208a2f471d50e56ac5000ab9e82f871e36b5a636b19bd02f70aa666a3bd03142f00
|
||||
|
||||
Can be decompose to:
|
||||
|
||||
+ `0x01`: node type prefix for leaf node
|
||||
+ `7f9d3bbc51d12566ecc6049ca6bf76e32828c22b197405f63a833b566fe7da0a`: node key as field
|
||||
+ `04`: value len (4 value fields)
|
||||
+ `040000`: compress flag, a 24 bit array, indicating the third field is compressed
|
||||
+ `0000000000000000000000000000000000000000000000000000000000000001`: value field 0 (nonce)
|
||||
+ `0000000000000000000000000000000000000000000000000000000000000000`: value field 1 (balance)
|
||||
+ `29b74e075daad9f17eb39cd893c2dd32f52ecd99084d63964842defd00ebcbe2`: value field 2 (codeHash, as byte32)
|
||||
+ `08a2f471d50e56ac5000ab9e82f871e36b5a636b19bd02f70aa666a3bd03142f`: value field 3 (storage root)
|
||||
+ `00`: key preimage is not avaliable
|
||||
|
||||
The key calculation for this node is:
|
||||
|
||||
```
|
||||
|
||||
arr = [<value field 0>, <value field 1>, <value field 2>, <value field 3>]
|
||||
|
||||
hash_pre = poseidon(<first 16 byte for value field 2>, <last 16 byte for value field 2>)
|
||||
|
||||
arr[2] = hash_pre
|
||||
|
||||
layer1 = [poseidon(arr[0], arr[1]), poseidon(arr[2], arr[3])]
|
||||
|
||||
key = poseidon(layer1[0], layer1[1])
|
||||
|
||||
```
|
||||
|
||||
Notice all field and compressed field are represented as **big endian** integer.
|
||||
|
||||
### A middle node in account trie:
|
||||
|
||||
> 0x00000000000000000000000000000000000000000000000000000000000000000004470b58d80eeb26da85b2c2db5c254900656fb459c07729f556ff02534ab32a
|
||||
|
||||
Notice the left child of this node is an empty node (so its key is field(0))
|
||||
|
|
@ -79,15 +79,13 @@ func NewNodeFromBytes(b []byte) (*Node, error) {
|
|||
if len(b) != 2*zkt.ElemBytesLen {
|
||||
return nil, ErrNodeBytesBadSize
|
||||
}
|
||||
n.ChildL, n.ChildR = &zkt.Hash{}, &zkt.Hash{}
|
||||
copy(n.ChildL[:], b[:zkt.ElemBytesLen])
|
||||
copy(n.ChildR[:], b[zkt.ElemBytesLen:zkt.ElemBytesLen*2])
|
||||
n.ChildL, _ = zkt.NewHashFromBytes(b[:zkt.ElemBytesLen])
|
||||
n.ChildR, _ = zkt.NewHashFromBytes(b[zkt.ElemBytesLen : zkt.ElemBytesLen*2])
|
||||
case NodeTypeLeaf:
|
||||
if len(b) < zkt.ElemBytesLen+4 {
|
||||
return nil, ErrNodeBytesBadSize
|
||||
}
|
||||
n.NodeKey = &zkt.Hash{}
|
||||
copy(n.NodeKey[:], b[0:32])
|
||||
n.NodeKey, _ = zkt.NewHashFromBytes(b[0:32])
|
||||
mark := binary.LittleEndian.Uint32(b[32:36])
|
||||
preimageLen := int(mark & 255)
|
||||
n.CompressedFlags = mark >> 8
|
||||
|
|
@ -181,12 +179,12 @@ func (n *Node) Value() []byte {
|
|||
switch n.Type {
|
||||
case NodeTypeMiddle: // {Type || ChildL || ChildR}
|
||||
bytes := []byte{byte(n.Type)}
|
||||
bytes = append(bytes, n.ChildL[:]...)
|
||||
bytes = append(bytes, n.ChildR[:]...)
|
||||
bytes = append(bytes, n.ChildL.Bytes()...)
|
||||
bytes = append(bytes, n.ChildR.Bytes()...)
|
||||
return bytes
|
||||
case NodeTypeLeaf: // {Type || Data...}
|
||||
bytes := []byte{byte(n.Type)}
|
||||
bytes = append(bytes, n.NodeKey[:]...)
|
||||
bytes = append(bytes, n.NodeKey.Bytes()...)
|
||||
tmp := make([]byte, 4)
|
||||
compressedFlag := (n.CompressedFlags << 8) + uint32(len(n.ValuePreimage))
|
||||
binary.LittleEndian.PutUint32(tmp, compressedFlag)
|
||||
|
|
|
|||
Loading…
Reference in a new issue