forked from varshard/merkletree
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtree.go
150 lines (120 loc) · 3.32 KB
/
tree.go
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package merkletree
// TODO: Make a custom UnmashalJSON
import (
"encoding/json"
"fmt"
"reflect"
)
// Tree a merkle tree structure
type Tree struct {
Root *Node `json:"root"`
Leaves []*Node `json:"leaves"`
}
// BuildTree build a tree out of Leaves
func (t *Tree) BuildTree() error {
return t.buildTree(t.Leaves)
}
// buildTree build a tree out of a slice of leaves
func (t *Tree) buildTree(leaves []*Node) error {
leavesCount := len(leaves)
if leavesCount < 1 {
return fmt.Errorf("Leaves is empty")
} else if leavesCount == 1 {
t.Root = leaves[0]
} else {
parents := []*Node{}
for i := 0; i < leavesCount; i += 2 {
left := leaves[i]
var right *Node
if i+1 == leavesCount {
right = nil
} else {
right = leaves[i+1]
}
parents = append(parents, NewParentNode(left, right))
}
t.buildTree(parents)
}
t.Leaves = leaves
return nil
}
// AuditProof returns the audit proof hashes to reconstruct the root hash.
func (t *Tree) AuditProof(hash []byte) ([]*ProofHash, error) {
var auditTrail []*ProofHash
var err error
leafNode := t.FindLeaf(hash)
if leafNode != nil {
if leafNode.parent == nil {
return nil, fmt.Errorf("expected leaf hash to have a parent hash")
}
parent := leafNode.parent
auditTrail, err = t.BuildAuditTrail(auditTrail, parent, leafNode)
if err != nil {
return nil, err
}
}
return auditTrail, nil
}
// VerifyAudit verify that if we walk up the tree from a particular leaf, we encounter the expected root hash.
func (t *Tree) VerifyAudit(rootHash []byte, targetHash []byte, auditTrail []*ProofHash) bool {
testHash := targetHash
for _, proof := range auditTrail {
if proof.Direction == RightBranch {
testHash = computeHash(append(testHash, proof.Hash...))
} else {
testHash = computeHash(append(proof.Hash, testHash...))
}
}
return reflect.DeepEqual(rootHash, testHash)
}
// Verify will verify that the rootHash and the targetHash are valid.
func (t *Tree) Verify(rootHash []byte, targetHash []byte) bool {
auditTrail, err := t.AuditProof(targetHash)
if err != nil {
return false
}
return t.VerifyAudit(rootHash, targetHash, auditTrail)
}
// BuildAuditTrail will build a trail composed of hash that are required to replicate the root hash
func (t *Tree) BuildAuditTrail(auditTrail []*ProofHash, parent *Node, child *Node) ([]*ProofHash, error) {
if parent != nil {
if child.parent != parent {
return nil, fmt.Errorf("parent of child is not expected parent")
}
sibling := parent.Left
direction := LeftBranch
if parent.Left == child {
sibling = parent.Right
direction = RightBranch
}
proof := ProofHash{
Hash: sibling.Hash,
Direction: direction,
}
auditTrail = append(auditTrail, &proof)
return t.BuildAuditTrail(auditTrail, parent.parent, parent)
}
return auditTrail, nil
}
// AppendLeaf append a leaf node to Leaves,
// to be built into a tree with BuildTree
func (t *Tree) AppendLeaf(leaf *Node) {
t.Leaves = append(t.Leaves, leaf)
}
// FindLeaf find a leaf node that match supplied hash
func (t *Tree) FindLeaf(hash []byte) *Node {
for _, leaf := range t.Leaves {
if reflect.DeepEqual(leaf.Hash, hash) {
return leaf
}
}
return nil
}
// FromJSON build a tree from JSON
func (t *Tree) FromJSON(raw []byte) error {
if err := json.Unmarshal(raw, t); err != nil {
return err
}
t.BuildTree()
return nil
}