forked from 0xPolygonHermez/zkevm-node
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
L1InfoTree implementation + tests (0xPolygonHermez#2786)
* implementation + tests * linter * Public HashLeafData
- Loading branch information
Showing
6 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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[:])) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
35
test/vectors/src/merkle-tree/l1-info-tree/root-vectors.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
] |