prysm-pulse/shared/trieutil/sparse_merkle.go
Raul Jordan 8ce8717676
Fix Prysm Deposit Formatting (#3394)
* proofs with proper size

* getting to the root of the problem, no pun intended

* add regression test and fix proofs

* debugging the receipt root

* debug

* fixed spec tests

* fixed up proofs!

* tests all pass
2019-09-03 12:47:47 -05:00

153 lines
4.6 KiB
Go

package trieutil
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
)
// MerkleTrie implements a sparse, general purpose Merkle trie to be used
// across ETH2.0 Phase 0 functionality.
type MerkleTrie 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) {
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) {
if len(items) == 0 {
return nil, errors.New("no items provided to generate Merkle trie")
}
layers := calcTreeFromLeaves(items, depth)
return &MerkleTrie{
branches: layers,
originalItems: items,
depth: uint(depth),
}, nil
}
// Items returns the original items passed in when creating the Merkle trie.
func (m *MerkleTrie) Items() [][]byte {
return m.originalItems
}
// Root returns the top-most, Merkle root of the trie.
func (m *MerkleTrie) 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[:]...))
}
// MerkleProof computes a proof from a trie's branches using a Merkle index.
func (m *MerkleTrie) 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 *MerkleTrie) 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)
}
// 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)
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
} else {
parentHash := hashutil.Hash(append(node[:], proof[i]...))
node = parentHash
}
}
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
}