github.com/iotexproject/iotex-core@v1.14.1-rc1/consensus/scheme/rolldpos/rolldposctx.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package rolldpos 7 8 import ( 9 "context" 10 "sync" 11 "time" 12 13 "github.com/facebookgo/clock" 14 fsm "github.com/iotexproject/go-fsm" 15 "github.com/iotexproject/go-pkgs/crypto" 16 "github.com/pkg/errors" 17 "github.com/prometheus/client_golang/prometheus" 18 "go.uber.org/zap" 19 20 "github.com/iotexproject/iotex-core/action/protocol/rolldpos" 21 "github.com/iotexproject/iotex-core/blockchain" 22 "github.com/iotexproject/iotex-core/blockchain/block" 23 "github.com/iotexproject/iotex-core/consensus/consensusfsm" 24 "github.com/iotexproject/iotex-core/consensus/scheme" 25 "github.com/iotexproject/iotex-core/db" 26 "github.com/iotexproject/iotex-core/endorsement" 27 "github.com/iotexproject/iotex-core/pkg/log" 28 ) 29 30 var ( 31 _timeSlotMtc = prometheus.NewGaugeVec( 32 prometheus.GaugeOpts{ 33 Name: "iotex_consensus_round", 34 Help: "Consensus round", 35 }, 36 []string{}, 37 ) 38 39 _blockIntervalMtc = prometheus.NewGaugeVec( 40 prometheus.GaugeOpts{ 41 Name: "iotex_consensus_block_interval", 42 Help: "Consensus block interval", 43 }, 44 []string{}, 45 ) 46 47 _consensusDurationMtc = prometheus.NewGaugeVec( 48 prometheus.GaugeOpts{ 49 Name: "iotex_consensus_elapse_time", 50 Help: "Consensus elapse time.", 51 }, 52 []string{}, 53 ) 54 55 _consensusHeightMtc = prometheus.NewGaugeVec( 56 prometheus.GaugeOpts{ 57 Name: "iotex_consensus_height", 58 Help: "Consensus height", 59 }, 60 []string{}, 61 ) 62 ) 63 64 func init() { 65 prometheus.MustRegister(_timeSlotMtc) 66 prometheus.MustRegister(_blockIntervalMtc) 67 prometheus.MustRegister(_consensusDurationMtc) 68 prometheus.MustRegister(_consensusHeightMtc) 69 } 70 71 type ( 72 // NodesSelectionByEpochFunc defines a function to select nodes 73 NodesSelectionByEpochFunc func(uint64) ([]string, error) 74 75 // RDPoSCtx is the context of RollDPoS 76 RDPoSCtx interface { 77 consensusfsm.Context 78 Chain() ChainManager 79 BlockDeserializer() *block.Deserializer 80 RoundCalculator() *roundCalculator 81 Clock() clock.Clock 82 CheckBlockProposer(uint64, *blockProposal, *endorsement.Endorsement) error 83 CheckVoteEndorser(uint64, *ConsensusVote, *endorsement.Endorsement) error 84 } 85 86 rollDPoSCtx struct { 87 consensusfsm.ConsensusConfig 88 89 // TODO: explorer dependency deleted at #1085, need to add api params here 90 chain ChainManager 91 blockDeserializer *block.Deserializer 92 broadcastHandler scheme.Broadcast 93 roundCalc *roundCalculator 94 eManagerDB db.KVStore 95 toleratedOvertime time.Duration 96 97 encodedAddr string 98 priKey crypto.PrivateKey 99 round *roundCtx 100 clock clock.Clock 101 active bool 102 mutex sync.RWMutex 103 } 104 ) 105 106 // NewRollDPoSCtx returns a context of RollDPoSCtx 107 func NewRollDPoSCtx( 108 cfg consensusfsm.ConsensusConfig, 109 consensusDBConfig db.Config, 110 active bool, 111 toleratedOvertime time.Duration, 112 timeBasedRotation bool, 113 chain ChainManager, 114 blockDeserializer *block.Deserializer, 115 rp *rolldpos.Protocol, 116 broadcastHandler scheme.Broadcast, 117 delegatesByEpochFunc NodesSelectionByEpochFunc, 118 proposersByEpochFunc NodesSelectionByEpochFunc, 119 encodedAddr string, 120 priKey crypto.PrivateKey, 121 clock clock.Clock, 122 beringHeight uint64, 123 ) (RDPoSCtx, error) { 124 if chain == nil { 125 return nil, errors.New("chain cannot be nil") 126 } 127 if rp == nil { 128 return nil, errors.New("roll dpos protocol cannot be nil") 129 } 130 if clock == nil { 131 return nil, errors.New("clock cannot be nil") 132 } 133 if delegatesByEpochFunc == nil { 134 return nil, errors.New("delegates by epoch function cannot be nil") 135 } 136 if proposersByEpochFunc == nil { 137 return nil, errors.New("proposers by epoch function cannot be nil") 138 } 139 if cfg.AcceptBlockTTL(0)+cfg.AcceptProposalEndorsementTTL(0)+cfg.AcceptLockEndorsementTTL(0)+cfg.CommitTTL(0) > cfg.BlockInterval(0) { 140 return nil, errors.Errorf( 141 "invalid ttl config, the sum of ttls should be equal to block interval. acceptBlockTTL %d, acceptProposalEndorsementTTL %d, acceptLockEndorsementTTL %d, commitTTL %d, blockInterval %d", 142 cfg.AcceptBlockTTL(0), 143 cfg.AcceptProposalEndorsementTTL(0), 144 cfg.AcceptLockEndorsementTTL(0), 145 cfg.CommitTTL(0), 146 cfg.BlockInterval(0), 147 ) 148 } 149 var eManagerDB db.KVStore 150 if len(consensusDBConfig.DbPath) > 0 { 151 eManagerDB = db.NewBoltDB(consensusDBConfig) 152 } 153 roundCalc := &roundCalculator{ 154 delegatesByEpochFunc: delegatesByEpochFunc, 155 proposersByEpochFunc: proposersByEpochFunc, 156 chain: chain, 157 rp: rp, 158 timeBasedRotation: timeBasedRotation, 159 beringHeight: beringHeight, 160 } 161 return &rollDPoSCtx{ 162 ConsensusConfig: cfg, 163 active: active, 164 encodedAddr: encodedAddr, 165 priKey: priKey, 166 chain: chain, 167 blockDeserializer: blockDeserializer, 168 broadcastHandler: broadcastHandler, 169 clock: clock, 170 roundCalc: roundCalc, 171 eManagerDB: eManagerDB, 172 toleratedOvertime: toleratedOvertime, 173 }, nil 174 } 175 176 func (ctx *rollDPoSCtx) Start(c context.Context) (err error) { 177 var eManager *endorsementManager 178 if ctx.eManagerDB != nil { 179 if err := ctx.eManagerDB.Start(c); err != nil { 180 return errors.Wrap(err, "Error when starting the collectionDB") 181 } 182 eManager, err = newEndorsementManager(ctx.eManagerDB, ctx.blockDeserializer) 183 } 184 ctx.round, err = ctx.roundCalc.NewRoundWithToleration(0, ctx.BlockInterval(0), ctx.clock.Now(), eManager, ctx.toleratedOvertime) 185 186 return err 187 } 188 189 func (ctx *rollDPoSCtx) Stop(c context.Context) error { 190 if ctx.eManagerDB != nil { 191 return ctx.eManagerDB.Stop(c) 192 } 193 return nil 194 } 195 196 func (ctx *rollDPoSCtx) Chain() ChainManager { 197 return ctx.chain 198 } 199 200 func (ctx *rollDPoSCtx) BlockDeserializer() *block.Deserializer { 201 return ctx.blockDeserializer 202 } 203 204 func (ctx *rollDPoSCtx) RoundCalculator() *roundCalculator { 205 return ctx.roundCalc 206 } 207 208 func (ctx *rollDPoSCtx) Clock() clock.Clock { 209 return ctx.clock 210 } 211 212 // CheckVoteEndorser checks if the endorsement's endorser is a valid delegate at the given height 213 func (ctx *rollDPoSCtx) CheckVoteEndorser( 214 height uint64, 215 vote *ConsensusVote, 216 en *endorsement.Endorsement, 217 ) error { 218 ctx.mutex.RLock() 219 defer ctx.mutex.RUnlock() 220 endorserAddr := en.Endorser().Address() 221 if endorserAddr == nil { 222 return errors.New("failed to get address") 223 } 224 if !ctx.roundCalc.IsDelegate(endorserAddr.String(), height) { 225 return errors.Errorf("%s is not delegate of the corresponding round", endorserAddr) 226 } 227 228 return nil 229 } 230 231 // CheckBlockProposer checks the block proposal 232 func (ctx *rollDPoSCtx) CheckBlockProposer( 233 height uint64, 234 proposal *blockProposal, 235 en *endorsement.Endorsement, 236 ) error { 237 ctx.mutex.RLock() 238 defer ctx.mutex.RUnlock() 239 if height != proposal.block.Height() { 240 return errors.Errorf( 241 "block height %d different from expected %d", 242 proposal.block.Height(), 243 height, 244 ) 245 } 246 endorserAddr := en.Endorser().Address() 247 if endorserAddr == nil { 248 return errors.New("failed to get address") 249 } 250 if proposer := ctx.roundCalc.Proposer(height, ctx.BlockInterval(height), en.Timestamp()); proposer != endorserAddr.String() { 251 return errors.Errorf( 252 "%s is not proposer of the corresponding round, %s expected", 253 endorserAddr.String(), 254 proposer, 255 ) 256 } 257 proposerAddr := proposal.ProposerAddress() 258 if ctx.roundCalc.Proposer(height, ctx.BlockInterval(height), proposal.block.Timestamp()) != proposerAddr { 259 return errors.Errorf("%s is not proposer of the corresponding round", proposerAddr) 260 } 261 if !proposal.block.VerifySignature() { 262 return errors.Errorf("invalid block signature") 263 } 264 if proposerAddr != endorserAddr.String() { 265 round, err := ctx.roundCalc.NewRound(height, ctx.BlockInterval(height), en.Timestamp(), nil) 266 if err != nil { 267 return err 268 } 269 if err := round.AddBlock(proposal.block); err != nil { 270 return err 271 } 272 blkHash := proposal.block.HashBlock() 273 for _, e := range proposal.proofOfLock { 274 if err := round.AddVoteEndorsement( 275 NewConsensusVote(blkHash[:], PROPOSAL), 276 e, 277 ); err == nil { 278 continue 279 } 280 if err := round.AddVoteEndorsement( 281 NewConsensusVote(blkHash[:], COMMIT), 282 e, 283 ); err != nil { 284 return err 285 } 286 } 287 if !round.EndorsedByMajority(blkHash[:], []ConsensusVoteTopic{PROPOSAL, COMMIT}) { 288 return errors.Wrap(ErrInsufficientEndorsements, "failed to verify proof of lock") 289 } 290 } 291 return nil 292 } 293 294 func (ctx *rollDPoSCtx) RoundCalc() *roundCalculator { 295 return ctx.roundCalc 296 } 297 298 ///////////////////////////////////// 299 // Context of consensusFSM interfaces 300 ///////////////////////////////////// 301 302 func (ctx *rollDPoSCtx) NewConsensusEvent( 303 eventType fsm.EventType, 304 data interface{}, 305 ) *consensusfsm.ConsensusEvent { 306 ctx.mutex.RLock() 307 defer ctx.mutex.RUnlock() 308 309 return ctx.newConsensusEvent(eventType, data) 310 } 311 312 func (ctx *rollDPoSCtx) NewBackdoorEvt( 313 dst fsm.State, 314 ) *consensusfsm.ConsensusEvent { 315 ctx.mutex.RLock() 316 defer ctx.mutex.RUnlock() 317 318 return ctx.newConsensusEvent(consensusfsm.BackdoorEvent, dst) 319 } 320 321 func (ctx *rollDPoSCtx) Logger() *zap.Logger { 322 ctx.mutex.RLock() 323 defer ctx.mutex.RUnlock() 324 325 return ctx.logger() 326 } 327 328 func (ctx *rollDPoSCtx) Prepare() error { 329 ctx.mutex.Lock() 330 defer ctx.mutex.Unlock() 331 height := ctx.chain.TipHeight() + 1 332 newRound, err := ctx.roundCalc.UpdateRound(ctx.round, height, ctx.BlockInterval(height), ctx.clock.Now(), ctx.toleratedOvertime) 333 if err != nil { 334 return err 335 } 336 ctx.logger().Debug( 337 "new round", 338 zap.Uint64("height", newRound.height), 339 zap.String("ts", ctx.clock.Now().String()), 340 zap.Uint64("epoch", newRound.epochNum), 341 zap.Uint64("epochStartHeight", newRound.epochStartHeight), 342 zap.Uint32("round", newRound.roundNum), 343 zap.String("roundStartTime", newRound.roundStartTime.String()), 344 ) 345 ctx.round = newRound 346 _consensusHeightMtc.WithLabelValues().Set(float64(ctx.round.height)) 347 _timeSlotMtc.WithLabelValues().Set(float64(ctx.round.roundNum)) 348 return nil 349 } 350 351 func (ctx *rollDPoSCtx) IsDelegate() bool { 352 ctx.mutex.RLock() 353 defer ctx.mutex.RUnlock() 354 355 return ctx.isDelegate() 356 } 357 358 func (ctx *rollDPoSCtx) Proposal() (interface{}, error) { 359 ctx.mutex.RLock() 360 defer ctx.mutex.RUnlock() 361 if ctx.round.Proposer() != ctx.encodedAddr { 362 return nil, nil 363 } 364 if ctx.round.IsLocked() { 365 return ctx.endorseBlockProposal(newBlockProposal( 366 ctx.round.Block(ctx.round.HashOfBlockInLock()), 367 ctx.round.ProofOfLock(), 368 )) 369 } 370 return ctx.mintNewBlock() 371 } 372 373 func (ctx *rollDPoSCtx) WaitUntilRoundStart() time.Duration { 374 ctx.mutex.RLock() 375 defer ctx.mutex.RUnlock() 376 now := ctx.clock.Now() 377 startTime := ctx.round.StartTime() 378 if now.Before(startTime) { 379 time.Sleep(startTime.Sub(now)) 380 return 0 381 } 382 overTime := now.Sub(startTime) 383 if !ctx.isDelegate() && ctx.toleratedOvertime > overTime { 384 time.Sleep(ctx.toleratedOvertime - overTime) 385 return 0 386 } 387 return overTime 388 } 389 390 func (ctx *rollDPoSCtx) PreCommitEndorsement() interface{} { 391 ctx.mutex.RLock() 392 defer ctx.mutex.RUnlock() 393 endorsement := ctx.round.ReadyToCommit(ctx.encodedAddr) 394 if endorsement == nil { 395 // DON'T CHANGE, this is on purpose, because endorsement as nil won't result in a nil "interface {}" 396 return nil 397 } 398 return endorsement 399 } 400 401 func (ctx *rollDPoSCtx) NewProposalEndorsement(msg interface{}) (interface{}, error) { 402 ctx.mutex.RLock() 403 defer ctx.mutex.RUnlock() 404 var blockHash []byte 405 if msg != nil { 406 ecm, ok := msg.(*EndorsedConsensusMessage) 407 if !ok { 408 return nil, errors.New("invalid endorsed block") 409 } 410 proposal, ok := ecm.Document().(*blockProposal) 411 if !ok { 412 return nil, errors.New("invalid endorsed block") 413 } 414 blkHash := proposal.block.HashBlock() 415 blockHash = blkHash[:] 416 if err := ctx.chain.ValidateBlock(proposal.block); err != nil { 417 return nil, errors.Wrapf(err, "error when validating the proposed block") 418 } 419 if err := ctx.round.AddBlock(proposal.block); err != nil { 420 return nil, err 421 } 422 ctx.loggerWithStats().Debug("accept block proposal", log.Hex("block", blockHash)) 423 } else if ctx.round.IsLocked() { 424 blockHash = ctx.round.HashOfBlockInLock() 425 } 426 427 return ctx.newEndorsement( 428 blockHash, 429 PROPOSAL, 430 ctx.round.StartTime().Add(ctx.AcceptBlockTTL(ctx.round.height)), 431 ) 432 } 433 434 func (ctx *rollDPoSCtx) NewLockEndorsement( 435 msg interface{}, 436 ) (interface{}, error) { 437 ctx.mutex.RLock() 438 defer ctx.mutex.RUnlock() 439 blkHash, err := ctx.verifyVote( 440 msg, 441 []ConsensusVoteTopic{PROPOSAL, COMMIT}, // commit is counted as one proposal 442 ) 443 switch errors.Cause(err) { 444 case ErrInsufficientEndorsements: 445 return nil, nil 446 case nil: 447 if len(blkHash) != 0 { 448 ctx.loggerWithStats().Debug("Locked", log.Hex("block", blkHash)) 449 return ctx.newEndorsement( 450 blkHash, 451 LOCK, 452 ctx.round.StartTime().Add( 453 ctx.AcceptBlockTTL(ctx.round.height)+ctx.AcceptProposalEndorsementTTL(ctx.round.height), 454 ), 455 ) 456 } 457 ctx.loggerWithStats().Debug("Unlocked") 458 } 459 return nil, err 460 } 461 462 func (ctx *rollDPoSCtx) NewPreCommitEndorsement( 463 msg interface{}, 464 ) (interface{}, error) { 465 ctx.mutex.RLock() 466 defer ctx.mutex.RUnlock() 467 blkHash, err := ctx.verifyVote( 468 msg, 469 []ConsensusVoteTopic{LOCK, COMMIT}, // commit endorse is counted as one lock endorse 470 ) 471 switch errors.Cause(err) { 472 case ErrInsufficientEndorsements: 473 return nil, nil 474 case nil: 475 ctx.loggerWithStats().Debug("Ready to pre-commit") 476 return ctx.newEndorsement( 477 blkHash, 478 COMMIT, 479 ctx.round.StartTime().Add( 480 ctx.AcceptBlockTTL(ctx.round.height)+ctx.AcceptProposalEndorsementTTL(ctx.round.height)+ctx.AcceptLockEndorsementTTL(ctx.round.height), 481 ), 482 ) 483 default: 484 return nil, err 485 } 486 } 487 488 func (ctx *rollDPoSCtx) Commit(msg interface{}) (bool, error) { 489 ctx.mutex.Lock() 490 defer ctx.mutex.Unlock() 491 blkHash, err := ctx.verifyVote(msg, []ConsensusVoteTopic{COMMIT}) 492 switch errors.Cause(err) { 493 case ErrInsufficientEndorsements: 494 return false, nil 495 case nil: 496 ctx.loggerWithStats().Debug("Ready to commit") 497 default: 498 return false, err 499 } 500 // this is redudant check for now, as we only accept endorsements of the received blocks 501 pendingBlock := ctx.round.Block(blkHash) 502 if pendingBlock == nil { 503 return false, nil 504 } 505 if ctx.round.Height()%100 == 0 { 506 ctx.logger().Info("consensus reached", zap.Uint64("blockHeight", ctx.round.Height())) 507 } 508 if err := pendingBlock.Finalize( 509 ctx.round.Endorsements(blkHash, []ConsensusVoteTopic{COMMIT}), 510 ctx.round.StartTime().Add( 511 ctx.AcceptBlockTTL(ctx.round.height)+ctx.AcceptProposalEndorsementTTL(ctx.round.height)+ctx.AcceptLockEndorsementTTL(ctx.round.height), 512 ), 513 ); err != nil { 514 return false, errors.Wrap(err, "failed to add endorsements to block") 515 } 516 517 // Commit and broadcast the pending block 518 switch err := ctx.chain.CommitBlock(pendingBlock); errors.Cause(err) { 519 case blockchain.ErrInvalidTipHeight: 520 return true, nil 521 case nil: 522 break 523 default: 524 log.L().Error("error when committing the block", zap.Error(err)) 525 return false, errors.Wrap(err, "error when committing a block") 526 } 527 // Broadcast the committed block to the network 528 if blkProto := pendingBlock.ConvertToBlockPb(); blkProto != nil { 529 if err := ctx.broadcastHandler(blkProto); err != nil { 530 ctx.logger().Error( 531 "error when broadcasting blkProto", 532 zap.Error(err), 533 zap.Uint64("block", pendingBlock.Height()), 534 ) 535 } 536 // putblock to parent chain if the current node is proposer and current chain is a sub chain 537 if ctx.round.Proposer() == ctx.encodedAddr && ctx.chain.ChainAddress() != "" { 538 // TODO: explorer dependency deleted at #1085, need to call putblock related method 539 } 540 } else { 541 ctx.logger().Panic( 542 "error when converting a block into a proto msg", 543 zap.Uint64("block", pendingBlock.Height()), 544 ) 545 } 546 547 _consensusDurationMtc.WithLabelValues().Set(float64(time.Since(ctx.round.roundStartTime))) 548 if pendingBlock.Height() > 1 { 549 prevBlkProposeTime, err := ctx.chain.BlockProposeTime(pendingBlock.Height() - 1) 550 if err != nil { 551 ctx.logger().Error("Error when getting the previous block header.", 552 zap.Error(err), 553 zap.Uint64("height", pendingBlock.Height()-1), 554 ) 555 } 556 _blockIntervalMtc.WithLabelValues().Set(float64(pendingBlock.Timestamp().Sub(prevBlkProposeTime))) 557 } 558 return true, nil 559 } 560 561 func (ctx *rollDPoSCtx) Broadcast(endorsedMsg interface{}) { 562 ctx.mutex.RLock() 563 defer ctx.mutex.RUnlock() 564 ecm, ok := endorsedMsg.(*EndorsedConsensusMessage) 565 if !ok { 566 ctx.loggerWithStats().Error("invalid message type", zap.Any("message", ecm)) 567 return 568 } 569 msg, err := ecm.Proto() 570 if err != nil { 571 ctx.loggerWithStats().Error("failed to generate protobuf message", zap.Error(err)) 572 return 573 } 574 if err := ctx.broadcastHandler(msg); err != nil { 575 ctx.loggerWithStats().Error("fail to broadcast", zap.Error(err)) 576 } 577 } 578 579 func (ctx *rollDPoSCtx) IsStaleEvent(evt *consensusfsm.ConsensusEvent) bool { 580 ctx.mutex.RLock() 581 defer ctx.mutex.RUnlock() 582 583 return ctx.round.IsStale(evt.Height(), evt.Round(), evt.Data()) 584 } 585 586 func (ctx *rollDPoSCtx) IsFutureEvent(evt *consensusfsm.ConsensusEvent) bool { 587 ctx.mutex.RLock() 588 defer ctx.mutex.RUnlock() 589 590 return ctx.round.IsFuture(evt.Height(), evt.Round()) 591 } 592 593 func (ctx *rollDPoSCtx) IsStaleUnmatchedEvent(evt *consensusfsm.ConsensusEvent) bool { 594 ctx.mutex.RLock() 595 defer ctx.mutex.RUnlock() 596 597 return ctx.clock.Now().Sub(evt.Timestamp()) > ctx.UnmatchedEventTTL(ctx.round.height) 598 } 599 600 func (ctx *rollDPoSCtx) Height() uint64 { 601 ctx.mutex.RLock() 602 defer ctx.mutex.RUnlock() 603 604 return ctx.round.Height() 605 } 606 607 func (ctx *rollDPoSCtx) Activate(active bool) { 608 ctx.mutex.Lock() 609 defer ctx.mutex.Unlock() 610 611 ctx.active = active 612 } 613 614 func (ctx *rollDPoSCtx) Active() bool { 615 ctx.mutex.RLock() 616 defer ctx.mutex.RUnlock() 617 618 return ctx.active 619 } 620 621 /////////////////////////////////////////// 622 // private functions 623 /////////////////////////////////////////// 624 625 func (ctx *rollDPoSCtx) mintNewBlock() (*EndorsedConsensusMessage, error) { 626 var err error 627 blk := ctx.round.CachedMintedBlock() 628 if blk == nil { 629 // in case that there is no cached block in eManagerDB, it mints a new block. 630 blk, err = ctx.chain.MintNewBlock(ctx.round.StartTime()) 631 if err != nil { 632 return nil, err 633 } 634 if err = ctx.round.SetMintedBlock(blk); err != nil { 635 return nil, err 636 } 637 } 638 639 var proofOfUnlock []*endorsement.Endorsement 640 if ctx.round.IsUnlocked() { 641 proofOfUnlock = ctx.round.ProofOfLock() 642 } 643 return ctx.endorseBlockProposal(newBlockProposal(blk, proofOfUnlock)) 644 } 645 646 func (ctx *rollDPoSCtx) isDelegate() bool { 647 if active := ctx.active; !active { 648 ctx.logger().Info("current node is in standby mode") 649 return false 650 } 651 return ctx.round.IsDelegate(ctx.encodedAddr) 652 } 653 654 func (ctx *rollDPoSCtx) endorseBlockProposal(proposal *blockProposal) (*EndorsedConsensusMessage, error) { 655 en, err := endorsement.Endorse(ctx.priKey, proposal, ctx.round.StartTime()) 656 if err != nil { 657 return nil, err 658 } 659 return NewEndorsedConsensusMessage(proposal.block.Height(), proposal, en), nil 660 } 661 662 func (ctx *rollDPoSCtx) logger() *zap.Logger { 663 return ctx.round.Log(log.Logger("consensus")) 664 } 665 666 func (ctx *rollDPoSCtx) newConsensusEvent( 667 eventType fsm.EventType, 668 data interface{}, 669 ) *consensusfsm.ConsensusEvent { 670 switch ed := data.(type) { 671 case *EndorsedConsensusMessage: 672 height := ed.Height() 673 roundNum, _, err := ctx.roundCalc.RoundInfo(height, ctx.BlockInterval(height), ed.Endorsement().Timestamp()) 674 if err != nil { 675 ctx.logger().Error( 676 "failed to calculate round for generating consensus event", 677 zap.String("eventType", string(eventType)), 678 zap.Uint64("height", ed.Height()), 679 zap.String("timestamp", ed.Endorsement().Timestamp().String()), 680 zap.Any("data", data), 681 zap.Error(err), 682 ) 683 return nil 684 } 685 return consensusfsm.NewConsensusEvent( 686 eventType, 687 data, 688 ed.Height(), 689 roundNum, 690 ctx.clock.Now(), 691 ) 692 default: 693 return consensusfsm.NewConsensusEvent( 694 eventType, 695 data, 696 ctx.round.Height(), 697 ctx.round.Number(), 698 ctx.clock.Now(), 699 ) 700 } 701 } 702 703 func (ctx *rollDPoSCtx) loggerWithStats() *zap.Logger { 704 return ctx.round.LogWithStats(log.Logger("consensus")) 705 } 706 707 func (ctx *rollDPoSCtx) verifyVote( 708 msg interface{}, 709 topics []ConsensusVoteTopic, 710 ) ([]byte, error) { 711 consensusMsg, ok := msg.(*EndorsedConsensusMessage) 712 if !ok { 713 return nil, errors.New("invalid msg") 714 } 715 vote, ok := consensusMsg.Document().(*ConsensusVote) 716 if !ok { 717 return nil, errors.New("invalid msg") 718 } 719 blkHash := vote.BlockHash() 720 endorsement := consensusMsg.Endorsement() 721 if err := ctx.round.AddVoteEndorsement(vote, endorsement); err != nil { 722 return blkHash, err 723 } 724 ctx.loggerWithStats().Debug( 725 "verified consensus vote", 726 log.Hex("block", blkHash), 727 zap.Uint8("topic", uint8(vote.Topic())), 728 zap.String("endorser", endorsement.Endorser().HexString()), 729 ) 730 if !ctx.round.EndorsedByMajority(blkHash, topics) { 731 return blkHash, ErrInsufficientEndorsements 732 } 733 return blkHash, nil 734 } 735 736 func (ctx *rollDPoSCtx) newEndorsement( 737 blkHash []byte, 738 topic ConsensusVoteTopic, 739 timestamp time.Time, 740 ) (*EndorsedConsensusMessage, error) { 741 vote := NewConsensusVote( 742 blkHash, 743 topic, 744 ) 745 en, err := endorsement.Endorse(ctx.priKey, vote, timestamp) 746 if err != nil { 747 return nil, err 748 } 749 750 return NewEndorsedConsensusMessage(ctx.round.Height(), vote, en), nil 751 }