github.com/turingchain2020/turingchain@v1.1.21/util/util.go (about) 1 // Copyright Turing Corp. 2018 All Rights Reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package util 6 7 import ( 8 "bytes" 9 "encoding/json" 10 "fmt" 11 "io/ioutil" 12 "math/rand" 13 "os" 14 "os/user" 15 "path/filepath" 16 "unicode" 17 18 "strings" 19 20 "github.com/turingchain2020/turingchain/common" 21 "github.com/turingchain2020/turingchain/common/address" 22 "github.com/turingchain2020/turingchain/common/crypto" 23 "github.com/turingchain2020/turingchain/common/db" 24 "github.com/turingchain2020/turingchain/common/log/log15" 25 "github.com/turingchain2020/turingchain/common/merkle" 26 "github.com/turingchain2020/turingchain/queue" 27 "github.com/turingchain2020/turingchain/types" 28 "github.com/pkg/errors" 29 ) 30 31 func init() { 32 rand.Seed(types.Now().UnixNano()) 33 } 34 35 var ulog = log15.New("module", "util") 36 37 //GetParaExecName : 如果 name 没有 paraName 前缀,那么加上这个前缀 38 func GetParaExecName(paraName string, name string) string { 39 if strings.HasPrefix(name, "user.p.") { 40 return name 41 } 42 return paraName + name 43 } 44 45 // MakeStringToUpper : 将给定的in字符串从pos开始一共count个转换为大写字母 46 func MakeStringToUpper(in string, pos, count int) (out string, err error) { 47 l := len(in) 48 if pos < 0 || pos >= l || (pos+count) >= l || count <= 0 { 49 err = fmt.Errorf("Invalid params. in=%s pos=%d count=%d", in, pos, count) 50 return 51 } 52 tmp := []rune(in) 53 for n := pos; n < pos+count; n++ { 54 tmp[n] = unicode.ToUpper(tmp[n]) 55 } 56 out = string(tmp) 57 return 58 } 59 60 // MakeStringToLower : 将给定的in字符串从pos开始一共count个转换为小写字母 61 func MakeStringToLower(in string, pos, count int) (out string, err error) { 62 l := len(in) 63 if pos < 0 || pos >= l || (pos+count) >= l || count <= 0 { 64 err = fmt.Errorf("Invalid params. in=%s pos=%d count=%d", in, pos, count) 65 return 66 } 67 tmp := []rune(in) 68 for n := pos; n < pos+count; n++ { 69 tmp[n] = unicode.ToLower(tmp[n]) 70 } 71 out = string(tmp) 72 return 73 } 74 75 //GenNoneTxs : 创建一些 none 执行器的 交易列表,一般用于测试 76 func GenNoneTxs(cfg *types.TuringchainConfig, priv crypto.PrivKey, n int64) (txs []*types.Transaction) { 77 for i := 0; i < int(n); i++ { 78 txs = append(txs, CreateNoneTx(cfg, priv)) 79 } 80 return txs 81 } 82 83 //GenCoinsTxs : generate txs to be executed on exector coin 84 func GenCoinsTxs(cfg *types.TuringchainConfig, priv crypto.PrivKey, n int64) (txs []*types.Transaction) { 85 to, _ := Genaddress() 86 for i := 0; i < int(n); i++ { 87 txs = append(txs, CreateCoinsTx(cfg, priv, to, n+1)) 88 } 89 return txs 90 } 91 92 //Genaddress : generate a address 93 func Genaddress() (string, crypto.PrivKey) { 94 cr, err := crypto.New(types.GetSignName("", types.SECP256K1)) 95 if err != nil { 96 panic(err) 97 } 98 privto, err := cr.GenKey() 99 if err != nil { 100 panic(err) 101 } 102 addrto := address.PubKeyToAddress(privto.PubKey().Bytes()) 103 return addrto.String(), privto 104 } 105 106 // CreateNoneTx : Create None Tx 107 func CreateNoneTx(cfg *types.TuringchainConfig, priv crypto.PrivKey) *types.Transaction { 108 return CreateTxWithExecer(cfg, priv, "none") 109 } 110 111 // UpdateExpireWithTxHeight 设置txHeight类型交易过期 112 func UpdateExpireWithTxHeight(tx *types.Transaction, priv crypto.PrivKey, txHeight int64) { 113 tx.Expire = txHeight + types.TxHeightFlag 114 if priv != nil { 115 tx.Sign(types.SECP256K1, priv) 116 } 117 } 118 119 // CreateCoinsTxWithTxHeight 使用txHeight作为交易过期 120 func CreateCoinsTxWithTxHeight(cfg *types.TuringchainConfig, priv crypto.PrivKey, to string, amount, txHeight int64) *types.Transaction { 121 122 tx := CreateCoinsTx(cfg, nil, to, amount) 123 UpdateExpireWithTxHeight(tx, priv, txHeight) 124 return tx 125 } 126 127 //CreateNoneTxWithTxHeight 使用txHeight作为交易过期 128 func CreateNoneTxWithTxHeight(cfg *types.TuringchainConfig, priv crypto.PrivKey, txHeight int64) *types.Transaction { 129 130 tx := CreateNoneTx(cfg, nil) 131 UpdateExpireWithTxHeight(tx, priv, txHeight) 132 return tx 133 } 134 135 // CreateTxWithExecer : Create Tx With Execer 136 func CreateTxWithExecer(cfg *types.TuringchainConfig, priv crypto.PrivKey, execer string) *types.Transaction { 137 if execer == "coins" { 138 to, _ := Genaddress() 139 return CreateCoinsTx(cfg, priv, to, types.Coin) 140 } 141 tx := &types.Transaction{Execer: []byte(execer), Payload: []byte("none")} 142 tx.To = address.ExecAddress(execer) 143 tx, err := types.FormatTx(cfg, execer, tx) 144 if err != nil { 145 return nil 146 } 147 if priv != nil { 148 tx.Sign(types.SECP256K1, priv) 149 } 150 return tx 151 } 152 153 //TestingT 测试类型 154 type TestingT interface { 155 Error(args ...interface{}) 156 Log(args ...interface{}) 157 } 158 159 // JSONPrint : print in json format 160 func JSONPrint(t TestingT, input interface{}) { 161 data, err := json.MarshalIndent(input, "", "\t") 162 if err != nil { 163 t.Error(err) 164 return 165 } 166 if t == nil { 167 fmt.Println(string(data)) 168 } else { 169 t.Log(string(data)) 170 } 171 } 172 173 // CreateManageTx : Create Manage Tx 174 func CreateManageTx(cfg *types.TuringchainConfig, priv crypto.PrivKey, key, op, value string) *types.Transaction { 175 v := &types.ModifyConfig{Key: key, Op: op, Value: value, Addr: ""} 176 exec := types.LoadExecutorType("manage") 177 if exec == nil { 178 panic("manage exec is not init") 179 } 180 tx, err := exec.Create("Modify", v) 181 if err != nil { 182 panic(err) 183 } 184 tx, err = types.FormatTx(cfg, "manage", tx) 185 if err != nil { 186 return nil 187 } 188 tx.Sign(types.SECP256K1, priv) 189 return tx 190 } 191 192 // CreateCoinsTx : Create Coins Tx 193 func CreateCoinsTx(cfg *types.TuringchainConfig, priv crypto.PrivKey, to string, amount int64) *types.Transaction { 194 tx := createCoinsTx(cfg, to, amount) 195 tx.Sign(types.SECP256K1, priv) 196 return tx 197 } 198 199 func createCoinsTx(cfg *types.TuringchainConfig, to string, amount int64) *types.Transaction { 200 exec := types.LoadExecutorType("coins") 201 if exec == nil { 202 panic("unknow driver coins") 203 } 204 tx, err := exec.AssertCreate(&types.CreateTx{ 205 To: to, 206 Amount: amount, 207 }) 208 if err != nil { 209 panic(err) 210 } 211 tx.To = to 212 tx, err = types.FormatTx(cfg, "coins", tx) 213 if err != nil { 214 return nil 215 } 216 return tx 217 } 218 219 //CreateTxWithTxHeight : Create Tx With Tx Height 220 func CreateTxWithTxHeight(cfg *types.TuringchainConfig, priv crypto.PrivKey, to string, amount, expire int64) *types.Transaction { 221 tx := createCoinsTx(cfg, to, amount) 222 tx.Expire = expire + types.TxHeightFlag 223 tx.Sign(types.SECP256K1, priv) 224 return tx 225 } 226 227 // GenTxsTxHeight : Gen Txs with Heigt 228 func GenTxsTxHeight(cfg *types.TuringchainConfig, priv crypto.PrivKey, n, height int64) (txs []*types.Transaction) { 229 to, _ := Genaddress() 230 for i := 0; i < int(n); i++ { 231 tx := CreateTxWithTxHeight(cfg, priv, to, types.Coin*(n+1), height) 232 txs = append(txs, tx) 233 } 234 return txs 235 } 236 237 var zeroHash [32]byte 238 239 // CreateNoneBlock : Create None Block 240 func CreateNoneBlock(cfg *types.TuringchainConfig, priv crypto.PrivKey, n int64) *types.Block { 241 newblock := &types.Block{} 242 newblock.Height = 1 243 newblock.BlockTime = types.Now().Unix() 244 newblock.ParentHash = zeroHash[:] 245 newblock.Txs = GenNoneTxs(cfg, priv, n) 246 newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs) 247 return newblock 248 } 249 250 //CreateCoinsBlock : create coins block, n size 251 func CreateCoinsBlock(cfg *types.TuringchainConfig, priv crypto.PrivKey, n int64) *types.Block { 252 newblock := &types.Block{} 253 newblock.Height = 1 254 newblock.BlockTime = types.Now().Unix() 255 newblock.ParentHash = zeroHash[:] 256 newblock.Txs = GenCoinsTxs(cfg, priv, n) 257 newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs) 258 return newblock 259 } 260 261 // ExecBlock : just exec block 262 func ExecBlock(client queue.Client, prevStateRoot []byte, block *types.Block, errReturn, sync, checkblock bool) (*types.BlockDetail, []*types.Transaction, error) { 263 ulog.Debug("ExecBlock", "height------->", block.Height, "ntx", len(block.Txs)) 264 beg := types.Now() 265 defer func() { 266 ulog.Info("ExecBlock", "height", block.Height, "ntx", len(block.Txs), "writebatchsync", sync, "cost", types.Since(beg)) 267 }() 268 269 detail, deltx, err := PreExecBlock(client, prevStateRoot, block, errReturn, sync, checkblock) 270 if err != nil { 271 return nil, nil, err 272 } 273 // 写数据库失败时需要及时返回错误,防止错误数据被写入localdb中TURINGCHAIN-567 274 err = ExecKVSetCommit(client, block.StateHash, false) 275 if err != nil { 276 return nil, nil, err 277 } 278 return detail, deltx, nil 279 } 280 281 // PreExecBlock : pre exec block 282 func PreExecBlock(client queue.Client, prevStateRoot []byte, block *types.Block, errReturn, sync, checkblock bool) (*types.BlockDetail, []*types.Transaction, error) { 283 //发送执行交易给execs模块 284 //通过consensus module 再次检查 285 config := client.GetConfig() 286 dupErrChan := make(chan error, 1) 287 cacheTxs := types.TxsToCache(block.Txs) 288 beg := types.Now() 289 //区块验签,只验证非本节点打包的区块 290 if errReturn && block.Height > 0 { 291 292 //首先向mempool模块查询是否存在该交易,避免重复验签,同步历史区块时方案无效 293 checkReq := &types.ReqCheckTxsExist{TxHashes: make([][]byte, len(block.Txs))} 294 for i, tx := range cacheTxs { 295 checkReq.TxHashes[i] = tx.Hash() 296 } 297 checkReqMsg := client.NewMessage("mempool", types.EventCheckTxsExist, checkReq) 298 err := client.Send(checkReqMsg, true) 299 if err != nil { 300 ulog.Error("PreExecBlock", "send mempool check txs exist err", err) 301 return nil, nil, err 302 } 303 reply, err := client.Wait(checkReqMsg) 304 if err != nil { 305 ulog.Error("PreExecBlock", "wait mempool check txs exist reply err", err) 306 return nil, nil, err 307 } 308 replyData := reply.GetData().(*types.ReplyCheckTxsExist) 309 unverifiedTxs := block.Txs 310 //区块中交易在mempool中已有存在情况,重新构造需要验签的交易列表 311 if replyData.ExistCount > 0 { 312 unverifiedTxs = make([]*types.Transaction, 0, len(block.Txs)-int(replyData.ExistCount)) 313 for index, exist := range replyData.ExistFlags { 314 //只需要对mempool中不存在的交易验签 315 if !exist { 316 unverifiedTxs = append(unverifiedTxs, block.Txs[index]) 317 } 318 } 319 } 320 signOK := types.VerifySignature(config, block, unverifiedTxs) 321 ulog.Debug("PreExecBlock", "height", block.GetHeight(), "checkCount", len(unverifiedTxs), "CheckSign", types.Since(beg)) 322 if !signOK { 323 return nil, nil, types.ErrSign 324 } 325 } 326 327 //check dup routine 328 go func() { 329 beg := types.Now() 330 defer func() { 331 ulog.Debug("PreExecBlock", "height", block.GetHeight(), "CheckTxDup", types.Since(beg)) 332 }() 333 //check tx Duplicate 334 var err error 335 cacheTxs, err = CheckTxDup(client, cacheTxs, block.Height) 336 if err != nil { 337 dupErrChan <- err 338 return 339 } 340 if len(block.Txs) != len(cacheTxs) { 341 ulog.Error("PreExecBlock", "prevtx", len(block.Txs), "newtx", len(cacheTxs)) 342 if errReturn { 343 err = types.ErrTxDup 344 } 345 } 346 dupErrChan <- err 347 }() 348 349 // exec tx routine 350 beg = types.Now() 351 //对区块的正确性保持乐观,交易查重和执行并行处理,提高效率 352 receipts, err := ExecTx(client, prevStateRoot, block) 353 ulog.Debug("PreExecBlock", "height", block.GetHeight(), "ExecTx", types.Since(beg)) 354 beg = types.Now() 355 356 //检查交易查重结果 357 if dupErr := <-dupErrChan; dupErr != nil { 358 ulog.Error("PreExecBlock", "height", block.GetHeight(), "CheckDupErr", dupErr) 359 return nil, nil, dupErr 360 } 361 //如果有重复交易, 需要重新赋值交易内容并执行 362 if len(block.Txs) != len(cacheTxs) { 363 block.Txs = types.CacheToTxs(cacheTxs) 364 receipts, err = ExecTx(client, prevStateRoot, block) 365 } 366 ulog.Debug("PreExecBlock", "height", block.GetHeight(), "WaitDupCheck", types.Since(beg)) 367 if err != nil { 368 return nil, nil, err 369 } 370 371 beg = types.Now() 372 kvset := make([]*types.KeyValue, 0, len(receipts.GetReceipts())) 373 rdata := make([]*types.ReceiptData, 0, len(receipts.GetReceipts())) //save to db receipt log 374 //删除无效的交易 375 var deltxs []*types.Transaction 376 index := 0 377 for i, receipt := range receipts.Receipts { 378 if receipt.Ty == types.ExecErr { 379 errTx := block.Txs[i] 380 ulog.Error("exec tx err", "err", receipt, "txhash", common.ToHex(errTx.Hash())) 381 if errReturn { //认为这个是一个错误的区块 382 return nil, nil, types.ErrBlockExec 383 } 384 deltxs = append(deltxs, errTx) 385 continue 386 } 387 block.Txs[index] = block.Txs[i] 388 cacheTxs[index] = cacheTxs[i] 389 index++ 390 rdata = append(rdata, &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}) 391 kvset = append(kvset, receipt.KV...) 392 } 393 block.Txs = block.Txs[:index] 394 cacheTxs = cacheTxs[:index] 395 396 //检查block的txhash值 397 var txHash []byte 398 height := block.Height 399 //此时需要区分主链和平行链 400 if config.IsPara() { 401 height = block.MainHeight 402 } 403 if !config.IsFork(height, "ForkRootHash") { 404 txHash = merkle.CalcMerkleRootCache(cacheTxs) 405 } else { 406 txHash = merkle.CalcMerkleRoot(config, height, types.TransactionSort(block.Txs)) 407 } 408 if errReturn && !bytes.Equal(txHash, block.TxHash) { 409 return nil, nil, types.ErrCheckTxHash 410 } 411 block.TxHash = txHash 412 ulog.Debug("PreExecBlock", "CalcMerkleRootCache", types.Since(beg)) 413 beg = types.Now() 414 kvset = DelDupKey(kvset) 415 stateHash, err := ExecKVMemSet(client, prevStateRoot, block.Height, kvset, sync, false) 416 if err != nil { 417 return nil, nil, err 418 } 419 //println("2") 420 if errReturn && !bytes.Equal(block.StateHash, stateHash) { 421 err = ExecKVSetRollback(client, stateHash) 422 if err != nil { 423 ulog.Error("PreExecBlock-->ExecKVSetRollback", "err", err) 424 } 425 if len(rdata) > 0 { 426 for i, rd := range rdata { 427 rd.OutputReceiptDetails(block.Txs[i].Execer, ulog) 428 } 429 } 430 return nil, nil, types.ErrCheckStateHash 431 } 432 block.StateHash = stateHash 433 var detail types.BlockDetail 434 detail.Block = block 435 detail.Receipts = rdata 436 if detail.Block.Height > 0 && checkblock { 437 err := CheckBlock(client, &detail) 438 if err != nil { 439 ulog.Error("PreExecBlock", "height", block.GetHeight(), "checkBlockErr", err) 440 return nil, nil, err 441 } 442 } 443 ulog.Debug("PreExecBlock", "CheckBlock", types.Since(beg)) 444 445 detail.KV = kvset 446 detail.PrevStatusHash = prevStateRoot 447 return &detail, deltxs, nil 448 } 449 450 // ExecBlockUpgrade : just exec block 451 func ExecBlockUpgrade(client queue.Client, prevStateRoot []byte, block *types.Block, sync bool) error { 452 //发送执行交易给execs模块 453 //通过consensus module 再次检查 454 ulog.Debug("ExecBlockUpgrade", "height------->", block.Height, "ntx", len(block.Txs)) 455 beg := types.Now() 456 beg1 := beg 457 defer func() { 458 ulog.Info("ExecBlockUpgrade", "height", block.Height, "ntx", len(block.Txs), "writebatchsync", sync, "cost", types.Since(beg1)) 459 }() 460 461 var err error 462 //println("1") 463 receipts, err := ExecTx(client, prevStateRoot, block) 464 if err != nil { 465 return err 466 } 467 ulog.Debug("ExecBlockUpgrade", "ExecTx", types.Since(beg)) 468 beg = types.Now() 469 var kvset []*types.KeyValue 470 for i := 0; i < len(receipts.Receipts); i++ { 471 receipt := receipts.Receipts[i] 472 kvset = append(kvset, receipt.KV...) 473 } 474 kvset = DelDupKey(kvset) 475 calcHash, err := ExecKVMemSet(client, prevStateRoot, block.Height, kvset, sync, true) 476 if err != nil { 477 return err 478 } 479 //println("2") 480 if !bytes.Equal(block.StateHash, calcHash) { 481 return types.ErrCheckStateHash 482 } 483 ulog.Debug("ExecBlockUpgrade", "CheckBlock", types.Since(beg)) 484 // 写数据库失败时需要及时返回错误,防止错误数据被写入localdb中TURINGCHAIN-567 485 err = ExecKVSetCommit(client, calcHash, true) 486 return err 487 } 488 489 //CreateNewBlock : Create a New Block 490 func CreateNewBlock(cfg *types.TuringchainConfig, parent *types.Block, txs []*types.Transaction) *types.Block { 491 newblock := &types.Block{} 492 newblock.Height = parent.Height + 1 493 newblock.BlockTime = parent.BlockTime + 1 494 newblock.ParentHash = parent.Hash(cfg) 495 newblock.Txs = append(newblock.Txs, txs...) 496 497 //需要首先对交易进行排序然后再计算TxHash 498 if cfg.IsFork(newblock.GetHeight(), "ForkRootHash") { 499 newblock.Txs = types.TransactionSort(newblock.Txs) 500 } 501 newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs) 502 return newblock 503 } 504 505 //ExecAndCheckBlock ... 506 func ExecAndCheckBlock(qclient queue.Client, block *types.Block, txs []*types.Transaction, result []int) (*types.Block, error) { 507 return ExecAndCheckBlockCB(qclient, block, txs, func(index int, receipt *types.ReceiptData) error { 508 if len(result) <= index { 509 return errors.New("txs num and status len not equal") 510 } 511 status := result[index] 512 if status == 0 && receipt != nil { 513 return errors.New("must failed, but index = " + fmt.Sprint(index)) 514 } 515 if status > 0 && receipt == nil { 516 return errors.New("must not faild, but index = " + fmt.Sprint(index)) 517 } 518 if status > 0 && receipt.Ty != int32(status) { 519 return errors.New("status must equal, but index = " + fmt.Sprint(index)) 520 } 521 return nil 522 }) 523 } 524 525 //ExecAndCheckBlockCB : 526 func ExecAndCheckBlockCB(qclient queue.Client, block *types.Block, txs []*types.Transaction, cb func(int, *types.ReceiptData) error) (*types.Block, error) { 527 block2 := CreateNewBlock(qclient.GetConfig(), block, txs) 528 detail, deltx, err := ExecBlock(qclient, block.StateHash, block2, false, true, false) 529 if err != nil { 530 return nil, err 531 } 532 for _, v := range deltx { 533 s, err := types.PBToJSON(v) 534 if err != nil { 535 return nil, err 536 } 537 println(string(s)) 538 } 539 var getIndex = func(hash []byte, txlist []*types.Transaction) int { 540 for i := 0; i < len(txlist); i++ { 541 if bytes.Equal(hash, txlist[i].Hash()) { 542 return i 543 } 544 } 545 return -1 546 } 547 for i := 0; i < len(txs); i++ { 548 if getIndex(txs[i].Hash(), deltx) >= 0 { 549 if err := cb(i, nil); err != nil { 550 return nil, err 551 } 552 } else if index := getIndex(txs[i].Hash(), detail.Block.Txs); index >= 0 { 553 if err := cb(i, detail.Receipts[index]); err != nil { 554 return nil, err 555 } 556 } 557 } 558 return detail.Block, nil 559 } 560 561 //ResetDatadir 重写datadir 562 func ResetDatadir(cfg *types.Config, datadir string) string { 563 // Check in case of paths like "/something/~/something/" 564 if len(datadir) >= 2 && datadir[:2] == "~/" { 565 usr, err := user.Current() 566 if err != nil { 567 panic(err) 568 } 569 dir := usr.HomeDir 570 datadir = filepath.Join(dir, datadir[2:]) 571 } 572 if len(datadir) >= 6 && datadir[:6] == "$TEMP/" { 573 dir, err := ioutil.TempDir("", "turingchaindatadir-") 574 if err != nil { 575 panic(err) 576 } 577 datadir = filepath.Join(dir, datadir[6:]) 578 } 579 ulog.Info("current user data dir is ", "dir", datadir) 580 cfg.Log.LogFile = filepath.Join(datadir, cfg.Log.LogFile) 581 cfg.BlockChain.DbPath = filepath.Join(datadir, cfg.BlockChain.DbPath) 582 cfg.P2P.DbPath = filepath.Join(datadir, cfg.P2P.DbPath) 583 cfg.Wallet.DbPath = filepath.Join(datadir, cfg.Wallet.DbPath) 584 cfg.Store.DbPath = filepath.Join(datadir, cfg.Store.DbPath) 585 return datadir 586 } 587 588 //CreateTestDB 创建一个测试数据库 589 func CreateTestDB() (string, db.DB, db.KVDB) { 590 dir, err := ioutil.TempDir("", "goleveldb") 591 if err != nil { 592 panic(err) 593 } 594 leveldb, err := db.NewGoLevelDB("goleveldb", dir, 128) 595 if err != nil { 596 panic(err) 597 } 598 return dir, leveldb, db.NewKVDB(leveldb) 599 } 600 601 //CloseTestDB 创建一个测试数据库 602 func CloseTestDB(dir string, dbm db.DB) { 603 err := os.RemoveAll(dir) 604 if err != nil { 605 ulog.Info("RemoveAll ", "dir", dir, "err", err) 606 } 607 dbm.Close() 608 } 609 610 //SaveKVList 保存kvs to database 611 func SaveKVList(kvdb db.DB, kvs []*types.KeyValue) { 612 //printKV(kvs) 613 batch := kvdb.NewBatch(true) 614 for i := 0; i < len(kvs); i++ { 615 if kvs[i].Value == nil { 616 batch.Delete(kvs[i].Key) 617 continue 618 } 619 batch.Set(kvs[i].Key, kvs[i].Value) 620 } 621 err := batch.Write() 622 if err != nil { 623 panic(err) 624 } 625 } 626 627 //PrintKV 打印KVList 628 func PrintKV(kvs []*types.KeyValue) { 629 for i := 0; i < len(kvs); i++ { 630 fmt.Printf("KV %d %s(%s)\n", i, string(kvs[i].Key), common.ToHex(kvs[i].Value)) 631 } 632 } 633 634 // MockModule struct 635 type MockModule struct { 636 Key string 637 } 638 639 // SetQueueClient method 640 func (m *MockModule) SetQueueClient(client queue.Client) { 641 go func() { 642 client.Sub(m.Key) 643 for msg := range client.Recv() { 644 msg.Reply(client.NewMessage(m.Key, types.EventReply, &types.Reply{IsOk: false, 645 Msg: []byte(fmt.Sprintf("mock %s module not handle message %v", m.Key, msg.Ty))})) 646 } 647 }() 648 } 649 650 // Wait for ready 651 func (m *MockModule) Wait() {} 652 653 // Close method 654 func (m *MockModule) Close() {}