mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-25 04:47:18 +00:00
412 lines
11 KiB
Go
412 lines
11 KiB
Go
package trie_test
|
|
|
|
import (
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
|
|
"github.com/prysmaticlabs/prysm/v3/config/params"
|
|
"github.com/prysmaticlabs/prysm/v3/container/trie"
|
|
contracts "github.com/prysmaticlabs/prysm/v3/contracts/deposit/mock"
|
|
"github.com/prysmaticlabs/prysm/v3/crypto/hash"
|
|
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
|
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
|
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
|
)
|
|
|
|
func TestCreateTrieFromProto_Validation(t *testing.T) {
|
|
h := hash.Hash([]byte("hi"))
|
|
genValidLayers := func(num int) []*ethpb.TrieLayer {
|
|
l := make([]*ethpb.TrieLayer, num)
|
|
for i := 0; i < num; i++ {
|
|
l[i] = ðpb.TrieLayer{
|
|
Layer: [][]byte{h[:]},
|
|
}
|
|
}
|
|
return l
|
|
}
|
|
tests := []struct {
|
|
trie *ethpb.SparseMerkleTrie
|
|
errString string
|
|
}{
|
|
{
|
|
trie: ðpb.SparseMerkleTrie{
|
|
Layers: []*ethpb.TrieLayer{},
|
|
Depth: 0,
|
|
},
|
|
errString: "no branches",
|
|
},
|
|
{
|
|
trie: ðpb.SparseMerkleTrie{
|
|
Layers: []*ethpb.TrieLayer{
|
|
{
|
|
Layer: [][]byte{h[:]},
|
|
},
|
|
{
|
|
Layer: [][]byte{h[:]},
|
|
},
|
|
{
|
|
Layer: [][]byte{},
|
|
},
|
|
},
|
|
Depth: 2,
|
|
},
|
|
errString: "invalid branches provided",
|
|
},
|
|
{
|
|
trie: ðpb.SparseMerkleTrie{
|
|
Layers: genValidLayers(3),
|
|
Depth: 12,
|
|
},
|
|
errString: "depth is greater than or equal to number of branches",
|
|
},
|
|
{
|
|
trie: ðpb.SparseMerkleTrie{
|
|
Layers: genValidLayers(66),
|
|
Depth: 63,
|
|
},
|
|
errString: "supported merkle trie depth exceeded (max uint64 depth is 63, " +
|
|
"theoretical max sparse merkle trie depth is 64)",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.errString, func(t *testing.T) {
|
|
_, err := trie.CreateTrieFromProto(tt.trie)
|
|
require.ErrorContains(t, tt.errString, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalDepositWithProof(t *testing.T) {
|
|
items := [][]byte{
|
|
[]byte("A"),
|
|
[]byte("BB"),
|
|
[]byte("CCC"),
|
|
[]byte("DDDD"),
|
|
[]byte("EEEEE"),
|
|
[]byte("FFFFFF"),
|
|
[]byte("GGGGGGG"),
|
|
}
|
|
m, err := trie.GenerateTrieFromItems(items, params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err)
|
|
proof, err := m.MerkleProof(2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(proof), int(params.BeaconConfig().DepositContractTreeDepth)+1)
|
|
someRoot := [32]byte{1, 2, 3, 4}
|
|
someSig := [96]byte{1, 2, 3, 4}
|
|
someKey := [fieldparams.BLSPubkeyLength]byte{1, 2, 3, 4}
|
|
dep := ðpb.Deposit{
|
|
Proof: proof,
|
|
Data: ðpb.Deposit_Data{
|
|
PublicKey: someKey[:],
|
|
WithdrawalCredentials: someRoot[:],
|
|
Amount: 32,
|
|
Signature: someSig[:],
|
|
},
|
|
}
|
|
enc, err := dep.MarshalSSZ()
|
|
require.NoError(t, err)
|
|
dec := ðpb.Deposit{}
|
|
require.NoError(t, dec.UnmarshalSSZ(enc))
|
|
require.DeepEqual(t, dec, dep)
|
|
}
|
|
|
|
func TestMerkleTrie_MerkleProofOutOfRange(t *testing.T) {
|
|
h := hash.Hash([]byte("hi"))
|
|
m, err := trie.CreateTrieFromProto(ðpb.SparseMerkleTrie{
|
|
Layers: []*ethpb.TrieLayer{
|
|
{
|
|
Layer: [][]byte{h[:]},
|
|
},
|
|
{
|
|
Layer: [][]byte{h[:]},
|
|
},
|
|
{
|
|
Layer: [][]byte{h[:]},
|
|
},
|
|
},
|
|
Depth: 2,
|
|
})
|
|
require.NoError(t, err)
|
|
if _, err := m.MerkleProof(6); err == nil {
|
|
t.Error("Expected out of range failure, received nil", err)
|
|
}
|
|
}
|
|
|
|
func TestMerkleTrieRoot_EmptyTrie(t *testing.T) {
|
|
newTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err)
|
|
testAccount, err := contracts.Setup()
|
|
require.NoError(t, err)
|
|
|
|
depRoot, err := testAccount.Contract.GetDepositRoot(&bind.CallOpts{})
|
|
require.NoError(t, err)
|
|
root, err := newTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, depRoot, root)
|
|
}
|
|
|
|
func TestGenerateTrieFromItems_NoItemsProvided(t *testing.T) {
|
|
if _, err := trie.GenerateTrieFromItems(nil, params.BeaconConfig().DepositContractTreeDepth); err == nil {
|
|
t.Error("Expected error when providing nil items received nil")
|
|
}
|
|
}
|
|
|
|
func TestGenerateTrieFromItems_DepthSupport(t *testing.T) {
|
|
items := [][]byte{
|
|
[]byte("A"),
|
|
[]byte("BB"),
|
|
[]byte("CCC"),
|
|
[]byte("DDDD"),
|
|
[]byte("EEEEE"),
|
|
[]byte("FFFFFF"),
|
|
[]byte("GGGGGGG"),
|
|
}
|
|
// max supported depth is 62 (uint64 will overflow above this)
|
|
// max theoretical depth is 64
|
|
var max_supported_trie_depth uint64 = 62
|
|
// Supported depth
|
|
m1, err := trie.GenerateTrieFromItems(items, max_supported_trie_depth)
|
|
require.NoError(t, err)
|
|
proof, err := m1.MerkleProof(2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(proof), int(max_supported_trie_depth)+1)
|
|
// Unsupported depth
|
|
_, err = trie.GenerateTrieFromItems(items, max_supported_trie_depth+1)
|
|
errString := "supported merkle trie depth exceeded (max uint64 depth is 63, " +
|
|
"theoretical max sparse merkle trie depth is 64)"
|
|
require.ErrorContains(t, errString, err)
|
|
}
|
|
|
|
func TestMerkleTrie_VerifyMerkleProofWithDepth(t *testing.T) {
|
|
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(t, err)
|
|
proof, err := m.MerkleProof(0)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int(params.BeaconConfig().DepositContractTreeDepth)+1, len(proof))
|
|
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")
|
|
}
|
|
proof, err = m.MerkleProof(3)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, trie.VerifyMerkleProofWithDepth(root[:], items[3], 3, proof, params.BeaconConfig().DepositContractTreeDepth))
|
|
require.Equal(t, false, trie.VerifyMerkleProofWithDepth(root[:], []byte("buzz"), 3, proof, params.BeaconConfig().DepositContractTreeDepth))
|
|
}
|
|
|
|
func TestMerkleTrie_VerifyMerkleProof(t *testing.T) {
|
|
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(t, err)
|
|
proof, err := m.MerkleProof(0)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int(params.BeaconConfig().DepositContractTreeDepth)+1, len(proof))
|
|
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")
|
|
}
|
|
proof, err = m.MerkleProof(3)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, trie.VerifyMerkleProof(root[:], items[3], 3, proof))
|
|
require.Equal(t, false, trie.VerifyMerkleProof(root[:], []byte("buzz"), 3, proof))
|
|
}
|
|
|
|
func TestMerkleTrie_NegativeIndexes(t *testing.T) {
|
|
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(t, err)
|
|
_, err = m.MerkleProof(-1)
|
|
require.ErrorContains(t, "merkle index is negative", err)
|
|
require.ErrorContains(t, "negative index provided", m.Insert([]byte{'J'}, -1))
|
|
}
|
|
|
|
func TestMerkleTrie_VerifyMerkleProof_TrieUpdated(t *testing.T) {
|
|
items := [][]byte{
|
|
{1},
|
|
{2},
|
|
{3},
|
|
{4},
|
|
}
|
|
depth := params.BeaconConfig().DepositContractTreeDepth + 1
|
|
m, err := trie.GenerateTrieFromItems(items, depth)
|
|
require.NoError(t, err)
|
|
proof, err := m.MerkleProof(0)
|
|
require.NoError(t, err)
|
|
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, 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")
|
|
}
|
|
if ok := trie.VerifyMerkleProofWithDepth(root[:], []byte{4}, 3, proof, depth); ok {
|
|
t.Error("Old item should not verify")
|
|
}
|
|
|
|
// Now we update the trie at an index larger than the number of items.
|
|
assert.NoError(t, m.Insert([]byte{6}, 15))
|
|
}
|
|
|
|
func TestRoundtripProto_OK(t *testing.T) {
|
|
items := [][]byte{
|
|
{1},
|
|
{2},
|
|
{3},
|
|
{4},
|
|
}
|
|
m, err := trie.GenerateTrieFromItems(items, params.BeaconConfig().DepositContractTreeDepth+1)
|
|
require.NoError(t, err)
|
|
|
|
protoTrie := m.ToProto()
|
|
depositRoot, err := m.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
newTrie, err := trie.CreateTrieFromProto(protoTrie)
|
|
require.NoError(t, err)
|
|
root, err := newTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, depositRoot, root)
|
|
|
|
}
|
|
|
|
func TestCopy_OK(t *testing.T) {
|
|
items := [][]byte{
|
|
{1},
|
|
{2},
|
|
{3},
|
|
{4},
|
|
}
|
|
source, err := trie.GenerateTrieFromItems(items, params.BeaconConfig().DepositContractTreeDepth+1)
|
|
require.NoError(t, err)
|
|
copiedTrie := source.Copy()
|
|
|
|
if copiedTrie == source {
|
|
t.Errorf("Original trie returned.")
|
|
}
|
|
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) {
|
|
items := [][]byte{
|
|
[]byte("A"),
|
|
[]byte("BB"),
|
|
[]byte("CCC"),
|
|
[]byte("DDDD"),
|
|
[]byte("EEEEE"),
|
|
[]byte("FFFFFF"),
|
|
[]byte("GGGGGGG"),
|
|
}
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := trie.GenerateTrieFromItems(items, params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(b, err, "Could not generate Merkle trie from items")
|
|
}
|
|
}
|
|
|
|
func BenchmarkInsertTrie_Optimized(b *testing.B) {
|
|
b.StopTimer()
|
|
numDeposits := 16000
|
|
items := make([][]byte, numDeposits)
|
|
for i := 0; i < numDeposits; i++ {
|
|
someRoot := bytesutil.ToBytes32([]byte(strconv.Itoa(i)))
|
|
items[i] = someRoot[:]
|
|
}
|
|
tr, err := trie.GenerateTrieFromItems(items, params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(b, err)
|
|
|
|
someItem := bytesutil.ToBytes32([]byte("hello-world"))
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
require.NoError(b, tr.Insert(someItem[:], i%numDeposits))
|
|
}
|
|
}
|
|
|
|
func BenchmarkGenerateProof(b *testing.B) {
|
|
b.StopTimer()
|
|
items := [][]byte{
|
|
[]byte("A"),
|
|
[]byte("BB"),
|
|
[]byte("CCC"),
|
|
[]byte("DDDD"),
|
|
[]byte("EEEEE"),
|
|
[]byte("FFFFFF"),
|
|
[]byte("GGGGGGG"),
|
|
}
|
|
normalTrie, err := trie.GenerateTrieFromItems(items, params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(b, err)
|
|
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := normalTrie.MerkleProof(3)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
func BenchmarkVerifyMerkleProofWithDepth(b *testing.B) {
|
|
b.StopTimer()
|
|
items := [][]byte{
|
|
[]byte("A"),
|
|
[]byte("BB"),
|
|
[]byte("CCC"),
|
|
[]byte("DDDD"),
|
|
[]byte("EEEEE"),
|
|
[]byte("FFFFFF"),
|
|
[]byte("GGGGGGG"),
|
|
}
|
|
m, err := trie.GenerateTrieFromItems(items, params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(b, err)
|
|
proof, err := m.MerkleProof(2)
|
|
require.NoError(b, err)
|
|
|
|
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 {
|
|
b.Error("Merkle proof did not verify")
|
|
}
|
|
}
|
|
}
|