github.com/aergoio/aergo@v1.3.1/consensus/impl/raftv2/blockfactory.go (about) 1 package raftv2 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "github.com/aergoio/aergo/p2p/p2pcommon" 10 "github.com/aergoio/aergo/p2p/p2pkey" 11 "runtime" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/aergoio/aergo/internal/enc" 17 "github.com/libp2p/go-libp2p-core/crypto" 18 19 "github.com/aergoio/aergo-lib/log" 20 bc "github.com/aergoio/aergo/chain" 21 "github.com/aergoio/aergo/config" 22 "github.com/aergoio/aergo/consensus" 23 "github.com/aergoio/aergo/consensus/chain" 24 "github.com/aergoio/aergo/contract" 25 "github.com/aergoio/aergo/pkg/component" 26 "github.com/aergoio/aergo/state" 27 "github.com/aergoio/aergo/types" 28 ) 29 30 var ( 31 logger *log.Logger 32 httpLogger *log.Logger 33 ) 34 35 var ( 36 ErrClusterNotReady = errors.New("cluster is not ready") 37 ErrNotRaftLeader = errors.New("this node is not leader") 38 ErrInvalidConsensusName = errors.New("invalid consensus name") 39 ErrCancelGenerate = errors.New("cancel generating block because work becomes stale") 40 ) 41 42 func init() { 43 logger = log.NewLogger("raft") 44 httpLogger = log.NewLogger("rafthttp") 45 } 46 47 type txExec struct { 48 execTx bc.TxExecFn 49 } 50 51 func newTxExec(ccc consensus.ChainConsensusCluster, cdb consensus.ChainDB, blockNo types.BlockNo, ts int64, prevHash []byte, chainID []byte) chain.TxOp { 52 // Block hash not determined yet 53 return &txExec{ 54 execTx: bc.NewTxExecutor(ccc, contract.ChainAccessor(cdb), blockNo, ts, prevHash, contract.BlockFactory, chainID), 55 } 56 } 57 58 func (te *txExec) Apply(bState *state.BlockState, tx types.Transaction) error { 59 err := te.execTx(bState, tx) 60 return err 61 } 62 63 type Work struct { 64 *types.Block 65 term uint64 66 } 67 68 func (work *Work) GetTimeout() time.Duration { 69 return BlockIntervalMs 70 } 71 72 func (work *Work) ToString() string { 73 return fmt.Sprintf("bestblock=%s, no=%d, term=%d", work.BlockID(), work.BlockNo(), work.term) 74 } 75 76 type leaderReady struct { 77 sync.RWMutex 78 79 ce *commitEntry 80 } 81 82 func (ready *leaderReady) set(ce *commitEntry) { 83 logger.Debug().Uint64("term", ce.term).Msg("set ready marker") 84 85 ready.Lock() 86 defer ready.Unlock() 87 88 ready.ce = ce 89 } 90 91 func (ready *leaderReady) isReady(curTerm uint64) bool { 92 ready.RLock() 93 defer ready.RUnlock() 94 95 if curTerm <= 0 { 96 logger.Fatal().Msg("failed to get status of raft") 97 return false 98 } 99 100 if ready.ce == nil { 101 logger.Debug().Msg("not exist ready marker") 102 return false 103 } 104 105 if ready.ce.term != curTerm { 106 logger.Debug().Uint64("ready-term", ready.ce.term).Uint64("cur-term", curTerm).Msg("not ready for producing") 107 return false 108 } 109 110 return true 111 } 112 113 // BlockFactory implments a raft block factory which generate block each cfg.Consensus.BlockIntervalMs if this node is leader of raft 114 // 115 // This can be used for testing purpose. 116 type BlockFactory struct { 117 *component.ComponentHub 118 consensus.ChainWAL 119 120 bpc *Cluster 121 rhw consensus.AergoRaftAccessor 122 123 workerQueue chan *Work 124 jobQueue chan interface{} 125 bpTimeoutC chan interface{} 126 quit chan interface{} 127 128 ready leaderReady 129 130 maxBlockBodySize uint32 131 ID string 132 privKey crypto.PrivKey 133 txOp chain.TxOp 134 sdb *state.ChainStateDB 135 prevBlock *types.Block // best block of last job 136 jobLock sync.RWMutex 137 138 raftOp *RaftOperator 139 raftServer *raftServer 140 } 141 142 // GetName returns the name of the consensus. 143 func GetName() string { 144 return consensus.ConsensusName[consensus.ConsensusRAFT] 145 } 146 147 // GetConstructor build and returns consensus.Constructor from New function. 148 func GetConstructor(cfg *config.Config, hub *component.ComponentHub, cdb consensus.ChainWAL, 149 sdb *state.ChainStateDB, pa p2pcommon.PeerAccessor) consensus.Constructor { 150 return func() (consensus.Consensus, error) { 151 return New(cfg, hub, cdb, sdb, pa) 152 } 153 } 154 155 // New returns a BlockFactory. 156 func New(cfg *config.Config, hub *component.ComponentHub, cdb consensus.ChainWAL, 157 sdb *state.ChainStateDB, pa p2pcommon.PeerAccessor) (*BlockFactory, error) { 158 159 bf := &BlockFactory{ 160 ComponentHub: hub, 161 ChainWAL: cdb, 162 jobQueue: make(chan interface{}), 163 workerQueue: make(chan *Work), 164 bpTimeoutC: make(chan interface{}, 1), 165 quit: make(chan interface{}), 166 maxBlockBodySize: chain.MaxBlockBodySize(), 167 ID: p2pkey.NodeSID(), 168 privKey: p2pkey.NodePrivKey(), 169 sdb: sdb, 170 } 171 172 if cfg.Consensus.EnableBp { 173 Init(cfg.Consensus.Raft) 174 175 if err := bf.newRaftServer(cfg); err != nil { 176 logger.Error().Err(err).Msg("failed to init raft server") 177 return bf, err 178 } 179 180 bf.raftServer.SetPeerAccessor(pa) 181 bf.rhw = &raftHttpWrapper{raftServer: bf.raftServer} 182 } else { 183 bf.rhw = &consensus.DummyRaftAccessor{} 184 } 185 186 bf.txOp = chain.NewCompTxOp( 187 // timeout check 188 chain.TxOpFn(func(bState *state.BlockState, txIn types.Transaction) error { 189 return bf.checkBpTimeout() 190 }), 191 ) 192 193 return bf, nil 194 } 195 196 func (bf *BlockFactory) newRaftServer(cfg *config.Config) error { 197 if err := bf.InitCluster(cfg); err != nil { 198 return err 199 } 200 201 bf.raftOp = newRaftOperator(bf.raftServer, bf.bpc) 202 203 logger.Info().Str("name", bf.bpc.NodeName()).Msg("create raft server") 204 205 bf.raftServer = newRaftServer(bf.ComponentHub, bf.bpc, 206 !cfg.Consensus.Raft.NewCluster, cfg.Consensus.Raft.UseBackup, nil, 207 RaftTick, bf.bpc.confChangeC, bf.raftOp.commitC, false, bf.ChainWAL) 208 209 bf.bpc.rs = bf.raftServer 210 bf.raftOp.rs = bf.raftServer 211 212 return nil 213 } 214 215 // Ticker returns a time.Ticker for the main consensus loop. 216 func (bf *BlockFactory) Ticker() *time.Ticker { 217 return time.NewTicker(BlockFactoryTickMs) 218 } 219 220 // QueueJob send a block triggering information to jq. 221 func (bf *BlockFactory) QueueJob(now time.Time, jq chan<- interface{}) { 222 bf.jobLock.Lock() 223 defer bf.jobLock.Unlock() 224 225 var ( 226 isReady bool 227 term uint64 228 ) 229 230 if isReady, term = bf.isLeaderReady(); !isReady { 231 //logger.Debug().Msg("skip producing block because this bp is leader but it's not ready to produce new block") 232 return 233 } 234 235 prevToString := func(prevBlock *types.Block) string { 236 if prevBlock == nil { 237 return "empty" 238 } else { 239 return prevBlock.ID() 240 } 241 } 242 243 if b, _ := bf.GetBestBlock(); b != nil { 244 if bf.prevBlock != nil && bf.prevBlock.BlockNo() == b.BlockNo() { 245 //logger.Debug().Uint64("bestno", b.BlockNo()).Msg("previous block not connected. skip to generate block") 246 return 247 } 248 249 // If requested block remains in commit channel, block factory must wait until all requests are completed. 250 // otherwise block of same height will be created and a fork will occur. 251 if !bf.raftServer.commitProgress.IsReadyToPropose() { 252 logger.Debug().Uint64("bestno", b.BlockNo()).Msg("pending request block not connected. skip to generate block") 253 return 254 } 255 256 prev := bf.prevBlock 257 bf.prevBlock = b 258 work := &Work{Block: b, term: term} 259 260 logger.Debug().Str("work", work.ToString()).Str("prev", prevToString(prev)).Msg("new work generated") 261 jq <- work 262 } 263 } 264 265 // isLeaderReady must be called after bf.jobLock 266 // check if block factory has finished all blocks of previous term. it can be checked that it has received raft marker of this term. 267 // TODO) term may be set when raft leader is changed from hardstate 268 func (bf *BlockFactory) isLeaderReady() (bool, uint64) { 269 var ( 270 status LeaderStatus 271 ) 272 273 if status = bf.raftServer.GetLeaderStatus(); !status.IsLeader { 274 return false, 0 275 } 276 277 return bf.ready.isReady(status.Term), status.Term 278 } 279 280 func (bf *BlockFactory) GetType() consensus.ConsensusType { 281 return consensus.ConsensusRAFT 282 } 283 284 // IsTransactionValid checks the onsensus level validity of a transaction 285 func (bf *BlockFactory) IsTransactionValid(tx *types.Tx) bool { 286 // BlockFactory has no tx valid check. 287 return true 288 } 289 290 // VerifyTimestamp checks the validity of the block timestamp. 291 func (bf *BlockFactory) VerifyTimestamp(*types.Block) bool { 292 // BlockFactory don't need to check timestamp. 293 return true 294 } 295 296 // VerifySign checks the consensus level validity of a block. 297 func (bf *BlockFactory) VerifySign(block *types.Block) error { 298 valid, err := block.VerifySign() 299 if !valid || err != nil { 300 return &consensus.ErrorConsensus{Msg: "bad block signature", Err: err} 301 } 302 return nil 303 } 304 305 // IsBlockValid checks the consensus level validity of a block. 306 func (bf *BlockFactory) IsBlockValid(block *types.Block, bestBlock *types.Block) error { 307 // BlockFactory has no block valid check. 308 _, err := block.BPID() 309 if err != nil { 310 return &consensus.ErrorConsensus{Msg: "bad public key in block", Err: err} 311 } 312 return nil 313 } 314 315 // QuitChan returns the channel from which consensus-related goroutines check 316 // when shutdown is initiated. 317 func (bf *BlockFactory) QuitChan() chan interface{} { 318 return bf.quit 319 } 320 321 // Update has nothging to do. 322 func (bf *BlockFactory) Update(block *types.Block) { 323 } 324 325 // Save has nothging to do. 326 func (bf *BlockFactory) Save(tx consensus.TxWriter) error { 327 return nil 328 } 329 330 // BlockFactory returns r itself. 331 func (bf *BlockFactory) BlockFactory() consensus.BlockFactory { 332 return bf 333 } 334 335 // NeedReorganization has nothing to do. 336 func (bf *BlockFactory) NeedReorganization(rootNo types.BlockNo) bool { 337 return true 338 } 339 340 // Start run a raft block factory service. 341 func (bf *BlockFactory) Start() { 342 bf.raftServer.Start() 343 344 go bf.worker() 345 go bf.controller() 346 } 347 348 func (bf *BlockFactory) controller() { 349 defer shutdownMsg("block factory controller") 350 351 beginBlock := func(work *Work) error { 352 // This is only for draining an unconsumed message, which means 353 // the previous block is generated within timeout. This code 354 // is needed since an empty block will be generated without it. 355 if err := bf.checkBpTimeout(); err == chain.ErrQuit { 356 return err 357 } 358 359 select { 360 case bf.workerQueue <- work: 361 default: 362 logger.Error().Msgf( 363 "skip block production for %s due to a pending job", work.ToString()) 364 } 365 return nil 366 } 367 368 notifyBpTimeout := func(work *Work) { 369 timeout := work.GetTimeout() 370 time.Sleep(timeout) 371 bf.bpTimeoutC <- struct{}{} 372 logger.Debug().Int64("timeout(ms)", timeout.Nanoseconds()/int64(time.Millisecond)).Msg("block production timeout signaled") 373 } 374 375 for { 376 select { 377 case info := <-bf.jobQueue: 378 work := info.(*Work) 379 380 logger.Debug().Msgf("received work: %s", 381 log.DoLazyEval(func() string { return work.ToString() })) 382 383 err := beginBlock(work) 384 if err == chain.ErrQuit { 385 return 386 } else if err != nil { 387 logger.Debug().Err(err).Msg("skip block production") 388 continue 389 } 390 391 notifyBpTimeout(work) 392 393 case <-bf.quit: 394 return 395 } 396 } 397 } 398 399 // worker() is different for each consensus 400 func (bf *BlockFactory) worker() { 401 defer logger.Info().Msg("shutdown initiated. stop the service") 402 403 runtime.LockOSThread() 404 405 for { 406 select { 407 case work := <-bf.workerQueue: 408 var ( 409 block *types.Block 410 blockState *state.BlockState 411 err error 412 ) 413 414 if block, blockState, err = bf.generateBlock(work); err != nil { 415 logger.Error().Err(err).Msg("failed to generate block") 416 if err == chain.ErrQuit { 417 logger.Info().Msg("quit worker of block factory") 418 return 419 } 420 421 bf.reset() 422 continue 423 } 424 425 if err = bf.raftOp.propose(block, blockState, work.term); err != nil { 426 logger.Error().Err(err).Msg("failed to propose block") 427 bf.reset() 428 } 429 430 case cEntry, ok := <-bf.commitC(): 431 logger.Debug().Msg("received block to connect from raft") 432 433 if !ok { 434 logger.Fatal().Msg("commit channel for raft is closed") 435 return 436 } 437 438 // RaftEmptyBlockLog: When the leader changes, the new raft leader creates an empty data log with a new term and index. 439 // When block factory receives empty block log, the blockfactory that is running as the leader should reset the proposal in progress. 440 // since it may have been dropped on the raft. Block factory must produce new block after all blocks of previous term are connected. Empty log can be a marker for that. 441 if cEntry.IsReadyMarker() { 442 bf.handleReadyMarker(cEntry) 443 continue 444 } 445 446 // add block that has produced by remote BP 447 if err := bf.connect(cEntry.block); err != nil { 448 logger.Error().Err(err).Msg("failed to connect block") 449 return 450 } 451 452 bf.raftServer.commitProgress.UpdateConnect(cEntry) 453 case <-bf.quit: 454 return 455 } 456 } 457 } 458 459 // @ReadyMarker: leader can make new block after receiving empty commit entry. It is ready marker. 460 // Receiving a marker ensures that all the blocks of previous term has been connected in chain 461 func (bf *BlockFactory) handleReadyMarker(ce *commitEntry) { 462 logger.Debug().Uint64("index", ce.index).Uint64("term", ce.term).Msg("set raft marker(empty block)") 463 464 // set ready to produce block for this term 465 bf.ready.set(ce) 466 bf.reset() 467 } 468 469 func (bf *BlockFactory) generateBlock(work *Work) (*types.Block, *state.BlockState, error) { 470 var ( 471 bestBlock *types.Block 472 err error 473 ) 474 475 bestBlock = work.Block 476 477 defer func() { 478 if panicMsg := recover(); panicMsg != nil { 479 err = fmt.Errorf("panic ocurred during block generation - %v", panicMsg) 480 } 481 }() 482 483 checkCancel := func() bool { 484 if !bf.raftServer.IsLeaderOfTerm(work.term) { 485 logger.Debug().Msg("cancel because blockfactory is not leader of term") 486 return true 487 } 488 489 if b, _ := bf.GetBestBlock(); b != nil && bestBlock.BlockNo() != b.BlockNo() { 490 logger.Debug().Uint64("best", b.BlockNo()).Msg("cancel because best block changed") 491 if StopDupCommit { 492 logger.Fatal().Str("work", work.ToString()).Msg("work duplicate") 493 } 494 return true 495 } 496 497 return false 498 } 499 500 if checkCancel() { 501 return nil, nil, ErrCancelGenerate 502 } 503 504 blockState := bf.sdb.NewBlockState(bestBlock.GetHeader().GetBlocksRootHash()) 505 506 ts := time.Now().UnixNano() 507 508 txOp := chain.NewCompTxOp( 509 bf.txOp, 510 newTxExec(bf, bf.ChainWAL, bestBlock.GetHeader().GetBlockNo()+1, ts, bestBlock.GetHash(), bestBlock.GetHeader().GetChainID()), 511 ) 512 513 block, err := chain.GenerateBlock(bf, bestBlock, blockState, txOp, ts, RaftSkipEmptyBlock) 514 if err == chain.ErrBlockEmpty { 515 //need reset previous work 516 return nil, nil, chain.ErrBlockEmpty 517 } else if err != nil { 518 logger.Info().Err(err).Msg("failed to generate block") 519 return nil, nil, err 520 } 521 522 if err = block.Sign(bf.privKey); err != nil { 523 logger.Error().Err(err).Msg("failed to sign in block") 524 return nil, nil, err 525 } 526 527 logger.Info().Str("blockProducer", bf.ID).Str("raftID", EtcdIDToString(bf.bpc.NodeID())). 528 Str("sroot", enc.ToString(block.GetHeader().GetBlocksRootHash())). 529 Uint64("no", block.GetHeader().GetBlockNo()). 530 Str("hash", block.ID()). 531 Msg("block produced") 532 533 return block, blockState, nil 534 } 535 536 func (bf *BlockFactory) commitC() chan *commitEntry { 537 return bf.raftOp.commitC 538 } 539 540 func (bf *BlockFactory) reset() { 541 bf.jobLock.Lock() 542 defer bf.jobLock.Unlock() 543 544 // empty jobQueue. Pushed works in job queue should be removed before resetting prev work of block factory. Otherwise, it can be possible to make same job since prev work is nil. 545 for len(bf.jobQueue) > 0 { 546 info := <-bf.jobQueue 547 drop := info.(*Work) 548 logger.Debug().Str("work", drop.ToString()).Msg("drop work for reset") 549 } 550 551 if bf.prevBlock != nil { 552 logger.Info().Str("prev proposed", bf.raftOp.toString()).Msg("reset previous work of block factory") 553 bf.prevBlock = nil 554 } 555 bf.bpc.resetSavedConfChangePropose() 556 } 557 558 // save block/block state to connect after commit 559 func (bf *BlockFactory) connect(block *types.Block) error { 560 proposed := bf.raftOp.proposed 561 var blockState *state.BlockState 562 563 if proposed != nil { 564 if !bytes.Equal(block.BlockHash(), proposed.block.BlockHash()) { 565 logger.Warn().Uint64("prop-no", proposed.block.GetHeader().GetBlockNo()).Str("prop", proposed.block.ID()).Uint64("commit-no", block.GetHeader().GetBlockNo()).Str("commit", block.ID()).Msg("commited block is not proposed by me. this node is probably not leader") 566 bf.raftOp.resetPropose() 567 } else { 568 blockState = proposed.blockState 569 } 570 } 571 572 logger.Info().Uint64("no", block.BlockNo()). 573 Str("hash", block.ID()). 574 Str("prev", block.PrevID()). 575 Bool("proposed", blockState != nil). 576 Msg("connect block") 577 578 // if bestblock is changed, connecting block failed. new block is generated in next tick 579 // On a slow server, chain service takes too long to add block in blockchain. In this case, raft server waits to send new block to commit channel. 580 if err := chain.ConnectBlock(bf, block, blockState, time.Second*300); err != nil { 581 logger.Fatal().Msg(err.Error()) 582 return err 583 } 584 585 return nil 586 } 587 588 /* 589 // waitUntilStartable wait until this chain synchronizes with more than half of all peers 590 func (bf *BlockFactory) waitSyncWithMajority() error { 591 ticker := time.NewTicker(peerCheckInterval) 592 593 for { 594 select { 595 case <-ticker.C: 596 if synced, err := bf.cl.hasSynced(); err != nil { 597 logger.Error().Err(err).Msg("failed to check sync with a majority of peers") 598 return err 599 } else if synced { 600 return nil 601 } 602 603 case <-bf.QuitChan(): 604 logger.Info().Msg("quit while wait sync") 605 return ErrBFQuit 606 default: 607 } 608 } 609 } 610 */ 611 // JobQueue returns the queue for block production triggering. 612 func (bf *BlockFactory) JobQueue() chan<- interface{} { 613 return bf.jobQueue 614 } 615 616 // Info retuns an empty string. 617 func (bf *BlockFactory) Info() string { 618 // TODO: Returns a appropriate information inx json format like current 619 // leader, etc. 620 info := consensus.NewInfo(GetName()) 621 if bf.raftServer == nil { 622 return info.AsJSON() 623 } 624 625 b, err := json.Marshal(bf.bpc.getRaftInfo(false)) 626 if err != nil { 627 logger.Error().Err(err).Msg("failed to marshalEntryData raft consensus") 628 } else { 629 m := json.RawMessage(b) 630 info.Status = &m 631 } 632 633 return info.AsJSON() 634 } 635 636 func (bf *BlockFactory) ConsensusInfo() *types.ConsensusInfo { 637 if bf.bpc == nil { 638 return &types.ConsensusInfo{Type: GetName()} 639 } 640 return bf.bpc.toConsensusInfo() 641 } 642 643 func (bf *BlockFactory) NeedNotify() bool { 644 return false 645 } 646 647 func (bf *BlockFactory) HasWAL() bool { 648 return true 649 } 650 651 func (bf *BlockFactory) IsForkEnable() bool { 652 return false 653 } 654 655 // check already connect block 656 // In raft, block hash may already have been writtern when writing log entry. 657 func (bf *BlockFactory) IsConnectedBlock(block *types.Block) bool { 658 savedBlk, err := bf.GetBlockByNo(block.GetHeader().GetBlockNo()) 659 if err == nil { 660 if bytes.Equal([]byte(savedBlk.BlockHash()), []byte(block.BlockHash())) { 661 return true 662 } 663 } 664 return false 665 } 666 667 type ErrorMembershipChange struct { 668 Err error 669 } 670 671 func (e ErrorMembershipChange) Error() string { 672 return fmt.Sprintf("failed to change membership: %s", e.Err.Error()) 673 } 674 675 // ConfChange change membership of raft cluster and returns new membership 676 func (bf *BlockFactory) ConfChange(req *types.MembershipChange) (*consensus.Member, error) { 677 if bf.bpc == nil { 678 return nil, ErrorMembershipChange{ErrClusterNotReady} 679 } 680 681 if !bf.raftServer.IsLeader() { 682 return nil, ErrorMembershipChange{ErrNotRaftLeader} 683 } 684 685 var member *consensus.Member 686 var err error 687 688 // set reqID by blockHash 689 var best *types.Block 690 if best, err = bf.GetBestBlock(); err != nil { 691 return nil, err 692 } 693 694 req.RequestID = binary.LittleEndian.Uint64(best.GetHash()[0:8]) 695 696 if member, err = bf.bpc.ChangeMembership(req, false); err != nil { 697 return nil, ErrorMembershipChange{err} 698 } 699 700 return member, nil 701 } 702 703 func (bf *BlockFactory) RaftAccessor() consensus.AergoRaftAccessor { 704 return bf.rhw 705 } 706 707 func (bf *BlockFactory) MakeConfChangeProposal(req *types.MembershipChange) (*consensus.ConfChangePropose, error) { 708 var ( 709 proposal *consensus.ConfChangePropose 710 err error 711 ) 712 713 if bf.bpc == nil { 714 return nil, ErrorMembershipChange{ErrClusterNotReady} 715 } 716 717 cl := bf.bpc 718 719 cl.Lock() 720 defer cl.Unlock() 721 722 if !bf.raftServer.IsLeader() { 723 logger.Info().Msg("skipped conf change request since node is not leader") 724 return nil, consensus.ErrorMembershipChangeSkip 725 } 726 727 logger.Info().Str("request", req.ToString()).Msg("make proposal of cluster conf change") 728 729 if proposal, err = cl.makeProposal(req, true); err != nil { 730 logger.Error().Uint64("requestID", req.GetRequestID()).Msg("failed to make proposal for conf change") 731 return nil, err 732 } 733 734 // To make cluster_test easier, this check called not in makeProposal() but here 735 if err = cl.isEnableChangeMembership(proposal.Cc); err != nil { 736 logger.Error().Err(err).Msg("failed cluster availability check to change membership") 737 return nil, err 738 } 739 740 return proposal, nil 741 } 742 743 // getHardStateOfBlock returns (term/commit) corresponding to best block hash. 744 // To get hardstateinfo, it needs to search all raft indexes. 745 func (bf *BlockFactory) getHardStateOfBlock(bestBlockHash []byte) (*types.HardStateInfo, error) { 746 var ( 747 bestBlock *types.Block 748 err error 749 hash []byte 750 ) 751 if bestBlock, err = bf.GetBlock(bestBlockHash); err != nil { 752 return nil, fmt.Errorf("block does not exist in chain") 753 } 754 755 entry, err := bf.ChainWAL.GetRaftEntryOfBlock(bestBlockHash) 756 if err == nil { 757 logger.Debug().Uint64("term", entry.Term).Uint64("comit", entry.Index).Msg("get hardstate of block") 758 759 return &types.HardStateInfo{Term: entry.Term, Commit: entry.Index}, nil 760 } 761 762 logger.Warn().Uint64("request no", bestBlock.BlockNo()).Msg("can't find raft entry for requested hash. so try to find closest raft entry.") 763 764 // find best hash mapping (no < bestBlock no) 765 for i := bestBlock.BlockNo() - 1; i >= 1; i-- { 766 if hash, err = bf.GetHashByNo(i); err == nil { 767 if entry, err = bf.ChainWAL.GetRaftEntryOfBlock(hash); err == nil { 768 logger.Debug().Str("entry", entry.ToString()).Msg("find best closest entry") 769 return &types.HardStateInfo{Term: entry.Term, Commit: entry.Index}, nil 770 } 771 } 772 } 773 774 return nil, fmt.Errorf("not exist proper raft entry for requested hash") 775 } 776 777 // ClusterInfo returns members of cluster and hardstate info corresponding to best block hash 778 func (bf *BlockFactory) ClusterInfo(bestBlockHash []byte) *types.GetClusterInfoResponse { 779 var ( 780 hardStateInfo *types.HardStateInfo 781 mbrAttrs []*types.MemberAttr 782 bestBlock *types.Block 783 err error 784 ) 785 786 if bf.bpc.ClusterID() == InvalidClusterID { 787 return &types.GetClusterInfoResponse{Error: ErrClusterNotReady.Error()} 788 } 789 790 if bestBlockHash != nil { 791 if hardStateInfo, err = bf.getHardStateOfBlock(bestBlockHash); err != nil { 792 return &types.GetClusterInfoResponse{Error: err.Error()} 793 } 794 } 795 796 if mbrAttrs, err = bf.bpc.getMemberAttrs(); err != nil { 797 return &types.GetClusterInfoResponse{Error: err.Error()} 798 } 799 800 if bestBlock, err = bf.GetBestBlock(); err != nil { 801 return &types.GetClusterInfoResponse{Error: err.Error()} 802 } 803 804 return &types.GetClusterInfoResponse{ChainID: bf.bpc.chainID, ClusterID: bf.bpc.ClusterID(), MbrAttrs: mbrAttrs, BestBlockNo: bestBlock.BlockNo(), HardStateInfo: hardStateInfo} 805 } 806 807 // ConfChangeInfo returns ConfChangeProgress queries by request ID of ConfChange 808 func (bf *BlockFactory) ConfChangeInfo(requestID uint64) (*types.ConfChangeProgress, error) { 809 return bf.GetConfChangeProgress(requestID) 810 } 811 812 func (bf *BlockFactory) checkBpTimeout() error { 813 select { 814 case <-bf.bpTimeoutC: 815 return chain.ErrTimeout{Kind: "block"} 816 case <-bf.quit: 817 return chain.ErrQuit 818 default: 819 return nil 820 } 821 } 822 823 type Proposed struct { 824 block *types.Block 825 blockState *state.BlockState 826 } 827 828 type RaftOperator struct { 829 commitC chan *commitEntry 830 831 cl *Cluster 832 rs *raftServer 833 834 proposed *Proposed 835 } 836 837 func newRaftOperator(rs *raftServer, cl *Cluster) *RaftOperator { 838 //commitC := make(chan *commitEntry, MaxCommitQueueLen) 839 commitC := make(chan *commitEntry) 840 841 return &RaftOperator{commitC: commitC, rs: rs, cl: cl} 842 } 843 844 func (rop *RaftOperator) propose(block *types.Block, blockState *state.BlockState, term uint64) error { 845 if !rop.rs.IsLeaderOfTerm(term) { 846 logger.Info().Msg("dropped produced block because this bp became no longer leader") 847 return ErrNotRaftLeader 848 } 849 850 debugRaftProposeSleep() 851 852 rop.proposed = &Proposed{block: block, blockState: blockState} 853 854 if err := rop.rs.Propose(block); err != nil { 855 return err 856 } 857 858 logger.Info().Msg("block proposed by blockfactory") 859 860 if blockState.CCProposal != nil { 861 if err := rop.ProposeConfChange(blockState.CCProposal); err != nil { 862 logger.Error().Err(err).Msg("failed to change membership") 863 return ErrorMembershipChange{err} 864 } 865 } 866 867 return nil 868 } 869 870 func (rop *RaftOperator) ProposeConfChange(proposal *consensus.ConfChangePropose) error { 871 var err error 872 873 if rop.cl == nil { 874 return ErrClusterNotReady 875 } 876 877 if err = rop.cl.submitProposal(proposal, true); err != nil { 878 return err 879 } 880 881 return nil 882 } 883 884 func (rop *RaftOperator) resetPropose() { 885 rop.proposed = nil 886 logger.Debug().Msg("reset proposed block") 887 } 888 889 func (rop *RaftOperator) toString() string { 890 buf := "proposed:" 891 if rop.proposed != nil && rop.proposed.block != nil { 892 buf = buf + fmt.Sprintf("[no=%d, hash=%s]", rop.proposed.block.BlockNo(), rop.proposed.block.BlockID().String()) 893 } else { 894 buf = buf + "empty" 895 } 896 return buf 897 } 898 899 func shutdownMsg(m string) { 900 logger.Info().Msgf("shutdown initiated. stop the %s", m) 901 } 902 903 func ValidateGenesis(genesis *types.Genesis) error { 904 if strings.ToLower(genesis.ID.Consensus) != consensus.ConsensusName[consensus.ConsensusRAFT] { 905 return ErrInvalidConsensusName 906 } 907 908 // validate BPS 909 if _, err := parseBpsToMembers(genesis.EnterpriseBPs); err != nil { 910 logger.Error().Err(err).Msg("failed to parse bp list of Genesis block") 911 return err 912 } 913 914 return nil 915 }