mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-26 13:18:57 +00:00
b742511193
* validator count * fix build errors * fix test error * randao mixes * active indices * lastReceivedMerkleIndex * redundant conversions * eth1FollowDistance * refs * fix compile error * slasher * validator * revert changes to lastReceivedMerkleIndex * remove todo * fix references test * fix committee test * Revert "validator count" This reverts commit 19b376e39914b88b620a113215daa539e3e66758. # Conflicts: # beacon-chain/rpc/beacon/validators.go * Revert "fix build errors" This reverts commit f4acd6e9776c899a0523df10b64e55783b44c451. * Revert "fix test error" This reverts commit 2a5c9eec63e4546139220c71fe3c07ad92bed3b6. * Revert "randao mixes" This reverts commit 2863f9c24dd8cfc44ffce16321f68ef3b793e9a2. * Revert "active indices" This reverts commit 6e8385f3956a08ef9c8980b5343365a1bee48542. * Revert "refs" This reverts commit c64a153f67cd26daa58c3bf5f911da05d41cfb5d. * Revert "fix references test" This reverts commit fe773b55a6f8cc38c6c5c1f96615fdb8bbd4397d. * Revert "fix committee test" This reverts commit 7a0931c4487707e3eaf75a592415577f04253772. * fix compilation errors * Revert "slasher" This reverts commit 8b34137931cff1e6cdffeaf65e1ef07eefdea647. * trieutil * some int() simplifications * fix deepsource issues * removed redundant fmt.Sprint Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
214 lines
6.5 KiB
Go
214 lines
6.5 KiB
Go
// Package trieutil defines utilities for sparse merkle tries for eth2.
|
|
package trieutil
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
|
|
protodb "github.com/prysmaticlabs/prysm/proto/beacon/db"
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
|
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
|
)
|
|
|
|
// SparseMerkleTrie implements a sparse, general purpose Merkle trie to be used
|
|
// across ETH2.0 Phase 0 functionality.
|
|
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 uint64) (*SparseMerkleTrie, error) {
|
|
var zeroBytes [32]byte
|
|
items := [][]byte{zeroBytes[:]}
|
|
return GenerateTrieFromItems(items, depth)
|
|
}
|
|
|
|
// CreateTrieFromProto creates a Sparse Merkle Trie from its corresponding merkle trie.
|
|
func CreateTrieFromProto(trieObj *protodb.SparseMerkleTrie) *SparseMerkleTrie {
|
|
trie := &SparseMerkleTrie{
|
|
depth: uint(trieObj.Depth),
|
|
originalItems: trieObj.OriginalItems,
|
|
}
|
|
branches := make([][][]byte, len(trieObj.Layers))
|
|
for i, layer := range trieObj.Layers {
|
|
branches[i] = layer.Layer
|
|
}
|
|
trie.branches = branches
|
|
return trie
|
|
}
|
|
|
|
// GenerateTrieFromItems constructs a Merkle trie from a sequence of byte slices.
|
|
func GenerateTrieFromItems(items [][]byte, depth uint64) (*SparseMerkleTrie, error) {
|
|
if len(items) == 0 {
|
|
return nil, errors.New("no items provided to generate Merkle trie")
|
|
}
|
|
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 := uint64(0); i < depth; i++ {
|
|
if len(layers[i])%2 == 1 {
|
|
layers[i] = append(layers[i], ZeroHashes[i][:])
|
|
}
|
|
updatedValues := make([][]byte, 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),
|
|
}, nil
|
|
}
|
|
|
|
// Items returns the original items passed in when creating the Merkle trie.
|
|
func (m *SparseMerkleTrie) Items() [][]byte {
|
|
return m.originalItems
|
|
}
|
|
|
|
// Root returns the top-most, Merkle root of the trie.
|
|
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
|
|
var neighbor []byte
|
|
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 *SparseMerkleTrie) MerkleProof(index int) ([][]byte, error) {
|
|
merkleIndex := uint(index)
|
|
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)
|
|
}
|
|
proof := make([][]byte, m.depth+1)
|
|
for i := uint(0); i < m.depth; i++ {
|
|
subIndex := (merkleIndex / (1 << i)) ^ 1
|
|
if subIndex < uint(len(m.branches[i])) {
|
|
item := bytesutil.ToBytes32(m.branches[i][subIndex])
|
|
proof[i] = item[:]
|
|
} else {
|
|
proof[i] = ZeroHashes[i][:]
|
|
}
|
|
}
|
|
enc := [32]byte{}
|
|
binary.LittleEndian.PutUint64(enc[:], uint64(len(m.originalItems)))
|
|
proof[len(proof)-1] = enc[:]
|
|
return proof, nil
|
|
}
|
|
|
|
// 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 {
|
|
var zeroBytes [32]byte
|
|
depositCount := uint64(len(m.originalItems))
|
|
if len(m.originalItems) == 1 && bytes.Equal(m.originalItems[0], zeroBytes[:]) {
|
|
// Accounting for empty tries
|
|
depositCount = 0
|
|
}
|
|
newNode := append(m.branches[len(m.branches)-1][0], bytesutil.Bytes8(depositCount)...)
|
|
newNode = append(newNode, zeroBytes[:24]...)
|
|
return hashutil.Hash(newNode)
|
|
}
|
|
|
|
// ToProto converts the underlying trie into its corresponding
|
|
// proto object
|
|
func (m *SparseMerkleTrie) ToProto() *protodb.SparseMerkleTrie {
|
|
trie := &protodb.SparseMerkleTrie{
|
|
Depth: uint64(m.depth),
|
|
Layers: make([]*protodb.TrieLayer, len(m.branches)),
|
|
OriginalItems: m.originalItems,
|
|
}
|
|
for i, l := range m.branches {
|
|
trie.Layers[i] = &protodb.TrieLayer{
|
|
Layer: l,
|
|
}
|
|
}
|
|
return trie
|
|
}
|
|
|
|
// VerifyMerkleBranch verifies a Merkle branch against a root of a trie.
|
|
func VerifyMerkleBranch(root, item []byte, merkleIndex int, proof [][]byte, depth uint64) bool {
|
|
if len(proof) != int(depth)+1 {
|
|
return false
|
|
}
|
|
node := bytesutil.ToBytes32(item)
|
|
for i := 0; i <= int(depth); i++ {
|
|
if (uint64(merkleIndex) / mathutil.PowerOf2(uint64(i)) % 2) != 0 {
|
|
node = hashutil.Hash(append(proof[i], node[:]...))
|
|
} else {
|
|
node = hashutil.Hash(append(node[:], proof[i]...))
|
|
}
|
|
}
|
|
|
|
return bytes.Equal(root, node[:])
|
|
}
|
|
|
|
// Copy performs a deep copy of the trie.
|
|
func (m *SparseMerkleTrie) Copy() *SparseMerkleTrie {
|
|
dstBranches := make([][][]byte, len(m.branches))
|
|
for i1, srcB1 := range m.branches {
|
|
dstBranches[i1] = bytesutil.Copy2dBytes(srcB1)
|
|
}
|
|
|
|
return &SparseMerkleTrie{
|
|
depth: m.depth,
|
|
branches: dstBranches,
|
|
originalItems: bytesutil.Copy2dBytes(m.originalItems),
|
|
}
|
|
}
|