Skip to content

Commit fcb2328

Browse files
authored
Ensure leafs are 32-bytes (#51)
1 parent e05293e commit fcb2328

File tree

6 files changed

+68
-45
lines changed

6 files changed

+68
-45
lines changed

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ go 1.23.2
55
require (
66
github.com/minio/sha256-simd v1.0.1
77
github.com/stretchr/testify v1.9.0
8+
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
89
)
910

1011
require (
1112
github.com/davecgh/go-spew v1.1.1 // indirect
12-
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
13+
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
1314
github.com/kr/pretty v0.3.1 // indirect
1415
github.com/pmezard/go-difflib v1.0.0 // indirect
15-
golang.org/x/sys v0.25.0 // indirect
16+
golang.org/x/sys v0.26.0 // indirect
1617
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
1718
gopkg.in/yaml.v3 v3.0.1 // indirect
1819
)

go.sum

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
22
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
33
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4-
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
5-
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
4+
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
5+
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
66
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
77
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
88
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -19,9 +19,11 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
1919
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
2020
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
2121
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
22-
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
23-
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
24-
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
22+
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
23+
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
24+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
25+
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
26+
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
2527
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2628
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
2729
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

iterators.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,23 @@ package merkle
22

33
import (
44
"errors"
5-
"sort"
5+
"slices"
6+
7+
"golang.org/x/exp/maps"
68
)
79

810
var noMoreItems = errors.New("no more items")
911

1012
type Set map[uint64]bool
1113

1214
func (s Set) AsSortedSlice() []uint64 {
13-
var ret []uint64
14-
for key, value := range s {
15-
if value {
16-
ret = append(ret, key)
17-
}
18-
}
19-
sort.Slice(ret, func(i, j int) bool { return ret[i] < ret[j] })
15+
ret := maps.Keys(s)
16+
slices.Sort(ret)
2017
return ret
2118
}
2219

2320
func SetOf(members ...uint64) Set {
24-
ret := make(Set)
21+
ret := make(Set, len(members))
2522
for _, member := range members {
2623
ret[member] = true
2724
}

merkle.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ type Tree struct {
104104
// AddLeaf incorporates a new leaf to the state of the tree. It updates the state required to eventually determine the
105105
// root of the tree and also updates the proof, if applicable.
106106
func (t *Tree) AddLeaf(value []byte) error {
107+
if len(value) != NodeSize {
108+
return fmt.Errorf("expected node size %d, got %d", NodeSize, len(value))
109+
}
107110
n := node{
108111
value: value,
109112
OnProvenPath: t.leavesToProve.Pop(),

merkle_test.go

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -466,49 +466,64 @@ func TestTree_GetParkedNodes(t *testing.T) {
466466
tree, err := NewTreeBuilder().Build()
467467
r.NoError(err)
468468

469-
r.NoError(tree.AddLeaf([]byte{0}))
470-
r.EqualValues(
471-
[][]byte{{0}},
472-
tree.GetParkedNodes(nil))
469+
leaf0 := [32]byte{}
470+
r.NoError(tree.AddLeaf(leaf0[:]))
471+
r.EqualValues([][]byte{leaf0[:]}, tree.GetParkedNodes(nil))
473472

474-
r.NoError(tree.AddLeaf([]byte{1}))
473+
leaf1 := [32]byte{}
474+
leaf1[0] = 1
475+
r.NoError(tree.AddLeaf(leaf1[:]))
475476
r.EqualValues(
476-
[][]byte{{}, decode(r, "b413f47d13ee2fe6c845b2ee141af81de858df4ec549a58b7970bb96645bc8d2")},
477-
tree.GetParkedNodes(nil))
477+
[][]byte{{}, decode(r, "cb592844121d926f1ca3ad4e1d6fb9d8e260ed6e3216361f7732e975a0e8bbf6")},
478+
tree.GetParkedNodes(nil),
479+
)
478480

479-
r.NoError(tree.AddLeaf([]byte{2}))
481+
leaf2 := [32]byte{}
482+
leaf2[0] = 2
483+
r.NoError(tree.AddLeaf(leaf2[:]))
480484
r.EqualValues(
481-
[][]byte{{2}, decode(r, "b413f47d13ee2fe6c845b2ee141af81de858df4ec549a58b7970bb96645bc8d2")},
482-
tree.GetParkedNodes(nil))
485+
[][]byte{leaf2[:], decode(r, "cb592844121d926f1ca3ad4e1d6fb9d8e260ed6e3216361f7732e975a0e8bbf6")},
486+
tree.GetParkedNodes(nil),
487+
)
483488

484-
r.NoError(tree.AddLeaf([]byte{3}))
489+
leaf3 := [32]byte{}
490+
leaf3[0] = 3
491+
r.NoError(tree.AddLeaf(leaf3[:]))
485492
r.EqualValues(
486-
[][]byte{{}, {}, decode(r, "7699a4fdd6b8b6908a344f73b8f05c8e1400f7253f544602c442ff5c65504b24")},
487-
tree.GetParkedNodes(nil))
493+
[][]byte{{}, {}, decode(r, "ba94ffe7edabf26ef12736f8eb5ce74d15bedb6af61444ae2906e926b1a95084")},
494+
tree.GetParkedNodes(nil),
495+
)
488496
}
489497

490498
func TestTree_SetParkedNodes(t *testing.T) {
491499
r := require.New(t)
492500

501+
node0 := [32]byte{}
502+
leaf1 := [32]byte{}
503+
leaf1[0] = 1
493504
tree, err := NewTreeBuilder().Build()
494505
r.NoError(err)
495-
r.NoError(tree.SetParkedNodes([][]byte{{0}}))
496-
r.NoError(tree.AddLeaf([]byte{1}))
497-
parkedNodes := [][]byte{{}, decode(r, "b413f47d13ee2fe6c845b2ee141af81de858df4ec549a58b7970bb96645bc8d2")}
506+
r.NoError(tree.SetParkedNodes([][]byte{node0[:]}))
507+
r.NoError(tree.AddLeaf(leaf1[:]))
508+
parkedNodes := [][]byte{{}, decode(r, "cb592844121d926f1ca3ad4e1d6fb9d8e260ed6e3216361f7732e975a0e8bbf6")}
498509
r.EqualValues(parkedNodes, tree.GetParkedNodes(nil))
499510

511+
leaf2 := [32]byte{}
512+
leaf2[0] = 2
500513
tree, err = NewTreeBuilder().Build()
501514
r.NoError(err)
502515
r.NoError(tree.SetParkedNodes(parkedNodes))
503-
r.NoError(tree.AddLeaf([]byte{2}))
504-
parkedNodes = [][]byte{{2}, decode(r, "b413f47d13ee2fe6c845b2ee141af81de858df4ec549a58b7970bb96645bc8d2")}
516+
r.NoError(tree.AddLeaf(leaf2[:]))
517+
parkedNodes = [][]byte{leaf2[:], decode(r, "cb592844121d926f1ca3ad4e1d6fb9d8e260ed6e3216361f7732e975a0e8bbf6")}
505518
r.EqualValues(parkedNodes, tree.GetParkedNodes(nil))
506519

520+
leaf3 := [32]byte{}
521+
leaf3[0] = 3
507522
tree, err = NewTreeBuilder().Build()
508523
r.NoError(err)
509524
r.NoError(tree.SetParkedNodes(parkedNodes))
510-
r.NoError(tree.AddLeaf([]byte{3}))
511-
parkedNodes = [][]byte{{}, {}, decode(r, "7699a4fdd6b8b6908a344f73b8f05c8e1400f7253f544602c442ff5c65504b24")}
525+
r.NoError(tree.AddLeaf(leaf3[:]))
526+
parkedNodes = [][]byte{{}, {}, decode(r, "ba94ffe7edabf26ef12736f8eb5ce74d15bedb6af61444ae2906e926b1a95084")}
512527
r.EqualValues(parkedNodes, tree.GetParkedNodes(nil))
513528
}
514529

validation.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ import (
44
"bytes"
55
"errors"
66
"fmt"
7-
"sort"
7+
8+
"golang.org/x/exp/slices"
89
)
910

1011
const MaxUint = ^uint(0)
1112

1213
// ValidatePartialTree uses leafIndices, leaves and proof to calculate the merkle root of the tree and then compares it
1314
// to expectedRoot.
14-
func ValidatePartialTree(leafIndices []uint64, leaves, proof [][]byte, expectedRoot []byte,
15+
func ValidatePartialTree(
16+
leafIndices []uint64,
17+
leaves, proof [][]byte,
18+
expectedRoot []byte,
1519
hash HashFunc,
1620
) (bool, error) {
1721
v, err := newValidator(leafIndices, leaves, proof, hash, false)
@@ -25,7 +29,10 @@ func ValidatePartialTree(leafIndices []uint64, leaves, proof [][]byte, expectedR
2529
// ValidatePartialTree uses leafIndices, leaves and proof to calculate the merkle root of the tree and then compares it
2630
// to expectedRoot. Additionally, it reconstructs the parked nodes when each proven leaf was originally added to the
2731
// tree and returns a list of snapshots. This method is ~15% slower than ValidatePartialTree.
28-
func ValidatePartialTreeWithParkingSnapshots(leafIndices []uint64, leaves, proof [][]byte, expectedRoot []byte,
32+
func ValidatePartialTreeWithParkingSnapshots(
33+
leafIndices []uint64,
34+
leaves, proof [][]byte,
35+
expectedRoot []byte,
2936
hash HashFunc,
3037
) (bool, []ParkingSnapshot, error) {
3138
v, err := newValidator(leafIndices, leaves, proof, hash, true)
@@ -38,22 +45,20 @@ func ValidatePartialTreeWithParkingSnapshots(leafIndices []uint64, leaves, proof
3845

3946
func newValidator(
4047
leafIndices []uint64,
41-
leaves,
42-
proof [][]byte,
48+
leaves, proof [][]byte,
4349
hash HashFunc,
4450
storeSnapshots bool,
4551
) (*Validator, error) {
4652
if len(leafIndices) != len(leaves) {
47-
return nil, fmt.Errorf("number of leaves (%d) must equal number of indices (%d)", len(leaves),
48-
len(leafIndices))
53+
return nil, fmt.Errorf("number of leaves (%d) must equal number of indices (%d)", len(leaves), len(leafIndices))
4954
}
5055
if len(leaves) == 0 {
5156
return nil, errors.New("at least one leaf is required for validation")
5257
}
53-
if !sort.SliceIsSorted(leafIndices, func(i, j int) bool { return leafIndices[i] < leafIndices[j] }) {
58+
if !slices.IsSorted(leafIndices) {
5459
return nil, errors.New("leafIndices are not sorted")
5560
}
56-
if len(SetOf(leafIndices...)) != len(leafIndices) {
61+
if len(slices.Compact(leafIndices)) != len(leafIndices) {
5762
return nil, errors.New("leafIndices contain duplicates")
5863
}
5964
proofNodes := &proofIterator{proof}

0 commit comments

Comments
 (0)