go-ethereum/beacon/merkle/binary_merkle.go

494 lines
16 KiB
Go

// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package merkle
import (
"math/bits"
"reflect"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/minio/sha256-simd"
)
// Value represents either a 32 byte value or hash node in a binary merkle tree/partial proof
type (
Value [32]byte
Values []Value
)
var ValueT = reflect.TypeOf(Value{})
// UnmarshalJSON parses a merkle value in hex syntax.
func (m *Value) UnmarshalJSON(input []byte) error {
return hexutil.UnmarshalFixedJSON(ValueT, input, m[:])
}
// VerifySingleProof verifies a Merkle proof branch for a single value in a
// binary Merkle tree (index is a generalized tree index).
func VerifySingleProof(proof Values, index uint64, value Value) (common.Hash, bool) {
hasher := sha256.New()
for _, proofHash := range proof {
hasher.Reset()
if index&1 == 0 {
hasher.Write(value[:])
hasher.Write(proofHash[:])
} else {
hasher.Write(proofHash[:])
hasher.Write(value[:])
}
hasher.Sum(value[:0])
index /= 2
if index == 0 {
return common.Hash{}, false
}
}
if index != 1 {
return common.Hash{}, false
}
return common.Hash(value), true
}
// ProofFormat defines the shape of a partial proof and allows traversing a subset of a tree
type ProofFormat interface {
Children() (left, right ProofFormat) // either both or neither should be nil
}
// ProofReader allows traversing and reading a tree structure or a subset of it.
// Note: the hash of each traversed node is always requested. If the internal
// hash is not available then subtrees are always traversed (first left, then right).
// If internal hash is available then subtrees are only traversed if needed by the writer.
type ProofReader interface {
Children() (left, right ProofReader) // subtrees accessible if not nil
ReadNode() (Value, bool) // hash should be available if children are nil (leaf node), optional otherwise (internal node)
}
// ProofWriter allow collecting data for a partial proof while a subset of a tree is traversed.
type ProofWriter interface {
Children() (left, right ProofWriter) // all non-nil subtrees are traversed
WriteNode(Value) // called for every traversed tree node (both leaf and internal)
}
// TraverseProof traverses a reader and a writer defined on the same tree
// simultaneously, copies data from the reader to the writer (if writer is not nil)
// and returns the root hash. At least the shape defined by the writer is traversed;
// subtrees not required by the writer are only traversed (with writer == nil)
// if the hash of the internal tree node is not provided by the reader.
func TraverseProof(reader ProofReader, writer ProofWriter) (common.Hash, bool) {
var (
wl ProofWriter
wr ProofWriter
)
if writer != nil {
wl, wr = writer.Children()
}
node, nodeAvailable := reader.ReadNode()
if nodeAvailable && wl == nil {
if writer != nil {
writer.WriteNode(node)
}
return common.Hash(node), true
}
rl, rr := reader.Children()
if rl == nil {
return common.Hash{}, false
}
lhash, ok := TraverseProof(rl, wl)
if !ok {
return common.Hash{}, false
}
rhash, ok := TraverseProof(rr, wr)
if !ok {
return common.Hash{}, false
}
if !nodeAvailable {
hasher := sha256.New()
hasher.Write(lhash[:])
hasher.Write(rhash[:])
hasher.Sum(node[:0])
}
if writer != nil {
writer.WriteNode(node)
}
return common.Hash(node), true
}
// MultiProof stores a partial Merkle tree proof
type MultiProof struct {
Format ProofFormat
Values Values
}
// multiProofReader implements ProofReader based on a MultiProof and also allows
// attaching further subtree readers at certain indices
// Note: valuePtr is stored and copied as a reference because child readers read
// from the same value list as the tree is traversed
type multiProofReader struct {
format ProofFormat // corresponding proof format
values Values // proof values
valuePtr *int // next index to be read from values
index uint64 // generalized tree index
subtrees func(uint64) ProofReader // attached subtrees
}
// children implements ProofReader
func (mpr multiProofReader) Children() (left, right ProofReader) {
lf, rf := mpr.format.Children()
if lf == nil {
if mpr.subtrees != nil {
if subtree := mpr.subtrees(mpr.index); subtree != nil {
return subtree.Children()
}
}
return nil, nil
}
return multiProofReader{format: lf, values: mpr.values, valuePtr: mpr.valuePtr, index: mpr.index * 2, subtrees: mpr.subtrees},
multiProofReader{format: rf, values: mpr.values, valuePtr: mpr.valuePtr, index: mpr.index*2 + 1, subtrees: mpr.subtrees}
}
// readNode implements ProofReader
func (mpr multiProofReader) ReadNode() (Value, bool) {
if l, _ := mpr.format.Children(); l == nil && len(mpr.values) > *mpr.valuePtr {
hash := mpr.values[*mpr.valuePtr]
(*mpr.valuePtr)++
return hash, true
}
return Value{}, false
}
// Reader creates a multiProofReader for the given proof; if subtrees != nil
// then also attaches subtree readers at indices where the function returns a
// non-nil reader.
// Note that the reader can only be traversed once as the values slice is
// sequentially consumed.
func (mp MultiProof) Reader(subtrees func(uint64) ProofReader) multiProofReader {
return multiProofReader{format: mp.Format, values: mp.Values, valuePtr: new(int), index: 1, subtrees: subtrees}
}
// Finished returns true if all values have been consumed by the traversal.
// Should be checked after TraverseProof if received from an untrusted source in
// order to prevent DoS attacks by excess proof values.
func (mpr multiProofReader) Finished() bool {
return len(mpr.values) == *mpr.valuePtr
}
// rootHash returns the root hash of the proven structure.
func (mp MultiProof) RootHash() common.Hash {
reader := mp.Reader(nil)
hash, ok := TraverseProof(reader, nil)
if !ok || !reader.Finished() {
log.Error("MultiProof.rootHash: invalid proof format")
}
return hash
}
// multiProofWriter implements ProofWriter and creates a MultiProof with the
// previously specified format. Also allows attaching further subtree writers at
// certain indices.
// Note: values is stored and copied as a reference because child writers append
// to the same value list as the tree is traversed
type multiProofWriter struct {
format ProofFormat // target proof format
values *Values // target proof value list
index uint64 // generalized tree index
subtrees func(uint64) ProofWriter // attached subtrees
}
// NewMultiProofWriter creates a new multiproof writer with the specified format.
// If subtrees != nil then further subtree writers are attached at indices where
// the function returns a non-nil writer.
// Note that the specified format should not include these attached subtrees;
// they should be attached at leaf indices of the given format.
// Also note that target can be nil in which case the nodes specified by the format
// are traversed but not stored; subtree writers might still store tree data.
func NewMultiProofWriter(format ProofFormat, target *Values, subtrees func(uint64) ProofWriter) multiProofWriter {
return multiProofWriter{format: format, values: target, index: 1, subtrees: subtrees}
}
// children implements ProofWriter
func (mpw multiProofWriter) Children() (left, right ProofWriter) {
if mpw.subtrees != nil {
if subtree := mpw.subtrees(mpw.index); subtree != nil {
return subtree.Children()
}
}
lf, rf := mpw.format.Children()
if lf == nil {
return nil, nil
}
return multiProofWriter{format: lf, values: mpw.values, index: mpw.index * 2, subtrees: mpw.subtrees},
multiProofWriter{format: rf, values: mpw.values, index: mpw.index*2 + 1, subtrees: mpw.subtrees}
}
// writeNode implements ProofWriter
func (mpw multiProofWriter) WriteNode(node Value) {
if mpw.values != nil {
if lf, _ := mpw.format.Children(); lf == nil {
*mpw.values = append(*mpw.values, node)
}
}
if mpw.subtrees != nil {
if subtree := mpw.subtrees(mpw.index); subtree != nil {
subtree.WriteNode(node)
}
}
}
// ProofFormatIndexMap creates a generalized tree index -> MultiProof value
// slice index association map based on the given proof format.
func ProofFormatIndexMap(f ProofFormat) map[uint64]int {
var (
m = make(map[uint64]int)
pos int
)
addToIndexMap(m, f, &pos, 1)
return m
}
// addToIndexMap recursively creates index associations for a given proof format subtree.
func addToIndexMap(m map[uint64]int, f ProofFormat, pos *int, index uint64) {
l, r := f.Children()
if l == nil {
m[index] = *pos
(*pos)++
} else {
addToIndexMap(m, l, pos, index*2)
addToIndexMap(m, r, pos, index*2+1)
}
}
// ChildIndex returns the generalized tree index of a subtree node in terms of
// the main tree where a is the main tree index of the subtree root and b is the
// subtree index of the node in question.
func ChildIndex(a, b uint64) uint64 {
return (a-1)<<(63-bits.LeadingZeros64(b)) + b
}
// IndexMapFormat implements ProofFormat based on an index map filled with
// AddLeaf calls. Subtree formats can also be attached at certain indices.
type IndexMapFormat struct {
leaves map[uint64]ProofFormat
index uint64
}
// NewIndexMapFormat returns an empty format.
func NewIndexMapFormat() IndexMapFormat {
return IndexMapFormat{leaves: make(map[uint64]ProofFormat), index: 1}
}
// AddLeaf adds either a single leaf or attaches a subtree at the given tree index.
func (f IndexMapFormat) AddLeaf(index uint64, subtree ProofFormat) IndexMapFormat {
if subtree != nil {
f.leaves[index] = subtree
}
for index > 1 {
index /= 2
f.leaves[index] = nil
}
return f
}
// children implements ProofFormat
func (f IndexMapFormat) Children() (left, right ProofFormat) {
if st, ok := f.leaves[f.index]; ok {
if st != nil {
return st.Children()
}
return IndexMapFormat{leaves: f.leaves, index: f.index * 2}, IndexMapFormat{leaves: f.leaves, index: f.index*2 + 1}
}
return nil, nil
}
// rangeFormat defined a proof format with a continuous range of leaf indices.
// Attaching subtree formats is also possible.
type rangeFormat struct {
begin, end, index uint64 // begin and end should be on the same level
subtree func(uint64) ProofFormat
}
// NewRangeFormat creates a new rangeFormat with leafs in the begin..end range.
// If subtrees != nil then further subtree formats are attached at indices where
// the function returns a non-nil format.
func NewRangeFormat(begin, end uint64, subtree func(uint64) ProofFormat) rangeFormat {
return rangeFormat{
begin: begin,
end: end,
index: 1,
subtree: subtree,
}
}
// children implements ProofFormat
func (rf rangeFormat) Children() (left, right ProofFormat) {
var (
lzr = bits.LeadingZeros64(rf.begin)
lzi = bits.LeadingZeros64(rf.index)
)
if lzi < lzr {
return nil, nil
}
if lzi == lzr {
if rf.subtree != nil && rf.index >= rf.begin && rf.index <= rf.end {
if st := rf.subtree(rf.index); st != nil {
return st.Children()
}
}
return nil, nil
}
var (
// i1..i2 are the descendants of rf.index at the tree level where begin and end are located
i1 = rf.index << (lzi - lzr)
i2 = ((rf.index + 1) << (lzi - lzr)) - 1
)
if i1 <= rf.end && i2 >= rf.begin {
// Return child formats if there is an overlap (rf.index has any descendants
// in the begin..end range).
// Note that if begin..end only touches one of the returned child subtrees,
// we still return a rangeFormat for both branches and the other one will
// not have any further children (that child of rf.index will be stored
// in the proof as a single sibling node).
return rangeFormat{begin: rf.begin, end: rf.end, index: rf.index * 2, subtree: rf.subtree},
rangeFormat{begin: rf.begin, end: rf.end, index: rf.index*2 + 1, subtree: rf.subtree}
}
return nil, nil
}
// MergedFormat implements ProofFormat and realizes the union of the included
// individual formats.
type MergedFormat []ProofFormat
// children implements ProofFormat
func (m MergedFormat) Children() (left, right ProofFormat) {
var (
l = make(MergedFormat, 0, len(m))
r = make(MergedFormat, 0, len(m))
)
for _, f := range m {
if left, right := f.Children(); left != nil {
l = append(l, left)
r = append(r, right)
}
}
if len(l) > 0 {
return l, r
}
return nil, nil
}
// MergedReader implements ProofReader and realizes the union of the included
// individual readers.
// Note that the readers belonging to the same structure (having the same root)
// is not checked by MergedReader.
// Also note that fully consuming underlying sequential readers is not guaranteed
// (MultiProofReader.Finalized will not necessarily return true so if necessary
// then the well-formedness of individual multiproofs should be checked separately).
type MergedReader []ProofReader
// children implements ProofReader
func (m MergedReader) Children() (left, right ProofReader) {
var (
l = make(MergedReader, 0, len(m))
r = make(MergedReader, 0, len(m))
)
for _, reader := range m {
if left, right := reader.Children(); left != nil {
l = append(l, left)
r = append(r, right)
}
}
if len(l) > 0 {
return l, r
}
return nil, nil
}
// readNode implements ProofReader
func (m MergedReader) ReadNode() (value Value, ok bool) {
var hasChildren bool
for _, reader := range m {
if left, _ := reader.Children(); left != nil {
// ensure that all readers are fully traversed
hasChildren = true
}
if v, o := reader.ReadNode(); o {
value, ok = v, o
}
}
if hasChildren {
return Value{}, false
}
return
}
// MergedWriter implements ProofWriter and realizes the union of the included
// individual writers. The shape traversed by MergedWriter is the union of the
// shapes traversed by individual writers.
type MergedWriter []ProofWriter
// children implements ProofWriter
func (m MergedWriter) Children() (left, right ProofWriter) {
var (
l = make(MergedWriter, 0, len(m))
r = make(MergedWriter, 0, len(m))
)
for _, w := range m {
if left, right := w.Children(); left != nil {
l = append(l, left)
r = append(r, right)
}
}
if len(l) > 0 {
return l, r
}
return nil, nil
}
// writeNode implements ProofWriter
func (m MergedWriter) WriteNode(value Value) {
for _, w := range m {
w.WriteNode(value)
}
}
// callbackWriter implements ProofWriter with a simple callback mechanism
type callbackWriter struct {
format ProofFormat
index uint64
storeCallback func(uint64, Value)
}
// NewCallbackWriter creates a callbackWriter that traverses the tree subset
// defined by the given proof format and calls callbackWriter for each traversed node
func NewCallbackWriter(format ProofFormat, storeCallback func(uint64, Value)) callbackWriter {
return callbackWriter{format: format, index: 1, storeCallback: storeCallback}
}
// children implements ProofWriter
func (cw callbackWriter) Children() (left, right ProofWriter) {
lf, rf := cw.format.Children()
if lf == nil {
return nil, nil
}
return callbackWriter{format: lf, index: cw.index * 2, storeCallback: cw.storeCallback},
callbackWriter{format: rf, index: cw.index*2 + 1, storeCallback: cw.storeCallback}
}
// writeNode implements ProofWriter
func (cw callbackWriter) WriteNode(node Value) {
cw.storeCallback(cw.index, node)
}