Skip to content

Commit

Permalink
Support sortedPairs/sortedLeaves/fillDefault options
Browse files Browse the repository at this point in the history
  • Loading branch information
exqlnet committed Mar 13, 2024
1 parent 298aa3e commit c07626f
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 20 deletions.
51 changes: 39 additions & 12 deletions merkletree.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ import (
"bytes"
"encoding/binary"
"encoding/hex"
"github.com/pkg/errors"
"math"
"sort"

"github.com/pkg/errors"
)

// MerkleTree is the structure for the Merkle tree.
Expand Down Expand Up @@ -212,19 +211,22 @@ func NewTree(params ...Parameter) (*MerkleTree, error) {
nodes[branchesLen:branchesLen+len(parameters.data)],
parameters.hash,
parameters.salt,
parameters.sorted,
parameters.sorted || parameters.sortedLeaves,
parameters.fillDefault,
)
// Pad the space left after the leaves.
for i := len(parameters.data) + branchesLen; i < len(nodes); i++ {
nodes[i] = make([]byte, parameters.hash.HashLength())
if parameters.fillDefault {
for i := len(parameters.data) + branchesLen; i < len(nodes); i++ {
nodes[i] = make([]byte, parameters.hash.HashLength())
}
}

// Branches.
createBranches(
nodes,
parameters.hash,
branchesLen,
parameters.sorted,
parameters.sorted || parameters.sortedPairs,
)

tree := &MerkleTree{
Expand All @@ -248,7 +250,7 @@ func New(data [][]byte) (*MerkleTree, error) {
// Hashes the data slice, placing the result hashes into dest.
// salt adds a salt to the hash using the index.
// sorted sorts the leaves and data by the value of the leaf hash.
func createLeaves(data [][]byte, dest [][]byte, hash HashType, salt, sorted bool) {
func createLeaves(data [][]byte, dest [][]byte, hash HashType, salt, sortedLeaves, fillDefault bool) {
indexSalt := make([]byte, 4)
for i := range data {
if salt {
Expand All @@ -259,7 +261,7 @@ func createLeaves(data [][]byte, dest [][]byte, hash HashType, salt, sorted bool
}
}

if sorted {
if sortedLeaves {
sorter := hashSorter{
data: data,
hashes: dest,
Expand All @@ -268,16 +270,41 @@ func createLeaves(data [][]byte, dest [][]byte, hash HashType, salt, sorted bool
}
}

func isZeroBytes(s []byte) bool {
for _, v := range s {
if v != 0 {
return false
}
}
return true
}

// Create the branch nodes from the existing leaf data.
func createBranches(nodes [][]byte, hash HashType, leafOffset int, sorted bool) {
func createBranches(nodes [][]byte, hash HashType, leafOffset int, sortedPairs bool) {
for leafIndex := leafOffset - 1; leafIndex > 0; leafIndex-- {
left := nodes[leafIndex*2]
right := nodes[leafIndex*2+1]

if sorted && bytes.Compare(left, right) == 1 {
nodes[leafIndex] = hash.Hash(right, left)
var pairs [][]byte
if len(left) != 0 {
pairs = append(pairs, left)
}
if len(right) != 0 {
pairs = append(pairs, right)
}

if sortedPairs {
sort.Slice(pairs, func(i, j int) bool {
return bytes.Compare(pairs[i], pairs[j]) < 0
})
}

if len(pairs) == 1 {
nodes[leafIndex] = pairs[0]
} else if len(pairs) > 1 {
nodes[leafIndex] = hash.Hash(pairs...)
} else {
nodes[leafIndex] = hash.Hash(left, right)
nodes[leafIndex] = []byte{}
}
}
}
Expand Down
38 changes: 30 additions & 8 deletions parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ import (
)

type parameters struct {
data [][]byte
values uint64
hashes map[uint64][]byte
indices []uint64
salt bool
sorted bool
hash HashType
data [][]byte
values uint64
hashes map[uint64][]byte
indices []uint64
salt bool
sorted bool
sortedPairs bool
sortedLeaves bool
fillDefault bool
hash HashType
}

// Parameter is the interface for service parameters.
Expand Down Expand Up @@ -94,10 +97,29 @@ func WithHashType(hash HashType) Parameter {
})
}

func WithSortedPairs(sortedPairs bool) Parameter {
return parameterFunc(func(p *parameters) {
p.sortedPairs = sortedPairs
})
}

func WithSortedLeaves(sortedLeaves bool) Parameter {
return parameterFunc(func(p *parameters) {
p.sortedLeaves = sortedLeaves
})
}

func WithFillDefault(fillDefault bool) Parameter {
return parameterFunc(func(p *parameters) {
p.fillDefault = fillDefault
})
}

// parseAndCheckTreeParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
func parseAndCheckTreeParameters(params ...Parameter) (*parameters, error) {
parameters := parameters{
hash: blake2b.New(),
hash: blake2b.New(),
fillDefault: true,
}
for _, p := range params {
if params != nil {
Expand Down

0 comments on commit c07626f

Please sign in to comment.