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() {}