forked from cosmos/iavl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
path.go
156 lines (138 loc) · 3.8 KB
/
path.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
151
152
153
154
155
156
package iavl
import (
"bytes"
"github.com/pkg/errors"
)
// PathToKey represents an inner path to a leaf node.
// Note that the nodes are ordered such that the last one is closest
// to the root of the tree.
type PathToKey struct {
InnerNodes []proofInnerNode `json:"inner_nodes"`
}
func (p *PathToKey) String() string {
str := ""
for i := len(p.InnerNodes) - 1; i >= 0; i-- {
str += p.InnerNodes[i].String() + "\n"
}
return str
}
// verify check that the leafNode's hash matches the path's LeafHash and that
// the root is the merkle hash of all the inner nodes.
func (p *PathToKey) verify(leafNode proofLeafNode, root []byte) error {
hash := leafNode.Hash()
for _, branch := range p.InnerNodes {
hash = branch.Hash(hash)
}
if !bytes.Equal(root, hash) {
return errors.WithStack(ErrInvalidProof)
}
return nil
}
func (p *PathToKey) isLeftmost() bool {
for _, node := range p.InnerNodes {
if len(node.Left) > 0 {
return false
}
}
return true
}
func (p *PathToKey) isRightmost() bool {
for _, node := range p.InnerNodes {
if len(node.Right) > 0 {
return false
}
}
return true
}
func (p *PathToKey) isEmpty() bool {
return p == nil || len(p.InnerNodes) == 0
}
func (p *PathToKey) dropRoot() *PathToKey {
if p.isEmpty() {
return p
}
return &PathToKey{
InnerNodes: p.InnerNodes[:len(p.InnerNodes)-1],
}
}
func (p *PathToKey) hasCommonRoot(p2 *PathToKey) bool {
if p.isEmpty() || p2.isEmpty() {
return false
}
leftEnd := p.InnerNodes[len(p.InnerNodes)-1]
rightEnd := p2.InnerNodes[len(p2.InnerNodes)-1]
return bytes.Equal(leftEnd.Left, rightEnd.Left) &&
bytes.Equal(leftEnd.Right, rightEnd.Right)
}
func (p *PathToKey) isLeftAdjacentTo(p2 *PathToKey) bool {
for p.hasCommonRoot(p2) {
p, p2 = p.dropRoot(), p2.dropRoot()
}
p, p2 = p.dropRoot(), p2.dropRoot()
return p.isRightmost() && p2.isLeftmost()
}
// PathWithNode is a path to a key which includes the leaf node at that key.
type pathWithNode struct {
Path *PathToKey `json:"path"`
Node proofLeafNode `json:"node"`
}
func (p *pathWithNode) verify(root []byte) error {
return p.Path.verify(p.Node, root)
}
// verifyPaths verifies the left and right paths individually, and makes sure
// the ordering is such that left < startKey <= endKey < right.
func verifyPaths(left, right *pathWithNode, startKey, endKey, root []byte) error {
if bytes.Compare(startKey, endKey) == 1 {
return ErrInvalidInputs
}
if left != nil {
if err := left.verify(root); err != nil {
return err
}
if !left.Node.isLesserThan(startKey) {
return errors.WithStack(ErrInvalidProof)
}
}
if right != nil {
if err := right.verify(root); err != nil {
return err
}
if !right.Node.isGreaterThan(endKey) {
return errors.WithStack(ErrInvalidProof)
}
}
return nil
}
// Checks that all paths are adjacent to one another, ie. that there are no
// keys missing.
func verifyNoMissingKeys(paths []*PathToKey) error {
ps := make([]*PathToKey, 0, len(paths))
for _, p := range paths {
if p != nil {
ps = append(ps, p)
}
}
for i := 0; i < len(ps)-1; i++ {
// Always check from left to right, since paths are always in ascending order.
if !ps[i].isLeftAdjacentTo(ps[i+1]) {
return errors.Errorf("paths #%d and #%d are not adjacent", i, i+1)
}
}
return nil
}
// Checks that with the given left and right paths, no keys can exist in between.
// Supports nil paths to signify out-of-range.
func verifyKeyAbsence(left, right *pathWithNode) error {
if left != nil && left.Path.isRightmost() {
// Range starts outside of the right boundary.
return nil
} else if right != nil && right.Path.isLeftmost() {
// Range ends outside of the left boundary.
return nil
} else if left != nil && right != nil &&
left.Path.isLeftAdjacentTo(right.Path) {
// Range is between two existing keys.
return nil
}
return errors.WithStack(ErrInvalidProof)
}