-
Notifications
You must be signed in to change notification settings - Fork 175
/
trie.go
340 lines (301 loc) · 7.49 KB
/
trie.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
package ledger
import (
"bytes"
"encoding/hex"
"fmt"
)
// TrieRead captures a trie read query
type TrieRead struct {
RootHash RootHash
Paths []Path
}
// TrieUpdate holds all data for a trie update
type TrieUpdate struct {
RootHash RootHash
Paths []Path
Payloads []*Payload
}
// Size returns number of paths in the trie update
func (u *TrieUpdate) Size() int {
return len(u.Paths)
}
// IsEmpty returns true if key or value is not empty
func (u *TrieUpdate) IsEmpty() bool {
return u.Size() == 0
}
func (u *TrieUpdate) String() string {
str := "Trie Update:\n "
str += "\t triehash : " + u.RootHash.String() + "\n"
tp := 0
for i, p := range u.Paths {
tp += p.Size()
str += fmt.Sprintf("\t\t path %d : %s\n", i, p)
}
str += fmt.Sprintf("\t paths len: %d , bytesize: %d\n", len(u.Paths), tp)
tp = 0
for _, p := range u.Payloads {
tp += p.Size()
}
str += fmt.Sprintf("\t total size of payloads : %d \n", tp)
return str
}
// Equals compares this trie update to another trie update
func (u *TrieUpdate) Equals(other *TrieUpdate) bool {
if other == nil {
return false
}
if !u.RootHash.Equals(other.RootHash) {
return false
}
if len(u.Paths) != len(other.Paths) {
return false
}
for i := range u.Paths {
if !u.Paths[i].Equals(other.Paths[i]) {
return false
}
}
if len(u.Payloads) != len(other.Payloads) {
return false
}
for i := range u.Payloads {
if !u.Payloads[i].Equals(other.Payloads[i]) {
return false
}
}
return true
}
// RootHash captures the root hash of a trie
type RootHash []byte
func (rh RootHash) String() string {
return hex.EncodeToString(rh)
}
// Equals compares the root hash to another one
func (rh RootHash) Equals(o RootHash) bool {
if o == nil {
return false
}
return bytes.Equal(rh, o)
}
// Path captures storage path of a payload;
// where we store a payload in the ledger
type Path []byte
func (p Path) String() string {
str := ""
for _, i := range p {
str += fmt.Sprintf("%08b", i)
}
if len(str) > 16 {
str = str[0:8] + "..." + str[len(str)-8:]
}
return str
}
// Size returns the size of the path
func (p *Path) Size() int {
return len(*p)
}
// Equals compares this path to another path
func (p Path) Equals(o Path) bool {
if o == nil {
return false
}
return bytes.Equal([]byte(p), []byte(o))
}
// DeepCopy returns a deep copy of the payload
func (p *Path) DeepCopy() Path {
newP := make([]byte, len(*p))
copy(newP, *p)
return Path(newP)
}
// Payload is the smallest immutable storable unit in ledger
type Payload struct {
Key Key
Value Value
}
// Size returns the size of the payload
func (p *Payload) Size() int {
return p.Key.Size() + p.Value.Size()
}
// IsEmpty returns true if key or value is not empty
func (p *Payload) IsEmpty() bool {
return p.Size() == 0
}
// TODO fix me
func (p *Payload) String() string {
// TODO improve this key, values
return p.Key.String() + " " + p.Value.String()
}
// Equals compares this payload to another payload
func (p *Payload) Equals(other *Payload) bool {
if other == nil {
return false
}
if !p.Key.Equals(&other.Key) {
return false
}
if !p.Value.Equals(other.Value) {
return false
}
return true
}
// DeepCopy returns a deep copy of the payload
func (p *Payload) DeepCopy() *Payload {
k := p.Key.DeepCopy()
v := p.Value.DeepCopy()
return &Payload{Key: k, Value: v}
}
// NewPayload returns a new payload
func NewPayload(key Key, value Value) *Payload {
return &Payload{Key: key, Value: value}
}
// EmptyPayload returns an empty payload
func EmptyPayload() *Payload {
return &Payload{}
}
// TrieProof includes all the information needed to walk
// through a trie branch from an specific leaf node (key)
// up to the root of the trie.
type TrieProof struct {
Path Path // path
Payload *Payload // payload
Interims [][]byte // the non-default intermediate nodes in the proof
Inclusion bool // flag indicating if this is an inclusion or exclusion
Flags []byte // The flags of the proofs (is set if an intermediate node has a non-default)
Steps uint8 // number of steps for the proof (path len) // TODO: should this be a type allowing for larger values?
}
// NewTrieProof creates a new instance of Trie Proof
func NewTrieProof() *TrieProof {
return &TrieProof{
Path: make([]byte, 0),
Payload: EmptyPayload(),
Interims: make([][]byte, 0),
Inclusion: false,
Flags: make([]byte, 0),
Steps: 0,
}
}
func (p *TrieProof) String() string {
flagStr := ""
for _, f := range p.Flags {
flagStr += fmt.Sprintf("%08b", f)
}
proofStr := fmt.Sprintf("size: %d flags: %v\n", p.Steps, flagStr)
proofStr += fmt.Sprintf("\t path: %v payload: %v\n", p.Path, p.Payload)
if p.Inclusion {
proofStr += "\t inclusion proof:\n"
} else {
proofStr += "\t noninclusion proof:\n"
}
interimIndex := 0
for j := 0; j < int(p.Steps); j++ {
// if bit is set
if p.Flags[j/8]&(1<<int(7-j%8)) != 0 {
proofStr += fmt.Sprintf("\t\t %d: [%x]\n", j, p.Interims[interimIndex])
interimIndex++
}
}
return proofStr
}
// Equals compares this proof to another payload
func (p *TrieProof) Equals(o *TrieProof) bool {
if o == nil {
return false
}
if !p.Path.Equals(o.Path) {
return false
}
if !p.Payload.Equals(o.Payload) {
return false
}
if len(p.Interims) != len(o.Interims) {
return false
}
for i, inter := range p.Interims {
if !bytes.Equal(inter, o.Interims[i]) {
return false
}
}
if p.Inclusion != o.Inclusion {
return false
}
if !bytes.Equal(p.Flags, o.Flags) {
return false
}
if p.Steps != o.Steps {
return false
}
return true
}
// TrieBatchProof is a struct that holds the proofs for several keys
//
// so there is no need for two calls (read, proofs)
type TrieBatchProof struct {
Proofs []*TrieProof
}
// NewTrieBatchProof creates a new instance of BatchProof
func NewTrieBatchProof() *TrieBatchProof {
bp := new(TrieBatchProof)
bp.Proofs = make([]*TrieProof, 0)
return bp
}
// NewTrieBatchProofWithEmptyProofs creates an instance of Batchproof
// filled with n newly created proofs (empty)
func NewTrieBatchProofWithEmptyProofs(numberOfProofs int) *TrieBatchProof {
bp := NewTrieBatchProof()
for i := 0; i < numberOfProofs; i++ {
bp.AppendProof(NewTrieProof())
}
return bp
}
// Size returns the number of proofs
func (bp *TrieBatchProof) Size() int {
return len(bp.Proofs)
}
// Paths returns the slice of paths for this batch proof
func (bp *TrieBatchProof) Paths() []Path {
paths := make([]Path, 0)
for _, p := range bp.Proofs {
paths = append(paths, p.Path)
}
return paths
}
// Payloads returns the slice of paths for this batch proof
func (bp *TrieBatchProof) Payloads() []*Payload {
payloads := make([]*Payload, 0)
for _, p := range bp.Proofs {
payloads = append(payloads, p.Payload)
}
return payloads
}
func (bp *TrieBatchProof) String() string {
res := fmt.Sprintf("trie batch proof includes %d proofs: \n", bp.Size())
for _, proof := range bp.Proofs {
res = res + "\n" + proof.String()
}
return res
}
// AppendProof adds a proof to the batch proof
func (bp *TrieBatchProof) AppendProof(p *TrieProof) {
bp.Proofs = append(bp.Proofs, p)
}
// MergeInto adds all of its proofs into the dest batch proof
func (bp *TrieBatchProof) MergeInto(dest *TrieBatchProof) {
for _, p := range bp.Proofs {
dest.AppendProof(p)
}
}
// Equals compares this batch proof to another batch proof
func (bp *TrieBatchProof) Equals(o *TrieBatchProof) bool {
if o == nil {
return false
}
if len(bp.Proofs) != len(o.Proofs) {
return false
}
for i, proof := range bp.Proofs {
if !proof.Equals(o.Proofs[i]) {
return false
}
}
return true
}