mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 03:30:35 +00:00
Optimize Insertion in Deposit Trie (#4299)
* current changes * change algorithm for tree insert * almost done with getting this to pass * unit test passes * tests now pass * fix in repo * Merge branch 'master' into optimizeDepositLogs * fix build * Merge branch 'optimizeDepositLogs' of github.com:prysmaticlabs/prysm into optimizeDepositLogs * remove tautology * fix tautology * fix up sparsity * Merge branch 'master' into optimizeDepositLogs * further fixes * Merge branch 'optimizeDepositLogs' of github.com:prysmaticlabs/prysm into optimizeDepositLogs * Update shared/trieutil/sparse_merkle.go * comments * Merge branch 'optimizeDepositLogs' of github.com:prysmaticlabs/prysm into optimizeDepositLogs * add bench for optimized * gaz * Merge refs/heads/master into optimizeDepositLogs
This commit is contained in:
parent
23a6c20dd4
commit
c41140e15a
@ -152,7 +152,7 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
|
||||
}
|
||||
leaves = append(leaves, hash[:])
|
||||
}
|
||||
var trie *trieutil.MerkleTrie
|
||||
var trie *trieutil.SparseMerkleTrie
|
||||
if len(leaves) > 0 {
|
||||
trie, err = trieutil.GenerateTrieFromItems(leaves, int(params.BeaconConfig().DepositContractTreeDepth))
|
||||
if err != nil {
|
||||
|
@ -110,7 +110,6 @@ func (s *Service) ProcessDepositLog(ctx context.Context, depositLog gethTypes.Lo
|
||||
|
||||
// We then decode the deposit input in order to create a deposit object
|
||||
// we can store in our persistent DB.
|
||||
validData := true
|
||||
depositData := ðpb.Deposit_Data{
|
||||
Amount: bytesutil.FromBytes8(amount),
|
||||
PublicKey: pubkey,
|
||||
@ -123,9 +122,7 @@ func (s *Service) ProcessDepositLog(ctx context.Context, depositLog gethTypes.Lo
|
||||
return errors.Wrap(err, "Unable to determine hashed value of deposit")
|
||||
}
|
||||
|
||||
if err := s.depositTrie.InsertIntoTrie(depositHash[:], int(index)); err != nil {
|
||||
return errors.Wrap(err, "Unable to insert deposit into trie")
|
||||
}
|
||||
s.depositTrie.Insert(depositHash[:], int(index))
|
||||
|
||||
proof, err := s.depositTrie.MerkleProof(int(index))
|
||||
if err != nil {
|
||||
@ -138,7 +135,7 @@ func (s *Service) ProcessDepositLog(ctx context.Context, depositLog gethTypes.Lo
|
||||
}
|
||||
|
||||
// Make sure duplicates are rejected pre-chainstart.
|
||||
if !s.chainStarted && validData {
|
||||
if !s.chainStarted {
|
||||
var pubkey = fmt.Sprintf("#%x", depositData.PublicKey)
|
||||
if s.depositCache.PubkeyInChainstart(ctx, pubkey) {
|
||||
log.Warnf("Pubkey %#x has already been submitted for chainstart", pubkey)
|
||||
@ -150,7 +147,7 @@ func (s *Service) ProcessDepositLog(ctx context.Context, depositLog gethTypes.Lo
|
||||
|
||||
// We always store all historical deposits in the DB.
|
||||
s.depositCache.InsertDeposit(ctx, deposit, big.NewInt(int64(depositLog.BlockNumber)), int(index), s.depositTrie.Root())
|
||||
|
||||
validData := true
|
||||
if !s.chainStarted {
|
||||
s.chainStartDeposits = append(s.chainStartDeposits, deposit)
|
||||
root := s.depositTrie.Root()
|
||||
|
@ -125,7 +125,7 @@ type Service struct {
|
||||
blockCache *blockCache // cache to store block hash/block height.
|
||||
depositContractCaller *contracts.DepositContractCaller
|
||||
depositRoot []byte
|
||||
depositTrie *trieutil.MerkleTrie
|
||||
depositTrie *trieutil.SparseMerkleTrie
|
||||
chainStartDeposits []*ethpb.Deposit
|
||||
chainStarted bool
|
||||
chainStartBlockNumber *big.Int
|
||||
@ -255,7 +255,7 @@ func (s *Service) DepositRoot() [32]byte {
|
||||
|
||||
// DepositTrie returns the sparse Merkle trie used for storing
|
||||
// deposits from the ETH1.0 deposit contract.
|
||||
func (s *Service) DepositTrie() *trieutil.MerkleTrie {
|
||||
func (s *Service) DepositTrie() *trieutil.SparseMerkleTrie {
|
||||
return s.depositTrie
|
||||
}
|
||||
|
||||
|
4
beacon-chain/powchain/testing/faulty_mock.go
generated
4
beacon-chain/powchain/testing/faulty_mock.go
generated
@ -62,8 +62,8 @@ func (f *FaultyMockPOWChain) DepositRoot() [32]byte {
|
||||
}
|
||||
|
||||
// DepositTrie --
|
||||
func (f *FaultyMockPOWChain) DepositTrie() *trieutil.MerkleTrie {
|
||||
return &trieutil.MerkleTrie{}
|
||||
func (f *FaultyMockPOWChain) DepositTrie() *trieutil.SparseMerkleTrie {
|
||||
return &trieutil.SparseMerkleTrie{}
|
||||
}
|
||||
|
||||
// ChainStartDeposits --
|
||||
|
@ -39,8 +39,8 @@ func (m *POWChain) Eth2GenesisPowchainInfo() (uint64, *big.Int) {
|
||||
}
|
||||
|
||||
// DepositTrie --
|
||||
func (m *POWChain) DepositTrie() *trieutil.MerkleTrie {
|
||||
return &trieutil.MerkleTrie{}
|
||||
func (m *POWChain) DepositTrie() *trieutil.SparseMerkleTrie {
|
||||
return &trieutil.SparseMerkleTrie{}
|
||||
}
|
||||
|
||||
// BlockExists --
|
||||
|
@ -356,7 +356,7 @@ func (ps *Server) defaultEth1DataResponse(ctx context.Context, currentHeight *bi
|
||||
}, nil
|
||||
}
|
||||
|
||||
func constructMerkleProof(trie *trieutil.MerkleTrie, index int, deposit *ethpb.Deposit) (*ethpb.Deposit, error) {
|
||||
func constructMerkleProof(trie *trieutil.SparseMerkleTrie, index int, deposit *ethpb.Deposit) (*ethpb.Deposit, error) {
|
||||
proof, err := trie.MerkleProof(index)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not generate merkle proof for deposit at index %d", index)
|
||||
|
@ -314,10 +314,7 @@ func TestPendingDeposits_OutsideEth1FollowWindow(t *testing.T) {
|
||||
t.Fatalf("Unable to determine hashed value of deposit %v", err)
|
||||
}
|
||||
|
||||
if err := depositTrie.InsertIntoTrie(depositHash[:], int(dp.Index)); err != nil {
|
||||
t.Fatalf("Unable to insert deposit into trie %v", err)
|
||||
}
|
||||
|
||||
depositTrie.Insert(depositHash[:], dp.Index)
|
||||
depositCache.InsertDeposit(ctx, dp.Deposit, dp.Block, dp.Index, depositTrie.Root())
|
||||
}
|
||||
for _, dp := range recentDeposits {
|
||||
@ -467,10 +464,7 @@ func TestPendingDeposits_FollowsCorrectEth1Block(t *testing.T) {
|
||||
t.Fatalf("Unable to determine hashed value of deposit %v", err)
|
||||
}
|
||||
|
||||
if err := depositTrie.InsertIntoTrie(depositHash[:], int(dp.Index)); err != nil {
|
||||
t.Fatalf("Unable to insert deposit into trie %v", err)
|
||||
}
|
||||
|
||||
depositTrie.Insert(depositHash[:], dp.Index)
|
||||
depositCache.InsertDeposit(ctx, dp.Deposit, dp.Block, dp.Index, depositTrie.Root())
|
||||
}
|
||||
for _, dp := range recentDeposits {
|
||||
@ -584,10 +578,7 @@ func TestPendingDeposits_CantReturnBelowStateEth1DepositIndex(t *testing.T) {
|
||||
t.Fatalf("Unable to determine hashed value of deposit %v", err)
|
||||
}
|
||||
|
||||
if err := depositTrie.InsertIntoTrie(depositHash[:], int(dp.Index)); err != nil {
|
||||
t.Fatalf("Unable to insert deposit into trie %v", err)
|
||||
}
|
||||
|
||||
depositTrie.Insert(depositHash[:], dp.Index)
|
||||
depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root())
|
||||
}
|
||||
for _, dp := range recentDeposits {
|
||||
@ -693,10 +684,7 @@ func TestPendingDeposits_CantReturnMoreThanMax(t *testing.T) {
|
||||
t.Fatalf("Unable to determine hashed value of deposit %v", err)
|
||||
}
|
||||
|
||||
if err := depositTrie.InsertIntoTrie(depositHash[:], int(dp.Index)); err != nil {
|
||||
t.Fatalf("Unable to insert deposit into trie %v", err)
|
||||
}
|
||||
|
||||
depositTrie.Insert(depositHash[:], dp.Index)
|
||||
depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root())
|
||||
}
|
||||
for _, dp := range recentDeposits {
|
||||
@ -800,10 +788,7 @@ func TestPendingDeposits_CantReturnMoreDepositCount(t *testing.T) {
|
||||
t.Fatalf("Unable to determine hashed value of deposit %v", err)
|
||||
}
|
||||
|
||||
if err := depositTrie.InsertIntoTrie(depositHash[:], int(dp.Index)); err != nil {
|
||||
t.Fatalf("Unable to insert deposit into trie %v", err)
|
||||
}
|
||||
|
||||
depositTrie.Insert(depositHash[:], dp.Index)
|
||||
depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root())
|
||||
}
|
||||
for _, dp := range recentDeposits {
|
||||
@ -1166,10 +1151,7 @@ func TestDeposits_ReturnsEmptyList_IfLatestEth1DataEqGenesisEth1Block(t *testing
|
||||
t.Fatalf("Unable to determine hashed value of deposit %v", err)
|
||||
}
|
||||
|
||||
if err := depositTrie.InsertIntoTrie(depositHash[:], int(dp.Index)); err != nil {
|
||||
t.Fatalf("Unable to insert deposit into trie %v", err)
|
||||
}
|
||||
|
||||
depositTrie.Insert(depositHash[:], dp.Index)
|
||||
depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root())
|
||||
}
|
||||
for _, dp := range recentDeposits {
|
||||
|
@ -598,7 +598,7 @@ func TestMultipleValidatorStatus_OK(t *testing.T) {
|
||||
dep = ðpb.Deposit{
|
||||
Data: depData,
|
||||
}
|
||||
depositTrie.InsertIntoTrie(dep.Data.Signature, 15)
|
||||
depositTrie.Insert(dep.Data.Signature, 15)
|
||||
depositCache.InsertDeposit(context.Background(), dep, big.NewInt(15), 0, depositTrie.Root())
|
||||
|
||||
if err := db.SaveValidatorIndex(ctx, bytesutil.ToBytes48(pubKeys[0]), 0); err != nil {
|
||||
|
@ -58,16 +58,11 @@ func TestDepositTrieRoot_OK(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = localTrie.InsertIntoTrie(item[:], i)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
localTrie.Insert(item[:], i)
|
||||
depRoot, err = testAcc.Contract.GetDepositRoot(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if depRoot != localTrie.HashTreeRoot() {
|
||||
t.Errorf("Local deposit trie root and contract deposit trie root are not equal for index %d. Expected %#x , Got %#x", i, depRoot, localTrie.Root())
|
||||
}
|
||||
@ -122,10 +117,7 @@ func TestDepositTrieRoot_Fail(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = localTrie.InsertIntoTrie(item[:], i)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
localTrie.Insert(item[:], i)
|
||||
|
||||
depRoot, err = testAcc.Contract.GetDepositRoot(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
|
@ -60,7 +60,7 @@ func GenerateGenesisState(genesisTime, numValidators uint64) (*pb.BeaconState, [
|
||||
}
|
||||
|
||||
// GenerateDepositsFromData a list of deposit items by creating proofs for each of them from a sparse Merkle trie.
|
||||
func GenerateDepositsFromData(depositDataItems []*ethpb.Deposit_Data, trie *trieutil.MerkleTrie) ([]*ethpb.Deposit, error) {
|
||||
func GenerateDepositsFromData(depositDataItems []*ethpb.Deposit_Data, trie *trieutil.SparseMerkleTrie) ([]*ethpb.Deposit, error) {
|
||||
deposits := make([]*ethpb.Deposit, len(depositDataItems))
|
||||
results, err := mputil.Scatter(len(depositDataItems), func(offset int, entries int, _ *sync.RWMutex) (interface{}, error) {
|
||||
return generateDepositsFromData(depositDataItems[offset:offset+entries], offset, trie)
|
||||
@ -79,7 +79,7 @@ func GenerateDepositsFromData(depositDataItems []*ethpb.Deposit_Data, trie *trie
|
||||
}
|
||||
|
||||
// generateDepositsFromData a list of deposit items by creating proofs for each of them from a sparse Merkle trie.
|
||||
func generateDepositsFromData(depositDataItems []*ethpb.Deposit_Data, offset int, trie *trieutil.MerkleTrie) ([]*ethpb.Deposit, error) {
|
||||
func generateDepositsFromData(depositDataItems []*ethpb.Deposit_Data, offset int, trie *trieutil.SparseMerkleTrie) ([]*ethpb.Deposit, error) {
|
||||
deposits := make([]*ethpb.Deposit, len(depositDataItems))
|
||||
for i, item := range depositDataItems {
|
||||
proof, err := trie.MerkleProof(i + offset)
|
||||
|
@ -22,7 +22,7 @@ var lock sync.Mutex
|
||||
// Caches
|
||||
var cachedDeposits []*ethpb.Deposit
|
||||
var privKeys []*bls.SecretKey
|
||||
var trie *trieutil.MerkleTrie
|
||||
var trie *trieutil.SparseMerkleTrie
|
||||
|
||||
// DeterministicDepositsAndKeys returns the entered amount of deposits and secret keys.
|
||||
// The deposits are configured such that for deposit n the validator
|
||||
@ -81,9 +81,7 @@ func DeterministicDepositsAndKeys(numDeposits uint64) ([]*ethpb.Deposit, []*bls.
|
||||
return nil, nil, errors.Wrap(err, "could not tree hash deposit data")
|
||||
}
|
||||
|
||||
if err := trie.InsertIntoTrie(hashedDeposit[:], int(numExisting+i)); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not tree hash deposit data")
|
||||
}
|
||||
trie.Insert(hashedDeposit[:], int(numExisting+i))
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,7 +103,7 @@ func DeterministicDepositsAndKeys(numDeposits uint64) ([]*ethpb.Deposit, []*bls.
|
||||
|
||||
// DeterministicDepositTrie returns a merkle trie of the requested size from the
|
||||
// deterministic deposits.
|
||||
func DeterministicDepositTrie(size int) (*trieutil.MerkleTrie, [][32]byte, error) {
|
||||
func DeterministicDepositTrie(size int) (*trieutil.SparseMerkleTrie, [][32]byte, error) {
|
||||
items := trie.Items()
|
||||
if size > len(items) {
|
||||
return nil, [][32]byte{}, errors.New("requested a larger tree than amount of deposits")
|
||||
@ -162,7 +160,7 @@ func DeterministicGenesisState(t testing.TB, numValidators uint64) (*pb.BeaconSt
|
||||
}
|
||||
|
||||
// DepositTrieFromDeposits takes an array of deposits and returns the deposit trie.
|
||||
func DepositTrieFromDeposits(deposits []*ethpb.Deposit) (*trieutil.MerkleTrie, [][32]byte, error) {
|
||||
func DepositTrieFromDeposits(deposits []*ethpb.Deposit) (*trieutil.SparseMerkleTrie, [][32]byte, error) {
|
||||
encodedDeposits := make([][]byte, len(deposits))
|
||||
for i := 0; i < len(encodedDeposits); i++ {
|
||||
hashedDeposit, err := ssz.HashTreeRoot(deposits[i].Data)
|
||||
|
@ -3,7 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"merkle_trie.go",
|
||||
"helpers.go",
|
||||
"sparse_merkle.go",
|
||||
"zerohashes.go",
|
||||
],
|
||||
@ -20,12 +20,13 @@ go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"merkle_trie_test.go",
|
||||
"helpers_test.go",
|
||||
"sparse_merkle_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//contracts/deposit-contract:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library",
|
||||
|
@ -10,44 +10,46 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
)
|
||||
|
||||
// MerkleTrie implements a sparse, general purpose Merkle trie to be used
|
||||
// SparseMerkleTrie implements a sparse, general purpose Merkle trie to be used
|
||||
// across ETH2.0 Phase 0 functionality.
|
||||
type MerkleTrie struct {
|
||||
type SparseMerkleTrie struct {
|
||||
depth uint
|
||||
branches [][][]byte
|
||||
originalItems [][]byte // list of provided items before hashing them into leaves.
|
||||
}
|
||||
|
||||
// NewTrie returns a new merkle trie filled with zerohashes to use.
|
||||
func NewTrie(depth int) (*MerkleTrie, error) {
|
||||
func NewTrie(depth int) (*SparseMerkleTrie, error) {
|
||||
var zeroBytes [32]byte
|
||||
items := [][]byte{zeroBytes[:]}
|
||||
return GenerateTrieFromItems(items, depth)
|
||||
}
|
||||
|
||||
// InsertIntoTrie inserts an item(deposit hash) into the trie.
|
||||
func (m *MerkleTrie) InsertIntoTrie(item []byte, index int) error {
|
||||
// Only insert new items which follow directly after the last
|
||||
// added element
|
||||
if index > len(m.originalItems) {
|
||||
return errors.New("invalid index to be inserting")
|
||||
}
|
||||
if index == len(m.originalItems) {
|
||||
m.originalItems = append(m.originalItems, item)
|
||||
return m.updateTrie()
|
||||
}
|
||||
|
||||
m.originalItems[index] = item
|
||||
return m.updateTrie()
|
||||
}
|
||||
|
||||
// GenerateTrieFromItems constructs a Merkle trie from a sequence of byte slices.
|
||||
func GenerateTrieFromItems(items [][]byte, depth int) (*MerkleTrie, error) {
|
||||
func GenerateTrieFromItems(items [][]byte, depth int) (*SparseMerkleTrie, error) {
|
||||
if len(items) == 0 {
|
||||
return nil, errors.New("no items provided to generate Merkle trie")
|
||||
}
|
||||
layers := calcTreeFromLeaves(items, depth)
|
||||
return &MerkleTrie{
|
||||
leaves := items
|
||||
layers := make([][][]byte, depth+1)
|
||||
transformedLeaves := make([][]byte, len(leaves))
|
||||
for i := range leaves {
|
||||
arr := bytesutil.ToBytes32(leaves[i])
|
||||
transformedLeaves[i] = arr[:]
|
||||
}
|
||||
layers[0] = transformedLeaves
|
||||
for i := 0; i < depth; i++ {
|
||||
if len(layers[i])%2 == 1 {
|
||||
layers[i] = append(layers[i], zeroHashes[i])
|
||||
}
|
||||
updatedValues := make([][]byte, 0, 0)
|
||||
for j := 0; j < len(layers[i]); j += 2 {
|
||||
concat := hashutil.Hash(append(layers[i][j], layers[i][j+1]...))
|
||||
updatedValues = append(updatedValues, concat[:])
|
||||
}
|
||||
layers[i+1] = updatedValues
|
||||
}
|
||||
return &SparseMerkleTrie{
|
||||
branches: layers,
|
||||
originalItems: items,
|
||||
depth: uint(depth),
|
||||
@ -55,19 +57,61 @@ func GenerateTrieFromItems(items [][]byte, depth int) (*MerkleTrie, error) {
|
||||
}
|
||||
|
||||
// Items returns the original items passed in when creating the Merkle trie.
|
||||
func (m *MerkleTrie) Items() [][]byte {
|
||||
func (m *SparseMerkleTrie) Items() [][]byte {
|
||||
return m.originalItems
|
||||
}
|
||||
|
||||
// Root returns the top-most, Merkle root of the trie.
|
||||
func (m *MerkleTrie) Root() [32]byte {
|
||||
func (m *SparseMerkleTrie) Root() [32]byte {
|
||||
enc := [32]byte{}
|
||||
binary.LittleEndian.PutUint64(enc[:], uint64(len(m.originalItems)))
|
||||
return hashutil.Hash(append(m.branches[len(m.branches)-1][0], enc[:]...))
|
||||
}
|
||||
|
||||
// Insert an item into the trie.
|
||||
func (m *SparseMerkleTrie) Insert(item []byte, index int) {
|
||||
for index >= len(m.branches[0]) {
|
||||
m.branches[0] = append(m.branches[0], zeroHashes[0])
|
||||
}
|
||||
someItem := bytesutil.ToBytes32(item)
|
||||
m.branches[0][index] = someItem[:]
|
||||
if index >= len(m.originalItems) {
|
||||
m.originalItems = append(m.originalItems, someItem[:])
|
||||
} else {
|
||||
m.originalItems[index] = someItem[:]
|
||||
}
|
||||
currentIndex := index
|
||||
root := bytesutil.ToBytes32(item)
|
||||
for i := 0; i < int(m.depth); i++ {
|
||||
isLeft := currentIndex%2 == 0
|
||||
neighborIdx := currentIndex ^ 1
|
||||
neighbor := make([]byte, 32)
|
||||
if neighborIdx >= len(m.branches[i]) {
|
||||
neighbor = zeroHashes[i][:]
|
||||
} else {
|
||||
neighbor = m.branches[i][neighborIdx]
|
||||
}
|
||||
if isLeft {
|
||||
parentHash := hashutil.Hash(append(root[:], neighbor...))
|
||||
root = parentHash
|
||||
} else {
|
||||
parentHash := hashutil.Hash(append(neighbor, root[:]...))
|
||||
root = parentHash
|
||||
}
|
||||
parentIdx := currentIndex / 2
|
||||
if len(m.branches[i+1]) == 0 || parentIdx >= len(m.branches[i+1]) {
|
||||
newItem := root
|
||||
m.branches[i+1] = append(m.branches[i+1], newItem[:])
|
||||
} else {
|
||||
newItem := root
|
||||
m.branches[i+1][parentIdx] = newItem[:]
|
||||
}
|
||||
currentIndex = parentIdx
|
||||
}
|
||||
}
|
||||
|
||||
// MerkleProof computes a proof from a trie's branches using a Merkle index.
|
||||
func (m *MerkleTrie) MerkleProof(index int) ([][]byte, error) {
|
||||
func (m *SparseMerkleTrie) MerkleProof(index int) ([][]byte, error) {
|
||||
merkleIndex := uint(index)
|
||||
leaves := m.branches[0]
|
||||
if index >= len(leaves) {
|
||||
@ -92,7 +136,7 @@ func (m *MerkleTrie) MerkleProof(index int) ([][]byte, error) {
|
||||
// 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 *MerkleTrie) HashTreeRoot() [32]byte {
|
||||
func (m *SparseMerkleTrie) HashTreeRoot() [32]byte {
|
||||
var zeroBytes [32]byte
|
||||
depositCount := uint64(len(m.originalItems))
|
||||
if len(m.originalItems) == 1 && bytes.Equal(m.originalItems[0], zeroBytes[:]) {
|
||||
@ -107,46 +151,14 @@ func (m *MerkleTrie) HashTreeRoot() [32]byte {
|
||||
// VerifyMerkleProof verifies a Merkle branch against a root of a trie.
|
||||
func VerifyMerkleProof(root []byte, item []byte, merkleIndex int, proof [][]byte) bool {
|
||||
node := bytesutil.ToBytes32(item)
|
||||
currentIndex := merkleIndex
|
||||
for i := 0; i < len(proof); i++ {
|
||||
isLeft := merkleIndex / (1 << uint64(i))
|
||||
if isLeft%2 != 0 {
|
||||
parentHash := hashutil.Hash(append(proof[i], node[:]...))
|
||||
node = parentHash
|
||||
if currentIndex%2 != 0 {
|
||||
node = hashutil.Hash(append(proof[i], node[:]...))
|
||||
} else {
|
||||
parentHash := hashutil.Hash(append(node[:], proof[i]...))
|
||||
node = parentHash
|
||||
node = hashutil.Hash(append(node[:], proof[i]...))
|
||||
}
|
||||
currentIndex = currentIndex / 2
|
||||
}
|
||||
return bytes.Equal(root, node[:])
|
||||
}
|
||||
|
||||
func calcTreeFromLeaves(leaves [][]byte, depth int) [][][]byte {
|
||||
layers := make([][][]byte, depth+1)
|
||||
transformedLeaves := make([][]byte, len(leaves))
|
||||
for i := range leaves {
|
||||
arr := bytesutil.ToBytes32(leaves[i])
|
||||
transformedLeaves[i] = arr[:]
|
||||
}
|
||||
layers[0] = transformedLeaves
|
||||
for i := 0; i < depth; i++ {
|
||||
if len(layers[i])%2 == 1 {
|
||||
layers[i] = append(layers[i], zeroHashes[i])
|
||||
}
|
||||
updatedValues := make([][]byte, 0, 0)
|
||||
for j := 0; j < len(layers[i]); j += 2 {
|
||||
concat := hashutil.Hash(append(layers[i][j], layers[i][j+1]...))
|
||||
updatedValues = append(updatedValues, concat[:])
|
||||
}
|
||||
layers[i+1] = updatedValues
|
||||
}
|
||||
return layers
|
||||
}
|
||||
|
||||
func (m *MerkleTrie) updateTrie() error {
|
||||
trie, err := GenerateTrieFromItems(m.originalItems, int(m.depth))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.branches = trie.branches
|
||||
return nil
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ package trieutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
@ -60,7 +62,7 @@ func TestMarshalDepositWithProof(t *testing.T) {
|
||||
|
||||
func TestMerkleTrie_MerkleProofOutOfRange(t *testing.T) {
|
||||
h := hashutil.Hash([]byte("hi"))
|
||||
m := &MerkleTrie{
|
||||
m := &SparseMerkleTrie{
|
||||
branches: [][][]byte{
|
||||
{
|
||||
h[:],
|
||||
@ -142,6 +144,44 @@ func TestMerkleTrie_VerifyMerkleProof(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMerkleTrie_VerifyMerkleProof_TrieUpdated(t *testing.T) {
|
||||
items := [][]byte{
|
||||
{1},
|
||||
{2},
|
||||
{3},
|
||||
{4},
|
||||
}
|
||||
m, err := GenerateTrieFromItems(items, 33)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate Merkle trie from items: %v", err)
|
||||
}
|
||||
proof, err := m.MerkleProof(0)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate Merkle proof: %v", err)
|
||||
}
|
||||
root := m.Root()
|
||||
if ok := VerifyMerkleProof(root[:], items[0], 0, proof); !ok {
|
||||
t.Error("First Merkle proof did not verify")
|
||||
}
|
||||
|
||||
// Now we update the trie.
|
||||
m.Insert([]byte{5}, 3)
|
||||
proof, err = m.MerkleProof(3)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate Merkle proof: %v", err)
|
||||
}
|
||||
root = m.Root()
|
||||
if ok := VerifyMerkleProof(root[:], []byte{5}, 3, proof); !ok {
|
||||
t.Error("Second Merkle proof did not verify")
|
||||
}
|
||||
if ok := VerifyMerkleProof(root[:], []byte{4}, 3, proof); ok {
|
||||
t.Error("Old item should not verify")
|
||||
}
|
||||
|
||||
// Now we update the trie at an index larger than the number of items.
|
||||
m.Insert([]byte{6}, 15)
|
||||
}
|
||||
|
||||
func BenchmarkGenerateTrieFromItems(b *testing.B) {
|
||||
items := [][]byte{
|
||||
[]byte("A"),
|
||||
@ -159,6 +199,25 @@ func BenchmarkGenerateTrieFromItems(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
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 := GenerateTrieFromItems(items, 32)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not generate Merkle trie from items: %v", err)
|
||||
}
|
||||
someItem := bytesutil.ToBytes32([]byte("hello-world"))
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
tr.Insert(someItem[:], i%numDeposits)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGenerateProof(b *testing.B) {
|
||||
b.StopTimer()
|
||||
items := [][]byte{
|
||||
|
Loading…
Reference in New Issue
Block a user