/
util.go
667 lines (611 loc) · 20 KB
/
util.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
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
// 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 util
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"os"
"os/user"
"path/filepath"
"unicode"
"strings"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/common/merkle"
"github.com/33cn/chain33/queue"
_ "github.com/33cn/chain33/system/address" //init address driver
"github.com/33cn/chain33/types"
"github.com/pkg/errors"
)
func init() {
rand.Seed(types.Now().UnixNano())
}
var ulog = log15.New("module", "util")
//GetParaExecName : 如果 name 没有 paraName 前缀,那么加上这个前缀
func GetParaExecName(paraName string, name string) string {
if strings.HasPrefix(name, "user.p.") {
return name
}
return paraName + name
}
// MakeStringToUpper : 将给定的in字符串从pos开始一共count个转换为大写字母
func MakeStringToUpper(in string, pos, count int) (out string, err error) {
l := len(in)
if pos < 0 || pos >= l || (pos+count) >= l || count <= 0 {
err = fmt.Errorf("Invalid params. in=%s pos=%d count=%d", in, pos, count)
return
}
tmp := []rune(in)
for n := pos; n < pos+count; n++ {
tmp[n] = unicode.ToUpper(tmp[n])
}
out = string(tmp)
return
}
// MakeStringToLower : 将给定的in字符串从pos开始一共count个转换为小写字母
func MakeStringToLower(in string, pos, count int) (out string, err error) {
l := len(in)
if pos < 0 || pos >= l || (pos+count) >= l || count <= 0 {
err = fmt.Errorf("Invalid params. in=%s pos=%d count=%d", in, pos, count)
return
}
tmp := []rune(in)
for n := pos; n < pos+count; n++ {
tmp[n] = unicode.ToLower(tmp[n])
}
out = string(tmp)
return
}
//GenNoneTxs : 创建一些 none 执行器的 交易列表,一般用于测试
func GenNoneTxs(cfg *types.Chain33Config, priv crypto.PrivKey, n int64) (txs []*types.Transaction) {
for i := 0; i < int(n); i++ {
txs = append(txs, CreateNoneTx(cfg, priv))
}
return txs
}
//GenCoinsTxs : generate txs to be executed on exector coin
func GenCoinsTxs(cfg *types.Chain33Config, priv crypto.PrivKey, n int64) (txs []*types.Transaction) {
to, _ := Genaddress()
for i := 0; i < int(n); i++ {
txs = append(txs, CreateCoinsTx(cfg, priv, to, n+1))
}
return txs
}
//Genaddress : generate a address
func Genaddress() (string, crypto.PrivKey) {
cr, err := crypto.Load(types.GetSignName("", types.SECP256K1), -1)
if err != nil {
panic(err)
}
privto, err := cr.GenKey()
if err != nil {
panic(err)
}
addrto := address.PubKeyToAddr(address.DefaultID, privto.PubKey().Bytes())
return addrto, privto
}
// CreateNoneTx : Create None Tx
func CreateNoneTx(cfg *types.Chain33Config, priv crypto.PrivKey) *types.Transaction {
return CreateTxWithExecer(cfg, priv, "none")
}
// UpdateExpireWithTxHeight 设置txHeight类型交易过期
func UpdateExpireWithTxHeight(tx *types.Transaction, priv crypto.PrivKey, txHeight int64) {
tx.Expire = txHeight + types.TxHeightFlag
if priv != nil {
tx.Sign(types.SECP256K1, priv)
}
}
// CreateCoinsTxWithTxHeight 使用txHeight作为交易过期
func CreateCoinsTxWithTxHeight(cfg *types.Chain33Config, priv crypto.PrivKey, to string, amount, txHeight int64) *types.Transaction {
tx := CreateCoinsTx(cfg, nil, to, amount)
UpdateExpireWithTxHeight(tx, priv, txHeight)
return tx
}
//CreateNoneTxWithTxHeight 使用txHeight作为交易过期
func CreateNoneTxWithTxHeight(cfg *types.Chain33Config, priv crypto.PrivKey, txHeight int64) *types.Transaction {
tx := CreateNoneTx(cfg, nil)
UpdateExpireWithTxHeight(tx, priv, txHeight)
return tx
}
// CreateTxWithExecer : Create Tx With Execer
func CreateTxWithExecer(cfg *types.Chain33Config, priv crypto.PrivKey, execer string) *types.Transaction {
if execer == cfg.GetCoinExec() {
to, _ := Genaddress()
return CreateCoinsTx(cfg, priv, to, cfg.GetCoinPrecision())
}
tx := &types.Transaction{Execer: []byte(execer), Payload: []byte("none")}
tx.To = address.ExecAddress(execer)
tx, err := types.FormatTx(cfg, execer, tx)
if err != nil {
return nil
}
if priv != nil {
tx.Sign(types.SECP256K1, priv)
}
return tx
}
//TestingT 测试类型
type TestingT interface {
Error(args ...interface{})
Log(args ...interface{})
}
// JSONPrint : print in json format
func JSONPrint(t TestingT, input interface{}) {
data, err := json.MarshalIndent(input, "", "\t")
if err != nil {
t.Error(err)
return
}
if t == nil {
fmt.Println(string(data))
} else {
t.Log(string(data))
}
}
// CreateManageTx : Create Manage Tx
func CreateManageTx(cfg *types.Chain33Config, priv crypto.PrivKey, key, op, value string) *types.Transaction {
v := &types.ModifyConfig{Key: key, Op: op, Value: value, Addr: ""}
exec := types.LoadExecutorType("manage")
if exec == nil {
panic("manage exec is not init")
}
tx, err := exec.Create("Modify", v)
if err != nil {
panic(err)
}
tx, err = types.FormatTx(cfg, "manage", tx)
if err != nil {
return nil
}
tx.Sign(types.SECP256K1, priv)
return tx
}
// CreateCoinsTx : Create Coins Tx
func CreateCoinsTx(cfg *types.Chain33Config, priv crypto.PrivKey, to string, amount int64) *types.Transaction {
tx := createCoinsTx(cfg, to, amount)
if priv != nil {
tx.Sign(types.SECP256K1, priv)
}
return tx
}
func createCoinsTx(cfg *types.Chain33Config, to string, amount int64) *types.Transaction {
exec := types.LoadExecutorType(cfg.GetCoinExec())
if exec == nil {
panic("unknow driver coins")
}
tx, err := exec.AssertCreate(&types.CreateTx{
To: to,
Amount: amount,
})
if err != nil {
panic(err)
}
tx.To = to
tx, err = types.FormatTx(cfg, cfg.GetCoinExec(), tx)
if err != nil {
return nil
}
return tx
}
//CreateTxWithTxHeight : Create Tx With Tx Height
func CreateTxWithTxHeight(cfg *types.Chain33Config, priv crypto.PrivKey, to string, amount, expire int64) *types.Transaction {
tx := createCoinsTx(cfg, to, amount)
tx.Expire = expire + types.TxHeightFlag
tx.Sign(types.SECP256K1, priv)
return tx
}
// GenTxsTxHeight : Gen Txs with Heigt
func GenTxsTxHeight(cfg *types.Chain33Config, priv crypto.PrivKey, n, height int64) (txs []*types.Transaction) {
to, _ := Genaddress()
for i := 0; i < int(n); i++ {
tx := CreateTxWithTxHeight(cfg, priv, to, (n+1)*cfg.GetCoinPrecision(), height)
txs = append(txs, tx)
}
return txs
}
var zeroHash [32]byte
// CreateNoneBlock : Create None Block
func CreateNoneBlock(cfg *types.Chain33Config, priv crypto.PrivKey, n int64) *types.Block {
newblock := &types.Block{}
newblock.Height = 1
newblock.BlockTime = types.Now().Unix()
newblock.ParentHash = zeroHash[:]
newblock.Txs = GenNoneTxs(cfg, priv, n)
newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs)
return newblock
}
//CreateCoinsBlock : create coins block, n size
func CreateCoinsBlock(cfg *types.Chain33Config, priv crypto.PrivKey, n int64) *types.Block {
newblock := &types.Block{}
newblock.Height = 1
newblock.BlockTime = types.Now().Unix()
newblock.ParentHash = zeroHash[:]
newblock.Txs = GenCoinsTxs(cfg, priv, n)
newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs)
return newblock
}
// ExecBlock : just exec block
func ExecBlock(client queue.Client, prevStateRoot []byte, block *types.Block, errReturn, sync, checkblock bool) (*types.BlockDetail, []*types.Transaction, error) {
ulog.Debug("ExecBlock", "height------->", block.Height, "ntx", len(block.Txs))
beg := types.Now()
defer func() {
ulog.Info("ExecBlock", "height", block.Height, "ntx", len(block.Txs), "writebatchsync", sync, "cost", types.Since(beg))
}()
detail, deltx, err := PreExecBlock(client, prevStateRoot, block, errReturn, sync, checkblock)
if err != nil {
return nil, nil, err
}
// 写数据库失败时需要及时返回错误,防止错误数据被写入localdb中CHAIN33-567
err = ExecKVSetCommit(client, block.StateHash, false)
if err != nil {
return nil, nil, err
}
return detail, deltx, nil
}
// PreExecBlock : pre exec block
func PreExecBlock(client queue.Client, prevStateRoot []byte, block *types.Block, errReturn, sync, checkblock bool) (*types.BlockDetail, []*types.Transaction, error) {
//发送执行交易给execs模块
//通过consensus module 再次检查
config := client.GetConfig()
dupErrChan := make(chan error, 1)
cacheTxs := types.TxsToCache(block.Txs)
beg := types.Now()
//区块验签,只验证非本节点打包的区块
if errReturn && block.Height > 0 {
//首先向mempool模块查询是否存在该交易,避免重复验签,同步历史区块时方案无效
checkReq := &types.ReqCheckTxsExist{TxHashes: make([][]byte, len(block.Txs))}
for i, tx := range cacheTxs {
checkReq.TxHashes[i] = tx.Hash()
}
checkReqMsg := client.NewMessage("mempool", types.EventCheckTxsExist, checkReq)
err := client.Send(checkReqMsg, true)
if err != nil {
ulog.Error("PreExecBlock", "send mempool check txs exist err", err)
return nil, nil, err
}
reply, err := client.Wait(checkReqMsg)
if err != nil {
ulog.Error("PreExecBlock", "wait mempool check txs exist reply err", err)
return nil, nil, err
}
replyData := reply.GetData().(*types.ReplyCheckTxsExist)
unverifiedTxs := block.Txs
//区块中交易在mempool中已有存在情况,重新构造需要验签的交易列表
if replyData.ExistCount > 0 {
unverifiedTxs = make([]*types.Transaction, 0, len(block.Txs)-int(replyData.ExistCount))
for index, exist := range replyData.ExistFlags {
//只需要对mempool中不存在的交易验签
if !exist {
unverifiedTxs = append(unverifiedTxs, block.Txs[index])
}
}
}
signOK := types.VerifySignature(config, block, unverifiedTxs)
ulog.Info("PreExecBlock", "height", block.GetHeight(), "checkCount", len(unverifiedTxs), "CheckSign", types.Since(beg))
if !signOK {
return nil, nil, types.ErrSign
}
}
//check dup routine
go func() {
beg := types.Now()
defer func() {
ulog.Debug("PreExecBlock", "height", block.GetHeight(), "CheckTxDup", types.Since(beg))
}()
//check tx Duplicate
var err error
cacheTxs, err = CheckTxDup(client, cacheTxs, block.Height)
if err != nil {
dupErrChan <- err
return
}
if len(block.Txs) != len(cacheTxs) {
ulog.Error("PreExecBlock", "prevtx", len(block.Txs), "newtx", len(cacheTxs))
if errReturn {
err = types.ErrTxDup
}
}
dupErrChan <- err
}()
// exec tx routine
beg = types.Now()
//对区块的正确性保持乐观,交易查重和执行并行处理,提高效率
receipts, err := ExecTx(client, prevStateRoot, block)
ulog.Info("PreExecBlock", "height", block.GetHeight(), "ExecTx", types.Since(beg))
beg = types.Now()
//检查交易查重结果
if dupErr := <-dupErrChan; dupErr != nil {
ulog.Error("PreExecBlock", "height", block.GetHeight(), "CheckDupErr", dupErr)
return nil, nil, dupErr
}
//如果有重复交易, 需要重新赋值交易内容并执行
if len(block.Txs) != len(cacheTxs) {
block.Txs = types.CacheToTxs(cacheTxs)
receipts, err = ExecTx(client, prevStateRoot, block)
}
ulog.Info("PreExecBlock", "height", block.GetHeight(), "WaitDupCheck", types.Since(beg))
if err != nil {
return nil, nil, err
}
beg = types.Now()
kvset := make([]*types.KeyValue, 0, len(receipts.GetReceipts()))
rdata := make([]*types.ReceiptData, 0, len(receipts.GetReceipts())) //save to db receipt log
//删除无效的交易
var deltxs []*types.Transaction
index := 0
for i, receipt := range receipts.Receipts {
if receipt.Ty == types.ExecErr {
errTx := block.Txs[i]
ulog.Error("exec tx err", "err", receipt, "txhash", common.ToHex(errTx.Hash()))
if errReturn { //认为这个是一个错误的区块
return nil, nil, types.ErrBlockExec
}
deltxs = append(deltxs, errTx)
continue
}
block.Txs[index] = block.Txs[i]
cacheTxs[index] = cacheTxs[i]
index++
rdata = append(rdata, &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs})
kvset = append(kvset, receipt.KV...)
}
block.Txs = block.Txs[:index]
cacheTxs = cacheTxs[:index]
height := block.Height
//此时需要区分主链和平行链
if config.IsPara() {
height = block.MainHeight
}
var txHash []byte
if !config.IsFork(height, "ForkRootHash") {
txHash = merkle.CalcMerkleRootCache(cacheTxs)
} else {
txHash = merkle.CalcMerkleRoot(config, height, types.TransactionSort(block.Txs))
}
// 非本节点产生的区块, 校验TxHash, 其他节点errReturn = true
if errReturn && !bytes.Equal(txHash, block.TxHash) {
ulog.Error("PreExecBlock", "height", block.GetHeight(), "blkHash", hex.EncodeToString(block.Hash(config)),
"txHash", hex.EncodeToString(txHash), "errTxHash", hex.EncodeToString(block.TxHash),
"blkData", hex.EncodeToString(types.Encode(block)))
return nil, nil, types.ErrCheckTxHash
}
// 存在错误交易, 或共识生成区块时未设置TxHash, 需要重新赋值
block.TxHash = txHash
ulog.Debug("PreExecBlock", "CalcMerkleRootCache", types.Since(beg))
beg = types.Now()
kvset = DelDupKey(kvset)
stateHash, err := ExecKVMemSet(client, prevStateRoot, block.Height, kvset, sync, false)
if err != nil {
return nil, nil, err
}
if errReturn && !bytes.Equal(block.StateHash, stateHash) {
err = ExecKVSetRollback(client, stateHash)
if err != nil {
ulog.Error("PreExecBlock-->ExecKVSetRollback", "err", err)
}
if len(rdata) > 0 {
for i, rd := range rdata {
rd.OutputReceiptDetails(block.Txs[i].Execer, ulog)
}
}
ulog.Error("PreExecBlock ErrCheckStateHash", "height", block.Height,
"calcHash", common.ToHex(stateHash), "recvHash", common.ToHex(block.StateHash))
return nil, nil, types.ErrCheckStateHash
}
block.StateHash = stateHash
var detail types.BlockDetail
detail.Block = block
detail.Receipts = rdata
if detail.Block.Height > 0 && checkblock {
err := CheckBlock(client, &detail)
if err != nil {
ulog.Error("PreExecBlock", "height", block.GetHeight(), "checkBlockErr", err)
return nil, nil, err
}
}
ulog.Debug("PreExecBlock", "CheckBlock", types.Since(beg))
if len(kvset) > 0 {
detail.KV = kvset
}
detail.PrevStatusHash = prevStateRoot
return &detail, deltxs, nil
}
// ExecBlockUpgrade : just exec block
func ExecBlockUpgrade(client queue.Client, prevStateRoot []byte, block *types.Block, sync bool) error {
//发送执行交易给execs模块
//通过consensus module 再次检查
ulog.Debug("ExecBlockUpgrade", "height------->", block.Height, "ntx", len(block.Txs))
beg := types.Now()
beg1 := beg
defer func() {
ulog.Info("ExecBlockUpgrade", "height", block.Height, "ntx", len(block.Txs), "writebatchsync", sync, "cost", types.Since(beg1))
}()
var err error
//println("1")
receipts, err := ExecTx(client, prevStateRoot, block)
if err != nil {
return err
}
ulog.Debug("ExecBlockUpgrade", "ExecTx", types.Since(beg))
beg = types.Now()
var kvset []*types.KeyValue
for i := 0; i < len(receipts.Receipts); i++ {
receipt := receipts.Receipts[i]
kvset = append(kvset, receipt.KV...)
}
kvset = DelDupKey(kvset)
calcHash, err := ExecKVMemSet(client, prevStateRoot, block.Height, kvset, sync, true)
if err != nil {
return err
}
//println("2")
if !bytes.Equal(block.StateHash, calcHash) {
return types.ErrCheckStateHash
}
ulog.Debug("ExecBlockUpgrade", "CheckBlock", types.Since(beg))
// 写数据库失败时需要及时返回错误,防止错误数据被写入localdb中CHAIN33-567
err = ExecKVSetCommit(client, calcHash, true)
return err
}
//CreateNewBlock : Create a New Block
func CreateNewBlock(cfg *types.Chain33Config, parent *types.Block, txs []*types.Transaction) *types.Block {
newblock := &types.Block{}
newblock.Height = parent.Height + 1
newblock.BlockTime = parent.BlockTime + 1
newblock.ParentHash = parent.Hash(cfg)
newblock.Txs = append(newblock.Txs, txs...)
//需要首先对交易进行排序然后再计算TxHash
if cfg.IsFork(newblock.GetHeight(), "ForkRootHash") {
newblock.Txs = types.TransactionSort(newblock.Txs)
}
newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs)
return newblock
}
//ExecAndCheckBlock ...
func ExecAndCheckBlock(qclient queue.Client, block *types.Block, txs []*types.Transaction, result []int) (*types.Block, error) {
return ExecAndCheckBlockCB(qclient, block, txs, func(index int, receipt *types.ReceiptData) error {
if len(result) <= index {
return errors.New("txs num and status len not equal")
}
status := result[index]
if status == 0 && receipt != nil {
return errors.New("must failed, but index = " + fmt.Sprint(index))
}
if status > 0 && receipt == nil {
return errors.New("must not faild, but index = " + fmt.Sprint(index))
}
if status > 0 && receipt.Ty != int32(status) {
return errors.New("status must equal, but index = " + fmt.Sprint(index))
}
return nil
})
}
//ExecAndCheckBlockCB :
func ExecAndCheckBlockCB(qclient queue.Client, block *types.Block, txs []*types.Transaction, cb func(int, *types.ReceiptData) error) (*types.Block, error) {
block2 := CreateNewBlock(qclient.GetConfig(), block, txs)
detail, deltx, err := ExecBlock(qclient, block.StateHash, block2, false, true, false)
if err != nil {
return nil, err
}
for _, v := range deltx {
s, err := types.PBToJSON(v)
if err != nil {
return nil, err
}
println(string(s))
}
var getIndex = func(hash []byte, txlist []*types.Transaction) int {
for i := 0; i < len(txlist); i++ {
if bytes.Equal(hash, txlist[i].Hash()) {
return i
}
}
return -1
}
for i := 0; i < len(txs); i++ {
if getIndex(txs[i].Hash(), deltx) >= 0 {
if err := cb(i, nil); err != nil {
return nil, err
}
} else if index := getIndex(txs[i].Hash(), detail.Block.Txs); index >= 0 {
if err := cb(i, detail.Receipts[index]); err != nil {
return nil, err
}
}
}
return detail.Block, nil
}
//ResetDatadir 重写datadir
func ResetDatadir(cfg *types.Config, datadir string) string {
// Check in case of paths like "/something/~/something/"
if len(datadir) >= 2 && datadir[:2] == "~/" {
usr, err := user.Current()
if err != nil {
panic(err)
}
dir := usr.HomeDir
datadir = filepath.Join(dir, datadir[2:])
}
if len(datadir) >= 6 && datadir[:6] == "$TEMP/" {
dir, err := ioutil.TempDir("", "chain33datadir-")
if err != nil {
panic(err)
}
datadir = filepath.Join(dir, datadir[6:])
}
ulog.Info("current user data dir is ", "dir", datadir)
cfg.Log.LogFile = filepath.Join(datadir, cfg.Log.LogFile)
cfg.BlockChain.DbPath = filepath.Join(datadir, cfg.BlockChain.DbPath)
cfg.P2P.DbPath = filepath.Join(datadir, cfg.P2P.DbPath)
cfg.Wallet.DbPath = filepath.Join(datadir, cfg.Wallet.DbPath)
cfg.Store.DbPath = filepath.Join(datadir, cfg.Store.DbPath)
return datadir
}
//CreateTestDB 创建一个测试数据库
func CreateTestDB() (string, db.DB, db.KVDB) {
dir, err := ioutil.TempDir("", "goleveldb")
if err != nil {
panic(err)
}
leveldb, err := db.NewGoLevelDB("goleveldb", dir, 128)
if err != nil {
panic(err)
}
return dir, leveldb, db.NewKVDB(leveldb)
}
//CloseTestDB 创建一个测试数据库
func CloseTestDB(dir string, dbm db.DB) {
err := os.RemoveAll(dir)
if err != nil {
ulog.Info("RemoveAll ", "dir", dir, "err", err)
}
dbm.Close()
}
//SaveKVList 保存kvs to database
func SaveKVList(kvdb db.DB, kvs []*types.KeyValue) {
//printKV(kvs)
batch := kvdb.NewBatch(true)
for i := 0; i < len(kvs); i++ {
if kvs[i].Value == nil {
batch.Delete(kvs[i].Key)
continue
}
batch.Set(kvs[i].Key, kvs[i].Value)
}
err := batch.Write()
if err != nil {
panic(err)
}
}
//PrintKV 打印KVList
func PrintKV(kvs []*types.KeyValue) {
for i := 0; i < len(kvs); i++ {
fmt.Printf("KV %d %s(%s)\n", i, string(kvs[i].Key), common.ToHex(kvs[i].Value))
}
}
// MockModule struct
type MockModule struct {
Key string
}
// SetQueueClient method
func (m *MockModule) SetQueueClient(client queue.Client) {
go func() {
client.Sub(m.Key)
for msg := range client.Recv() {
msg.Reply(client.NewMessage(m.Key, types.EventReply, &types.Reply{IsOk: false,
Msg: []byte(fmt.Sprintf("mock %s module not handle message %v", m.Key, msg.Ty))}))
}
}()
}
// Wait for ready
func (m *MockModule) Wait() {}
// Close method
func (m *MockModule) Close() {}