-
Notifications
You must be signed in to change notification settings - Fork 56
Expand file tree
/
Copy pathmt.go
More file actions
133 lines (115 loc) · 3.57 KB
/
mt.go
File metadata and controls
133 lines (115 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// In-memory Merkle Tree which implements an authenticated list.
package mt
import (
"crypto/sha256"
"fmt"
"iter"
)
type Digest = [sha256.Size]byte
var MerklePlaceholderDigest = Digest([]byte("MERKLE_PLACEHOLDER_HASH_________"))
var LeafSeparator = []byte("MT::LeafNode")
var InternalSeparator = []byte("MT::InternalNode")
func digestInternal(leftChildDigest Digest, rightChildDigest Digest) Digest {
hash := sha256.New()
hash.Write(InternalSeparator)
hash.Write(leftChildDigest[:])
hash.Write(rightChildDigest[:])
return Digest(hash.Sum(nil))
}
func digestLeaf(preimage []byte) Digest {
hash := sha256.New()
hash.Write(LeafSeparator)
hash.Write(preimage)
return Digest(hash.Sum(nil))
}
func buildTreeLevels(leafPreimages [][]byte) iter.Seq[[]Digest] {
return func(yield func([]Digest) bool) {
leafCount := len(leafPreimages)
if leafCount == 0 {
if !yield([]Digest{MerklePlaceholderDigest}) {
return
}
}
// Start with the leaf digests
currentLayer := make([]Digest, leafCount)
for i, leafPreimage := range leafPreimages {
currentLayer[i] = digestLeaf(leafPreimage)
}
if !yield(currentLayer) {
return
}
// Build the tree upwards, padding with placeholders when odd number of nodes
for len(currentLayer) > 1 {
nextLayerSize := (len(currentLayer) + 1) / 2 // Ceiling division
nextLayer := make([]Digest, 0, nextLayerSize)
for i := 0; i < len(currentLayer); i += 2 {
leftDigest := currentLayer[i]
rightDigest := MerklePlaceholderDigest
if i+1 < len(currentLayer) {
rightDigest = currentLayer[i+1]
}
nextLayer = append(nextLayer, digestInternal(leftDigest, rightDigest))
}
currentLayer = nextLayer
if !yield(currentLayer) {
return
}
}
}
}
// Root computes the Merkle tree root from leaf preimages.
func Root(leafPreimages [][]byte) Digest {
var rootDigest Digest
for treeLevel := range buildTreeLevels(leafPreimages) {
if len(treeLevel) == 1 {
rootDigest = treeLevel[0]
}
}
return rootDigest
}
// Prove generates a Merkle inclusion proof that the leafPreimage at index is
// included in the tree rooted at Root(leafPreimages).
func Prove(leafPreimages [][]byte, index uint64) ([]Digest, error) {
leafCount := len(leafPreimages)
if leafCount == 0 {
return nil, fmt.Errorf("cannot prove inclusion in empty tree")
}
if index >= uint64(leafCount) {
return nil, fmt.Errorf("index %d is out of bounds for %d leaves", index, leafCount)
}
var proof []Digest
currentIndex := index
for treeLevel := range buildTreeLevels(leafPreimages) {
if len(treeLevel) <= 1 {
break
}
siblingIndex := currentIndex ^ 1
siblingDigest := MerklePlaceholderDigest
if siblingIndex < uint64(len(treeLevel)) {
siblingDigest = treeLevel[siblingIndex]
}
proof = append(proof, siblingDigest)
currentIndex /= 2
}
return proof, nil
}
// Verify verifies that leafPreimage is preimage of the index-th leaf in the
// Merkle tree rooted at expectedRootDigest.
func Verify(expectedRootDigest Digest, index uint64, leafPreimage []byte, proof []Digest) error {
currentDigest := digestLeaf(leafPreimage)
currentIndex := index
for _, siblingDigest := range proof {
if currentIndex%2 == 0 {
// Current node is left child, sibling is right
currentDigest = digestInternal(currentDigest, siblingDigest)
} else {
// Current node is right child, sibling is left
currentDigest = digestInternal(siblingDigest, currentDigest)
}
currentIndex /= 2
}
if currentDigest != expectedRootDigest {
return fmt.Errorf("computed root digest mismatch: computed %x, expected %x", currentDigest, expectedRootDigest)
}
return nil
}