forked from sei-protocol/sei-iavl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
proof_ics23.go
168 lines (146 loc) · 4.52 KB
/
proof_ics23.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
157
158
159
160
161
162
163
164
165
166
167
168
package iavl
import (
"encoding/binary"
"fmt"
ics23 "github.com/confio/ics23/go"
)
/*
GetMembershipProof will produce a CommitmentProof that the given key (and queries value) exists in the iavl tree.
If the key doesn't exist in the tree, this will return an error.
*/
func (t *ImmutableTree) GetMembershipProof(key []byte) (*ics23.CommitmentProof, error) {
exist, err := createExistenceProof(t, key)
if err != nil {
return nil, err
}
proof := &ics23.CommitmentProof{
Proof: &ics23.CommitmentProof_Exist{
Exist: exist,
},
}
return proof, nil
}
/*
GetNonMembershipProof will produce a CommitmentProof that the given key doesn't exist in the iavl tree.
If the key exists in the tree, this will return an error.
*/
func (t *ImmutableTree) GetNonMembershipProof(key []byte) (*ics23.CommitmentProof, error) {
// idx is one node right of what we want....
var err error
idx, val, err := t.GetWithIndex(key)
if err != nil {
return nil, err
}
if val != nil {
return nil, fmt.Errorf("cannot create NonExistanceProof when Key in State")
}
nonexist := &ics23.NonExistenceProof{
Key: key,
}
if idx >= 1 {
leftkey, _, err := t.GetByIndex(idx - 1)
if err != nil {
return nil, err
}
nonexist.Left, err = createExistenceProof(t, leftkey)
if err != nil {
return nil, err
}
}
// this will be nil if nothing right of the queried key
rightkey, _, err := t.GetByIndex(idx)
if err != nil {
return nil, err
}
if rightkey != nil {
nonexist.Right, err = createExistenceProof(t, rightkey)
if err != nil {
return nil, err
}
}
proof := &ics23.CommitmentProof{
Proof: &ics23.CommitmentProof_Nonexist{
Nonexist: nonexist,
},
}
return proof, nil
}
func createExistenceProof(tree *ImmutableTree, key []byte) (*ics23.ExistenceProof, error) {
value, proof, err := tree.GetWithProof(key)
if err != nil {
return nil, err
}
if value == nil {
return nil, fmt.Errorf("cannot create ExistanceProof when Key not in State")
}
return convertExistenceProof(proof, key, value)
}
// convertExistenceProof will convert the given proof into a valid
// existence proof, if that's what it is.
//
// This is the simplest case of the range proof and we will focus on
// demoing compatibility here
func convertExistenceProof(p *RangeProof, key, value []byte) (*ics23.ExistenceProof, error) {
if len(p.Leaves) != 1 {
return nil, fmt.Errorf("existence proof requires RangeProof to have exactly one leaf")
}
return &ics23.ExistenceProof{
Key: key,
Value: value,
Leaf: convertLeafOp(p.Leaves[0].Version),
Path: convertInnerOps(p.LeftPath),
}, nil
}
func convertLeafOp(version int64) *ics23.LeafOp {
var varintBuf [binary.MaxVarintLen64]byte
// this is adapted from iavl/proof.go:proofLeafNode.Hash()
prefix := convertVarIntToBytes(0, varintBuf)
prefix = append(prefix, convertVarIntToBytes(1, varintBuf)...)
prefix = append(prefix, convertVarIntToBytes(version, varintBuf)...)
return &ics23.LeafOp{
Hash: ics23.HashOp_SHA256,
PrehashValue: ics23.HashOp_SHA256,
Length: ics23.LengthOp_VAR_PROTO,
Prefix: prefix,
}
}
// we cannot get the proofInnerNode type, so we need to do the whole path in one function
func convertInnerOps(path PathToLeaf) []*ics23.InnerOp {
steps := make([]*ics23.InnerOp, 0, len(path))
// lengthByte is the length prefix prepended to each of the sha256 sub-hashes
var lengthByte byte = 0x20
var varintBuf [binary.MaxVarintLen64]byte
// we need to go in reverse order, iavl starts from root to leaf,
// we want to go up from the leaf to the root
for i := len(path) - 1; i >= 0; i-- {
// this is adapted from iavl/proof.go:proofInnerNode.Hash()
prefix := convertVarIntToBytes(int64(path[i].Height), varintBuf)
prefix = append(prefix, convertVarIntToBytes(path[i].Size, varintBuf)...)
prefix = append(prefix, convertVarIntToBytes(path[i].Version, varintBuf)...)
var suffix []byte
if len(path[i].Left) > 0 {
// length prefixed left side
prefix = append(prefix, lengthByte)
prefix = append(prefix, path[i].Left...)
// prepend the length prefix for child
prefix = append(prefix, lengthByte)
} else {
// prepend the length prefix for child
prefix = append(prefix, lengthByte)
// length-prefixed right side
suffix = []byte{lengthByte}
suffix = append(suffix, path[i].Right...)
}
op := &ics23.InnerOp{
Hash: ics23.HashOp_SHA256,
Prefix: prefix,
Suffix: suffix,
}
steps = append(steps, op)
}
return steps
}
func convertVarIntToBytes(orig int64, buf [binary.MaxVarintLen64]byte) []byte {
n := binary.PutVarint(buf[:], orig)
return buf[:n]
}