-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathcsmt.go
146 lines (120 loc) · 3.79 KB
/
csmt.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
package csmt
import (
"github.com/phoreproject/synapse/chainhash"
"github.com/phoreproject/synapse/primitives"
)
var emptyHash = chainhash.Hash{}
// isRight checks if the key is in the left or right subtree at a certain level. Level 255 is the root level.
func isRight(key chainhash.Hash, level uint8) bool {
return key[level/8]&(1<<uint(level%8)) != 0
}
// calculateSubtreeHashWithOneLeaf calculates the hash of a subtree with only a single leaf at a certain height.
// atLevel is the height to calculate at.
func calculateSubtreeHashWithOneLeaf(key *chainhash.Hash, value *chainhash.Hash, atLevel uint8) chainhash.Hash {
h := *value
for i := uint8(0); i < atLevel; i++ {
right := isRight(*key, i+1)
// the key is in the right subtree
if right {
h = primitives.CombineHashes(&primitives.EmptyTrees[i], &h)
} else {
h = primitives.CombineHashes(&h, &primitives.EmptyTrees[i])
}
}
return h
}
func insertIntoTree(t TreeDatabaseTransaction, root *Node, key chainhash.Hash, value chainhash.Hash, level uint8) (*Node, error) {
right := isRight(key, level)
if level == 0 {
if root != nil && !root.Empty() {
// remove the old node if it exists
err := t.DeleteNode(root.GetHash())
if err != nil {
return nil, err
}
}
// bottom leafs should have no siblings and a value
return t.NewNode(nil, nil, value)
}
// if this tree is empty and we're inserting, we know it's the only key in the subtree, so let's mark it as such and
// fill in the necessary values
if root == nil || root.Empty() {
return t.NewSingleNode(key, value, calculateSubtreeHashWithOneLeaf(&key, &value, level))
}
leftHash := root.Left()
rightHash := root.Right()
var newLeftBranch *Node
var newRightBranch *Node
if leftHash != nil {
oldLeftBranch, err := t.GetNode(*leftHash)
if err != nil {
return nil, err
}
newLeftBranch = oldLeftBranch
}
if rightHash != nil {
oldRightBranch, err := t.GetNode(*rightHash)
if err != nil {
return nil, err
}
newRightBranch = oldRightBranch
}
// if there is only one key in this subtree,
if root.IsSingle() {
rootKey := root.GetSingleKey()
// this operation is an update
if rootKey.IsEqual(&key) {
// delete the old root
err := t.DeleteNode(root.GetHash())
if err != nil {
return nil, err
}
// calculate the new root hash for this subtree
return t.NewSingleNode(key, value, calculateSubtreeHashWithOneLeaf(&key, &value, level))
}
// check if the old key goes in the left or right
subRight := isRight(rootKey, level)
// we know this is a single, so the left and right should be nil
if subRight {
rightBranchInserted, err := insertIntoTree(t, newRightBranch, rootKey, root.GetSingleValue(), level-1)
if err != nil {
return nil, err
}
newRightBranch = rightBranchInserted
} else {
leftBranchInserted, err := insertIntoTree(t, newLeftBranch, rootKey, root.GetSingleValue(), level-1)
if err != nil {
return nil, err
}
newLeftBranch = leftBranchInserted
}
}
// delete the old root because it was added to left or right branch
err := t.DeleteNode(root.GetHash())
if err != nil {
return nil, err
}
if right {
rightBranchInserted, err := insertIntoTree(t, newRightBranch, key, value, level-1)
if err != nil {
return nil, err
}
newRightBranch = rightBranchInserted
} else {
leftBranchInserted, err := insertIntoTree(t, newLeftBranch, key, value, level-1)
if err != nil {
return nil, err
}
newLeftBranch = leftBranchInserted
}
lv := primitives.EmptyTrees[level-1]
if newLeftBranch != nil && !newLeftBranch.Empty() {
lv = newLeftBranch.GetHash()
}
rv := primitives.EmptyTrees[level-1]
if newRightBranch != nil && !newRightBranch.Empty() {
rv = newRightBranch.GetHash()
}
newHash := primitives.CombineHashes(&lv, &rv)
return t.NewNode(newLeftBranch, newRightBranch, newHash)
}