github.com/turingchain2020/turingchain@v1.1.21/util/testnode/testnode.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 testnode 提供一个通用的测试节点,用于单元测试和集成测试。
     6  
     7  package testnode
     8  
     9  import (
    10  	"fmt"
    11  	"math/rand"
    12  	"os"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/turingchain2020/turingchain/p2p"
    18  
    19  	"github.com/turingchain2020/turingchain/account"
    20  	"github.com/turingchain2020/turingchain/blockchain"
    21  	"github.com/turingchain2020/turingchain/client"
    22  	"github.com/turingchain2020/turingchain/common"
    23  	"github.com/turingchain2020/turingchain/common/address"
    24  	"github.com/turingchain2020/turingchain/common/crypto"
    25  	"github.com/turingchain2020/turingchain/common/limits"
    26  	"github.com/turingchain2020/turingchain/common/log"
    27  	"github.com/turingchain2020/turingchain/common/log/log15"
    28  	"github.com/turingchain2020/turingchain/consensus"
    29  	"github.com/turingchain2020/turingchain/executor"
    30  	"github.com/turingchain2020/turingchain/mempool"
    31  	"github.com/turingchain2020/turingchain/pluginmgr"
    32  	"github.com/turingchain2020/turingchain/queue"
    33  	"github.com/turingchain2020/turingchain/rpc"
    34  	"github.com/turingchain2020/turingchain/rpc/jsonclient"
    35  	rpctypes "github.com/turingchain2020/turingchain/rpc/types"
    36  	"github.com/turingchain2020/turingchain/store"
    37  	"github.com/turingchain2020/turingchain/types"
    38  	"github.com/turingchain2020/turingchain/util"
    39  	"github.com/turingchain2020/turingchain/wallet"
    40  )
    41  
    42  func init() {
    43  	err := limits.SetLimits()
    44  	if err != nil {
    45  		panic(err)
    46  	}
    47  	log.SetLogLevel("info")
    48  }
    49  
    50  //保证只有一个turingchain 会运行
    51  var lognode = log15.New("module", "lognode")
    52  
    53  //TuringchainMock :
    54  type TuringchainMock struct {
    55  	random   *rand.Rand
    56  	q        queue.Queue
    57  	client   queue.Client
    58  	api      client.QueueProtocolAPI
    59  	chain    *blockchain.BlockChain
    60  	mem      queue.Module
    61  	cs       queue.Module
    62  	exec     *executor.Executor
    63  	wallet   queue.Module
    64  	network  queue.Module
    65  	store    queue.Module
    66  	rpc      *rpc.RPC
    67  	cfg      *types.Config
    68  	sub      *types.ConfigSubModule
    69  	datadir  string
    70  	lastsend []byte
    71  	mu       sync.Mutex
    72  }
    73  
    74  //GetDefaultConfig :
    75  func GetDefaultConfig() *types.TuringchainConfig {
    76  	return types.NewTuringchainConfig(types.GetDefaultCfgstring())
    77  }
    78  
    79  //NewWithConfig :
    80  func NewWithConfig(cfg *types.TuringchainConfig, mockapi client.QueueProtocolAPI) *TuringchainMock {
    81  	return newWithConfig(cfg, mockapi)
    82  }
    83  
    84  // NewWithRPC 创建测试节点 并开放rpc服务
    85  func NewWithRPC(cfg *types.TuringchainConfig, mockapi client.QueueProtocolAPI) *TuringchainMock {
    86  	mock := newWithConfigNoLock(cfg, mockapi)
    87  	mock.rpc.Close()
    88  	server := rpc.New(cfg)
    89  	server.SetAPI(mock.api)
    90  	server.SetQueueClient(mock.q.Client())
    91  	mock.rpc = server
    92  	return mock
    93  }
    94  
    95  func newWithConfig(cfg *types.TuringchainConfig, mockapi client.QueueProtocolAPI) *TuringchainMock {
    96  	return newWithConfigNoLock(cfg, mockapi)
    97  }
    98  
    99  func newWithConfigNoLock(cfg *types.TuringchainConfig, mockapi client.QueueProtocolAPI) *TuringchainMock {
   100  	mfg := cfg.GetModuleConfig()
   101  	sub := cfg.GetSubConfig()
   102  	crypto.Init(mfg.Crypto, sub.Crypto)
   103  	q := queue.New("channel")
   104  	q.SetConfig(cfg)
   105  	types.Debug = false
   106  	datadir := util.ResetDatadir(mfg, "$TEMP/")
   107  	mock := &TuringchainMock{cfg: mfg, sub: sub, q: q, datadir: datadir}
   108  	mock.random = rand.New(rand.NewSource(types.Now().UnixNano()))
   109  
   110  	mock.exec = executor.New(cfg)
   111  	mock.exec.SetQueueClient(q.Client())
   112  	lognode.Info("init exec")
   113  
   114  	mock.store = store.New(cfg)
   115  	mock.store.SetQueueClient(q.Client())
   116  	lognode.Info("init store")
   117  
   118  	mock.chain = blockchain.New(cfg)
   119  	mock.chain.SetQueueClient(q.Client())
   120  	lognode.Info("init blockchain")
   121  
   122  	mock.cs = consensus.New(cfg)
   123  	mock.cs.SetQueueClient(q.Client())
   124  	lognode.Info("init consensus " + mfg.Consensus.Name)
   125  
   126  	mock.mem = mempool.New(cfg)
   127  	mock.mem.SetQueueClient(q.Client())
   128  	mock.mem.Wait()
   129  	lognode.Info("init mempool")
   130  	if mfg.P2P.Enable {
   131  		mock.network = p2p.NewP2PMgr(cfg)
   132  		mock.network.SetQueueClient(q.Client())
   133  	} else {
   134  		mock.network = &mockP2P{}
   135  		mock.network.SetQueueClient(q.Client())
   136  	}
   137  	lognode.Info("init P2P")
   138  	cli := q.Client()
   139  	w := wallet.New(cfg)
   140  	mock.client = q.Client()
   141  	mock.wallet = w
   142  	mock.wallet.SetQueueClient(cli)
   143  	lognode.Info("init wallet")
   144  	if mockapi == nil {
   145  		var err error
   146  		mockapi, err = client.New(q.Client(), nil)
   147  		if err != nil {
   148  			return nil
   149  		}
   150  		newWalletRealize(mockapi)
   151  	}
   152  	mock.api = mockapi
   153  	server := rpc.New(cfg)
   154  	server.SetAPI(mock.api)
   155  	server.SetQueueClientNoListen(q.Client())
   156  	mock.rpc = server
   157  	return mock
   158  }
   159  
   160  //New :
   161  func New(cfgpath string, mockapi client.QueueProtocolAPI) *TuringchainMock {
   162  	var cfg *types.TuringchainConfig
   163  	if cfgpath == "" || cfgpath == "--notset--" || cfgpath == "--free--" {
   164  		cfg = types.NewTuringchainConfig(types.GetDefaultCfgstring())
   165  		if cfgpath == "--free--" {
   166  			setFee(cfg.GetModuleConfig(), 0)
   167  			cfg.SetMinFee(0)
   168  		}
   169  	} else {
   170  		cfg = types.NewTuringchainConfig(types.ReadFile(cfgpath))
   171  	}
   172  	return newWithConfig(cfg, mockapi)
   173  }
   174  
   175  //Listen :
   176  func (mock *TuringchainMock) Listen() {
   177  	pluginmgr.AddRPC(mock.rpc)
   178  	var portgrpc, portjsonrpc int
   179  	for {
   180  		portgrpc, portjsonrpc = mock.rpc.Listen()
   181  		if portgrpc != 0 && portjsonrpc != 0 {
   182  			break
   183  		}
   184  	}
   185  
   186  	if strings.HasSuffix(mock.cfg.RPC.JrpcBindAddr, ":0") {
   187  		l := len(mock.cfg.RPC.JrpcBindAddr)
   188  		mock.cfg.RPC.JrpcBindAddr = mock.cfg.RPC.JrpcBindAddr[0:l-2] + ":" + fmt.Sprint(portjsonrpc)
   189  	}
   190  	if strings.HasSuffix(mock.cfg.RPC.GrpcBindAddr, ":0") {
   191  		l := len(mock.cfg.RPC.GrpcBindAddr)
   192  		mock.cfg.RPC.GrpcBindAddr = mock.cfg.RPC.GrpcBindAddr[0:l-2] + ":" + fmt.Sprint(portgrpc)
   193  	}
   194  }
   195  
   196  //ModifyParaClient modify para config
   197  func ModifyParaClient(cfg *types.TuringchainConfig, gaddr string) {
   198  	sub := cfg.GetSubConfig()
   199  	if sub.Consensus["para"] != nil {
   200  		data, err := types.ModifySubConfig(sub.Consensus["para"], "ParaRemoteGrpcClient", gaddr)
   201  		if err != nil {
   202  			panic(err)
   203  		}
   204  		sub.Consensus["para"] = data
   205  		cfg.S("config.consensus.sub.para.ParaRemoteGrpcClient", gaddr)
   206  	}
   207  }
   208  
   209  //GetBlockChain :
   210  func (mock *TuringchainMock) GetBlockChain() *blockchain.BlockChain {
   211  	return mock.chain
   212  }
   213  
   214  func setFee(cfg *types.Config, fee int64) {
   215  	cfg.Mempool.MinTxFeeRate = fee
   216  	cfg.Wallet.MinFee = fee
   217  }
   218  
   219  //GetJSONC :
   220  func (mock *TuringchainMock) GetJSONC() *jsonclient.JSONClient {
   221  	jsonc, err := jsonclient.NewJSONClient("http://" + mock.cfg.RPC.JrpcBindAddr + "/")
   222  	if err != nil {
   223  		return nil
   224  	}
   225  	return jsonc
   226  }
   227  
   228  //SendAndSign :
   229  func (mock *TuringchainMock) SendAndSign(priv crypto.PrivKey, hextx string) ([]byte, error) {
   230  	txbytes, err := common.FromHex(hextx)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	tx := &types.Transaction{}
   235  	err = types.Decode(txbytes, tx)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	tx.Fee = 1e6
   240  	tx.Sign(types.SECP256K1, priv)
   241  	reply, err := mock.api.SendTx(tx)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	return reply.GetMsg(), nil
   246  }
   247  
   248  //SendAndSignNonce 用外部传入的nonce 重写nonce
   249  func (mock *TuringchainMock) SendAndSignNonce(priv crypto.PrivKey, hextx string, nonce int64) ([]byte, error) {
   250  	txbytes, err := common.FromHex(hextx)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  	tx := &types.Transaction{}
   255  	err = types.Decode(txbytes, tx)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  	tx.Nonce = nonce
   260  	tx.ChainID = mock.q.GetConfig().GetChainID()
   261  
   262  	tx.Fee = 1e6
   263  	tx.Sign(types.SECP256K1, priv)
   264  	reply, err := mock.api.SendTx(tx)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  	return reply.GetMsg(), nil
   269  }
   270  
   271  func newWalletRealize(qAPI client.QueueProtocolAPI) {
   272  	seed := &types.SaveSeedByPw{
   273  		Seed:   "subject hamster apple parent vital can adult chapter fork business humor pen tiger void elephant",
   274  		Passwd: "123456fuzamei",
   275  	}
   276  	reply, err := qAPI.ExecWalletFunc("wallet", "SaveSeed", seed)
   277  	if !reply.(*types.Reply).IsOk && err != nil {
   278  		panic(err)
   279  	}
   280  	reply, err = qAPI.ExecWalletFunc("wallet", "WalletUnLock", &types.WalletUnLock{Passwd: "123456fuzamei"})
   281  	if !reply.(*types.Reply).IsOk && err != nil {
   282  		panic(err)
   283  	}
   284  	for i, priv := range util.TestPrivkeyHex {
   285  		privkey := &types.ReqWalletImportPrivkey{Privkey: priv, Label: fmt.Sprintf("label%d", i)}
   286  		acc, err := qAPI.ExecWalletFunc("wallet", "WalletImportPrivkey", privkey)
   287  		if err != nil {
   288  			panic(err)
   289  		}
   290  		lognode.Info("import", "index", i, "addr", acc.(*types.WalletAccount).Acc.Addr)
   291  	}
   292  	req := &types.ReqAccountList{WithoutBalance: true}
   293  	_, err = qAPI.ExecWalletFunc("wallet", "WalletGetAccountList", req)
   294  	if err != nil {
   295  		panic(err)
   296  	}
   297  }
   298  
   299  //GetAPI :
   300  func (mock *TuringchainMock) GetAPI() client.QueueProtocolAPI {
   301  	return mock.api
   302  }
   303  
   304  //GetRPC :
   305  func (mock *TuringchainMock) GetRPC() *rpc.RPC {
   306  	return mock.rpc
   307  }
   308  
   309  //GetCfg :
   310  func (mock *TuringchainMock) GetCfg() *types.Config {
   311  	return mock.cfg
   312  }
   313  
   314  //GetLastSendTx :
   315  func (mock *TuringchainMock) GetLastSendTx() []byte {
   316  	return mock.lastsend
   317  }
   318  
   319  //Close :
   320  func (mock *TuringchainMock) Close() {
   321  	mock.closeNoLock()
   322  }
   323  
   324  func (mock *TuringchainMock) closeNoLock() {
   325  	lognode.Info("network close")
   326  	mock.network.Close()
   327  	lognode.Info("network close")
   328  	mock.rpc.Close()
   329  	lognode.Info("rpc close")
   330  	mock.mem.Close()
   331  	lognode.Info("mem close")
   332  	mock.exec.Close()
   333  	lognode.Info("exec close")
   334  	mock.cs.Close()
   335  	lognode.Info("cs close")
   336  	mock.wallet.Close()
   337  	lognode.Info("wallet close")
   338  	mock.chain.Close()
   339  	lognode.Info("chain close")
   340  	mock.store.Close()
   341  	lognode.Info("store close")
   342  	mock.client.Close()
   343  	err := os.RemoveAll(mock.datadir)
   344  	if err != nil {
   345  		return
   346  	}
   347  }
   348  
   349  //WaitHeight :
   350  func (mock *TuringchainMock) WaitHeight(height int64) error {
   351  	for {
   352  		header, err := mock.api.GetLastHeader()
   353  		if err != nil {
   354  			return err
   355  		}
   356  		if header.Height >= height {
   357  			break
   358  		}
   359  		time.Sleep(time.Second / 10)
   360  	}
   361  	return nil
   362  }
   363  
   364  //WaitTx :
   365  func (mock *TuringchainMock) WaitTx(hash []byte) (*rpctypes.TransactionDetail, error) {
   366  	if hash == nil {
   367  		return nil, nil
   368  	}
   369  	for {
   370  		param := &types.ReqHash{Hash: hash}
   371  		_, err := mock.api.QueryTx(param)
   372  		if err != nil {
   373  			time.Sleep(time.Second / 10)
   374  			continue
   375  		}
   376  		var testResult rpctypes.TransactionDetail
   377  		data := rpctypes.QueryParm{
   378  			Hash: common.ToHex(hash),
   379  		}
   380  		err = mock.GetJSONC().Call("Turingchain.QueryTransaction", data, &testResult)
   381  		return &testResult, err
   382  	}
   383  }
   384  
   385  //SendHot :
   386  func (mock *TuringchainMock) SendHot() error {
   387  	types.AssertConfig(mock.client)
   388  	tx := util.CreateCoinsTx(mock.client.GetConfig(), mock.GetGenesisKey(), mock.GetHotAddress(), 10000*types.Coin)
   389  	mock.SendTx(tx)
   390  	return mock.Wait()
   391  }
   392  
   393  //SendTx :
   394  func (mock *TuringchainMock) SendTx(tx *types.Transaction) []byte {
   395  	reply, err := mock.GetAPI().SendTx(tx)
   396  	if err != nil {
   397  		panic(err)
   398  	}
   399  	mock.SetLastSend(reply.GetMsg())
   400  	return reply.GetMsg()
   401  }
   402  
   403  //SetLastSend :
   404  func (mock *TuringchainMock) SetLastSend(hash []byte) {
   405  	mock.mu.Lock()
   406  	mock.lastsend = hash
   407  	mock.mu.Unlock()
   408  }
   409  
   410  //SendTxRPC :
   411  func (mock *TuringchainMock) SendTxRPC(tx *types.Transaction) []byte {
   412  	var txhash string
   413  	hextx := common.ToHex(types.Encode(tx))
   414  	err := mock.GetJSONC().Call("Turingchain.SendTransaction", &rpctypes.RawParm{Data: hextx}, &txhash)
   415  	if err != nil {
   416  		panic(err)
   417  	}
   418  	hash, err := common.FromHex(txhash)
   419  	if err != nil {
   420  		panic(err)
   421  	}
   422  	mock.lastsend = hash
   423  	return hash
   424  }
   425  
   426  //Wait :
   427  func (mock *TuringchainMock) Wait() error {
   428  	if mock.lastsend == nil {
   429  		return nil
   430  	}
   431  	_, err := mock.WaitTx(mock.lastsend)
   432  	return err
   433  }
   434  
   435  //GetAccount :
   436  func (mock *TuringchainMock) GetAccount(stateHash []byte, addr string) *types.Account {
   437  	statedb := executor.NewStateDB(mock.client, stateHash, nil, nil)
   438  	types.AssertConfig(mock.client)
   439  	acc := account.NewCoinsAccount(mock.client.GetConfig())
   440  	acc.SetDB(statedb)
   441  	return acc.LoadAccount(addr)
   442  }
   443  
   444  //GetExecAccount :get execer account info
   445  func (mock *TuringchainMock) GetExecAccount(stateHash []byte, execer, addr string) *types.Account {
   446  	statedb := executor.NewStateDB(mock.client, stateHash, nil, nil)
   447  	types.AssertConfig(mock.client)
   448  	acc := account.NewCoinsAccount(mock.client.GetConfig())
   449  	acc.SetDB(statedb)
   450  	return acc.LoadExecAccount(addr, address.ExecAddress(execer))
   451  }
   452  
   453  //GetBlock :
   454  func (mock *TuringchainMock) GetBlock(height int64) *types.Block {
   455  	blocks, err := mock.api.GetBlocks(&types.ReqBlocks{Start: height, End: height})
   456  	if err != nil {
   457  		panic(err)
   458  	}
   459  	return blocks.Items[0].Block
   460  }
   461  
   462  //GetLastBlock :
   463  func (mock *TuringchainMock) GetLastBlock() *types.Block {
   464  	header, err := mock.api.GetLastHeader()
   465  	if err != nil {
   466  		panic(err)
   467  	}
   468  	return mock.GetBlock(header.Height)
   469  }
   470  
   471  //GetClient :
   472  func (mock *TuringchainMock) GetClient() queue.Client {
   473  	return mock.client
   474  }
   475  
   476  //GetHotKey :
   477  func (mock *TuringchainMock) GetHotKey() crypto.PrivKey {
   478  	return util.TestPrivkeyList[0]
   479  }
   480  
   481  //GetHotAddress :
   482  func (mock *TuringchainMock) GetHotAddress() string {
   483  	return address.PubKeyToAddress(mock.GetHotKey().PubKey().Bytes()).String()
   484  }
   485  
   486  //GetGenesisKey :
   487  func (mock *TuringchainMock) GetGenesisKey() crypto.PrivKey {
   488  	return util.TestPrivkeyList[1]
   489  }
   490  
   491  //GetGenesisAddress :
   492  func (mock *TuringchainMock) GetGenesisAddress() string {
   493  	return address.PubKeyToAddress(mock.GetGenesisKey().PubKey().Bytes()).String()
   494  }
   495  
   496  type mockP2P struct {
   497  }
   498  
   499  //SetQueueClient :
   500  func (m *mockP2P) SetQueueClient(client queue.Client) {
   501  	go func() {
   502  		p2pKey := "p2p"
   503  		client.Sub(p2pKey)
   504  		for msg := range client.Recv() {
   505  			switch msg.Ty {
   506  			case types.EventPeerInfo:
   507  				msg.Reply(client.NewMessage(p2pKey, types.EventPeerList, &types.PeerList{}))
   508  			case types.EventGetNetInfo:
   509  				msg.Reply(client.NewMessage(p2pKey, types.EventPeerList, &types.NodeNetInfo{}))
   510  			case types.EventTxBroadcast, types.EventBlockBroadcast:
   511  				client.FreeMessage(msg)
   512  			default:
   513  				msg.ReplyErr("p2p->Do not support "+types.GetEventName(int(msg.Ty)), types.ErrNotSupport)
   514  			}
   515  		}
   516  	}()
   517  }
   518  
   519  //Wait for ready
   520  func (m *mockP2P) Wait() {}
   521  
   522  //Close :
   523  func (m *mockP2P) Close() {
   524  }