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 }