Skip to content

Commit 7974fbe

Browse files
authored
Merge pull request #5 from dk-open/perf/mmr-initialisation-improvements
Multiple performance improvements
2 parents cb7fed8 + 711a5f2 commit 7974fbe

14 files changed

Lines changed: 447 additions & 153 deletions

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ go get -u github.com/discretemind/mmr
7676
```go
7777
package main
7878

79-
8079
import (
8180
"context"
8281
"fmt"
@@ -91,24 +90,30 @@ func main() {
9190
ctx := context.Background()
9291
memoryIndexes := store.MemoryIndexSource[uint64, types.Hash256]()
9392
m := merkle.NewMountainRange[uint64, types.Hash256](hasher.Sha3_256, memoryIndexes)
94-
93+
var transactions []types.Hash256
9594
for i := 0; i < 10; i++ {
9695
h := hasher.Sha3_256([]byte(fmt.Sprintf("test data %d", i)))
96+
transactions = append(transactions, h)
9797
fmt.Printf("Adding at %d item %x\n", i, h)
98-
if err := m.Add(ctx, h); err != nil {
99-
log.Fatal(err)
100-
}
98+
99+
}
100+
if err := m.Add(ctx, transactions...); err != nil {
101+
log.Fatal(err)
101102
}
102103
fmt.Println()
103-
root := m.Root()
104+
root, err := m.Root(ctx)
105+
if err != nil {
106+
log.Fatal(err)
107+
}
108+
104109
fmt.Printf("Root: %x\n", root)
105110
item3, err := m.Get(ctx, 3)
106111
if err != nil {
107112
log.Fatal(err)
108113
}
109114
fmt.Printf("Item at index 3: %x\n", item3)
110115
fmt.Println("Create a proof for item 4")
111-
prooft, err := m.CreateProof(ctx, 4)
116+
prooft, err := m.ProofByIndex(ctx, 4)
112117
if err != nil {
113118
log.Fatal(err)
114119
}
@@ -118,7 +123,7 @@ func main() {
118123
log.Fatal("Proof is not invalid")
119124
}
120125

121-
fmt.Println("Proof is valid")
126+
fmt.Println("Proof is Valid")
122127
}
123128

124129
```

merkle/index/index.leaf.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ func (l *leafIndex[TI]) GetSibling() Index[TI] {
4949

5050
// RightUp moves the current node to its parent if it's a right child in the tree hierarchy.
5151
func (l *leafIndex[TI]) RightUp() Index[TI] {
52-
// value := x.Index()
5352
if l.IsRight() {
5453
return NodeIndex(l.value)
5554
}

merkle/index/index.node_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package index_test
2+
3+
import (
4+
"github.com/dk-open/go-mmr/merkle/index"
5+
"github.com/stretchr/testify/assert"
6+
"testing"
7+
)
8+
9+
func getNodeIndex(value uint32) index.Index[uint32] {
10+
return index.NodeIndex[uint32](value)
11+
}
12+
13+
func TestNodeIndex_Creation(t *testing.T) {
14+
// Test creating a node index
15+
value := uint32(4)
16+
node := getNodeIndex(value)
17+
assert.Equal(t, value, node.Index(), "Node index should be set correctly")
18+
assert.Equal(t, 2, node.GetHeight(), "Height should be calculated correctly")
19+
}
20+
21+
func TestNodeIndex_LeftBranch(t *testing.T) {
22+
// Test left branch calculation
23+
node := getNodeIndex(uint32(6))
24+
leftBranch := node.LeftBranch()
25+
assert.NotNil(t, leftBranch, "Left branch should exist")
26+
assert.Equal(t, uint32(2), leftBranch.Index(), "Left branch should be calculated correctly")
27+
}
28+
29+
func TestNodeIndex_GetSibling(t *testing.T) {
30+
// Test sibling calculation for both left and right nodes
31+
node := getNodeIndex(uint32(3)) // Right sibling
32+
sibling := node.GetSibling()
33+
assert.Equal(t, uint32(1), sibling.Index(), "Right sibling should be calculated correctly")
34+
35+
node = getNodeIndex(uint32(2)) // Left sibling
36+
sibling = node.GetSibling()
37+
assert.Equal(t, uint32(6), sibling.Index(), "Left sibling should be calculated correctly")
38+
}
39+
40+
func TestNodeIndex_RightUp(t *testing.T) {
41+
// Test moving to the parent node from a right child
42+
node := getNodeIndex(uint32(6)) // Right child of parent
43+
parent := node.RightUp()
44+
assert.NotNil(t, parent, "Parent should exist")
45+
assert.Equal(t, uint32(4), parent.Index(), "Parent node should be calculated correctly")
46+
47+
// Test when already at the top
48+
node = getNodeIndex(uint32(4)) // Top node
49+
parent = node.RightUp()
50+
assert.Nil(t, parent, "There should be no parent when already at the top")
51+
}
52+
53+
func TestNodeIndex_Up(t *testing.T) {
54+
// Test moving up in the hierarchy
55+
node := getNodeIndex(uint32(3)) // Right child
56+
parent := node.Up()
57+
assert.Equal(t, uint32(2), parent.Index(), "Parent node should be calculated correctly")
58+
59+
node = getNodeIndex(uint32(2)) // Left child
60+
parent = node.Up()
61+
assert.Equal(t, uint32(4), parent.Index(), "Parent node should be calculated correctly")
62+
}
63+
64+
func TestNodeIndex_IsRight(t *testing.T) {
65+
// Test checking if a node is a right child
66+
node := getNodeIndex(uint32(3)) // Right child
67+
assert.True(t, node.IsRight(), "Node should be identified as a right child")
68+
69+
node = getNodeIndex(uint32(2)) // Left child
70+
assert.False(t, node.IsRight(), "Node should be identified as a left child")
71+
}
72+
73+
func TestNodeIndex_Top(t *testing.T) {
74+
// Test finding the top ancestor
75+
node := getNodeIndex(uint32(6)) // Start at node 6
76+
top := node.Top()
77+
assert.Equal(t, uint32(4), top.Index(), "Top ancestor should be calculated correctly")
78+
}
79+
80+
func TestNodeIndex_Children(t *testing.T) {
81+
// Test retrieving the children of a node
82+
node := getNodeIndex(uint32(6)) // Node with height > 0
83+
children := node.Children()
84+
assert.Len(t, children, 2, "Node should have two children")
85+
assert.Equal(t, uint32(5), children[0].Index(), "Left child should be calculated correctly")
86+
assert.Equal(t, uint32(7), children[1].Index(), "Right child should be calculated correctly")
87+
88+
node = getNodeIndex(uint32(1)) // Node with height 0
89+
children = node.Children()
90+
assert.Len(t, children, 2, "Leaf node should have two children")
91+
assert.Equal(t, uint32(0), children[0].Index(), "Left child should be calculated correctly for leaf")
92+
assert.Equal(t, uint32(1), children[1].Index(), "Right child should be calculated correctly for leaf")
93+
}
94+
95+
func TestNodeIndex_IsLeaf(t *testing.T) {
96+
// Test checking if a node is a leaf
97+
node := getNodeIndex(uint32(1)) // Node at height 0
98+
assert.False(t, node.IsLeaf(), "Node should not be identified as a leaf")
99+
}
100+
101+
func TestNodeIndex_Key(t *testing.T) {
102+
// Test generating the key for the node
103+
node := getNodeIndex(uint32(5))
104+
key := node.Key()
105+
assert.Equal(t, "node_5", key, "Node key should be generated correctly")
106+
}

merkle/index/index_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,4 @@ func TestPeaks(t *testing.T) {
9898
assert.Equal(t, 13, peaks14[1].Index())
9999
assert.Equal(t, 10, peaks14[2].Index())
100100
assert.Equal(t, 4, peaks14[3].Index())
101-
102101
}

merkle/index/node.leaf_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package index_test
2+
3+
import (
4+
"github.com/dk-open/go-mmr/merkle/index"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func getLeafIndex(value uint32) index.Index[uint32] {
11+
return index.LeafIndex[uint32](value)
12+
}
13+
14+
func TestLeafIndex_Creation(t *testing.T) {
15+
// Test creating a leaf index
16+
value := uint32(5)
17+
leaf := getLeafIndex(value)
18+
assert.Equal(t, value, leaf.Index(), "Leaf index should be set correctly")
19+
}
20+
21+
func TestLeafIndex_IsLeaf(t *testing.T) {
22+
// Test if the node is correctly identified as a leaf
23+
leaf := getLeafIndex(uint32(3))
24+
assert.True(t, leaf.IsLeaf(), "Leaf index should return true for IsLeaf")
25+
}
26+
27+
func TestLeafIndex_GetHeight(t *testing.T) {
28+
// Test that the height of a leaf node is always 0
29+
leaf := getLeafIndex(uint32(3))
30+
assert.Equal(t, 0, leaf.GetHeight(), "Leaf height should always be 0")
31+
}
32+
33+
func TestLeafIndex_LeftBranch(t *testing.T) {
34+
// Test left branch calculation
35+
leaf := getLeafIndex(uint32(2)) // Left child
36+
leftBranch := leaf.LeftBranch()
37+
assert.NotNil(t, leftBranch, "Left branch should exist for non-zero, non-right node")
38+
assert.Equal(t, uint32(1), leftBranch.Index(), "Left branch should be correctly calculated")
39+
40+
// Test no left branch for node 0
41+
leaf = getLeafIndex(uint32(0)) // Node 0 has no left branch
42+
leftBranch = leaf.LeftBranch()
43+
assert.Nil(t, leftBranch, "Left branch should not exist for node 0")
44+
}
45+
46+
func TestLeafIndex_GetSibling(t *testing.T) {
47+
// Test sibling calculation for both left and right nodes
48+
leaf := getLeafIndex(uint32(3)) // Right sibling
49+
sibling := leaf.GetSibling()
50+
assert.Equal(t, uint32(2), sibling.Index(), "Right sibling should be calculated correctly")
51+
52+
leaf = getLeafIndex(uint32(2)) // Left sibling
53+
sibling = leaf.GetSibling()
54+
assert.Equal(t, uint32(3), sibling.Index(), "Left sibling should be calculated correctly")
55+
}
56+
57+
func TestLeafIndex_RightUp(t *testing.T) {
58+
// Test moving to the parent node from a right child
59+
leaf := getLeafIndex(uint32(3)) // Right child of parent
60+
parent := leaf.RightUp()
61+
assert.NotNil(t, parent, "Parent should exist")
62+
assert.Equal(t, uint32(3), parent.Index(), "Parent node should be calculated correctly")
63+
64+
// Test when there is no parent
65+
leaf = getLeafIndex(uint32(0)) // Node 0 has no parent
66+
parent = leaf.RightUp()
67+
assert.Nil(t, parent, "There should be no parent for node 0")
68+
}
69+
70+
func TestLeafIndex_Up(t *testing.T) {
71+
// Test moving up in the hierarchy
72+
leaf := getLeafIndex(uint32(3)) // Right child
73+
parent := leaf.Up()
74+
assert.Equal(t, uint32(3), parent.Index(), "Parent node should be calculated correctly")
75+
76+
leaf = getLeafIndex(uint32(2)) // Left child
77+
parent = leaf.Up()
78+
assert.Equal(t, uint32(3), parent.Index(), "Parent node should be calculated correctly")
79+
}
80+
81+
func TestLeafIndex_IsRight(t *testing.T) {
82+
// Test checking if a leaf is a right child
83+
leaf := getLeafIndex(uint32(3)) // Right child
84+
assert.True(t, leaf.IsRight(), "Leaf should be identified as a right child")
85+
86+
leaf = getLeafIndex(uint32(2)) // Left child
87+
assert.False(t, leaf.IsRight(), "Leaf should be identified as a left child")
88+
}
89+
90+
func TestLeafIndex_Top(t *testing.T) {
91+
// Test finding the top ancestor
92+
leaf := getLeafIndex(uint32(3)) // Start at leaf 3
93+
top := leaf.Top()
94+
assert.Equal(t, uint32(2), top.Index(), "Top ancestor should be calculated correctly")
95+
96+
leaf = getLeafIndex(uint32(2)) // Start at leaf 2
97+
top = leaf.Top()
98+
assert.Equal(t, uint32(2), top.Index(), "Top ancestor should return the leaf itself when it is the top")
99+
}
100+
101+
func TestLeafIndex_Children(t *testing.T) {
102+
// Test that leaf nodes have no children
103+
leaf := getLeafIndex(uint32(3))
104+
children := leaf.Children()
105+
assert.Nil(t, children, "Leaf nodes should have no children")
106+
}
107+
108+
func TestLeafIndex_Key(t *testing.T) {
109+
// Test generating the key for the leaf
110+
leaf := getLeafIndex(uint32(5))
111+
key := leaf.Key()
112+
assert.Equal(t, "leaf_5", key, "Leaf key should be generated correctly")
113+
}

merkle/merkle.go

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@ import (
1010
)
1111

1212
type IMountainRange[TIndex index.Value, THash types.HashType] interface {
13-
Add(ctx context.Context, value THash) error
13+
Add(ctx context.Context, values ...THash) error
1414
Get(ctx context.Context, index TIndex) (THash, error)
15-
CreateProof(ctx context.Context, index TIndex) (*Proof[TIndex, THash], error)
16-
Root() IRoot[TIndex, THash]
15+
ProofByIndex(ctx context.Context, index TIndex) (*Proof[TIndex, THash], error)
16+
Proof(ctx context.Context, item THash) (*Proof[TIndex, THash], error)
17+
Root(ctx context.Context) (IRoot[TIndex, THash], error)
1718
Size() TIndex
1819
}
1920

2021
type mmr[TIndex index.Value, THash types.HashType] struct {
2122
sync.RWMutex
22-
root THash
23+
//root THash
2324
size TIndex
2425
hf types.Hasher[THash]
2526
indexes store.IIndexSource[TIndex, THash]
@@ -40,11 +41,15 @@ func (m *mmr[TIndex, THash]) Get(ctx context.Context, index TIndex) (res THash,
4041
return
4142
}
4243

43-
func (m *mmr[TIndex, THash]) Add(ctx context.Context, value THash) error {
44+
func (m *mmr[TIndex, THash]) Add(ctx context.Context, value ...THash) error {
4445
m.Lock()
45-
err := m.appendMerkle(ctx, value)
46-
m.Unlock()
47-
return err
46+
defer m.Unlock()
47+
for _, v := range value {
48+
if err := m.appendMerkle(ctx, v); err != nil {
49+
return err
50+
}
51+
}
52+
return nil
4853
}
4954

5055
// getProofIndexes collects the indexes needed to create a proof for the given item.
@@ -65,7 +70,15 @@ func (m *mmr[TIndex, THash]) getProofIndexes(item index.Index[TIndex], maxIndex
6570
return res
6671
}
6772

68-
func (m *mmr[TIndex, THash]) CreateProof(ctx context.Context, i TIndex) (*Proof[TIndex, THash], error) {
73+
func (m *mmr[TIndex, THash]) Proof(ctx context.Context, item THash) (*Proof[TIndex, THash], error) {
74+
leafIndex, err := m.indexes.LeafIndex(ctx, item)
75+
if err != nil {
76+
return nil, err
77+
}
78+
return m.ProofByIndex(ctx, leafIndex)
79+
}
80+
81+
func (m *mmr[TIndex, THash]) ProofByIndex(ctx context.Context, i TIndex) (*Proof[TIndex, THash], error) {
6982
m.RLock()
7083
defer m.RUnlock()
7184

@@ -132,6 +145,24 @@ func (m *mmr[TIndex, THash]) Size() TIndex {
132145
return m.size
133146
}
134147

135-
func (m *mmr[TIndex, THash]) Root() IRoot[TIndex, THash] {
136-
return newRoot[TIndex, THash](m.root, m.hf)
148+
func (m *mmr[TIndex, THash]) Root(ctx context.Context) (IRoot[TIndex, THash], error) {
149+
150+
m.RLock()
151+
defer m.RUnlock()
152+
peaks := index.GetPeaks(index.LeafIndex(m.size - 1))
153+
hashes := make([][]byte, len(peaks))
154+
for i, p := range peaks {
155+
h, hErr := m.indexes.Get(ctx, p.IsLeaf(), p.Index())
156+
if hErr != nil {
157+
return nil, hErr
158+
}
159+
data, hErr := types.HashBytes[THash](h)
160+
if hErr != nil {
161+
return nil, hErr
162+
}
163+
hashes[i] = data
164+
}
165+
r := m.hf(hashes...)
166+
167+
return newRoot[TIndex, THash](r, m.hf), nil
137168
}

merkle/merkle.internal.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,6 @@ func (m *mmr[TIndex, THash]) appendMerkle(ctx context.Context, value THash) (err
6363
return err
6464
}
6565

66-
peaks := index.GetPeaks(leafIndex)
67-
hashes := make([][]byte, len(peaks))
68-
for i, p := range peaks {
69-
h, hErr := m.indexes.Get(ctx, p.IsLeaf(), p.Index())
70-
if hErr != nil {
71-
return hErr
72-
}
73-
data, hErr := types.HashBytes[THash](h)
74-
if hErr != nil {
75-
return hErr
76-
}
77-
hashes[i] = data
78-
}
79-
m.root = m.hf(hashes...)
8066
m.size = m.size + 1
8167
return nil
8268
}

0 commit comments

Comments
 (0)