github.com/turingchain2020/turingchain@v1.1.21/system/consensus/base.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 consensus 6 7 import ( 8 "bytes" 9 "errors" 10 "math/rand" 11 "sync" 12 "sync/atomic" 13 14 "github.com/turingchain2020/turingchain/client" 15 "github.com/turingchain2020/turingchain/common" 16 log "github.com/turingchain2020/turingchain/common/log/log15" 17 "github.com/turingchain2020/turingchain/common/merkle" 18 "github.com/turingchain2020/turingchain/queue" 19 "github.com/turingchain2020/turingchain/types" 20 "github.com/turingchain2020/turingchain/util" 21 ) 22 23 var tlog = log.New("module", "consensus") 24 25 var ( 26 zeroHash [32]byte 27 ) 28 29 var randgen *rand.Rand 30 31 func init() { 32 randgen = rand.New(rand.NewSource(types.Now().UnixNano())) 33 QueryData.Register("base", &BaseClient{}) 34 } 35 36 //Miner 矿工 37 type Miner interface { 38 CreateGenesisTx() []*types.Transaction 39 GetGenesisBlockTime() int64 40 CreateBlock() 41 AddBlock(b *types.Block) error 42 CheckBlock(parent *types.Block, current *types.BlockDetail) error 43 ProcEvent(msg *queue.Message) bool 44 CmpBestBlock(newBlock *types.Block, cmpBlock *types.Block) bool 45 } 46 47 //BaseClient ... 48 type BaseClient struct { 49 client queue.Client 50 api client.QueueProtocolAPI 51 minerStart int32 52 isclosed int32 53 once sync.Once 54 Cfg *types.Consensus 55 currentBlock *types.Block 56 mulock sync.Mutex 57 child Miner 58 minerstartCB func() 59 isCaughtUp int32 60 } 61 62 //NewBaseClient ... 63 func NewBaseClient(cfg *types.Consensus) *BaseClient { 64 var flag int32 65 if cfg.Minerstart { 66 flag = 1 67 } 68 client := &BaseClient{minerStart: flag, isCaughtUp: 0} 69 client.Cfg = cfg 70 log.Info("Enter consensus " + cfg.Name) 71 return client 72 } 73 74 //GetGenesisBlockTime 获取创世区块时间 75 func (bc *BaseClient) GetGenesisBlockTime() int64 { 76 return bc.Cfg.GenesisBlockTime 77 } 78 79 //SetChild ... 80 func (bc *BaseClient) SetChild(c Miner) { 81 bc.child = c 82 } 83 84 //GetAPI 获取api 85 func (bc *BaseClient) GetAPI() client.QueueProtocolAPI { 86 return bc.api 87 } 88 89 //SetAPI ... 90 func (bc *BaseClient) SetAPI(api client.QueueProtocolAPI) { 91 bc.api = api 92 } 93 94 //AddBlock 添加区块的时候,通知系统做处理 95 func (bc *BaseClient) AddBlock(b *types.Block) error { 96 return nil 97 } 98 99 //InitClient 初始化 100 func (bc *BaseClient) InitClient(c queue.Client, minerstartCB func()) { 101 log.Info("Enter SetQueueClient method of consensus") 102 bc.client = c 103 bc.minerstartCB = minerstartCB 104 var err error 105 bc.api, err = client.New(c, nil) 106 if err != nil { 107 panic(err) 108 } 109 bc.InitMiner() 110 } 111 112 //GetQueueClient 获取客户端队列 113 func (bc *BaseClient) GetQueueClient() queue.Client { 114 return bc.client 115 } 116 117 //RandInt64 随机数 118 func (bc *BaseClient) RandInt64() int64 { 119 return randgen.Int63() 120 } 121 122 //InitMiner 初始化矿工 123 func (bc *BaseClient) InitMiner() { 124 bc.once.Do(bc.minerstartCB) 125 } 126 127 //Wait wait for ready 128 func (bc *BaseClient) Wait() {} 129 130 //SetQueueClient 设置客户端队列 131 func (bc *BaseClient) SetQueueClient(c queue.Client) { 132 bc.InitClient(c, func() { 133 //call init block 134 bc.InitBlock() 135 }) 136 go bc.EventLoop() 137 go bc.child.CreateBlock() 138 } 139 140 //InitBlock change init block 141 func (bc *BaseClient) InitBlock() { 142 cfg := bc.client.GetConfig() 143 block, err := bc.RequestLastBlock() 144 if err != nil { 145 panic(err) 146 } 147 if block == nil { 148 // 创世区块 149 newblock := &types.Block{} 150 newblock.Height = 0 151 newblock.BlockTime = bc.child.GetGenesisBlockTime() 152 // TODO: 下面这些值在创世区块中赋值nil,是否合理? 153 newblock.ParentHash = zeroHash[:] 154 tx := bc.child.CreateGenesisTx() 155 newblock.Txs = tx 156 newblock.TxHash = merkle.CalcMerkleRoot(cfg, newblock.Height, newblock.Txs) 157 if newblock.Height == 0 { 158 types.AssertConfig(bc.client) 159 newblock.Difficulty = cfg.GetP(0).PowLimitBits 160 } 161 err := bc.WriteBlock(zeroHash[:], newblock) 162 if err != nil { 163 panic(err) 164 } 165 } else { 166 bc.SetCurrentBlock(block) 167 } 168 } 169 170 //Close 关闭 171 func (bc *BaseClient) Close() { 172 atomic.StoreInt32(&bc.minerStart, 0) 173 atomic.StoreInt32(&bc.isclosed, 1) 174 bc.client.Close() 175 log.Info("consensus base closed") 176 } 177 178 //IsClosed 是否已经关闭 179 func (bc *BaseClient) IsClosed() bool { 180 return atomic.LoadInt32(&bc.isclosed) == 1 181 } 182 183 //CheckTxDup 为了不引起交易检查时候产生的无序 184 func (bc *BaseClient) CheckTxDup(txs []*types.Transaction) (transactions []*types.Transaction) { 185 cacheTxs := types.TxsToCache(txs) 186 var err error 187 cacheTxs, err = util.CheckTxDup(bc.client, cacheTxs, 0) 188 if err != nil { 189 return txs 190 } 191 return types.CacheToTxs(cacheTxs) 192 } 193 194 //IsMining 是否在挖矿 195 func (bc *BaseClient) IsMining() bool { 196 return atomic.LoadInt32(&bc.minerStart) == 1 197 } 198 199 //IsCaughtUp 是否追上最新高度 200 func (bc *BaseClient) IsCaughtUp() bool { 201 if bc.client == nil { 202 panic("bc not bind message queue.") 203 } 204 msg := bc.client.NewMessage("blockchain", types.EventIsSync, nil) 205 err := bc.client.Send(msg, true) 206 if err != nil { 207 return false 208 } 209 resp, err := bc.client.Wait(msg) 210 if err != nil { 211 return false 212 } 213 return resp.GetData().(*types.IsCaughtUp).GetIscaughtup() 214 } 215 216 //ExecConsensus 执行共识 217 func (bc *BaseClient) ExecConsensus(data *types.ChainExecutor) (types.Message, error) { 218 param, err := QueryData.Decode(data.Driver, data.FuncName, data.Param) 219 if err != nil { 220 return nil, err 221 } 222 return QueryData.Call(data.Driver, data.FuncName, param) 223 } 224 225 //EventLoop 准备新区块 226 func (bc *BaseClient) EventLoop() { 227 // 监听blockchain模块,获取当前最高区块 228 bc.client.Sub("consensus") 229 go func() { 230 for msg := range bc.client.Recv() { 231 tlog.Debug("consensus recv", "msg", msg) 232 if msg.Ty == types.EventConsensusQuery { 233 exec := msg.GetData().(*types.ChainExecutor) 234 param, err := QueryData.Decode(exec.Driver, exec.FuncName, exec.Param) 235 if err != nil { 236 msg.Reply(bc.api.NewMessage("", 0, err)) 237 continue 238 } 239 reply, err := QueryData.Call(exec.Driver, exec.FuncName, param) 240 if err != nil { 241 msg.Reply(bc.api.NewMessage("", 0, err)) 242 } else { 243 msg.Reply(bc.api.NewMessage("", 0, reply)) 244 } 245 } else if msg.Ty == types.EventAddBlock { 246 block := msg.GetData().(*types.BlockDetail).Block 247 bc.SetCurrentBlock(block) 248 bc.child.AddBlock(block) 249 } else if msg.Ty == types.EventCheckBlock { 250 block := msg.GetData().(*types.BlockDetail) 251 err := bc.CheckBlock(block) 252 msg.ReplyErr("EventCheckBlock", err) 253 } else if msg.Ty == types.EventMinerStart { 254 if !atomic.CompareAndSwapInt32(&bc.minerStart, 0, 1) { 255 msg.ReplyErr("EventMinerStart", types.ErrMinerIsStared) 256 } else { 257 bc.InitMiner() 258 msg.ReplyErr("EventMinerStart", nil) 259 } 260 } else if msg.Ty == types.EventMinerStop { 261 if !atomic.CompareAndSwapInt32(&bc.minerStart, 1, 0) { 262 msg.ReplyErr("EventMinerStop", types.ErrMinerNotStared) 263 } else { 264 msg.ReplyErr("EventMinerStop", nil) 265 } 266 } else if msg.Ty == types.EventDelBlock { 267 block := msg.GetData().(*types.BlockDetail).Block 268 bc.UpdateCurrentBlock(block) 269 } else if msg.Ty == types.EventCmpBestBlock { 270 var reply types.Reply 271 cmpBlock := msg.GetData().(*types.CmpBlock) 272 reply.IsOk = bc.CmpBestBlock(cmpBlock.Block, cmpBlock.CmpHash) 273 msg.Reply(bc.api.NewMessage("", 0, &reply)) 274 } else { 275 if !bc.child.ProcEvent(msg) { 276 msg.ReplyErr("BaseClient.EventLoop() ", types.ErrActionNotSupport) 277 } 278 } 279 } 280 }() 281 } 282 283 //CheckBlock 检查区块 284 func (bc *BaseClient) CheckBlock(block *types.BlockDetail) error { 285 //check parent 286 if block.Block.Height <= 0 { //genesis block not check 287 return nil 288 } 289 parent, err := bc.RequestBlock(block.Block.Height - 1) 290 if err != nil { 291 return err 292 } 293 //check base info 294 if parent.Height+1 != block.Block.Height { 295 return types.ErrBlockHeight 296 } 297 types.AssertConfig(bc.client) 298 cfg := bc.client.GetConfig() 299 if cfg.IsFork(block.Block.Height, "ForkCheckBlockTime") && parent.BlockTime > block.Block.BlockTime { 300 return types.ErrBlockTime 301 } 302 //check parent hash 303 if string(block.Block.GetParentHash()) != string(parent.Hash(cfg)) { 304 return types.ErrParentHash 305 } 306 //check block size and tx count 307 if cfg.IsFork(block.Block.Height, "ForkBlockCheck") { 308 if block.Block.Size() > types.MaxBlockSize { 309 return types.ErrBlockSize 310 } 311 if int64(len(block.Block.Txs)) > cfg.GetP(block.Block.Height).MaxTxNumber { 312 return types.ErrManyTx 313 } 314 } 315 //check by drivers 316 err = bc.child.CheckBlock(parent, block) 317 return err 318 } 319 320 //RequestTx Mempool中取交易列表 321 func (bc *BaseClient) RequestTx(listSize int, txHashList [][]byte) []*types.Transaction { 322 if bc.client == nil { 323 panic("bc not bind message queue.") 324 } 325 msg := bc.client.NewMessage("mempool", types.EventTxList, &types.TxHashList{Hashes: txHashList, Count: int64(listSize)}) 326 err := bc.client.Send(msg, true) 327 if err != nil { 328 return nil 329 } 330 resp, err := bc.client.Wait(msg) 331 if err != nil { 332 return nil 333 } 334 return resp.GetData().(*types.ReplyTxList).GetTxs() 335 } 336 337 //RequestBlock 请求区块 338 func (bc *BaseClient) RequestBlock(start int64) (*types.Block, error) { 339 if bc.client == nil { 340 panic("bc not bind message queue.") 341 } 342 reqblock := &types.ReqBlocks{Start: start, End: start, IsDetail: false, Pid: []string{""}} 343 msg := bc.client.NewMessage("blockchain", types.EventGetBlocks, reqblock) 344 err := bc.client.Send(msg, true) 345 if err != nil { 346 return nil, err 347 } 348 resp, err := bc.client.Wait(msg) 349 if err != nil { 350 return nil, err 351 } 352 blocks := resp.GetData().(*types.BlockDetails) 353 return blocks.Items[0].Block, nil 354 } 355 356 //RequestLastBlock 获取最新的block从blockchain模块 357 func (bc *BaseClient) RequestLastBlock() (*types.Block, error) { 358 if bc.client == nil { 359 panic("client not bind message queue.") 360 } 361 msg := bc.client.NewMessage("blockchain", types.EventGetLastBlock, nil) 362 err := bc.client.Send(msg, true) 363 if err != nil { 364 return nil, err 365 } 366 resp, err := bc.client.Wait(msg) 367 if err != nil { 368 return nil, err 369 } 370 block := resp.GetData().(*types.Block) 371 return block, nil 372 } 373 374 //del mempool 375 func (bc *BaseClient) delMempoolTx(deltx []*types.Transaction) error { 376 hashList := buildHashList(deltx) 377 msg := bc.client.NewMessage("mempool", types.EventDelTxList, hashList) 378 err := bc.client.Send(msg, true) 379 if err != nil { 380 return err 381 } 382 resp, err := bc.client.Wait(msg) 383 if err != nil { 384 return err 385 } 386 if resp.GetData().(*types.Reply).GetIsOk() { 387 return nil 388 } 389 return errors.New(string(resp.GetData().(*types.Reply).GetMsg())) 390 } 391 392 func buildHashList(deltx []*types.Transaction) *types.TxHashList { 393 list := &types.TxHashList{} 394 for i := 0; i < len(deltx); i++ { 395 list.Hashes = append(list.Hashes, deltx[i].Hash()) 396 } 397 return list 398 } 399 400 //WriteBlock 向blockchain写区块 401 func (bc *BaseClient) WriteBlock(prev []byte, block *types.Block) error { 402 //保存block的原始信息用于删除mempool中的错误交易 403 rawtxs := make([]*types.Transaction, len(block.Txs)) 404 copy(rawtxs, block.Txs) 405 406 blockdetail := &types.BlockDetail{Block: block} 407 msg := bc.client.NewMessage("blockchain", types.EventAddBlockDetail, blockdetail) 408 err := bc.client.Send(msg, true) 409 if err != nil { 410 return err 411 } 412 resp, err := bc.client.Wait(msg) 413 if err != nil { 414 return err 415 } 416 blockdetail = resp.GetData().(*types.BlockDetail) 417 //从mempool 中删除错误的交易 418 deltx := diffTx(rawtxs, blockdetail.Block.Txs) 419 if len(deltx) > 0 { 420 err := bc.delMempoolTx(deltx) 421 if err != nil { 422 return err 423 } 424 } 425 if blockdetail != nil { 426 bc.SetCurrentBlock(blockdetail.Block) 427 } else { 428 return errors.New("block detail is nil") 429 } 430 return nil 431 } 432 433 // PreExecBlock 预执行区块, 用于raft, tendermint等共识, errReturn表示区块来源于自己还是别人 434 func (bc *BaseClient) PreExecBlock(block *types.Block, errReturn bool) *types.Block { 435 lastBlock, err := bc.RequestBlock(block.Height - 1) 436 if err != nil { 437 log.Error("PreExecBlock RequestBlock fail", "err", err) 438 return nil 439 } 440 blockdetail, deltx, err := util.PreExecBlock(bc.client, lastBlock.StateHash, block, errReturn, false, true) 441 if err != nil { 442 log.Error("util.PreExecBlock fail", "err", err) 443 return nil 444 } 445 if len(deltx) > 0 { 446 err := bc.delMempoolTx(deltx) 447 if err != nil { 448 log.Error("PreExecBlock delMempoolTx fail", "err", err) 449 return nil 450 } 451 } 452 return blockdetail.Block 453 } 454 455 func diffTx(tx1, tx2 []*types.Transaction) (deltx []*types.Transaction) { 456 txlist2 := make(map[string]bool) 457 for _, tx := range tx2 { 458 txlist2[string(tx.Hash())] = true 459 } 460 for _, tx := range tx1 { 461 hash := string(tx.Hash()) 462 if _, ok := txlist2[hash]; !ok { 463 deltx = append(deltx, tx) 464 } 465 } 466 return deltx 467 } 468 469 //SetCurrentBlock 设置当前区块 470 func (bc *BaseClient) SetCurrentBlock(b *types.Block) { 471 bc.mulock.Lock() 472 bc.currentBlock = b 473 bc.mulock.Unlock() 474 } 475 476 //UpdateCurrentBlock 更新当前区块 477 func (bc *BaseClient) UpdateCurrentBlock(b *types.Block) { 478 bc.mulock.Lock() 479 defer bc.mulock.Unlock() 480 block, err := bc.RequestLastBlock() 481 if err != nil { 482 log.Error("UpdateCurrentBlock", "RequestLastBlock", err) 483 return 484 } 485 bc.currentBlock = block 486 } 487 488 //GetCurrentBlock 获取当前区块 489 func (bc *BaseClient) GetCurrentBlock() (b *types.Block) { 490 bc.mulock.Lock() 491 defer bc.mulock.Unlock() 492 return bc.currentBlock 493 } 494 495 //GetCurrentHeight 获取当前高度 496 func (bc *BaseClient) GetCurrentHeight() int64 { 497 bc.mulock.Lock() 498 start := bc.currentBlock.Height 499 bc.mulock.Unlock() 500 return start 501 } 502 503 //Lock 上锁 504 func (bc *BaseClient) Lock() { 505 bc.mulock.Lock() 506 } 507 508 //Unlock 解锁 509 func (bc *BaseClient) Unlock() { 510 bc.mulock.Unlock() 511 } 512 513 //ConsensusTicketMiner ... 514 func (bc *BaseClient) ConsensusTicketMiner(iscaughtup *types.IsCaughtUp) { 515 if !atomic.CompareAndSwapInt32(&bc.isCaughtUp, 0, 1) { 516 log.Info("ConsensusTicketMiner", "isCaughtUp", bc.isCaughtUp) 517 } else { 518 log.Info("ConsensusTicketMiner", "isCaughtUp", bc.isCaughtUp) 519 } 520 } 521 522 //AddTxsToBlock 添加交易到区块中 523 func (bc *BaseClient) AddTxsToBlock(block *types.Block, txs []*types.Transaction) []*types.Transaction { 524 size := block.Size() 525 max := types.MaxBlockSize - 100000 //留下100K空间,添加其他的交易 526 currentCount := int64(len(block.Txs)) 527 types.AssertConfig(bc.client) 528 cfg := bc.client.GetConfig() 529 maxTx := cfg.GetP(block.Height).MaxTxNumber 530 addedTx := make([]*types.Transaction, 0, len(txs)) 531 for i := 0; i < len(txs); i++ { 532 txGroup, err := txs[i].GetTxGroup() 533 if err != nil { 534 continue 535 } 536 if txGroup == nil { 537 currentCount++ 538 if currentCount > maxTx { 539 return addedTx 540 } 541 size += txs[i].Size() 542 if size > max { 543 return addedTx 544 } 545 addedTx = append(addedTx, txs[i]) 546 block.Txs = append(block.Txs, txs[i]) 547 } else { 548 currentCount += int64(len(txGroup.Txs)) 549 if currentCount > maxTx { 550 return addedTx 551 } 552 for i := 0; i < len(txGroup.Txs); i++ { 553 size += txGroup.Txs[i].Size() 554 } 555 if size > max { 556 return addedTx 557 } 558 addedTx = append(addedTx, txGroup.Txs...) 559 block.Txs = append(block.Txs, txGroup.Txs...) 560 } 561 } 562 return addedTx 563 } 564 565 //CheckTxExpire 此时的tx交易组都是展开的,过滤掉已经过期的tx交易,目前只有ticket共识需要在updateBlock时调用 566 func (bc *BaseClient) CheckTxExpire(txs []*types.Transaction, height int64, blocktime int64) (transactions []*types.Transaction) { 567 var txlist types.Transactions 568 var hasTxExpire bool 569 570 types.AssertConfig(bc.client) 571 cfg := bc.client.GetConfig() 572 for i := 0; i < len(txs); i++ { 573 574 groupCount := txs[i].GroupCount 575 if groupCount == 0 { 576 if isExpire(cfg, txs[i:i+1], height, blocktime) { 577 txs[i] = nil 578 hasTxExpire = true 579 } 580 continue 581 } 582 583 //判断GroupCount 是否会产生越界 584 if i+int(groupCount) > len(txs) { 585 continue 586 } 587 588 //交易组有过期交易时需要将整个交易组都删除 589 grouptxs := txs[i : i+int(groupCount)] 590 if isExpire(cfg, grouptxs, height, blocktime) { 591 for j := i; j < i+int(groupCount); j++ { 592 txs[j] = nil 593 hasTxExpire = true 594 } 595 } 596 i = i + int(groupCount) - 1 597 } 598 599 //有过期交易时需要重新组装交易 600 if hasTxExpire { 601 for _, tx := range txs { 602 if tx != nil { 603 txlist.Txs = append(txlist.Txs, tx) 604 } 605 } 606 return txlist.GetTxs() 607 } 608 609 return txs 610 } 611 612 //检测交易数组是否过期,只要有一个过期就认为整个交易组过期 613 func isExpire(cfg *types.TuringchainConfig, txs []*types.Transaction, height int64, blocktime int64) bool { 614 for _, tx := range txs { 615 if height > 0 && blocktime > 0 && tx.IsExpire(cfg, height, blocktime) { 616 log.Debug("isExpire", "height", height, "blocktime", blocktime, "hash", common.ToHex(tx.Hash()), "Expire", tx.Expire) 617 return true 618 } 619 } 620 return false 621 } 622 623 //CmpBestBlock 最优区块的比较 624 //height,BlockTime,ParentHash必须一致才可以继续比较 625 //通过比较newBlock是最优区块就返回true,否则返回false 626 func (bc *BaseClient) CmpBestBlock(newBlock *types.Block, cmpHash []byte) bool { 627 628 cfg := bc.client.GetConfig() 629 curBlock := bc.GetCurrentBlock() 630 631 //需要比较的区块就是当前区块, 632 if bytes.Equal(cmpHash, curBlock.Hash(cfg)) { 633 if curBlock.GetHeight() == newBlock.GetHeight() && curBlock.BlockTime == newBlock.BlockTime && bytes.Equal(newBlock.GetParentHash(), curBlock.GetParentHash()) { 634 return bc.child.CmpBestBlock(newBlock, curBlock) 635 } 636 return false 637 } 638 //需要比较的区块不是当前区块,需要从blockchain模块获取cmpHash对应的block信息 639 block, err := bc.ReqBlockByHash(cmpHash) 640 if err != nil { 641 log.Error("CmpBestBlock:RequestBlockByHash", "Hash", common.ToHex(cmpHash), "err", err) 642 return false 643 } 644 645 if block.GetHeight() == newBlock.GetHeight() && block.BlockTime == newBlock.BlockTime && bytes.Equal(block.GetParentHash(), newBlock.GetParentHash()) { 646 return bc.child.CmpBestBlock(newBlock, block) 647 } 648 return false 649 } 650 651 //ReqBlockByHash 通过区块hash获取区块信息 652 func (bc *BaseClient) ReqBlockByHash(hash []byte) (*types.Block, error) { 653 if bc.client == nil { 654 panic("bc not bind message queue.") 655 } 656 hashes := types.ReqHashes{} 657 hashes.Hashes = append(hashes.Hashes, hash) 658 msg := bc.client.NewMessage("blockchain", types.EventGetBlockByHashes, &hashes) 659 err := bc.client.Send(msg, true) 660 if err != nil { 661 return nil, err 662 } 663 resp, err := bc.client.Wait(msg) 664 if err != nil { 665 return nil, err 666 } 667 blocks := resp.GetData().(*types.BlockDetails) 668 if len(blocks.Items) == 1 && blocks.Items[0] != nil { 669 return blocks.Items[0].Block, nil 670 } 671 return nil, types.ErrHashNotExist 672 }