Skip to content

Commit

Permalink
L1InfoTree implementation + tests (0xPolygonHermez#2786)
Browse files Browse the repository at this point in the history
* implementation + tests

* linter

* Public HashLeafData
  • Loading branch information
ARR552 authored Nov 16, 2023
1 parent ded0fb4 commit 26713cf
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 0 deletions.
43 changes: 43 additions & 0 deletions l1infotree/hash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package l1infotree

import (
"encoding/binary"

"github.com/0xPolygonHermez/zkevm-node/log"
"github.com/ethereum/go-ethereum/common"
"github.com/iden3/go-iden3-crypto/keccak256"
"golang.org/x/crypto/sha3"
)

// Hash calculates the keccak hash of elements.
func Hash(data ...[32]byte) [32]byte {
var res [32]byte
hash := sha3.NewLegacyKeccak256()
for _, d := range data {
hash.Write(d[:]) //nolint:errcheck,gosec
}
copy(res[:], hash.Sum(nil))
return res
}

func generateZeroHashes(height uint8) [][32]byte {
var zeroHashes = [][32]byte{
common.Hash{},
}
// This generates a leaf = HashZero in position 0. In the rest of the positions that are equivalent to the ascending levels,
// we set the hashes of the nodes. So all nodes from level i=5 will have the same value and same children nodes.
for i := 1; i <= int(height); i++ {
zeroHashes = append(zeroHashes, Hash(zeroHashes[i-1], zeroHashes[i-1]))
}
return zeroHashes
}

// HashLeafData calculates the keccak hash of the leaf values.
func HashLeafData(ger, prevBlockHash common.Hash, minTimestamp uint64) [32]byte {
var res [32]byte
t := make([]byte, 8) //nolint:gomnd
binary.BigEndian.PutUint64(t, minTimestamp)
log.Debug(ger.Bytes(), prevBlockHash.Bytes(), t)
copy(res[:], keccak256.Hash(ger.Bytes(), prevBlockHash.Bytes(), t))
return res
}
20 changes: 20 additions & 0 deletions l1infotree/hash_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package l1infotree

import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
)

func TestHashLeaf(t *testing.T) {
expectedLeafHash := common.HexToHash("0xf62f487534b899b1c362242616725878188ca891fab60854b792ca0628286de7")

prevBlockHash := common.HexToHash("0x24a5871d68723340d9eadc674aa8ad75f3e33b61d5a9db7db92af856a19270bb")
var minTimestamp uint64 = 1697231573
ger := common.HexToHash("0x16994edfddddb9480667b64174fc00d3b6da7290d37b8db3a16571b4ddf0789f")

leaf := HashLeafData(ger, prevBlockHash, minTimestamp)

assert.Equal(t, expectedLeafHash, common.BytesToHash(leaf[:]))
}
59 changes: 59 additions & 0 deletions l1infotree/tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package l1infotree

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
)

// L1InfoTree provides methods to compute L1InfoTree
type L1InfoTree struct {
height uint8
zeroHashes [][32]byte
}

// NewL1InfoTree creates new L1InfoTree.
func NewL1InfoTree(height uint8) *L1InfoTree {
return &L1InfoTree{
zeroHashes: generateZeroHashes(height),
height: height,
}
}

func buildIntermediate(leaves [][32]byte) ([][][]byte, [][32]byte) {
var (
nodes [][][]byte
hashes [][32]byte
)
for i := 0; i < len(leaves); i += 2 {
var left, right int = i, i + 1
hash := Hash(leaves[left], leaves[right])
nodes = append(nodes, [][]byte{hash[:], leaves[left][:], leaves[right][:]})
hashes = append(hashes, hash)
}
return nodes, hashes
}

// BuildL1InfoRoot computes the root given the leaves of the tree
func (mt *L1InfoTree) BuildL1InfoRoot(leaves [][32]byte) (common.Hash, error) {
var (
nodes [][][][]byte
ns [][][]byte
)
if len(leaves) == 0 {
leaves = append(leaves, mt.zeroHashes[0])
}

for h := uint8(0); h < mt.height; h++ {
if len(leaves)%2 == 1 {
leaves = append(leaves, mt.zeroHashes[h])
}
ns, leaves = buildIntermediate(leaves)
nodes = append(nodes, ns)
}
if len(ns) != 1 {
return common.Hash{}, fmt.Errorf("error: more than one root detected: %+v", nodes)
}

return common.BytesToHash(ns[0][0]), nil
}
39 changes: 39 additions & 0 deletions l1infotree/tree_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package l1infotree

import (
"encoding/json"
"os"
"testing"

"github.com/0xPolygonHermez/zkevm-node/test/vectors"
"github.com/stretchr/testify/require"
)

func TestComputeTreeRoot(t *testing.T) {
data, err := os.ReadFile("../test/vectors/src/merkle-tree/l1-info-tree/root-vectors.json")
require.NoError(t, err)
var mtTestVectors []vectors.L1InfoTree
err = json.Unmarshal(data, &mtTestVectors)
require.NoError(t, err)
for _, testVector := range mtTestVectors {
input := testVector.PreviousLeafValues
mt := NewL1InfoTree(uint8(32))
require.NoError(t, err)

var leaves [][32]byte
for _, v := range input {
leaves = append(leaves, v)
}

if len(leaves) != 0 {
root, err := mt.BuildL1InfoRoot(leaves)
require.NoError(t, err)
require.Equal(t, testVector.CurrentRoot, root)
}

leaves = append(leaves, testVector.NewLeafValue)
newRoot, err := mt.BuildL1InfoRoot(leaves)
require.NoError(t, err)
require.Equal(t, testVector.NewRoot, newRoot)
}
}
13 changes: 13 additions & 0 deletions test/vectors/l1infotree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package vectors

import (
"github.com/ethereum/go-ethereum/common"
)

// L1InfoTree holds the test vector for the merkle tree
type L1InfoTree struct {
PreviousLeafValues []common.Hash `json:"previousLeafValues"`
CurrentRoot common.Hash `json:"currentRoot"`
NewLeafValue common.Hash `json:"newLeafValue"`
NewRoot common.Hash `json:"newRoot"`
}
35 changes: 35 additions & 0 deletions test/vectors/src/merkle-tree/l1-info-tree/root-vectors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[
{
"previousLeafValues": [],
"currentRoot": "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757",
"newLeafValue": "0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5",
"newRoot": "0xbf7ddbb59aa018a4c74e061f5172973ff09e4cb7f58405af117fc521f1ca46aa"
},
{
"previousLeafValues": [
"0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5"
],
"currentRoot": "0xbf7ddbb59aa018a4c74e061f5172973ff09e4cb7f58405af117fc521f1ca46aa",
"newLeafValue": "0x315fee1aa202bf4a6bd0fde560c89be90b6e6e2aaf92dc5e8d118209abc3410f",
"newRoot": "0xa7042a3ce14f384bbff63f1cee6ee5579193c2d7002e0034854963322cda6128"
},
{
"previousLeafValues": [
"0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5",
"0x315fee1aa202bf4a6bd0fde560c89be90b6e6e2aaf92dc5e8d118209abc3410f"
],
"currentRoot": "0xa7042a3ce14f384bbff63f1cee6ee5579193c2d7002e0034854963322cda6128",
"newLeafValue": "0xb598ce65aa15c08dda126a2985ba54f0559eaac562bb43ba430c7344261fbc5d",
"newRoot": "0x88e652896cb1de5962a0173a222059f51e6b943a2ba6dfc9acbff051ceb1abb5"
},
{
"previousLeafValues": [
"0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5",
"0x315fee1aa202bf4a6bd0fde560c89be90b6e6e2aaf92dc5e8d118209abc3410f",
"0xb598ce65aa15c08dda126a2985ba54f0559eaac562bb43ba430c7344261fbc5d"
],
"currentRoot": "0x88e652896cb1de5962a0173a222059f51e6b943a2ba6dfc9acbff051ceb1abb5",
"newLeafValue": "0xe6585bdf74b6a46b9ede8b1b877e1232fb79ee93106c4db8ffd49cf1685bf242",
"newRoot": "0x42d3339fe8eb57770953423f20a029e778a707e8d58aaf110b40d5eb4dd25721"
}
]

0 comments on commit 26713cf

Please sign in to comment.