/
committree.go
470 lines (396 loc) · 13.1 KB
/
committree.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
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package executor
import (
"bytes"
"encoding/hex"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/mix/executor/merkletree"
mixTy "github.com/33cn/plugin/plugin/dapp/mix/types"
"github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc"
"github.com/golang/protobuf/proto"
"github.com/pkg/errors"
)
func getCommitLeaves(db dbm.KV, key []byte) (*mixTy.CommitTreeLeaves, error) {
v, err := db.Get(key)
if err != nil {
return nil, errors.Wrapf(err, "get db")
}
var leaves mixTy.CommitTreeLeaves
err = types.Decode(v, &leaves)
if err != nil {
return nil, errors.Wrapf(err, "decode db verify key")
}
return &leaves, nil
}
func getCommitTreeStatus(db dbm.KV, assetExec, assetSymbol string) (*mixTy.CommitTreeStatus, error) {
v, err := db.Get(calcCommitTreeCurrentStatusKey(assetExec, assetSymbol))
if isNotFound(err) {
//系统初始化开始,没有任何状态,初始seq设为1,如果一个merkle树完成后,status清空状态,seq也要初始为1,作为数据库占位,不然会往前查找
return &mixTy.CommitTreeStatus{
AssetExec: assetExec,
AssetSymbol: assetSymbol,
SubTrees: &mixTy.CommitSubTrees{},
ArchiveRootsSeq: 1}, nil
}
if err != nil {
return nil, errors.Wrapf(err, "db.getCommitTreeStatus")
}
var status mixTy.CommitTreeStatus
err = types.Decode(v, &status)
if err != nil {
return nil, errors.Wrapf(err, "db.decode CommitTreeStatus")
}
return &status, nil
}
func getSubLeaves(db dbm.KV, exec, symbol string, currentSeq int32) (*mixTy.CommitTreeLeaves, error) {
var leaves mixTy.CommitTreeLeaves
for i := int32(1); i <= currentSeq; i++ {
l, err := getCommitLeaves(db, calcSubLeavesKey(exec, symbol, i))
if err != nil {
return nil, errors.Wrapf(err, "getSubLeaves seq=%d", i)
}
leaves.Leaves = append(leaves.Leaves, l.Leaves...)
}
return &leaves, nil
}
func getCommitRootLeaves(db dbm.KV, exec, symbol, rootHash string) (*mixTy.CommitTreeLeaves, error) {
return getCommitLeaves(db, calcCommitTreeRootLeaves(exec, symbol, rootHash))
}
func getCommitTreeRoots(db dbm.KV, key []byte) (*mixTy.CommitTreeRoots, error) {
v, err := db.Get(key)
if err != nil {
return nil, errors.Wrapf(err, "get db")
}
var roots mixTy.CommitTreeRoots
err = types.Decode(v, &roots)
if err != nil {
return nil, errors.Wrapf(err, "decode db verify key")
}
return &roots, nil
}
func getSubRoots(db dbm.KV, exec, symbol string, seq int32) (*mixTy.CommitTreeRoots, error) {
return getCommitTreeRoots(db, calcSubRootsKey(exec, symbol, seq))
}
func getArchiveRoots(db dbm.KV, exec, symbol string, seq uint64) (*mixTy.CommitTreeRoots, error) {
return getCommitTreeRoots(db, calcArchiveRootsKey(exec, symbol, seq))
}
//TODO seed config
func getNewTree() *merkletree.Tree {
return merkletree.New(mimc.NewMiMC(mixTy.MimcHashSeed))
}
func calcTreeRoot(leaves *mixTy.CommitTreeLeaves) []byte {
tree := getNewTree()
for _, leaf := range leaves.Leaves {
tree.Push(leaf)
}
return tree.Root()
}
func makeArchiveRootReceipt(exec, symbol string, seq uint64, root string) *types.Receipt {
key := calcArchiveRootsKey(exec, symbol, seq)
log := &mixTy.ReceiptArchiveTreeRoot{
RootHash: root,
Seq: seq,
}
return &types.Receipt{
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(&mixTy.CommitTreeRoots{Roots: [][]byte{mixTy.Str2Byte(root)}})},
},
Logs: []*types.ReceiptLog{
{
Ty: mixTy.TyLogCommitTreeArchiveRoot,
Log: types.Encode(log),
},
},
}
}
func makeArchiveLeavesReceipt(exec, symbol, root string, leaves *mixTy.CommitTreeLeaves) *types.Receipt {
key := calcCommitTreeRootLeaves(exec, symbol, root)
log := &mixTy.ReceiptArchiveLeaves{
RootHash: root,
Count: int32(len(leaves.Leaves)),
LastLeaf: hex.EncodeToString(leaves.Leaves[len(leaves.Leaves)-1]),
}
return &types.Receipt{
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(leaves)},
},
Logs: []*types.ReceiptLog{
{
Ty: mixTy.TyLogArchiveRootLeaves,
Log: types.Encode(log),
},
},
}
}
func makeSubRootsReceipt(exec, symbol string, seq int32, root []byte) *types.Receipt {
key := calcSubRootsKey(exec, symbol, seq)
log := &mixTy.ReceiptCommitSubRoots{
Seq: seq,
Root: mixTy.Byte2Str(root),
}
return &types.Receipt{
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(&mixTy.CommitTreeRoots{Roots: [][]byte{root}})},
},
Logs: []*types.ReceiptLog{
{
Ty: mixTy.TyLogSubRoots,
Log: types.Encode(log),
},
},
}
}
func makeSubLeavesReceipt(exec, symbol string, seq int32, leaf []byte) *types.Receipt {
key := calcSubLeavesKey(exec, symbol, seq)
log := &mixTy.ReceiptCommitSubLeaves{
Seq: seq,
Leaf: mixTy.Byte2Str(leaf),
}
return &types.Receipt{
KV: []*types.KeyValue{
{Key: key, Value: types.Encode(&mixTy.CommitTreeLeaves{Leaves: [][]byte{leaf}})},
},
Logs: []*types.ReceiptLog{
{
Ty: mixTy.TyLogSubLeaves,
Log: types.Encode(log),
},
},
}
}
func makeTreeStatusReceipt(exec, symbol string, prev, current *mixTy.CommitTreeStatus) *types.Receipt {
keyStatus := calcCommitTreeCurrentStatusKey(exec, symbol)
log := &mixTy.ReceiptCommitTreeStatus{
Prev: prev,
Current: current,
}
return &types.Receipt{
Ty: types.ExecOk,
KV: []*types.KeyValue{
{Key: keyStatus, Value: types.Encode(current)},
},
Logs: []*types.ReceiptLog{
{
Ty: mixTy.TyLogCommitTreeStatus,
Log: types.Encode(log),
},
},
}
}
func getArchivedSubLeaves(db dbm.KV, exec, symbol string, maxTreeLeaves int32) (*mixTy.CommitTreeLeaves, error) {
var leaves mixTy.CommitTreeLeaves
//获取前1023个leaf
for i := int32(1); i < maxTreeLeaves; i++ {
r, err := getCommitLeaves(db, calcSubLeavesKey(exec, symbol, i))
if err != nil {
return nil, errors.Wrapf(err, "getArchivedSubTreeLeaves,i=%d", i)
}
leaves.Leaves = append(leaves.Leaves, r.Leaves...)
}
return &leaves, nil
}
func restoreSubTrees(subTrees *mixTy.CommitSubTrees) (*merkletree.Tree, error) {
tree := getNewTree()
for _, t := range subTrees.SubTrees {
err := tree.PushSubTree(int(t.Height), t.Hash)
if err != nil {
return nil, errors.Wrapf(err, "restoreSubTrees.pushSubTree=%d", t.Height)
}
}
return tree, nil
}
//merkle树由子树构成,任何一个节点都是一个子树,如果一个子树高度不为0,则是一个高阶子树,高阶子树可以直接参与root计算,不需要再统计其叶子节点
//高阶子树一定是一个完全子树,即左右叶子节点都存在,只有0阶子树不是完全子树,只有一个叶子
//为了计算1024个叶子节点任何一个新节点n的roothash,需要累计前n-1个叶子节点,由于1024个叶子存取耗时,这里只保存最多10个高阶子树即可计算roothash
func joinSubTrees(status *mixTy.CommitTreeStatus, leaf []byte) ([]byte, error) {
tree, err := restoreSubTrees(status.SubTrees)
if err != nil {
return nil, errors.Wrapf(err, "joinSubTrees")
}
tree.Push(leaf)
var newSubTrees mixTy.CommitSubTrees
allSubTrees := tree.GetAllSubTrees()
for _, s := range allSubTrees {
newSubTrees.SubTrees = append(newSubTrees.SubTrees, &mixTy.CommitSubTree{Height: int32(s.GetHeight()), Hash: s.GetSum()})
}
status.SubTrees = &newSubTrees
return tree.Root(), nil
}
func joinLeaves(db dbm.KV, status *mixTy.CommitTreeStatus, leaf []byte, maxTreeLeaves int32) (*types.Receipt, error) {
receipts := &types.Receipt{Ty: types.ExecOk}
//seq从1开始记录前1023个叶子和root
if status.SubLeavesSeq < maxTreeLeaves {
status.SubLeavesSeq++
r := makeSubLeavesReceipt(status.AssetExec, status.AssetSymbol, status.SubLeavesSeq, leaf)
mergeReceipt(receipts, r)
//恢复并重新计算子树
root, err := joinSubTrees(status, leaf)
if err != nil {
return nil, errors.Wrapf(err, "joinLeaves.joinSubTrees")
}
r = makeSubRootsReceipt(status.AssetExec, status.AssetSymbol, status.SubLeavesSeq, root)
mergeReceipt(receipts, r)
return receipts, nil
}
//累积到1024个叶子,需要归档
sumLeaves, err := getArchivedSubLeaves(db, status.AssetExec, status.AssetSymbol, maxTreeLeaves)
if err != nil {
return nil, errors.Wrapf(err, "pushTree.joinLeaves")
}
//加第1024个leaf
sumLeaves.Leaves = append(sumLeaves.Leaves, leaf)
//重新计算1024个叶子root,确保正确
root := mixTy.Byte2Str(calcTreeRoot(sumLeaves))
//root-leaves保存leaves
r := makeArchiveLeavesReceipt(status.AssetExec, status.AssetSymbol, root, sumLeaves)
mergeReceipt(receipts, r)
//1024叶子的root归档到相应archiveSeq
r = makeArchiveRootReceipt(status.AssetExec, status.AssetSymbol, status.ArchiveRootsSeq, root)
mergeReceipt(receipts, r)
status.ArchiveRootsSeq++
//reset 重新开始统计
status.SubLeavesSeq = 0
status.SubTrees = &mixTy.CommitSubTrees{}
return receipts, nil
}
/*
1. 增加到当前leaves 和roots
2. 如果leaves 达到最大比如1024,则按root归档leaves,并归档相应root
3. 归档同时初始化新的current leaves 和roots
*/
func pushTree(db dbm.KV, exec, symbol string, leaves [][]byte, maxTreeLeaves int32) (*types.Receipt, error) {
status, err := getCommitTreeStatus(db, exec, symbol)
if err != nil {
return nil, err
}
prev := proto.Clone(status).(*mixTy.CommitTreeStatus)
receipts := &types.Receipt{Ty: types.ExecOk}
for i, leaf := range leaves {
if len(leaf) <= 0 {
return nil, errors.Wrapf(types.ErrInvalidParam, "the %d leaf is null", i)
}
r, err := joinLeaves(db, status, leaf, maxTreeLeaves)
if err != nil {
return nil, errors.Wrapf(err, "pushTree.joinLeaves leaf=%s", mixTy.Byte2Str(leaf))
}
mergeReceipt(receipts, r)
}
r := makeTreeStatusReceipt(exec, symbol, prev, status)
mergeReceipt(receipts, r)
return receipts, nil
}
func checkExist(target []byte, list [][]byte) bool {
for _, r := range list {
if bytes.Equal(r, target) {
return true
}
}
return false
}
func checkTreeRootHashExist(db dbm.KV, exec, symbol string, hash []byte) (bool, error) {
status, err := getCommitTreeStatus(db, exec, symbol)
if err != nil {
return false, errors.Wrapf(err, "checkTreeRootHashExist")
}
//查归档的subRoots,当前subSeq还未归档
for i := int32(1); i <= status.SubLeavesSeq; i++ {
subRoots, err := getSubRoots(db, exec, symbol, i)
if err != nil {
return false, errors.Wrapf(err, "checkTreeRootHashExist.getSubRoots seq=%d", i)
}
if checkExist(hash, subRoots.Roots) {
return true, nil
}
}
//再查归档的roots
for i := status.ArchiveRootsSeq; i > 0; i-- {
subRoots, err := getArchiveRoots(db, exec, symbol, i)
if err != nil {
return false, errors.Wrapf(err, "checkTreeRootHashExist.getArchiveRoots seq=%d", i)
}
if checkExist(hash, subRoots.Roots) {
return true, nil
}
}
return false, nil
}
func getProveData(targetLeaf []byte, leaves [][]byte) (*mixTy.CommitTreeProve, error) {
index := 0
found := false
for i, key := range leaves {
if bytes.Equal(key, targetLeaf) {
index = i
found = true
break
}
}
if !found {
return nil, mixTy.ErrLeafNotFound
}
tree := getNewTree()
tree.SetIndex(uint64(index))
for _, key := range leaves {
tree.Push(key)
}
root, proofSet, proofIndex, num := tree.Prove()
var prove mixTy.CommitTreeProve
prove.RootHash = mixTy.Byte2Str(root)
prove.ProofIndex = uint32(proofIndex)
prove.NumLeaves = uint32(num)
//set[0] 是targetLeaf
for _, s := range proofSet {
prove.ProofSet = append(prove.ProofSet, mixTy.Byte2Str(s))
}
helpers := merkletree.GenerateProofHelper(proofSet, proofIndex, num)
for _, i := range helpers {
prove.Helpers = append(prove.Helpers, uint32(i))
}
return &prove, nil
}
//1. 首先在当前tree查找
//2. 如果提供了rootHash,则根据roothash+leaf查找,否则全局遍历查找
func CalcTreeProve(db dbm.KV, exec, symbol, rootHash, leaf string) (*mixTy.CommitTreeProve, error) {
if len(leaf) <= 0 {
return nil, errors.Wrap(types.ErrInvalidParam, "leaf is null")
}
status, err := getCommitTreeStatus(db, exec, symbol)
if err != nil {
return nil, errors.Wrapf(err, "CalcTreeProve.getCommitTreeStatus")
}
leaves, err := getSubLeaves(db, exec, symbol, status.SubLeavesSeq)
if err == nil {
p, err := getProveData(mixTy.Str2Byte(leaf), leaves.Leaves)
if err == nil {
return p, nil
}
}
if len(rootHash) > 0 {
leaves, err := getCommitRootLeaves(db, exec, symbol, rootHash)
if err != nil {
return nil, errors.Wrapf(err, "getCommitRootLeaves rootHash=%s", rootHash)
}
//指定leaf 没有在指定的rootHash里面,返回错误,可以不指定rootHash 全局遍历
p, err := getProveData(mixTy.Str2Byte(leaf), leaves.Leaves)
if err != nil {
return nil, errors.Wrapf(err, "hash=%s,leaf=%s", rootHash, leaf)
}
return p, nil
}
for i := status.ArchiveRootsSeq; i > 0; i-- {
roots, err := getArchiveRoots(db, exec, symbol, i)
if err != nil {
return nil, errors.Wrapf(err, "getArchiveRoots,i=%d", i)
}
leaves, err := getCommitRootLeaves(db, exec, symbol, mixTy.Byte2Str(roots.Roots[0]))
if err == nil {
p, err := getProveData(mixTy.Str2Byte(leaf), leaves.Leaves)
if err == nil {
return p, nil
}
}
}
return nil, errors.Wrapf(mixTy.ErrLeafNotFound, "hash=%s,leaf=%s", rootHash, leaf)
}