mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 23:18:15 -05:00
fuzz: Add fuzz tests for sparse merkle trie (#10662)
* Add fuzz tests for sparse merkle trie and change HTR signature to return an error * fix capitalization of error message
This commit is contained in:
@@ -19,7 +19,11 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["sparse_merkle_test.go"],
|
||||
srcs = [
|
||||
"sparse_merkle_test.go",
|
||||
"sparse_merkle_trie_fuzz_test.go",
|
||||
],
|
||||
data = glob(["testdata/**"]),
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
@@ -31,5 +35,6 @@ go_test(
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library",
|
||||
"@com_github_golang_protobuf//proto:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -81,7 +81,11 @@ func (m *SparseMerkleTrie) Items() [][]byte {
|
||||
// HashTreeRoot of the Merkle trie as defined in the deposit contract.
|
||||
// Spec Definition:
|
||||
// sha256(concat(node, self.to_little_endian_64(self.deposit_count), slice(zero_bytes32, start=0, len=24)))
|
||||
func (m *SparseMerkleTrie) HashTreeRoot() [32]byte {
|
||||
func (m *SparseMerkleTrie) HashTreeRoot() ([32]byte, error) {
|
||||
if len(m.branches) == 0 || len(m.branches[len(m.branches)-1]) == 0 {
|
||||
return [32]byte{}, errors.New("invalid branches provided to compute root")
|
||||
}
|
||||
|
||||
enc := [32]byte{}
|
||||
depositCount := uint64(len(m.originalItems))
|
||||
if len(m.originalItems) == 1 && bytes.Equal(m.originalItems[0], ZeroHashes[0][:]) {
|
||||
@@ -89,7 +93,7 @@ func (m *SparseMerkleTrie) HashTreeRoot() [32]byte {
|
||||
depositCount = 0
|
||||
}
|
||||
binary.LittleEndian.PutUint64(enc[:], depositCount)
|
||||
return hash.Hash(append(m.branches[len(m.branches)-1][0], enc[:]...))
|
||||
return hash.Hash(append(m.branches[len(m.branches)-1][0], enc[:]...)), nil
|
||||
}
|
||||
|
||||
// Insert an item into the trie.
|
||||
@@ -97,6 +101,12 @@ func (m *SparseMerkleTrie) Insert(item []byte, index int) error {
|
||||
if index < 0 {
|
||||
return fmt.Errorf("negative index provided: %d", index)
|
||||
}
|
||||
if len(m.branches) == 0 {
|
||||
return errors.New("invalid trie: no branches")
|
||||
}
|
||||
if m.depth > uint(len(m.branches)) {
|
||||
return errors.New("invalid trie: depth is greater than number of branches")
|
||||
}
|
||||
for index >= len(m.branches[0]) {
|
||||
m.branches[0] = append(m.branches[0], ZeroHashes[0][:])
|
||||
}
|
||||
@@ -143,11 +153,17 @@ func (m *SparseMerkleTrie) MerkleProof(index int) ([][]byte, error) {
|
||||
if index < 0 {
|
||||
return nil, fmt.Errorf("merkle index is negative: %d", index)
|
||||
}
|
||||
merkleIndex := uint(index)
|
||||
if len(m.branches) == 0 {
|
||||
return nil, errors.New("invalid trie: no branches")
|
||||
}
|
||||
leaves := m.branches[0]
|
||||
if index >= len(leaves) {
|
||||
return nil, fmt.Errorf("merkle index out of range in trie, max range: %d, received: %d", len(leaves), index)
|
||||
}
|
||||
if m.depth > uint(len(m.branches)) {
|
||||
return nil, errors.New("invalid trie: depth is greater than number of branches")
|
||||
}
|
||||
merkleIndex := uint(index)
|
||||
proof := make([][]byte, m.depth+1)
|
||||
for i := uint(0); i < m.depth; i++ {
|
||||
subIndex := (merkleIndex / (1 << i)) ^ 1
|
||||
@@ -185,6 +201,9 @@ func VerifyMerkleProofWithDepth(root, item []byte, merkleIndex uint64, proof [][
|
||||
if uint64(len(proof)) != depth+1 {
|
||||
return false
|
||||
}
|
||||
if depth >= 64 {
|
||||
return false // PowerOf2 would overflow.
|
||||
}
|
||||
node := bytesutil.ToBytes32(item)
|
||||
for i := uint64(0); i <= depth; i++ {
|
||||
if (merkleIndex / math.PowerOf2(i) % 2) != 0 {
|
||||
@@ -200,15 +219,10 @@ func VerifyMerkleProofWithDepth(root, item []byte, merkleIndex uint64, proof [][
|
||||
// VerifyMerkleProof given a trie root, a leaf, the generalized merkle index
|
||||
// of the leaf in the trie, and the proof itself.
|
||||
func VerifyMerkleProof(root, item []byte, merkleIndex uint64, proof [][]byte) bool {
|
||||
node := bytesutil.ToBytes32(item)
|
||||
for i := 0; i < len(proof); i++ {
|
||||
if (merkleIndex / math.PowerOf2(uint64(i)) % 2) != 0 {
|
||||
node = hash.Hash(append(proof[i], node[:]...))
|
||||
} else {
|
||||
node = hash.Hash(append(node[:], proof[i]...))
|
||||
}
|
||||
if len(proof) == 0 {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(root, node[:])
|
||||
return VerifyMerkleProofWithDepth(root, item, merkleIndex, proof, uint64(len(proof)-1))
|
||||
}
|
||||
|
||||
// Copy performs a deep copy of the trie.
|
||||
|
||||
@@ -79,7 +79,9 @@ func TestMerkleTrieRoot_EmptyTrie(t *testing.T) {
|
||||
|
||||
depRoot, err := testAccount.Contract.GetDepositRoot(&bind.CallOpts{})
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, depRoot, trie.HashTreeRoot())
|
||||
root, err := trie.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, depRoot, root)
|
||||
}
|
||||
|
||||
func TestGenerateTrieFromItems_NoItemsProvided(t *testing.T) {
|
||||
@@ -104,7 +106,8 @@ func TestMerkleTrie_VerifyMerkleProofWithDepth(t *testing.T) {
|
||||
proof, err := m.MerkleProof(0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int(params.BeaconConfig().DepositContractTreeDepth)+1, len(proof))
|
||||
root := m.HashTreeRoot()
|
||||
root, err := m.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
if ok := trie.VerifyMerkleProofWithDepth(root[:], items[0], 0, proof, params.BeaconConfig().DepositContractTreeDepth); !ok {
|
||||
t.Error("First Merkle proof did not verify")
|
||||
}
|
||||
@@ -130,7 +133,8 @@ func TestMerkleTrie_VerifyMerkleProof(t *testing.T) {
|
||||
proof, err := m.MerkleProof(0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int(params.BeaconConfig().DepositContractTreeDepth)+1, len(proof))
|
||||
root := m.HashTreeRoot()
|
||||
root, err := m.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
if ok := trie.VerifyMerkleProof(root[:], items[0], 0, proof); !ok {
|
||||
t.Error("First Merkle proof did not verify")
|
||||
}
|
||||
@@ -170,14 +174,16 @@ func TestMerkleTrie_VerifyMerkleProof_TrieUpdated(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
proof, err := m.MerkleProof(0)
|
||||
require.NoError(t, err)
|
||||
root := m.HashTreeRoot()
|
||||
root, err := m.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, trie.VerifyMerkleProofWithDepth(root[:], items[0], 0, proof, depth))
|
||||
|
||||
// Now we update the trie.
|
||||
assert.NoError(t, m.Insert([]byte{5}, 3))
|
||||
proof, err = m.MerkleProof(3)
|
||||
require.NoError(t, err)
|
||||
root = m.HashTreeRoot()
|
||||
root, err = m.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
if ok := trie.VerifyMerkleProofWithDepth(root[:], []byte{5}, 3, proof, depth); !ok {
|
||||
t.Error("Second Merkle proof did not verify")
|
||||
}
|
||||
@@ -200,10 +206,13 @@ func TestRoundtripProto_OK(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
protoTrie := m.ToProto()
|
||||
depositRoot := m.HashTreeRoot()
|
||||
depositRoot, err := m.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
newTrie := trie.CreateTrieFromProto(protoTrie)
|
||||
require.DeepEqual(t, depositRoot, newTrie.HashTreeRoot())
|
||||
root, err := newTrie.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, depositRoot, root)
|
||||
|
||||
}
|
||||
|
||||
@@ -221,8 +230,11 @@ func TestCopy_OK(t *testing.T) {
|
||||
if copiedTrie == source {
|
||||
t.Errorf("Original trie returned.")
|
||||
}
|
||||
copyHash := copiedTrie.HashTreeRoot()
|
||||
require.DeepEqual(t, copyHash, copiedTrie.HashTreeRoot())
|
||||
a, err := copiedTrie.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
b, err := source.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, a, b)
|
||||
}
|
||||
|
||||
func BenchmarkGenerateTrieFromItems(b *testing.B) {
|
||||
@@ -296,7 +308,8 @@ func BenchmarkVerifyMerkleProofWithDepth(b *testing.B) {
|
||||
proof, err := m.MerkleProof(2)
|
||||
require.NoError(b, err)
|
||||
|
||||
root := m.HashTreeRoot()
|
||||
root, err := m.HashTreeRoot()
|
||||
require.NoError(b, err)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if ok := trie.VerifyMerkleProofWithDepth(root[:], items[2], 2, proof, params.BeaconConfig().DepositContractTreeDepth); !ok {
|
||||
|
||||
148
container/trie/sparse_merkle_trie_fuzz_test.go
Normal file
148
container/trie/sparse_merkle_trie_fuzz_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package trie_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/container/trie"
|
||||
"github.com/prysmaticlabs/prysm/crypto/hash"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func FuzzSparseMerkleTrie_HashTreeRoot(f *testing.F) {
|
||||
h := hash.Hash([]byte("hi"))
|
||||
pb := ðpb.SparseMerkleTrie{
|
||||
Layers: []*ethpb.TrieLayer{
|
||||
{
|
||||
Layer: [][]byte{h[:]},
|
||||
},
|
||||
{
|
||||
Layer: [][]byte{h[:]},
|
||||
},
|
||||
{
|
||||
Layer: [][]byte{},
|
||||
},
|
||||
},
|
||||
Depth: 4,
|
||||
}
|
||||
b, err := proto.Marshal(pb)
|
||||
require.NoError(f, err)
|
||||
f.Add(b)
|
||||
|
||||
f.Fuzz(func(t *testing.T, b []byte) {
|
||||
pb := ðpb.SparseMerkleTrie{}
|
||||
if err := proto.Unmarshal(b, pb); err != nil {
|
||||
return
|
||||
}
|
||||
_, err := trie.CreateTrieFromProto(pb).HashTreeRoot()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzSparseMerkleTrie_MerkleProof(f *testing.F) {
|
||||
h := hash.Hash([]byte("hi"))
|
||||
pb := ðpb.SparseMerkleTrie{
|
||||
Layers: []*ethpb.TrieLayer{
|
||||
{
|
||||
Layer: [][]byte{h[:]},
|
||||
},
|
||||
{
|
||||
Layer: [][]byte{h[:]},
|
||||
},
|
||||
{
|
||||
Layer: [][]byte{},
|
||||
},
|
||||
},
|
||||
Depth: 4,
|
||||
}
|
||||
b, err := proto.Marshal(pb)
|
||||
require.NoError(f, err)
|
||||
f.Add(b, 0)
|
||||
|
||||
f.Fuzz(func(t *testing.T, b []byte, i int) {
|
||||
pb := ðpb.SparseMerkleTrie{}
|
||||
if err := proto.Unmarshal(b, pb); err != nil {
|
||||
return
|
||||
}
|
||||
_, err := trie.CreateTrieFromProto(pb).MerkleProof(i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzSparseMerkleTrie_Insert(f *testing.F) {
|
||||
h := hash.Hash([]byte("hi"))
|
||||
pb := ðpb.SparseMerkleTrie{
|
||||
Layers: []*ethpb.TrieLayer{
|
||||
{
|
||||
Layer: [][]byte{h[:]},
|
||||
},
|
||||
{
|
||||
Layer: [][]byte{h[:]},
|
||||
},
|
||||
{
|
||||
Layer: [][]byte{},
|
||||
},
|
||||
},
|
||||
Depth: 4,
|
||||
}
|
||||
b, err := proto.Marshal(pb)
|
||||
require.NoError(f, err)
|
||||
f.Add(b, []byte{}, 0)
|
||||
|
||||
f.Fuzz(func(t *testing.T, b, item []byte, i int) {
|
||||
pb := ðpb.SparseMerkleTrie{}
|
||||
if err := proto.Unmarshal(b, pb); err != nil {
|
||||
return
|
||||
}
|
||||
if err := trie.CreateTrieFromProto(pb).Insert(item, i); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzSparseMerkleTrie_VerifyMerkleProofWithDepth(f *testing.F) {
|
||||
splitProofs := func(proofRaw []byte) [][]byte {
|
||||
var proofs [][]byte
|
||||
for i := 0; i < len(proofRaw); i += 32 {
|
||||
end := i + 32
|
||||
if end >= len(proofRaw) {
|
||||
end = len(proofRaw) - 1
|
||||
}
|
||||
proofs = append(proofs, proofRaw[i:end])
|
||||
}
|
||||
return proofs
|
||||
}
|
||||
|
||||
items := [][]byte{
|
||||
[]byte("A"),
|
||||
[]byte("B"),
|
||||
[]byte("C"),
|
||||
[]byte("D"),
|
||||
[]byte("E"),
|
||||
[]byte("F"),
|
||||
[]byte("G"),
|
||||
[]byte("H"),
|
||||
}
|
||||
m, err := trie.GenerateTrieFromItems(items, params.BeaconConfig().DepositContractTreeDepth)
|
||||
require.NoError(f, err)
|
||||
proof, err := m.MerkleProof(0)
|
||||
require.NoError(f, err)
|
||||
require.Equal(f, int(params.BeaconConfig().DepositContractTreeDepth)+1, len(proof))
|
||||
root, err := m.HashTreeRoot()
|
||||
require.NoError(f, err)
|
||||
var proofRaw []byte
|
||||
for _, p := range proof {
|
||||
proofRaw = append(proofRaw, p...)
|
||||
}
|
||||
f.Add(root[:], items[0], uint64(0), proofRaw, params.BeaconConfig().DepositContractTreeDepth)
|
||||
|
||||
f.Fuzz(func(t *testing.T, root, item []byte, merkleIndex uint64, proofRaw []byte, depth uint64) {
|
||||
trie.VerifyMerkleProofWithDepth(root, item, merkleIndex, splitProofs(proofRaw), depth)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("")
|
||||
@@ -0,0 +1,4 @@
|
||||
go test fuzz v1
|
||||
[]byte("")
|
||||
[]byte("0")
|
||||
int(41)
|
||||
@@ -0,0 +1,3 @@
|
||||
go test fuzz v1
|
||||
[]byte("")
|
||||
int(63)
|
||||
@@ -0,0 +1,6 @@
|
||||
go test fuzz v1
|
||||
[]byte("0")
|
||||
[]byte("000000000")
|
||||
uint64(0)
|
||||
[]byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))00000000000000000000000000000000000000000000000000000000000000")
|
||||
uint64(71)
|
||||
Reference in New Issue
Block a user