diff --git a/core/types/zktrie/README.md b/core/types/zktrie/README.md new file mode 100644 index 000000000000..33cb1aa09543 --- /dev/null +++ b/core/types/zktrie/README.md @@ -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 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, * 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(, ) +``` + +For leaf node: + +``` +key = poseidon(
, )
+
+pre key = poseidon(field(1), )
+
+value hash = poseidon(, ) | poseidon(, )
+
+leaf element =  | poseidon(, ) | 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(, )
+
+```
+
+## 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 = [, , , ]
+
+hash_pre = poseidon(, )
+
+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))
\ No newline at end of file
diff --git a/trie/zk_trie_node.go b/trie/zk_trie_node.go
index 487f922378b9..f94507cf6aac 100644
--- a/trie/zk_trie_node.go
+++ b/trie/zk_trie_node.go
@@ -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)