core/merkle/merkle_test.go
package merkle_test
import (
"fmt"
"testing"
"github.com/brianvoe/gofakeit/v6"
. "github.com/stretchr/testify/assert"
"github.com/synapsecns/sanguine/core/merkle"
)
const (
leafsAmount uint32 = 100
)
func TestMerkleTree(t *testing.T) {
tree := merkle.NewTree(merkle.MessageTreeHeight)
// Generate test leafs
leafs := make([][]byte, leafsAmount)
for i := uint32(0); i < leafsAmount; i++ {
leafs[i] = fakeLeaf()
fmt.Printf("%d: %v\n", i, leafs[i])
}
// Insert test leafs
for i := uint32(0); i < leafsAmount; i++ {
tree.Insert(leafs[i])
Equal(t, tree.NumOfItems(), i+1)
}
// Check Items()
items := tree.Items()
Equal(t, len(items), int(leafsAmount))
for i := uint32(0); i < leafsAmount; i++ {
Equal(t, items[i], leafs[i])
}
// Check Item()
for i := uint32(0); i < leafsAmount; i++ {
item, err := tree.Item(i)
Nil(t, err)
Equal(t, item, leafs[i])
}
// Check proofs
for count := uint32(1); count <= leafsAmount; count++ {
// Get root after `count` leafs were inserted
root, err := tree.Root(count)
Nil(t, err)
// Root should be a bytes32 value.
Equal(t, len(root), 32)
for index := uint32(0); index < count; index++ {
proof, err := tree.MerkleProof(index, count)
Nil(t, err)
// Proof should have length equal to `TreeDepth`.
Equal(t, len(proof), int(merkle.MessageTreeHeight))
for i := 0; i < len(proof); i++ {
// Each element should be a bytes32 value.
Equal(t, len(proof[i]), 32)
}
branchRoot, err := merkle.BranchRoot(leafs[index], index, proof, merkle.MessageTreeHeight)
Nil(t, err)
// Should match the correct bytes32 value for the root.
Equal(t, root, branchRoot)
True(t, merkle.VerifyMerkleProof(root, leafs[index], index, proof, merkle.MessageTreeHeight))
}
}
}
func TestIncorrectRequests(t *testing.T) {
tree := merkle.NewTree(merkle.MessageTreeHeight)
// Generate test leafs.
leafs := make([][]byte, leafsAmount)
for i := uint32(0); i < leafsAmount; i++ {
leafs[i] = fakeLeaf()
}
// Insert test leafs.
for i := uint32(0); i < leafsAmount; i++ {
tree.Insert(leafs[i])
}
// Check Item() with index out of bound.
item, err := tree.Item(leafsAmount)
Nil(t, item)
NotNil(t, err)
// Check Root() with count out of bound.
root, err := tree.Root(leafsAmount + 1)
Nil(t, root)
NotNil(t, err)
// Check MerkleProof() with count out of bound.
proof, err := tree.MerkleProof(0, leafsAmount+1)
Nil(t, proof)
NotNil(t, err)
// Check MerkleProof() with index >= count.
proof, err = tree.MerkleProof(10, 10)
Nil(t, proof)
NotNil(t, err)
// Generate a valid proof.
proof, err = tree.MerkleProof(0, leafsAmount)
NotNil(t, proof)
Nil(t, err)
// Check BranchRoot() with incorrect proof length.
proof = make([][]byte, merkle.MessageTreeHeight+1)
branchRoot, err := merkle.BranchRoot(leafs[0], leafsAmount, proof, merkle.MessageTreeHeight)
Nil(t, branchRoot)
NotNil(t, err)
// Get current and historical root.
root, err = tree.Root(leafsAmount)
NotNil(t, root)
Nil(t, err)
histRoot, err := tree.Root(leafsAmount - 1)
NotNil(t, histRoot)
Nil(t, err)
// Check VerifyMerkleProof() with incorrect proof length.
False(t, merkle.VerifyMerkleProof(root, leafs[0], 0, proof, merkle.MessageTreeHeight))
// Generate proofs against current root, they should not work with the historical root.
for i := uint32(0); i < leafsAmount; i++ {
proof, err = tree.MerkleProof(i, leafsAmount)
NotNil(t, proof)
Nil(t, err)
// Should not be able to prove against the historical root.
False(t, merkle.VerifyMerkleProof(histRoot, leafs[i], i, proof, merkle.MessageTreeHeight))
// Should be able to prove against the current root.
True(t, merkle.VerifyMerkleProof(root, leafs[i], i, proof, merkle.MessageTreeHeight))
}
}
func TestNewTreeFromItems(t *testing.T) {
tree := merkle.NewTree(merkle.MessageTreeHeight)
// Generate test leafs
leafs := make([][]byte, leafsAmount)
for i := uint32(0); i < leafsAmount; i++ {
leafs[i] = fakeLeaf()
fmt.Printf("%d: %v\n", i, leafs[i])
}
// Insert test leafs
for i := uint32(0); i < leafsAmount; i++ {
tree.Insert(leafs[i])
Equal(t, tree.NumOfItems(), i+1)
}
// Get items and generate a new tree from them
items := tree.Items()
newTree := merkle.NewTreeFromItems(items, merkle.MessageTreeHeight)
// Check that the number of items are the same
Equal(t, tree.NumOfItems(), newTree.NumOfItems())
// Check that the new tree has the same root
root, err := tree.Root(leafsAmount)
Nil(t, err)
newRoot, err := newTree.Root(leafsAmount)
Nil(t, err)
Equal(t, root, newRoot)
}
func fakeLeaf() []byte {
leaf := make([]byte, 32)
for i := 0; i < 32; i++ {
leaf[i] = gofakeit.Uint8()
}
return leaf
}