github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/consensus/state.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "context" 6 goerrors "errors" 7 "fmt" 8 "log/slog" 9 "reflect" 10 "runtime/debug" 11 "sync" 12 "time" 13 14 "github.com/gnolang/gno/tm2/pkg/amino" 15 cnscfg "github.com/gnolang/gno/tm2/pkg/bft/consensus/config" 16 cstypes "github.com/gnolang/gno/tm2/pkg/bft/consensus/types" 17 "github.com/gnolang/gno/tm2/pkg/bft/fail" 18 sm "github.com/gnolang/gno/tm2/pkg/bft/state" 19 "github.com/gnolang/gno/tm2/pkg/bft/types" 20 tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time" 21 walm "github.com/gnolang/gno/tm2/pkg/bft/wal" 22 "github.com/gnolang/gno/tm2/pkg/crypto" 23 "github.com/gnolang/gno/tm2/pkg/errors" 24 "github.com/gnolang/gno/tm2/pkg/events" 25 osm "github.com/gnolang/gno/tm2/pkg/os" 26 "github.com/gnolang/gno/tm2/pkg/p2p" 27 "github.com/gnolang/gno/tm2/pkg/service" 28 "github.com/gnolang/gno/tm2/pkg/telemetry" 29 "github.com/gnolang/gno/tm2/pkg/telemetry/metrics" 30 ) 31 32 // ----------------------------------------------------------------------------- 33 // Errors 34 35 var ( 36 ErrInvalidProposalSignature = errors.New("Error invalid proposal signature") 37 ErrInvalidProposalPOLRound = errors.New("Error invalid proposal POL round") 38 ErrAddingVote = errors.New("Error adding vote") 39 ErrVoteHeightMismatch = errors.New("Error vote height mismatch") 40 ) 41 42 // ----------------------------------------------------------------------------- 43 // Messages 44 45 var msgQueueSize = 1000 46 47 // WAL message. 48 type newRoundStepInfo struct { 49 cstypes.HRS `json:"hrs"` 50 } 51 52 // WAL message. 53 // msgs from the reactor which may update the state 54 type msgInfo struct { 55 Msg ConsensusMessage `json:"msg"` 56 PeerID p2p.ID `json:"peer_key"` 57 } 58 59 // WAL message. 60 // internally generated messages which may update the state 61 type timeoutInfo struct { 62 Duration time.Duration `json:"duration"` 63 Height int64 `json:"height"` 64 Round int `json:"round"` 65 Step cstypes.RoundStepType `json:"step"` 66 } 67 68 func (ti *timeoutInfo) String() string { 69 return fmt.Sprintf("%v ; %d/%d %v", ti.Duration, ti.Height, ti.Round, ti.Step) 70 } 71 72 func (ti *timeoutInfo) GetHRS() cstypes.HRS { 73 if ti == nil { 74 return cstypes.HRS{} 75 } else { 76 return cstypes.HRS{ti.Height, ti.Round, ti.Step} 77 } 78 } 79 80 // interface to the mempool 81 type txNotifier interface { 82 TxsAvailable() <-chan struct{} 83 } 84 85 // ConsensusState handles execution of the consensus algorithm. 86 // It processes votes and proposals, and upon reaching agreement, 87 // commits blocks to the chain and executes them against the application. 88 // The internal state machine receives input from peers, the internal validator, and from a timer. 89 type ConsensusState struct { 90 service.BaseService 91 92 // config details 93 config *cnscfg.ConsensusConfig 94 privValidator types.PrivValidator // for signing votes 95 96 // store blocks and commits 97 blockStore sm.BlockStore 98 99 // create and execute blocks 100 blockExec *sm.BlockExecutor 101 102 // notify us if txs are available 103 txNotifier txNotifier 104 105 // internal state 106 mtx sync.RWMutex 107 cstypes.RoundState 108 state sm.State // State until height-1. 109 110 // state changes may be triggered by: msgs from peers, 111 // msgs from ourself, or by timeouts 112 peerMsgQueue chan msgInfo 113 internalMsgQueue chan msgInfo 114 timeoutTicker TimeoutTicker 115 116 // information about about added votes and block parts are written on this channel 117 // so statistics can be computed by reactor 118 statsMsgQueue chan msgInfo 119 120 // we use evsw to trigger event broadcasts in the reactor, and to notify 121 // non-consensus subscribers, notably the file logger, which external 122 // processes consume to serve e.g. websocket clients. 123 evsw events.EventSwitch 124 125 // a Write-Ahead Log ensures we can recover from any kind of crash 126 // and helps us avoid signing conflicting votes 127 wal walm.WAL 128 walDisabled bool 129 replayMode bool // so we don't log signing errors during replay 130 doWALCatchup bool // determines if we even try to do the catchup 131 132 // for tests where we want to limit the number of transitions the state makes 133 nSteps int 134 135 // some functions can be overwritten for testing 136 decideProposal func(height int64, round int) 137 doPrevote func(height int64, round int) 138 setProposal func(proposal *types.Proposal) error 139 140 // closed when we finish shutting down 141 done chan struct{} 142 } 143 144 // StateOption sets an optional parameter on the ConsensusState. 145 type StateOption func(*ConsensusState) 146 147 // NewConsensusState returns a new ConsensusState. 148 func NewConsensusState( 149 config *cnscfg.ConsensusConfig, 150 state sm.State, 151 blockExec *sm.BlockExecutor, 152 blockStore sm.BlockStore, 153 txNotifier txNotifier, 154 options ...StateOption, 155 ) *ConsensusState { 156 cs := &ConsensusState{ 157 config: config, 158 blockExec: blockExec, 159 blockStore: blockStore, 160 txNotifier: txNotifier, 161 peerMsgQueue: make(chan msgInfo, msgQueueSize), 162 internalMsgQueue: make(chan msgInfo, msgQueueSize), 163 timeoutTicker: NewTimeoutTicker(), 164 statsMsgQueue: make(chan msgInfo, msgQueueSize), 165 done: nil, 166 doWALCatchup: true, 167 evsw: events.NewEventSwitch(), 168 wal: walm.NopWAL{}, 169 walDisabled: config.WALDisabled, 170 } 171 // set function defaults (may be overwritten before calling Start) 172 cs.decideProposal = cs.defaultDecideProposal 173 cs.doPrevote = cs.defaultDoPrevote 174 cs.setProposal = cs.defaultSetProposal 175 176 cs.updateToState(state) 177 178 // Don't call scheduleRound0 yet. 179 // We do that upon Start(). 180 cs.reconstructLastCommit(state) 181 cs.BaseService = *service.NewBaseService(nil, "ConsensusState", cs) 182 for _, option := range options { 183 option(cs) 184 } 185 return cs 186 } 187 188 // ---------------------------------------- 189 // Public interface 190 191 // SetLogger implements Service. 192 func (cs *ConsensusState) SetLogger(l *slog.Logger) { 193 cs.BaseService.Logger = l 194 cs.timeoutTicker.SetLogger(l) 195 } 196 197 // SetEventSwitch sets event bus. 198 func (cs *ConsensusState) SetEventSwitch(evsw events.EventSwitch) { 199 cs.evsw = evsw 200 cs.blockExec.SetEventSwitch(evsw) 201 } 202 203 // String returns a string. 204 func (cs *ConsensusState) String() string { 205 // better not to access shared variables 206 return fmt.Sprintf("ConsensusState") // (H:%v R:%v S:%v", cs.Height, cs.Round, cs.Step) 207 } 208 209 // GetConfig returns a copy of the chain state. 210 func (cs *ConsensusState) GetConfigDeepCopy() *cnscfg.ConsensusConfig { 211 cs.mtx.RLock() 212 defer cs.mtx.RUnlock() 213 return amino.DeepCopy(cs.config).(*cnscfg.ConsensusConfig) 214 } 215 216 // GetState returns a copy of the chain state. 217 func (cs *ConsensusState) GetState() sm.State { 218 cs.mtx.RLock() 219 defer cs.mtx.RUnlock() 220 return cs.state.Copy() 221 } 222 223 // GetLastHeight returns the last height committed. 224 // If there were no blocks, returns 0. 225 func (cs *ConsensusState) GetLastHeight() int64 { 226 cs.mtx.RLock() 227 defer cs.mtx.RUnlock() 228 return cs.RoundState.Height - 1 229 } 230 231 // GetRoundState returns a shallow copy of the internal consensus state. 232 func (cs *ConsensusState) GetRoundState() *cstypes.RoundState { 233 cs.mtx.RLock() 234 rs := cs.RoundState // copy 235 cs.mtx.RUnlock() 236 return &rs 237 } 238 239 // GetRoundStateDeepCopy returns a deep copy of the internal consensus state. 240 func (cs *ConsensusState) GetRoundStateDeepCopy() *cstypes.RoundState { 241 cs.mtx.RLock() 242 defer cs.mtx.RUnlock() 243 rs := amino.DeepCopy(cs.RoundState).(cstypes.RoundState) 244 return &rs 245 } 246 247 // GetRoundStateSimple returns a simplified representation, RoundStateSimple. 248 func (cs *ConsensusState) GetRoundStateSimple() cstypes.RoundStateSimple { 249 cs.mtx.RLock() 250 defer cs.mtx.RUnlock() 251 return cs.RoundState.RoundStateSimple() 252 } 253 254 func (cs *ConsensusState) GetHRS() cstypes.HRS { 255 cs.mtx.RLock() 256 defer cs.mtx.RUnlock() 257 return cs.RoundState.GetHRS() 258 } 259 260 // GetValidators returns a copy of the current validators. 261 func (cs *ConsensusState) GetValidators() (int64, []*types.Validator) { 262 cs.mtx.RLock() 263 defer cs.mtx.RUnlock() 264 return cs.state.LastBlockHeight, cs.state.Validators.Copy().Validators 265 } 266 267 // SetPrivValidator sets the private validator account for signing votes. 268 func (cs *ConsensusState) SetPrivValidator(priv types.PrivValidator) { 269 cs.mtx.Lock() 270 cs.privValidator = priv 271 cs.mtx.Unlock() 272 } 273 274 // SetTimeoutTicker sets the local timer. It may be useful to overwrite for testing. 275 func (cs *ConsensusState) SetTimeoutTicker(timeoutTicker TimeoutTicker) { 276 cs.mtx.Lock() 277 cs.timeoutTicker = timeoutTicker 278 cs.mtx.Unlock() 279 } 280 281 // LoadCommit loads the commit for a given height. 282 func (cs *ConsensusState) LoadCommit(height int64) *types.Commit { 283 cs.mtx.RLock() 284 defer cs.mtx.RUnlock() 285 if height == cs.blockStore.Height() { 286 return cs.blockStore.LoadSeenCommit(height) 287 } 288 return cs.blockStore.LoadBlockCommit(height) 289 } 290 291 // OnStart implements service.Service. 292 // It loads the latest state via the WAL, and starts the timeout and receive routines. 293 func (cs *ConsensusState) OnStart() error { 294 cs.done = make(chan struct{}) 295 296 if err := cs.evsw.Start(); err != nil && !goerrors.Is(err, service.ErrAlreadyStarted) { 297 return err 298 } 299 300 // we may set the WAL in testing before calling Start, 301 // so only OpenWAL if its still the walm.NopWAL 302 if _, ok := cs.wal.(walm.NopWAL); ok && !cs.walDisabled { 303 walFile := cs.config.WalFile() 304 wal, err := cs.OpenWAL(walFile) 305 if err != nil { 306 cs.Logger.Error("Error loading ConsensusState wal", "err", err.Error()) 307 return err 308 } 309 cs.wal = wal 310 } 311 312 // we need the timeoutRoutine for replay so 313 // we don't block on the tick chan. 314 // NOTE: we will get a build up of garbage go routines 315 // firing on the tockChan until the receiveRoutine is started 316 // to deal with them (by that point, at most one will be valid) 317 if err := cs.timeoutTicker.Start(); err != nil { 318 return err 319 } 320 321 // we may have lost some votes if the process crashed 322 // reload from consensus log to catchup 323 if cs.doWALCatchup { 324 if err := cs.catchupReplay(cs.Height); err != nil { 325 // don't try to recover from data corruption error 326 if walm.IsDataCorruptionError(err) { 327 cs.Logger.Error("Encountered corrupt WAL file", "err", err.Error()) 328 cs.Logger.Error("Please repair the WAL file before restarting") 329 fmt.Println(`You can attempt to repair the WAL as follows: 330 331 ---- 332 WALFILE=~/.tendermint/data/cs.wal/wal 333 cp $WALFILE ${WALFILE}.bak # backup the file 334 go run scripts/wal2json/main.go $WALFILE > wal.json # this will panic, but can be ignored 335 rm $WALFILE # remove the corrupt file 336 go run scripts/json2wal/main.go wal.json $WALFILE # rebuild the file without corruption 337 ----`) 338 339 return err 340 } 341 342 cs.Logger.Error("Error on catchup replay. Proceeding to start ConsensusState anyway", "err", err.Error()) 343 // NOTE: if we ever do return an error here, 344 // make sure to stop the timeoutTicker 345 } 346 } 347 348 // now start the receiveRoutine 349 go cs.receiveRoutine(0) 350 351 // schedule the first round! 352 // use GetRoundState so we don't race the receiveRoutine for access 353 cs.scheduleRound0(cs.GetRoundState()) 354 355 return nil 356 } 357 358 // Useful for testing. 359 func (cs *ConsensusState) StartWithoutWALCatchup() { 360 cs.doWALCatchup = false // TODO not safe. 361 cs.Start() 362 } 363 364 // OnStop implements service.Service. 365 func (cs *ConsensusState) OnStop() { 366 cs.evsw.Stop() 367 cs.timeoutTicker.Stop() 368 // WAL is stopped in receiveRoutine. 369 } 370 371 // Wait waits for the the main routine to return. 372 // NOTE: be sure to Stop() the event switch and drain 373 // any event listeners or this may deadlock 374 func (cs *ConsensusState) Wait() { 375 if cs.done != nil { 376 <-cs.done 377 } 378 } 379 380 // OpenWAL opens a file to log all consensus messages and timeouts for deterministic accountability 381 func (cs *ConsensusState) OpenWAL(walFile string) (walm.WAL, error) { 382 wal, err := walm.NewWAL(walFile, maxMsgSize) 383 if err != nil { 384 cs.Logger.Error("Failed to open WAL for consensus state", "wal", walFile, "err", err) 385 return nil, err 386 } 387 wal.SetLogger(cs.Logger.With("wal", walFile)) 388 if err := wal.Start(); err != nil { 389 return nil, err 390 } 391 return wal, nil 392 } 393 394 // ------------------------------------------------------------ 395 // Public interface for passing messages into the consensus state, possibly causing a state transition. 396 // If peerID == "", the msg is considered internal. 397 // Messages are added to the appropriate queue (peer or internal). 398 // If the queue is full, the function may block. 399 // TODO: should these return anything or let callers just use events? 400 401 // AddVote inputs a vote. 402 func (cs *ConsensusState) AddVote(vote *types.Vote, peerID p2p.ID) (added bool, err error) { 403 if peerID == "" { 404 cs.internalMsgQueue <- msgInfo{&VoteMessage{vote}, ""} 405 } else { 406 cs.peerMsgQueue <- msgInfo{&VoteMessage{vote}, peerID} 407 } 408 409 // TODO: wait for event?! 410 return false, nil 411 } 412 413 // SetProposal inputs a proposal. 414 func (cs *ConsensusState) SetProposal(proposal *types.Proposal, peerID p2p.ID) error { 415 if peerID == "" { 416 cs.internalMsgQueue <- msgInfo{&ProposalMessage{proposal}, ""} 417 } else { 418 cs.peerMsgQueue <- msgInfo{&ProposalMessage{proposal}, peerID} 419 } 420 421 // TODO: wait for event?! 422 return nil 423 } 424 425 // AddProposalBlockPart inputs a part of the proposal block. 426 func (cs *ConsensusState) AddProposalBlockPart(height int64, round int, part *types.Part, peerID p2p.ID) error { 427 if peerID == "" { 428 cs.internalMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, ""} 429 } else { 430 cs.peerMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, peerID} 431 } 432 433 // TODO: wait for event?! 434 return nil 435 } 436 437 // SetProposalAndBlock inputs the proposal and all block parts. 438 func (cs *ConsensusState) SetProposalAndBlock(proposal *types.Proposal, block *types.Block, parts *types.PartSet, peerID p2p.ID) error { 439 if err := cs.SetProposal(proposal, peerID); err != nil { 440 return err 441 } 442 for i := 0; i < parts.Total(); i++ { 443 part := parts.GetPart(i) 444 if err := cs.AddProposalBlockPart(proposal.Height, proposal.Round, part, peerID); err != nil { 445 return err 446 } 447 } 448 return nil 449 } 450 451 // ------------------------------------------------------------ 452 // internal functions for managing the state 453 454 func (cs *ConsensusState) updateHeight(height int64) { 455 cs.Height = height 456 } 457 458 func (cs *ConsensusState) updateRoundStep(round int, step cstypes.RoundStepType) { 459 cs.Round = round 460 cs.Step = step 461 } 462 463 // enterNewRound(height, 0) at cs.StartTime. 464 func (cs *ConsensusState) scheduleRound0(rs *cstypes.RoundState) { 465 // cs.Logger.Info("scheduleRound0", "now", tmtime.Now(), "startTime", cs.StartTime) 466 sleepDuration := rs.StartTime.Sub(tmtime.Now()) 467 cs.scheduleTimeout(sleepDuration, rs.Height, 0, cstypes.RoundStepNewHeight) 468 } 469 470 // Attempt to schedule a timeout (by sending timeoutInfo on the tickChan) 471 func (cs *ConsensusState) scheduleTimeout(duration time.Duration, height int64, round int, step cstypes.RoundStepType) { 472 cs.timeoutTicker.ScheduleTimeout(timeoutInfo{duration, height, round, step}) 473 } 474 475 // send a msg into the receiveRoutine regarding our own proposal, block part, or vote 476 func (cs *ConsensusState) sendInternalMessage(mi msgInfo) { 477 select { 478 case cs.internalMsgQueue <- mi: 479 default: 480 // NOTE: using the go-routine means our votes can 481 // be processed out of order. 482 // TODO: use CList here for strict determinism and 483 // attempt push to internalMsgQueue in receiveRoutine 484 cs.Logger.Info("Internal msg queue is full. Using a go-routine") 485 go func() { cs.internalMsgQueue <- mi }() 486 } 487 } 488 489 // Reconstruct LastCommit from SeenCommit, which we saved along with the block, 490 // (which happens even before saving the state) 491 func (cs *ConsensusState) reconstructLastCommit(state sm.State) { 492 if state.LastBlockHeight == 0 { 493 return 494 } 495 seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) 496 lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastValidators) 497 if !lastPrecommits.HasTwoThirdsMajority() { 498 panic("Failed to reconstruct LastCommit: Does not have +2/3 maj") 499 } 500 cs.LastCommit = lastPrecommits 501 } 502 503 // Updates ConsensusState and increments height to match that of state. 504 // The round becomes 0 and cs.Step becomes cstypes.RoundStepNewHeight. 505 func (cs *ConsensusState) updateToState(state sm.State) { 506 if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight { 507 panic(fmt.Sprintf("updateToState() expected state height of %v but found %v", 508 cs.Height, state.LastBlockHeight)) 509 } 510 if !cs.state.IsEmpty() && cs.state.LastBlockHeight+1 != cs.Height { 511 // This might happen when someone else is mutating cs.state. 512 // Someone forgot to pass in state.Copy() somewhere?! 513 panic(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v", 514 cs.state.LastBlockHeight+1, cs.Height)) 515 } 516 517 // If state isn't further out than cs.state, just ignore. 518 // This happens when SwitchToConsensus() is called in the reactor. 519 // We don't want to reset e.g. the Votes, but we still want to 520 // signal the new round step, because other services (eg. txNotifier) 521 // depend on having an up-to-date peer state! 522 if !cs.state.IsEmpty() && (state.LastBlockHeight <= cs.state.LastBlockHeight) { 523 cs.Logger.Info("Ignoring updateToState()", "newHeight", state.LastBlockHeight+1, "oldHeight", cs.state.LastBlockHeight+1) 524 cs.newStep() 525 return 526 } 527 528 // Reset fields based on state. 529 validators := state.Validators 530 lastPrecommits := (*types.VoteSet)(nil) 531 if cs.CommitRound > -1 && cs.Votes != nil { 532 if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() { 533 panic("updateToState(state) called but last Precommit round didn't have +2/3") 534 } 535 lastPrecommits = cs.Votes.Precommits(cs.CommitRound) 536 } 537 538 // Next desired block height 539 height := state.LastBlockHeight + 1 540 541 // RoundState fields 542 cs.updateHeight(height) 543 cs.updateRoundStep(0, cstypes.RoundStepNewHeight) 544 if cs.CommitTime.IsZero() { 545 // "Now" makes it easier to sync up dev nodes. 546 // We add timeoutCommit to allow transactions 547 // to be gathered for the first block. 548 // And alternative solution that relies on clocks: 549 // cs.StartTime = state.LastBlockTime.Add(timeoutCommit) 550 cs.StartTime = cs.config.Commit(tmtime.Now()) 551 } else { 552 cs.StartTime = cs.config.Commit(cs.CommitTime) 553 } 554 555 cs.Validators = validators 556 cs.Proposal = nil 557 cs.ProposalBlock = nil 558 cs.ProposalBlockParts = nil 559 cs.LockedRound = -1 560 cs.LockedBlock = nil 561 cs.LockedBlockParts = nil 562 cs.ValidRound = -1 563 cs.ValidBlock = nil 564 cs.ValidBlockParts = nil 565 cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) 566 cs.CommitRound = -1 567 cs.LastCommit = lastPrecommits 568 cs.LastValidators = state.LastValidators 569 cs.TriggeredTimeoutPrecommit = false 570 571 cs.state = state 572 573 // Finally, broadcast RoundState 574 cs.newStep() 575 } 576 577 func (cs *ConsensusState) newStep() { 578 nrsInfo := newRoundStepInfo{HRS: cs.RoundState.GetHRS()} 579 cs.wal.Write(nrsInfo) 580 cs.nSteps++ 581 // newStep is called by updateToState in NewConsensusState before the event switch is set! 582 if cs.evsw != nil { 583 cs.evsw.FireEvent(cs.EventNewRoundStep()) 584 } 585 } 586 587 // ----------------------------------------- 588 // the main go routines 589 590 // receiveRoutine handles messages which may cause state transitions. 591 // it's argument (n) is the number of messages to process before exiting - use 0 to run forever 592 // It keeps the RoundState and is the only thing that updates it. 593 // Updates (state transitions) happen on timeouts, complete proposals, and 2/3 majorities. 594 // ConsensusState must be locked before any internal state is updated. 595 func (cs *ConsensusState) receiveRoutine(maxSteps int) { 596 onExit := func() { 597 // NOTE: the internalMsgQueue may have signed messages from our 598 // priv_val that haven't hit the WAL, but its ok because 599 // priv_val tracks LastSig 600 601 // close wal now that we're done writing to it 602 cs.wal.Stop() 603 cs.wal.Wait() 604 605 close(cs.done) 606 } 607 608 defer func() { 609 if r := recover(); r != nil { 610 cs.Logger.Error("CONSENSUS FAILURE!!!", "err", r, "stack", string(debug.Stack())) 611 // stop gracefully 612 // 613 // NOTE: We most probably shouldn't be running any further when there is 614 // some unexpected panic. Some unknown error happened, and so we don't 615 // know if that will result in the validator signing an invalid thing. It 616 // might be worthwhile to explore a mechanism for manual resuming via 617 // some console or secure RPC system, but for now, halting the chain upon 618 // unexpected consensus bugs sounds like the better option. 619 onExit() 620 } else { 621 onExit() 622 } 623 }() 624 625 for { 626 if maxSteps > 0 { 627 panic("maxSteps not supported") // XXX 628 } 629 rs := cs.RoundState 630 var mi msgInfo 631 632 select { 633 case <-cs.txNotifier.TxsAvailable(): 634 cs.handleTxsAvailable() 635 case mi = <-cs.peerMsgQueue: 636 cs.wal.Write(mi) 637 // handles proposals, block parts, votes 638 // may generate internal events (votes, complete proposals, 2/3 majorities) 639 cs.handleMsg(mi) 640 case mi = <-cs.internalMsgQueue: 641 err := cs.wal.WriteSync(mi) // NOTE: fsync 642 if err != nil { 643 panic(fmt.Sprintf("Failed to write %v msg to consensus wal due to %v. Check your FS and restart the node", mi, err)) 644 } 645 646 if _, ok := mi.Msg.(*VoteMessage); ok { 647 // we actually want to simulate failing during 648 // the previous WriteSync, but this isn't easy to do. 649 // Equivalent would be to fail here and manually remove 650 // some bytes from the end of the wal. 651 fail.Fail() // XXX 652 } 653 654 // handles proposals, block parts, votes 655 cs.handleMsg(mi) 656 case ti := <-cs.timeoutTicker.Chan(): // tockChan: 657 cs.wal.Write(ti) 658 // if the timeout is relevant to the rs 659 // go to the next step 660 cs.handleTimeout(ti, rs) 661 case <-cs.Quit(): 662 return 663 } 664 } 665 } 666 667 // state transitions on complete-proposal, 2/3-any, 2/3-one 668 func (cs *ConsensusState) handleMsg(mi msgInfo) { 669 cs.mtx.Lock() 670 defer cs.mtx.Unlock() 671 672 var ( 673 added bool 674 err error 675 ) 676 msg, peerID := mi.Msg, mi.PeerID 677 switch msg := msg.(type) { 678 case *ProposalMessage: 679 // will not cause transition. 680 // once proposal is set, we can receive block parts 681 err = cs.setProposal(msg.Proposal) 682 case *BlockPartMessage: 683 // if the proposal is complete, we'll enterPrevote or tryFinalizeCommit 684 added, err = cs.addProposalBlockPart(msg, peerID) 685 if added { 686 cs.statsMsgQueue <- mi 687 } 688 689 if err != nil && msg.Round != cs.Round { 690 cs.Logger.Debug("Received block part from wrong round", "height", cs.Height, "csRound", cs.Round, "blockRound", msg.Round) 691 err = nil 692 } 693 case *VoteMessage: 694 // attempt to add the vote and dupeout the validator if its a duplicate signature 695 // if the vote gives us a 2/3-any or 2/3-one, we transition 696 added, err = cs.tryAddVote(msg.Vote, peerID) 697 if added { 698 cs.statsMsgQueue <- mi 699 } 700 701 // if err == ErrAddingVote { 702 // TODO: punish peer 703 // We probably don't want to stop the peer here. The vote does not 704 // necessarily comes from a malicious peer but can be just broadcasted by 705 // a typical peer. 706 // https://github.com/tendermint/classic/issues/1281 707 // } 708 709 // NOTE: the vote is broadcast to peers by the reactor listening 710 // for vote events 711 712 // TODO: If rs.Height == vote.Height && rs.Round < vote.Round, 713 // the peer is sending us CatchupCommit precommits. 714 // We could make note of this and help filter in broadcastHasVoteMessage(). 715 default: 716 cs.Logger.Error("Unknown msg type", "type", reflect.TypeOf(msg)) 717 return 718 } 719 720 if err != nil { //nolint:staticcheck 721 // Causes TestReactorValidatorSetChanges to timeout 722 // https://github.com/tendermint/classic/issues/3406 723 // cs.Logger.Error("Error with msg", "height", cs.Height, "round", cs.Round, 724 // "peer", peerID, "err", err, "msg", msg) 725 } 726 } 727 728 func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) { 729 cs.Logger.Debug("Received tock", "timeout", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) 730 731 // timeouts must be for current height, round, step 732 if ti.Height != rs.Height || ti.Round < rs.Round || (ti.Round == rs.Round && ti.Step < rs.Step) { 733 cs.Logger.Debug("Ignoring tock because we're ahead", "height", rs.Height, "round", rs.Round, "step", rs.Step) 734 return 735 } 736 737 // the timeout will now cause a state transition 738 cs.mtx.Lock() 739 defer cs.mtx.Unlock() 740 741 switch ti.Step { 742 case cstypes.RoundStepNewHeight: 743 // NewRound event fired from enterNewRound. 744 // XXX: should we fire timeout here (for timeout commit)? 745 cs.enterNewRound(ti.Height, 0) 746 case cstypes.RoundStepNewRound: 747 cs.enterPropose(ti.Height, 0) 748 case cstypes.RoundStepPropose: 749 cs.evsw.FireEvent(cstypes.EventTimeoutPropose{cs.RoundState.GetHRS()}) 750 cs.enterPrevote(ti.Height, ti.Round) 751 case cstypes.RoundStepPrevoteWait: 752 cs.evsw.FireEvent(cstypes.EventTimeoutWait{cs.RoundState.GetHRS()}) 753 cs.enterPrecommit(ti.Height, ti.Round) 754 case cstypes.RoundStepPrecommitWait: 755 cs.evsw.FireEvent(cstypes.EventTimeoutWait{cs.RoundState.GetHRS()}) 756 cs.enterPrecommit(ti.Height, ti.Round) 757 cs.enterNewRound(ti.Height, ti.Round+1) 758 default: 759 panic(fmt.Sprintf("Invalid timeout step: %v", ti.Step)) 760 } 761 } 762 763 func (cs *ConsensusState) handleTxsAvailable() { 764 cs.mtx.Lock() 765 defer cs.mtx.Unlock() 766 767 // We only need to do this for round 0. 768 if cs.Round != 0 { 769 return 770 } 771 772 switch cs.Step { 773 case cstypes.RoundStepNewHeight: // timeoutCommit phase 774 if cs.needProofBlock(cs.Height) { 775 // enterPropose will be called by enterNewRound 776 return 777 } 778 779 // +1ms to ensure RoundStepNewRound timeout always happens after RoundStepNewHeight 780 timeoutCommit := cs.StartTime.Sub(tmtime.Now()) + 1*time.Millisecond 781 cs.scheduleTimeout(timeoutCommit, cs.Height, 0, cstypes.RoundStepNewRound) 782 case cstypes.RoundStepNewRound: // after timeoutCommit 783 cs.enterPropose(cs.Height, 0) 784 } 785 } 786 787 // ----------------------------------------------------------------------------- 788 // State functions 789 // Used internally by handleTimeout and handleMsg to make state transitions 790 791 // Enter: `timeoutNewHeight` by startTime (commitTime+timeoutCommit), 792 // 793 // or, if SkipTimeoutCommit==true, after receiving all precommits from (height,round-1) 794 // 795 // Enter: `timeoutPrecommits` after any +2/3 precommits from (height,round-1) 796 // Enter: +2/3 precommits for nil at (height,round-1) 797 // Enter: +2/3 prevotes any or +2/3 precommits for block or any from (height, round) 798 // NOTE: cs.StartTime was already set for height. 799 func (cs *ConsensusState) enterNewRound(height int64, round int) { 800 logger := cs.Logger.With("height", height, "round", round) 801 802 if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != cstypes.RoundStepNewHeight) { 803 logger.Debug(fmt.Sprintf("enterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 804 return 805 } 806 807 if now := tmtime.Now(); cs.StartTime.After(now) { 808 logger.Info("Need to set a buffer and log message here for sanity.", "startTime", cs.StartTime, "now", now) 809 } 810 811 logger.Info(fmt.Sprintf("enterNewRound(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 812 813 // Increment validators if necessary 814 validators := cs.Validators 815 if cs.Round < round { 816 validators = validators.Copy() 817 validators.IncrementProposerPriority(round - cs.Round) 818 } 819 820 // Setup new round 821 // we don't fire newStep for this step, 822 // but we fire an event, so update the round step first 823 cs.updateRoundStep(round, cstypes.RoundStepNewRound) 824 cs.Validators = validators 825 if round == 0 { 826 // We've already reset these upon new height, 827 // and meanwhile we might have received a proposal 828 // for round 0. 829 } else { 830 logger.Info("Resetting Proposal info") 831 cs.Proposal = nil 832 cs.ProposalBlock = nil 833 cs.ProposalBlockParts = nil 834 } 835 cs.Votes.SetRound(round + 1) // also track next round (round+1) to allow round-skipping 836 cs.TriggeredTimeoutPrecommit = false 837 838 cs.evsw.FireEvent(cs.EventNewRound()) 839 840 // Wait for txs to be available in the mempool 841 // before we enterPropose in round 0. If the last block changed the app hash, 842 // we may need an empty "proof" block, and enterPropose immediately. 843 waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height) 844 if waitForTxs { 845 if cs.config.CreateEmptyBlocksInterval > 0 { 846 cs.scheduleTimeout(cs.config.CreateEmptyBlocksInterval, height, round, 847 cstypes.RoundStepNewRound) 848 } else { 849 // wait until mempool pings us. 850 } 851 } else { 852 cs.enterPropose(height, round) 853 } 854 } 855 856 // needProofBlock returns true on the first height (so the genesis app hash is signed right away) 857 // and where the last block (height-1) caused the app hash to change 858 func (cs *ConsensusState) needProofBlock(height int64) bool { 859 if height == 1 { 860 return true 861 } 862 863 lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1) 864 return !bytes.Equal(cs.state.AppHash, lastBlockMeta.Header.AppHash) 865 } 866 867 // Enter (CreateEmptyBlocks): from enterNewRound(height,round) 868 // Enter (CreateEmptyBlocks, CreateEmptyBlocksInterval > 0 ): after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval 869 // Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool 870 func (cs *ConsensusState) enterPropose(height int64, round int) { 871 logger := cs.Logger.With("height", height, "round", round) 872 873 if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPropose <= cs.Step) { 874 logger.Debug(fmt.Sprintf("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 875 return 876 } 877 logger.Info(fmt.Sprintf("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 878 879 defer func() { 880 // Done enterPropose: 881 cs.updateRoundStep(round, cstypes.RoundStepPropose) 882 cs.newStep() 883 884 // If we have the whole proposal + POL, then goto Prevote now. 885 // else, we'll enterPrevote when the rest of the proposal is received (in AddProposalBlockPart), 886 // or else after timeoutPropose 887 if cs.isProposalComplete() { 888 cs.enterPrevote(height, cs.Round) 889 } 890 }() 891 892 // If we don't get the proposal and all block parts quick enough, enterPrevote 893 cs.scheduleTimeout(cs.config.Propose(round), height, round, cstypes.RoundStepPropose) 894 895 // Nothing more to do if we're not a validator 896 if cs.privValidator == nil { 897 logger.Debug("This node is not a validator") 898 return 899 } 900 901 // if not a validator, we're done 902 address := cs.privValidator.GetPubKey().Address() 903 if !cs.Validators.HasAddress(address) { 904 logger.Debug("This node is not a validator", "addr", address, "vals", cs.Validators) 905 return 906 } 907 logger.Debug("This node is a validator") 908 909 if cs.isProposer(address) { 910 logger.Info("enterPropose: Our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator) 911 cs.decideProposal(height, round) 912 } else { 913 logger.Info("enterPropose: Not our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator) 914 } 915 } 916 917 func (cs *ConsensusState) isProposer(address crypto.Address) bool { 918 return cs.Validators.GetProposer().Address == address 919 } 920 921 func (cs *ConsensusState) defaultDecideProposal(height int64, round int) { 922 var block *types.Block 923 var blockParts *types.PartSet 924 925 // Decide on block 926 if cs.ValidBlock != nil { 927 // If there is valid block, choose that. 928 block, blockParts = cs.ValidBlock, cs.ValidBlockParts 929 } else { 930 // Create a new proposal block from state/txs from the mempool. 931 block, blockParts = cs.createProposalBlock() 932 if block == nil { // on error 933 return 934 } 935 } 936 937 // Flush the WAL. Otherwise, we may not recompute the same proposal to sign, and the privValidator will refuse to sign anything. 938 cs.wal.FlushAndSync() 939 940 // Make proposal 941 propBlockId := types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()} 942 proposal := types.NewProposal(height, round, cs.ValidRound, propBlockId) 943 if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal); err == nil { 944 // send proposal and block parts on internal msg queue 945 cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""}) 946 for i := 0; i < blockParts.Total(); i++ { 947 part := blockParts.GetPart(i) 948 cs.sendInternalMessage(msgInfo{&BlockPartMessage{cs.Height, cs.Round, part}, ""}) 949 } 950 951 cs.Logger.Info( 952 "Signed proposal", 953 "height", height, 954 "round", round, 955 "proposal block ID", proposal.BlockID.String(), 956 "proposal round", proposal.POLRound, 957 "proposal timestamp", proposal.Timestamp.Unix(), 958 ) 959 } else if !cs.replayMode { 960 cs.Logger.Error("enterPropose: Error signing proposal", "height", height, "round", round, "err", err) 961 } 962 } 963 964 // Returns true if the proposal block is complete && 965 // (if POLRound was proposed, we have +2/3 prevotes from there). 966 func (cs *ConsensusState) isProposalComplete() bool { 967 if cs.Proposal == nil || cs.ProposalBlock == nil { 968 return false 969 } 970 // we have the proposal. if there's a POLRound, 971 // make sure we have the prevotes from it too 972 if cs.Proposal.POLRound < 0 { 973 return true 974 } 975 // if this is false the proposer is lying or we haven't received the POL yet 976 return cs.Votes.Prevotes(cs.Proposal.POLRound).HasTwoThirdsMajority() 977 } 978 979 // Create the next block to propose and return it. 980 // We really only need to return the parts, but the block 981 // is returned for convenience so we can log the proposal block. 982 // Returns nil block upon error. 983 // NOTE: keep it side-effect free for clarity. 984 func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts *types.PartSet) { 985 var commit *types.Commit 986 switch { 987 case cs.Height == 1: 988 // We're creating a proposal for the first block. 989 // The commit is empty, but not nil. 990 commit = types.NewCommit(types.BlockID{}, nil) 991 case cs.LastCommit.HasTwoThirdsMajority(): 992 // Make the commit from LastCommit 993 commit = cs.LastCommit.MakeCommit() 994 default: 995 // This shouldn't happen. 996 cs.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block.") 997 return 998 } 999 1000 if telemetry.MetricsEnabled() { 1001 startTime := time.Now() 1002 defer func(t time.Time) { 1003 metrics.BuildBlockTimer.Record(context.Background(), time.Since(t).Milliseconds()) 1004 }(startTime) 1005 } 1006 1007 proposerAddr := cs.privValidator.GetPubKey().Address() 1008 return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr) 1009 } 1010 1011 // Enter: `timeoutPropose` after entering Propose. 1012 // Enter: proposal block and POL is ready. 1013 // Prevote for LockedBlock if we're locked, or ProposalBlock if valid. 1014 // Otherwise vote nil. 1015 func (cs *ConsensusState) enterPrevote(height int64, round int) { 1016 if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevote <= cs.Step) { 1017 cs.Logger.Debug(fmt.Sprintf("enterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 1018 return 1019 } 1020 1021 defer func() { 1022 // Done enterPrevote: 1023 cs.updateRoundStep(round, cstypes.RoundStepPrevote) 1024 cs.newStep() 1025 }() 1026 1027 cs.Logger.Info(fmt.Sprintf("enterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 1028 1029 // Sign and broadcast vote as necessary 1030 cs.doPrevote(height, round) 1031 1032 // Once `addVote` hits any +2/3 prevotes, we will go to PrevoteWait 1033 // (so we have more time to try and collect +2/3 prevotes for a single block) 1034 } 1035 1036 func (cs *ConsensusState) defaultDoPrevote(height int64, round int) { 1037 logger := cs.Logger.With("height", height, "round", round) 1038 1039 // If a block is locked, prevote that. 1040 if cs.LockedBlock != nil { 1041 logger.Info("enterPrevote: Block was locked") 1042 cs.signAddVote(types.PrevoteType, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header()) 1043 return 1044 } 1045 1046 // If ProposalBlock is nil, prevote nil. 1047 if cs.ProposalBlock == nil { 1048 logger.Info("enterPrevote: ProposalBlock is nil") 1049 cs.signAddVote(types.PrevoteType, nil, types.PartSetHeader{}) 1050 return 1051 } 1052 1053 // Validate proposal block 1054 err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock) 1055 if err != nil { 1056 // ProposalBlock is invalid, prevote nil. 1057 logger.Error("enterPrevote: ProposalBlock is invalid", "err", err) 1058 cs.signAddVote(types.PrevoteType, nil, types.PartSetHeader{}) 1059 return 1060 } 1061 1062 // Prevote cs.ProposalBlock 1063 // NOTE: the proposal signature is validated when it is received, 1064 // and the proposal block parts are validated as they are received (against the merkle hash in the proposal) 1065 logger.Info("enterPrevote: ProposalBlock is valid") 1066 cs.signAddVote(types.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header()) 1067 } 1068 1069 // Enter: any +2/3 prevotes at next round. 1070 func (cs *ConsensusState) enterPrevoteWait(height int64, round int) { 1071 logger := cs.Logger.With("height", height, "round", round) 1072 1073 if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevoteWait <= cs.Step) { 1074 logger.Debug(fmt.Sprintf("enterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 1075 return 1076 } 1077 if !cs.Votes.Prevotes(round).HasTwoThirdsAny() { 1078 panic(fmt.Sprintf("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round)) 1079 } 1080 logger.Info(fmt.Sprintf("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 1081 1082 defer func() { 1083 // Done enterPrevoteWait: 1084 cs.updateRoundStep(round, cstypes.RoundStepPrevoteWait) 1085 cs.newStep() 1086 }() 1087 1088 // Wait for some more prevotes; enterPrecommit 1089 cs.scheduleTimeout(cs.config.Prevote(round), height, round, cstypes.RoundStepPrevoteWait) 1090 } 1091 1092 // Enter: `timeoutPrevote` after any +2/3 prevotes. 1093 // Enter: `timeoutPrecommit` after any +2/3 precommits. 1094 // Enter: +2/3 precomits for block or nil. 1095 // Lock & precommit the ProposalBlock if we have enough prevotes for it (a POL in this round) 1096 // else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil, 1097 // else, precommit nil otherwise. 1098 func (cs *ConsensusState) enterPrecommit(height int64, round int) { 1099 logger := cs.Logger.With("height", height, "round", round) 1100 1101 if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommit <= cs.Step) { 1102 logger.Debug(fmt.Sprintf("enterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 1103 return 1104 } 1105 1106 logger.Info(fmt.Sprintf("enterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 1107 1108 defer func() { 1109 // Done enterPrecommit: 1110 cs.updateRoundStep(round, cstypes.RoundStepPrecommit) 1111 cs.newStep() 1112 }() 1113 1114 // check for a polka 1115 blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority() 1116 1117 // If we don't have a polka, we must precommit nil. 1118 if !ok { 1119 if cs.LockedBlock != nil { 1120 logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit while we're locked. Precommitting nil") 1121 } else { 1122 logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit. Precommitting nil.") 1123 } 1124 cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{}) 1125 return 1126 } 1127 1128 // At this point +2/3 prevoted for a particular block or nil. 1129 cs.evsw.FireEvent(cstypes.EventPolka{cs.RoundState.GetHRS()}) 1130 1131 // the latest POLRound should be this round. 1132 polRound, _ := cs.Votes.POLInfo() 1133 if polRound < round { 1134 panic(fmt.Sprintf("This POLRound should be %v but got %v", round, polRound)) 1135 } 1136 1137 // +2/3 prevoted nil. Unlock and precommit nil. 1138 if len(blockID.Hash) == 0 { 1139 if cs.LockedBlock == nil { 1140 logger.Info("enterPrecommit: +2/3 prevoted for nil.") 1141 } else { 1142 logger.Info("enterPrecommit: +2/3 prevoted for nil. Unlocking") 1143 cs.LockedRound = -1 1144 cs.LockedBlock = nil 1145 cs.LockedBlockParts = nil 1146 cs.evsw.FireEvent(cstypes.EventUnlock{cs.RoundState.GetHRS()}) 1147 } 1148 cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{}) 1149 return 1150 } 1151 1152 // At this point, +2/3 prevoted for a particular block. 1153 1154 // If we're already locked on that block, precommit it, and update the LockedRound 1155 if cs.LockedBlock.HashesTo(blockID.Hash) { 1156 logger.Info("enterPrecommit: +2/3 prevoted locked block. Relocking") 1157 cs.LockedRound = round 1158 cs.evsw.FireEvent(cstypes.EventRelock{cs.RoundState.GetHRS()}) 1159 cs.signAddVote(types.PrecommitType, blockID.Hash, blockID.PartsHeader) 1160 return 1161 } 1162 1163 // If +2/3 prevoted for proposal block, stage and precommit it 1164 if cs.ProposalBlock.HashesTo(blockID.Hash) { 1165 logger.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", blockID.Hash) 1166 // Validate the block. 1167 if err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock); err != nil { 1168 panic(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err)) 1169 } 1170 cs.LockedRound = round 1171 cs.LockedBlock = cs.ProposalBlock 1172 cs.LockedBlockParts = cs.ProposalBlockParts 1173 cs.evsw.FireEvent(cstypes.EventLock{cs.RoundState.GetHRS()}) 1174 cs.signAddVote(types.PrecommitType, blockID.Hash, blockID.PartsHeader) 1175 return 1176 } 1177 1178 // There was a polka in this round for a block we don't have. 1179 // Fetch that block, unlock, and precommit nil. 1180 // The +2/3 prevotes for this round is the POL for our unlock. 1181 // TODO: In the future save the POL prevotes for justification. 1182 cs.LockedRound = -1 1183 cs.LockedBlock = nil 1184 cs.LockedBlockParts = nil 1185 if !cs.ProposalBlockParts.HasHeader(blockID.PartsHeader) { 1186 cs.ProposalBlock = nil 1187 cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader) 1188 } 1189 cs.evsw.FireEvent(cstypes.EventUnlock{cs.RoundState.GetHRS()}) 1190 cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{}) 1191 } 1192 1193 // Enter: any +2/3 precommits for next round. 1194 func (cs *ConsensusState) enterPrecommitWait(height int64, round int) { 1195 logger := cs.Logger.With("height", height, "round", round) 1196 1197 if cs.Height != height || round < cs.Round || (cs.Round == round && cs.TriggeredTimeoutPrecommit) { 1198 logger.Debug( 1199 fmt.Sprintf( 1200 "enterPrecommitWait(%v/%v): Invalid args. "+ 1201 "Current state is Height/Round: %v/%v, TriggeredTimeoutPrecommit:%v", 1202 height, round, cs.Height, cs.Round, cs.TriggeredTimeoutPrecommit)) 1203 return 1204 } 1205 if !cs.Votes.Precommits(round).HasTwoThirdsAny() { 1206 panic(fmt.Sprintf("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round)) 1207 } 1208 logger.Info(fmt.Sprintf("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) 1209 1210 defer func() { 1211 // Done enterPrecommitWait: 1212 cs.TriggeredTimeoutPrecommit = true 1213 cs.updateRoundStep(cs.Round, cstypes.RoundStepPrecommitWait) 1214 cs.newStep() 1215 }() 1216 1217 // Wait for some more precommits; enterNewRound 1218 cs.scheduleTimeout(cs.config.Precommit(round), height, round, cstypes.RoundStepPrecommitWait) 1219 } 1220 1221 // Enter: +2/3 precommits for block 1222 func (cs *ConsensusState) enterCommit(height int64, commitRound int) { 1223 logger := cs.Logger.With("height", height, "commitRound", commitRound) 1224 1225 if cs.Height != height || cstypes.RoundStepCommit <= cs.Step { 1226 logger.Debug(fmt.Sprintf("enterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step)) 1227 return 1228 } 1229 logger.Info(fmt.Sprintf("enterCommit(%v/%v). Current: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step)) 1230 1231 defer func() { 1232 // Done enterCommit: 1233 // keep cs.Round the same, commitRound points to the right Precommits set. 1234 cs.updateRoundStep(cs.Round, cstypes.RoundStepCommit) 1235 cs.CommitRound = commitRound 1236 cs.CommitTime = tmtime.Now() 1237 cs.newStep() 1238 1239 // Maybe finalize immediately. 1240 cs.tryFinalizeCommit(height) 1241 }() 1242 1243 blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority() 1244 if !ok { 1245 panic("RunActionCommit() expects +2/3 precommits") 1246 } 1247 1248 // The Locked* fields no longer matter. 1249 // Move them over to ProposalBlock if they match the commit hash, 1250 // otherwise they'll be cleared in updateToState. 1251 if cs.LockedBlock.HashesTo(blockID.Hash) { 1252 logger.Info("Commit is for locked block. Set ProposalBlock=LockedBlock", "blockHash", blockID.Hash) 1253 cs.ProposalBlock = cs.LockedBlock 1254 cs.ProposalBlockParts = cs.LockedBlockParts 1255 } 1256 1257 // If we don't have the block being committed, set up to get it. 1258 if !cs.ProposalBlock.HashesTo(blockID.Hash) { 1259 if !cs.ProposalBlockParts.HasHeader(blockID.PartsHeader) { 1260 logger.Info("Commit is for a block we don't know about. Set ProposalBlock=nil", "proposal", cs.ProposalBlock.Hash(), "commit", blockID.Hash) 1261 // We're getting the wrong block. 1262 // Set up ProposalBlockParts and keep waiting. 1263 cs.ProposalBlock = nil 1264 cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader) 1265 cs.evsw.FireEvent(cs.EventNewValidBlock()) 1266 } 1267 // else { 1268 // We just need to keep waiting. 1269 // } 1270 } 1271 } 1272 1273 // If we have the block AND +2/3 commits for it, finalize. 1274 func (cs *ConsensusState) tryFinalizeCommit(height int64) { 1275 logger := cs.Logger.With("height", height) 1276 1277 if cs.Height != height { 1278 panic(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height)) 1279 } 1280 1281 blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() 1282 if !ok || len(blockID.Hash) == 0 { 1283 logger.Error("Attempt to finalize failed. There was no +2/3 majority, or +2/3 was for <nil>.") 1284 return 1285 } 1286 if !cs.ProposalBlock.HashesTo(blockID.Hash) { 1287 // TODO: this happens every time if we're not a validator (ugly logs) 1288 // TODO: ^^ wait, why does it matter that we're a validator? 1289 logger.Info("Attempt to finalize failed. We don't have the commit block.", "proposal-block", cs.ProposalBlock.Hash(), "commit-block", blockID.Hash) 1290 return 1291 } 1292 1293 // go 1294 cs.finalizeCommit(height) 1295 } 1296 1297 // Increment height and goto cstypes.RoundStepNewHeight 1298 func (cs *ConsensusState) finalizeCommit(height int64) { 1299 if cs.Height != height || cs.Step != cstypes.RoundStepCommit { 1300 cs.Logger.Debug(fmt.Sprintf("finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) 1301 return 1302 } 1303 1304 blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() 1305 block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts 1306 1307 if !ok { 1308 panic(fmt.Sprintf("Cannot finalizeCommit, commit does not have two thirds majority")) 1309 } 1310 if !blockParts.HasHeader(blockID.PartsHeader) { 1311 panic(fmt.Sprintf("Expected ProposalBlockParts header to be commit header")) 1312 } 1313 if !block.HashesTo(blockID.Hash) { 1314 panic(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash")) 1315 } 1316 if err := cs.blockExec.ValidateBlock(cs.state, block); err != nil { 1317 panic(fmt.Sprintf("+2/3 committed an invalid block: %v", err)) 1318 } 1319 1320 cs.Logger.Info( 1321 "Finalizing commit of block", 1322 "root", block.AppHash, 1323 "height", block.Height, 1324 "hash", block.Hash(), 1325 "num txs", block.NumTxs, 1326 ) 1327 1328 fail.Fail() // XXX 1329 1330 // Save to blockStore. 1331 if cs.blockStore.Height() < block.Height { 1332 // NOTE: the seenCommit is local justification to commit this block, 1333 // but may differ from the LastCommit included in the next block 1334 precommits := cs.Votes.Precommits(cs.CommitRound) 1335 seenCommit := precommits.MakeCommit() 1336 cs.blockStore.SaveBlock(block, blockParts, seenCommit) 1337 } else { 1338 // Happens during replay if we already saved the block but didn't commit 1339 cs.Logger.Info("Calling finalizeCommit on already stored block", "height", block.Height) 1340 } 1341 1342 fail.Fail() // XXX 1343 1344 // Write MetaMessage{Height+1} for this height, implying that the 1345 // blockstore has saved the block for height Height. 1346 // 1347 // If we crash before writing this meta message, we will recover by 1348 // running ApplyBlock during the ABCI handshake when we restart. If we 1349 // didn't save the block to the blockstore before writing 1350 // EndHeightMessage{}, we'd have to change WAL replay -- currently it 1351 // complains about replaying for heights where a #{"h"} entry already 1352 // exists. 1353 // 1354 // Either way, the ConsensusState should not be resumed until we 1355 // successfully call ApplyBlock (ie. later here, or in Handshake after 1356 // restart). 1357 meta := walm.MetaMessage{Height: height + 1} 1358 if err := cs.wal.WriteMetaSync(meta); err != nil { // NOTE: fsync 1359 panic(fmt.Sprintf("Failed to write %v msg to consensus wal due to %v. Check your FS and restart the node", meta, err)) 1360 } 1361 1362 fail.Fail() // XXX 1363 1364 // Create a copy of the state for staging and an event cache for txs. 1365 stateCopy := cs.state.Copy() 1366 1367 // Execute and commit the block, update and save the state, and update the mempool. 1368 // NOTE The block.AppHash wont reflect these txs until the next block. 1369 var err error 1370 stateCopy, err = cs.blockExec.ApplyBlock(stateCopy, types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()}, block) 1371 if err != nil { 1372 cs.Logger.Error("Error on ApplyBlock. Did the application crash? Please restart tendermint", "err", err) 1373 err := osm.Kill() 1374 if err != nil { 1375 cs.Logger.Error("Failed to kill this process - please do so manually", "err", err) 1376 } 1377 return 1378 } 1379 1380 fail.Fail() // XXX 1381 1382 // NewHeightStep! 1383 cs.updateToState(stateCopy) 1384 1385 fail.Fail() // XXX 1386 1387 // cs.StartTime is already set. 1388 // Schedule Round0 to start soon. 1389 cs.scheduleRound0(&cs.RoundState) 1390 1391 // By here, 1392 // * cs.Height has been increment to height+1 1393 // * cs.Step is now cstypes.RoundStepNewHeight 1394 // * cs.StartTime is set to when we will start round0. 1395 } 1396 1397 // ----------------------------------------------------------------------------- 1398 1399 func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error { 1400 // Already have one 1401 // TODO: possibly catch double proposals 1402 if cs.Proposal != nil { 1403 return nil 1404 } 1405 1406 // Does not apply 1407 if proposal.Height != cs.Height || proposal.Round != cs.Round { 1408 return nil 1409 } 1410 1411 // Verify POLRound, which must be -1 or in range [0, proposal.Round). 1412 if proposal.POLRound < -1 || 1413 (proposal.POLRound >= 0 && proposal.POLRound >= proposal.Round) { 1414 return ErrInvalidProposalPOLRound 1415 } 1416 1417 // Verify signature 1418 if !cs.Validators.GetProposer().PubKey.VerifyBytes(proposal.SignBytes(cs.state.ChainID), proposal.Signature) { 1419 return ErrInvalidProposalSignature 1420 } 1421 1422 cs.Proposal = proposal 1423 // We don't update cs.ProposalBlockParts if it is already set. 1424 // This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round. 1425 // TODO: We can check if Proposal is for a different block as this is a sign of misbehavior! 1426 if cs.ProposalBlockParts == nil { 1427 cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockID.PartsHeader) 1428 } 1429 1430 cs.Logger.Info( 1431 "Received proposal", 1432 "height", proposal.Height, 1433 "round", proposal.Round, 1434 "proposal block ID", proposal.BlockID.String(), 1435 "proposal round", proposal.POLRound, 1436 "proposal timestamp", proposal.Timestamp.Unix(), 1437 ) 1438 1439 return nil 1440 } 1441 1442 // NOTE: block is not necessarily valid. 1443 // Asynchronously triggers either enterPrevote (before we timeout of propose) or tryFinalizeCommit, once we have the full block. 1444 func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.ID) (added bool, err error) { 1445 height, round, part := msg.Height, msg.Round, msg.Part 1446 1447 // Blocks might be reused, so round mismatch is OK 1448 if cs.Height != height { 1449 cs.Logger.Debug("Received block part from wrong height", "height", height, "round", round) 1450 return false, nil 1451 } 1452 1453 // We're not expecting a block part. 1454 if cs.ProposalBlockParts == nil { 1455 // NOTE: this can happen when we've gone to a higher round and 1456 // then receive parts from the previous round - not necessarily a bad peer. 1457 cs.Logger.Info("Received a block part when we're not expecting any", 1458 "height", height, "round", round, "index", part.Index, "peer", peerID) 1459 return false, nil 1460 } 1461 1462 added, err = cs.ProposalBlockParts.AddPart(part) 1463 if err != nil { 1464 return added, err 1465 } 1466 if added && cs.ProposalBlockParts.IsComplete() { 1467 // Added and completed! 1468 _, err = amino.UnmarshalSizedReader( 1469 cs.ProposalBlockParts.GetReader(), 1470 &cs.ProposalBlock, 1471 cs.state.ConsensusParams.Block.MaxDataBytes, 1472 ) 1473 if err != nil { 1474 return added, err 1475 } 1476 // NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal 1477 cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) 1478 cs.evsw.FireEvent(cs.EventCompleteProposal()) 1479 1480 // Update Valid* if we can. 1481 prevotes := cs.Votes.Prevotes(cs.Round) 1482 blockID, hasTwoThirds := prevotes.TwoThirdsMajority() 1483 if hasTwoThirds && !blockID.IsZero() && (cs.ValidRound < cs.Round) { 1484 if cs.ProposalBlock.HashesTo(blockID.Hash) { 1485 cs.Logger.Info("Updating valid block to new proposal block", 1486 "valid-round", cs.Round, "valid-block-hash", cs.ProposalBlock.Hash()) 1487 cs.ValidRound = cs.Round 1488 cs.ValidBlock = cs.ProposalBlock 1489 cs.ValidBlockParts = cs.ProposalBlockParts 1490 } 1491 // TODO: In case there is +2/3 majority in Prevotes set for some 1492 // block and cs.ProposalBlock contains different block, either 1493 // proposer is faulty or voting power of faulty processes is more 1494 // than 1/3. We should trigger in the future accountability 1495 // procedure at this point. 1496 } 1497 1498 if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() { 1499 // Move onto the next step 1500 cs.enterPrevote(height, cs.Round) 1501 if hasTwoThirds { // this is optimisation as this will be triggered when prevote is added 1502 cs.enterPrecommit(height, cs.Round) 1503 } 1504 } else if cs.Step == cstypes.RoundStepCommit { 1505 // If we're waiting on the proposal block... 1506 cs.tryFinalizeCommit(height) 1507 } 1508 return added, nil 1509 } 1510 return added, nil 1511 } 1512 1513 // Attempt to add the vote. if its a duplicate signature, dupeout the validator 1514 func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) { 1515 added, err := cs.addVote(vote, peerID) 1516 if err != nil { 1517 // If the vote height is off, we'll just ignore it, 1518 // But if it's a conflicting sig, add it to the cs.mempool. 1519 // If it's otherwise invalid, punish peer. 1520 if goerrors.Is(err, ErrVoteHeightMismatch) { 1521 return added, err 1522 } else if _, ok := err.(*types.VoteConflictingVotesError); ok { 1523 /* XXX 1524 addr := cs.privValidator.GetPubKey().Address() 1525 if bytes.Equal(vote.ValidatorAddress, addr) { 1526 cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type) 1527 return added, err 1528 } 1529 cs.evpool.AddEvidence(voteErr.DuplicateVoteEvidence) 1530 return added, err 1531 */ 1532 panic("not yet implemented") 1533 } else { 1534 // Either 1535 // 1) bad peer OR 1536 // 2) not a bad peer? this can also err sometimes with "Unexpected step" OR 1537 // 3) tmkms use with multiple validators connecting to a single tmkms instance (https://github.com/tendermint/classic/issues/3839). 1538 cs.Logger.Info("Error attempting to add vote", "err", err) 1539 return added, ErrAddingVote 1540 } 1541 } 1542 return added, nil 1543 } 1544 1545 // ----------------------------------------------------------------------------- 1546 1547 func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error) { 1548 cs.Logger.Debug("addVote", "voteHeight", vote.Height, "voteType", vote.Type, "valIndex", vote.ValidatorIndex, "csHeight", cs.Height) 1549 1550 // A precommit for the previous height? 1551 // These come in while we wait timeoutCommit 1552 if vote.Height+1 == cs.Height { 1553 if !(cs.Step == cstypes.RoundStepNewHeight && vote.Type == types.PrecommitType) { 1554 // TODO: give the reason .. 1555 // fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.") 1556 return added, ErrVoteHeightMismatch 1557 } 1558 added, err = cs.LastCommit.AddVote(vote) 1559 if !added { 1560 return added, err 1561 } 1562 1563 cs.Logger.Info(fmt.Sprintf("Added to lastPrecommits: %v", cs.LastCommit.StringShort())) 1564 cs.evsw.FireEvent(types.EventVote{Vote: vote}) 1565 1566 // if we can skip timeoutCommit and have all the votes now, 1567 if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() { 1568 // go straight to new round (skip timeout commit) 1569 // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight) 1570 cs.enterNewRound(cs.Height, 0) 1571 } 1572 1573 return 1574 } 1575 1576 // Height mismatch is ignored. 1577 // Not necessarily a bad peer, but not favourable behaviour. 1578 if vote.Height != cs.Height { 1579 err = ErrVoteHeightMismatch 1580 cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "peerID", peerID) 1581 return 1582 } 1583 1584 height := cs.Height 1585 added, err = cs.Votes.AddVote(vote, peerID) 1586 if !added { 1587 // Either duplicate, or error upon cs.Votes.AddByIndex() 1588 return 1589 } 1590 1591 cs.evsw.FireEvent(types.EventVote{vote}) 1592 1593 switch vote.Type { 1594 case types.PrevoteType: 1595 prevotes := cs.Votes.Prevotes(vote.Round) 1596 cs.Logger.Debug( 1597 "Added to prevote", 1598 "type", vote.Type, 1599 "vote height", vote.Height, 1600 "vote round", vote.Round, 1601 "prevotes", prevotes.StringShort(), 1602 ) 1603 1604 // If +2/3 prevotes for a block or nil for *any* round: 1605 if blockID, ok := prevotes.TwoThirdsMajority(); ok { 1606 // There was a polka! 1607 // If we're locked but this is a recent polka, unlock. 1608 // If it matches our ProposalBlock, update the ValidBlock 1609 1610 // Unlock if `cs.LockedRound < vote.Round <= cs.Round` 1611 // NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round 1612 if (cs.LockedBlock != nil) && 1613 (cs.LockedRound < vote.Round) && 1614 (vote.Round <= cs.Round) && 1615 !cs.LockedBlock.HashesTo(blockID.Hash) { 1616 cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round) 1617 cs.LockedRound = -1 1618 cs.LockedBlock = nil 1619 cs.LockedBlockParts = nil 1620 cs.evsw.FireEvent(cstypes.EventUnlock{cs.RoundState.GetHRS()}) 1621 } 1622 1623 // Update Valid* if we can. 1624 // NOTE: our proposal block may be nil or not what received a polka.. 1625 if len(blockID.Hash) != 0 && (cs.ValidRound < vote.Round) && (vote.Round == cs.Round) { 1626 if cs.ProposalBlock.HashesTo(blockID.Hash) { 1627 cs.Logger.Info( 1628 "Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", vote.Round) 1629 cs.ValidRound = vote.Round 1630 cs.ValidBlock = cs.ProposalBlock 1631 cs.ValidBlockParts = cs.ProposalBlockParts 1632 } else { 1633 cs.Logger.Info( 1634 "Valid block we don't know about. Set ProposalBlock=nil", 1635 "proposal", cs.ProposalBlock.Hash(), "blockId", blockID.Hash) 1636 // We're getting the wrong block. 1637 cs.ProposalBlock = nil 1638 } 1639 if !cs.ProposalBlockParts.HasHeader(blockID.PartsHeader) { 1640 cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader) 1641 } 1642 cs.evsw.FireEvent(cs.EventNewValidBlock()) 1643 } 1644 } 1645 1646 // If +2/3 prevotes for *anything* for future round: 1647 switch { 1648 case cs.Round < vote.Round && prevotes.HasTwoThirdsAny(): 1649 // Round-skip if there is any 2/3+ of votes ahead of us 1650 cs.enterNewRound(height, vote.Round) 1651 case cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step: // current round 1652 blockID, ok := prevotes.TwoThirdsMajority() 1653 if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) { 1654 cs.enterPrecommit(height, vote.Round) 1655 } else if prevotes.HasTwoThirdsAny() { 1656 cs.enterPrevoteWait(height, vote.Round) 1657 } 1658 case cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round: 1659 // If the proposal is now complete, enter prevote of cs.Round. 1660 if cs.isProposalComplete() { 1661 cs.enterPrevote(height, cs.Round) 1662 } 1663 } 1664 1665 case types.PrecommitType: 1666 precommits := cs.Votes.Precommits(vote.Round) 1667 cs.Logger.Debug( 1668 "Added to precommit", 1669 "type", vote.Type, 1670 "vote height", vote.Height, 1671 "vote round", vote.Round, 1672 "precommits", precommits.StringShort(), 1673 ) 1674 1675 blockID, ok := precommits.TwoThirdsMajority() 1676 if ok { 1677 // Executed as TwoThirdsMajority could be from a higher round 1678 cs.enterNewRound(height, vote.Round) 1679 cs.enterPrecommit(height, vote.Round) 1680 if len(blockID.Hash) != 0 { 1681 cs.enterCommit(height, vote.Round) 1682 if cs.config.SkipTimeoutCommit && precommits.HasAll() { 1683 cs.enterNewRound(cs.Height, 0) 1684 } 1685 } else { 1686 cs.enterPrecommitWait(height, vote.Round) 1687 } 1688 } else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { 1689 cs.enterNewRound(height, vote.Round) 1690 cs.enterPrecommitWait(height, vote.Round) 1691 } 1692 1693 default: 1694 panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // go-amino should prevent this. 1695 } 1696 1697 return added, err 1698 } 1699 1700 func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) { 1701 // Flush the WAL. Otherwise, we may not recompute the same vote to sign, and the privValidator will refuse to sign anything. 1702 cs.wal.FlushAndSync() 1703 1704 addr := cs.privValidator.GetPubKey().Address() 1705 valIndex, _ := cs.Validators.GetByAddress(addr) 1706 1707 vote := &types.Vote{ 1708 ValidatorAddress: addr, 1709 ValidatorIndex: valIndex, 1710 Height: cs.Height, 1711 Round: cs.Round, 1712 Timestamp: cs.voteTime(), 1713 Type: type_, 1714 BlockID: types.BlockID{Hash: hash, PartsHeader: header}, 1715 } 1716 err := cs.privValidator.SignVote(cs.state.ChainID, vote) 1717 return vote, err 1718 } 1719 1720 func (cs *ConsensusState) voteTime() time.Time { 1721 now := tmtime.Now() 1722 minVoteTime := now 1723 // TODO: We should remove next line in case we don't vote for v in case cs.ProposalBlock == nil, 1724 // even if cs.LockedBlock != nil. See https://github.com/tendermint/spec. 1725 timeIota := time.Duration(cs.state.ConsensusParams.Block.TimeIotaMS) * time.Millisecond 1726 if cs.LockedBlock != nil { 1727 // See the BFT time spec https://tendermint.com/docs/spec/consensus/bft-time.html 1728 minVoteTime = cs.LockedBlock.Time.Add(timeIota) 1729 } else if cs.ProposalBlock != nil { 1730 minVoteTime = cs.ProposalBlock.Time.Add(timeIota) 1731 } 1732 1733 if now.After(minVoteTime) { 1734 return now 1735 } 1736 return minVoteTime 1737 } 1738 1739 // sign the vote and publish on internalMsgQueue 1740 func (cs *ConsensusState) signAddVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote { 1741 // if we don't have a key or we're not in the validator set, do nothing 1742 if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetPubKey().Address()) { 1743 return nil 1744 } 1745 vote, err := cs.signVote(type_, hash, header) 1746 if err == nil { 1747 cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""}) 1748 1749 cs.Logger.Info( 1750 "Signed and pushed vote", 1751 "height", cs.Height, 1752 "round", cs.Round, 1753 "type", vote.Type, 1754 "timestamp", vote.Timestamp.String(), 1755 "height", vote.Height, 1756 "round", vote.Round, 1757 "validator address", vote.ValidatorAddress, 1758 "validator index", vote.ValidatorIndex, 1759 ) 1760 1761 return vote 1762 } 1763 // if !cs.replayMode { 1764 cs.Logger.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err) 1765 // } 1766 return nil 1767 } 1768 1769 // --------------------------------------------------------- 1770 1771 func CompareHRS(h1 int64, r1 int, s1 cstypes.RoundStepType, h2 int64, r2 int, s2 cstypes.RoundStepType) int { 1772 if h1 < h2 { 1773 return -1 1774 } else if h1 > h2 { 1775 return 1 1776 } 1777 if r1 < r2 { 1778 return -1 1779 } else if r1 > r2 { 1780 return 1 1781 } 1782 if s1 < s2 { 1783 return -1 1784 } else if s1 > s2 { 1785 return 1 1786 } 1787 return 0 1788 }