erigon-pulse/cl/merkle_tree/merkle_root.go
a 37da9ec1e3
[caplin] ssz byteobjects (#7454)
instead of converting from ssz -> struct -> ssz, it may be better to
just stay as ssz, then use methods to read the data.

this pr explores this concept, while maintaining compatiblity with the
existing codebase.
2023-05-10 21:37:50 +02:00

96 lines
2.7 KiB
Go

package merkle_tree
import (
"errors"
"reflect"
"unsafe"
"github.com/prysmaticlabs/gohashtree"
)
// HashByteSlice is gohashtree HashBytSlice but using our hopefully safer header converstion
func HashByteSlice(out, in []byte) error {
if len(in) == 0 {
return errors.New("zero leaves provided")
}
if len(out)%32 != 0 {
return errors.New("output must be multple of 32")
}
if len(in)%64 != 0 {
return errors.New("input must be multple of 64")
}
c_in := convertHeader(in)
c_out := convertHeader(out)
err := gohashtree.Hash(c_out, c_in)
if err != nil {
return err
}
return nil
}
func convertHeader(xs []byte) [][32]byte {
// this commented naive method of conversion supposedly leads to corruption https://github.com/golang/go/issues/40701
//header := *(*reflect.SliceHeader)(unsafe.Pointer(&xs))
//header.Len /= 32
//header.Cap /= 32
//chunkedChunks := *(*[][32]byte)(unsafe.Pointer(&header))
//supposedly, this is because escape analysis does not correctly analyze this, and so you have this ghost header?
// i wont pretend to understand, but my solution for the problem is as so
// first i grab the slice header of the input
header := (*reflect.SliceHeader)(unsafe.Pointer(&xs))
// then i allocate a new result slice of no size - this should make the escape analyzer happy i think?
dat := make([][32]byte, 0)
// we then get the header of our output to modify
chunkedHeader := (*reflect.SliceHeader)(unsafe.Pointer(&dat))
// then we move over the values
chunkedHeader.Len = header.Len / 32
chunkedHeader.Cap = header.Cap / 32
chunkedHeader.Data = header.Data
return dat
}
func MerkleRootFromLeaves(leaves [][32]byte) ([32]byte, error) {
if len(leaves) == 0 {
return [32]byte{}, errors.New("zero leaves provided")
}
if len(leaves) == 1 {
return leaves[0], nil
}
hashLayer := leaves
return globalHasher.merkleizeTrieLeaves(hashLayer)
}
func MerkleRootFromFlatLeaves(leaves []byte, out []byte) (err error) {
if len(leaves) <= 32 {
copy(out, leaves)
return
}
return globalHasher.merkleizeTrieLeavesFlat(leaves, out)
}
// getDepth returns the depth of a merkle tree with a given number of nodes.
// The depth is defined as the number of levels in the tree, with the root
// node at level 0 and each child node at a level one greater than its parent.
// If the number of nodes is less than or equal to 1, the depth is 0.
func getDepth(v uint64) uint8 {
// If there are 0 or 1 nodes, the depth is 0.
if v <= 1 {
return 0
}
// Initialize the depth to 0.
depth := uint8(0)
// Divide the number of nodes by 2 until it is less than or equal to 1.
// The number of iterations is the depth of the tree.
for v > 1 {
v >>= 1
depth++
}
return depth
}