github.com/Finschia/ostracon@v1.1.5/consensus/state.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "runtime/debug" 9 "sort" 10 "time" 11 12 "github.com/gogo/protobuf/proto" 13 "github.com/pkg/errors" 14 15 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 16 17 cfg "github.com/Finschia/ostracon/config" 18 cstypes "github.com/Finschia/ostracon/consensus/types" 19 "github.com/Finschia/ostracon/crypto" 20 tmevents "github.com/Finschia/ostracon/libs/events" 21 "github.com/Finschia/ostracon/libs/fail" 22 tmjson "github.com/Finschia/ostracon/libs/json" 23 "github.com/Finschia/ostracon/libs/log" 24 tmmath "github.com/Finschia/ostracon/libs/math" 25 tmos "github.com/Finschia/ostracon/libs/os" 26 "github.com/Finschia/ostracon/libs/service" 27 tmsync "github.com/Finschia/ostracon/libs/sync" 28 "github.com/Finschia/ostracon/p2p" 29 ocproto "github.com/Finschia/ostracon/proto/ostracon/types" 30 sm "github.com/Finschia/ostracon/state" 31 "github.com/Finschia/ostracon/types" 32 tmtime "github.com/Finschia/ostracon/types/time" 33 ) 34 35 // Consensus sentinel errors 36 var ( 37 ErrInvalidProposalSignature = errors.New("error invalid proposal signature") 38 ErrInvalidProposalPOLRound = errors.New("error invalid proposal POL round") 39 ErrAddingVote = errors.New("error adding vote") 40 ErrSignatureFoundInPastBlocks = errors.New("found signature from the same key") 41 42 errPubKeyIsNotSet = errors.New("pubkey is not set. Look for \"Can't get private validator pubkey\" errors") 43 ) 44 45 var msgQueueSize = 1000 46 47 // msgs from the reactor which may update the state 48 type msgInfo struct { 49 Msg Message `json:"msg"` 50 PeerID p2p.ID `json:"peer_key"` 51 } 52 53 // internally generated messages which may update the state 54 type timeoutInfo struct { 55 Duration time.Duration `json:"duration"` 56 Height int64 `json:"height"` 57 Round int32 `json:"round"` 58 Step cstypes.RoundStepType `json:"step"` 59 } 60 61 func (ti *timeoutInfo) String() string { 62 return fmt.Sprintf("%v ; %d/%d %v", ti.Duration, ti.Height, ti.Round, ti.Step) 63 } 64 65 type StepTimes struct { 66 Proposal types.StepDuration 67 Prevote types.StepDuration 68 Precommit types.StepDuration 69 sm.CommitStepTimes 70 WaitingForNewRound types.StepDuration 71 } 72 73 func (st *StepTimes) StartNewRound() time.Time { 74 now := tmtime.Now() 75 if st.Current == &st.WaitingForNewRound { 76 st.Current.End = now 77 } 78 st.Current = &st.Proposal 79 st.Current.Start = now 80 return now 81 } 82 83 func (st *StepTimes) ToPrevoteStep() time.Time { 84 return st.ToNextStep(&st.Proposal, &st.Prevote) 85 } 86 87 func (st *StepTimes) ToPrecommitStep() time.Time { 88 return st.ToNextStep(&st.Prevote, &st.Precommit) 89 } 90 91 func (st *StepTimes) ToCommitExecuting() time.Time { 92 return st.ToNextStep(&st.Precommit, &st.CommitExecuting) 93 } 94 95 func (st *StepTimes) EndRound() time.Time { 96 now := tmtime.Now() 97 if st.Current == &st.CommitRechecking { 98 st.Current.End = now 99 st.Current = &st.WaitingForNewRound 100 } 101 return now 102 } 103 104 func (st *StepTimes) StartWaiting() time.Time { 105 now := tmtime.Now() 106 if st.Current == &st.WaitingForNewRound { 107 st.Current.Start = now 108 } 109 return now 110 } 111 112 // interface to the mempool 113 type txNotifier interface { 114 TxsAvailable() <-chan struct{} 115 } 116 117 // interface to the evidence pool 118 type evidencePool interface { 119 // reports conflicting votes to the evidence pool to be processed into evidence 120 ReportConflictingVotes(voteA, voteB *types.Vote) 121 } 122 123 // State handles execution of the consensus algorithm. 124 // It processes votes and proposals, and upon reaching agreement, 125 // commits blocks to the chain and executes them against the application. 126 // The internal state machine receives input from peers, the internal validator, and from a timer. 127 type State struct { 128 service.BaseService 129 130 // config details 131 config *cfg.ConsensusConfig 132 privValidator types.PrivValidator // for signing votes 133 134 // store blocks and commits 135 blockStore sm.BlockStore 136 137 // create and execute blocks 138 blockExec *sm.BlockExecutor 139 140 // notify us if txs are available 141 txNotifier txNotifier 142 143 // add evidence to the pool 144 // when it's detected 145 evpool evidencePool 146 147 // internal state 148 mtx tmsync.RWMutex 149 cstypes.RoundState 150 state sm.State // State until height-1. 151 // privValidator pubkey, memoized for the duration of one block 152 // to avoid extra requests to HSM 153 privValidatorPubKey crypto.PubKey 154 155 // state changes may be triggered by: msgs from peers, 156 // msgs from ourself, or by timeouts 157 peerMsgQueue chan msgInfo 158 internalMsgQueue chan msgInfo 159 timeoutTicker TimeoutTicker 160 161 // information about about added votes and block parts are written on this channel 162 // so statistics can be computed by reactor 163 statsMsgQueue chan msgInfo 164 165 // we use eventBus to trigger msg broadcasts in the reactor, 166 // and to notify external subscribers, eg. through a websocket 167 eventBus *types.EventBus 168 169 // a Write-Ahead Log ensures we can recover from any kind of crash 170 // and helps us avoid signing conflicting votes 171 wal WAL 172 replayMode bool // so we don't log signing errors during replay 173 doWALCatchup bool // determines if we even try to do the catchup 174 175 // for tests where we want to limit the number of transitions the state makes 176 nSteps int 177 178 // some functions can be overwritten for testing 179 decideProposal func(height int64, round int32) 180 doPrevote func(height int64, round int32) 181 setProposal func(proposal *types.Proposal) error 182 183 // closed when we finish shutting down 184 done chan struct{} 185 186 // synchronous pubsub between consensus state and reactor. 187 // state only emits EventNewRoundStep and EventVote 188 evsw tmevents.EventSwitch 189 190 // for reporting metrics 191 metrics *Metrics 192 193 // times of each step 194 stepTimes *StepTimes 195 } 196 197 // StateOption sets an optional parameter on the State. 198 type StateOption func(*State) 199 200 // NewState returns a new State. 201 func NewState( 202 config *cfg.ConsensusConfig, 203 state sm.State, 204 blockExec *sm.BlockExecutor, 205 blockStore sm.BlockStore, 206 txNotifier txNotifier, 207 evpool evidencePool, 208 options ...StateOption, 209 ) *State { 210 cs := &State{ 211 config: config, 212 blockExec: blockExec, 213 blockStore: blockStore, 214 txNotifier: txNotifier, 215 peerMsgQueue: make(chan msgInfo, msgQueueSize), 216 internalMsgQueue: make(chan msgInfo, msgQueueSize), 217 timeoutTicker: NewTimeoutTicker(), 218 statsMsgQueue: make(chan msgInfo, msgQueueSize), 219 done: make(chan struct{}), 220 doWALCatchup: true, 221 wal: nilWAL{}, 222 evpool: evpool, 223 evsw: tmevents.NewEventSwitch(), 224 metrics: NopMetrics(), 225 stepTimes: &StepTimes{}, 226 } 227 228 // set function defaults (may be overwritten before calling Start) 229 cs.decideProposal = cs.defaultDecideProposal 230 cs.doPrevote = cs.defaultDoPrevote 231 cs.setProposal = cs.defaultSetProposal 232 233 // We have no votes, so reconstruct LastCommit from SeenCommit. 234 if state.LastBlockHeight > 0 { 235 cs.reconstructLastCommit(state) 236 } 237 238 cs.updateToState(state) 239 240 // NOTE: we do not call scheduleRound0 yet, we do that upon Start() 241 242 cs.BaseService = *service.NewBaseService(nil, "State", cs) 243 for _, option := range options { 244 option(cs) 245 } 246 247 return cs 248 } 249 250 // SetLogger implements Service. 251 func (cs *State) SetLogger(l log.Logger) { 252 cs.BaseService.Logger = l 253 cs.timeoutTicker.SetLogger(l) 254 } 255 256 // SetEventBus sets event bus. 257 func (cs *State) SetEventBus(b *types.EventBus) { 258 cs.eventBus = b 259 cs.blockExec.SetEventBus(b) 260 } 261 262 // StateMetrics sets the metrics. 263 func StateMetrics(metrics *Metrics) StateOption { 264 return func(cs *State) { cs.metrics = metrics } 265 } 266 267 // String returns a string. 268 func (cs *State) String() string { 269 // better not to access shared variables 270 return "ConsensusState" 271 } 272 273 // GetState returns a copy of the chain state. 274 func (cs *State) GetState() sm.State { 275 cs.mtx.RLock() 276 defer cs.mtx.RUnlock() 277 return cs.state.Copy() 278 } 279 280 // GetLastHeight returns the last height committed. 281 // If there were no blocks, returns 0. 282 func (cs *State) GetLastHeight() int64 { 283 cs.mtx.RLock() 284 defer cs.mtx.RUnlock() 285 return cs.RoundState.Height - 1 286 } 287 288 // GetRoundState returns a shallow copy of the internal consensus state. 289 func (cs *State) GetRoundState() *cstypes.RoundState { 290 cs.mtx.RLock() 291 rs := cs.RoundState // copy 292 cs.mtx.RUnlock() 293 return &rs 294 } 295 296 // GetRoundStateJSON returns a json of RoundState. 297 func (cs *State) GetRoundStateJSON() ([]byte, error) { 298 cs.mtx.RLock() 299 defer cs.mtx.RUnlock() 300 return tmjson.Marshal(cs.RoundState) 301 } 302 303 // GetRoundStateSimpleJSON returns a json of RoundStateSimple 304 func (cs *State) GetRoundStateSimpleJSON() ([]byte, error) { 305 cs.mtx.RLock() 306 defer cs.mtx.RUnlock() 307 return tmjson.Marshal(cs.RoundState.RoundStateSimple()) 308 } 309 310 // GetValidators returns a copy of the current validators. 311 func (cs *State) GetValidators() (int64, []*types.Validator) { 312 cs.mtx.RLock() 313 defer cs.mtx.RUnlock() 314 return cs.state.LastBlockHeight, cs.state.Validators.Copy().Validators 315 } 316 317 // SetPrivValidator sets the private validator account for signing votes. It 318 // immediately requests pubkey and caches it. 319 func (cs *State) SetPrivValidator(priv types.PrivValidator) { 320 cs.mtx.Lock() 321 defer cs.mtx.Unlock() 322 323 cs.privValidator = priv 324 325 if err := cs.updatePrivValidatorPubKey(); err != nil { 326 cs.Logger.Error("failed to get private validator pubkey", "err", err) 327 } 328 } 329 330 // SetTimeoutTicker sets the local timer. It may be useful to overwrite for 331 // testing. 332 func (cs *State) SetTimeoutTicker(timeoutTicker TimeoutTicker) { 333 cs.mtx.Lock() 334 cs.timeoutTicker = timeoutTicker 335 cs.mtx.Unlock() 336 } 337 338 // LoadCommit loads the commit for a given height. 339 func (cs *State) LoadCommit(height int64) *types.Commit { 340 cs.mtx.RLock() 341 defer cs.mtx.RUnlock() 342 343 if height == cs.blockStore.Height() { 344 return cs.blockStore.LoadSeenCommit(height) 345 } 346 347 return cs.blockStore.LoadBlockCommit(height) 348 } 349 350 // OnStart loads the latest state via the WAL, and starts the timeout and 351 // receive routines. 352 func (cs *State) OnStart() error { 353 // We may set the WAL in testing before calling Start, so only OpenWAL if its 354 // still the nilWAL. 355 if _, ok := cs.wal.(nilWAL); ok { 356 if err := cs.loadWalFile(); err != nil { 357 return err 358 } 359 } 360 361 // we need the timeoutRoutine for replay so 362 // we don't block on the tick chan. 363 // NOTE: we will get a build up of garbage go routines 364 // firing on the tockChan until the receiveRoutine is started 365 // to deal with them (by that point, at most one will be valid) 366 if err := cs.timeoutTicker.Start(); err != nil { 367 return err 368 } 369 370 // We may have lost some votes if the process crashed reload from consensus 371 // log to catchup. 372 if cs.doWALCatchup { 373 repairAttempted := false 374 375 LOOP: 376 for { 377 err := cs.catchupReplay(cs.Height) 378 switch { 379 case err == nil: 380 break LOOP 381 382 case !IsDataCorruptionError(err): 383 cs.Logger.Error("error on catchup replay; proceeding to start state anyway", "err", err) 384 break LOOP 385 386 case repairAttempted: 387 return err 388 } 389 390 cs.Logger.Error("the WAL file is corrupted; attempting repair", "err", err) 391 392 // 1) prep work 393 if err := cs.wal.Stop(); err != nil { 394 return err 395 } 396 397 repairAttempted = true 398 399 // 2) backup original WAL file 400 corruptedFile := fmt.Sprintf("%s.CORRUPTED", cs.config.WalFile()) 401 if err := tmos.CopyFile(cs.config.WalFile(), corruptedFile); err != nil { 402 return err 403 } 404 405 cs.Logger.Debug("backed up WAL file", "src", cs.config.WalFile(), "dst", corruptedFile) 406 407 // 3) try to repair (WAL file will be overwritten!) 408 if err := repairWalFile(corruptedFile, cs.config.WalFile()); err != nil { 409 cs.Logger.Error("the WAL repair failed", "err", err) 410 return err 411 } 412 413 cs.Logger.Info("successful WAL repair") 414 415 // reload WAL file 416 if err := cs.loadWalFile(); err != nil { 417 return err 418 } 419 } 420 } 421 422 if err := cs.evsw.Start(); err != nil { 423 return err 424 } 425 426 // Double Signing Risk Reduction 427 if err := cs.checkDoubleSigningRisk(cs.Height); err != nil { 428 return err 429 } 430 431 // now start the receiveRoutine 432 go cs.receiveRoutine(0) 433 434 // schedule the first round! 435 // use GetRoundState so we don't race the receiveRoutine for access 436 cs.scheduleRound0(cs.GetRoundState()) 437 438 return nil 439 } 440 441 // timeoutRoutine: receive requests for timeouts on tickChan and fire timeouts on tockChan 442 // receiveRoutine: serializes processing of proposoals, block parts, votes; coordinates state transitions 443 func (cs *State) startRoutines(maxSteps int) { 444 err := cs.timeoutTicker.Start() 445 if err != nil { 446 cs.Logger.Error("failed to start timeout ticker", "err", err) 447 return 448 } 449 450 go cs.receiveRoutine(maxSteps) 451 } 452 453 // loadWalFile loads WAL data from file. It overwrites cs.wal. 454 func (cs *State) loadWalFile() error { 455 wal, err := cs.OpenWAL(cs.config.WalFile()) 456 if err != nil { 457 cs.Logger.Error("failed to load state WAL", "err", err) 458 return err 459 } 460 461 cs.wal = wal 462 return nil 463 } 464 465 // OnStop implements service.Service. 466 func (cs *State) OnStop() { 467 if err := cs.evsw.Stop(); err != nil { 468 cs.Logger.Error("failed trying to stop eventSwitch", "error", err) 469 } 470 471 if err := cs.timeoutTicker.Stop(); err != nil { 472 cs.Logger.Error("failed trying to stop timeoutTicket", "error", err) 473 } 474 // WAL is stopped in receiveRoutine. 475 } 476 477 // Wait waits for the the main routine to return. 478 // NOTE: be sure to Stop() the event switch and drain 479 // any event channels or this may deadlock 480 func (cs *State) Wait() { 481 <-cs.done 482 } 483 484 // OpenWAL opens a file to log all consensus messages and timeouts for 485 // deterministic accountability. 486 func (cs *State) OpenWAL(walFile string) (WAL, error) { 487 wal, err := NewWAL(walFile) 488 if err != nil { 489 cs.Logger.Error("failed to open WAL", "file", walFile, "err", err) 490 return nil, err 491 } 492 493 wal.SetLogger(cs.Logger.With("wal", walFile)) 494 495 if err := wal.Start(); err != nil { 496 cs.Logger.Error("failed to start WAL", "err", err) 497 return nil, err 498 } 499 500 return wal, nil 501 } 502 503 //------------------------------------------------------------ 504 // Public interface for passing messages into the consensus state, possibly causing a state transition. 505 // If peerID == "", the msg is considered internal. 506 // Messages are added to the appropriate queue (peer or internal). 507 // If the queue is full, the function may block. 508 // TODO: should these return anything or let callers just use events? 509 510 // AddVote inputs a vote. 511 func (cs *State) AddVote(vote *types.Vote, peerID p2p.ID) (added bool, err error) { 512 if peerID == "" { 513 cs.internalMsgQueue <- msgInfo{&VoteMessage{vote}, ""} 514 } else { 515 cs.peerMsgQueue <- msgInfo{&VoteMessage{vote}, peerID} 516 } 517 518 // TODO: wait for event?! 519 return false, nil 520 } 521 522 // SetProposal inputs a proposal. 523 func (cs *State) SetProposal(proposal *types.Proposal, peerID p2p.ID) error { 524 if peerID == "" { 525 cs.internalMsgQueue <- msgInfo{&ProposalMessage{proposal}, ""} 526 } else { 527 cs.peerMsgQueue <- msgInfo{&ProposalMessage{proposal}, peerID} 528 } 529 530 // TODO: wait for event?! 531 return nil 532 } 533 534 // AddProposalBlockPart inputs a part of the proposal block. 535 func (cs *State) AddProposalBlockPart(height int64, round int32, part *types.Part, peerID p2p.ID) error { 536 if peerID == "" { 537 cs.internalMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, ""} 538 } else { 539 cs.peerMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, peerID} 540 } 541 542 // TODO: wait for event?! 543 return nil 544 } 545 546 // SetProposalAndBlock inputs the proposal and all block parts. 547 func (cs *State) SetProposalAndBlock( 548 proposal *types.Proposal, 549 block *types.Block, 550 parts *types.PartSet, 551 peerID p2p.ID, 552 ) error { 553 if err := cs.SetProposal(proposal, peerID); err != nil { 554 return err 555 } 556 557 for i := 0; i < int(parts.Total()); i++ { 558 part := parts.GetPart(i) 559 if err := cs.AddProposalBlockPart(proposal.Height, proposal.Round, part, peerID); err != nil { 560 return err 561 } 562 } 563 564 return nil 565 } 566 567 //------------------------------------------------------------ 568 // internal functions for managing the state 569 570 func (cs *State) updateHeight(height int64) { 571 cs.metrics.Height.Set(float64(height)) 572 cs.Height = height 573 } 574 575 func (cs *State) updateRoundStep(round int32, step cstypes.RoundStepType) { 576 cs.Round = round 577 cs.Step = step 578 } 579 580 // enterNewRound(height, 0) at cs.StartTime. 581 func (cs *State) scheduleRound0(rs *cstypes.RoundState) { 582 // cs.Logger.Info("scheduleRound0", "now", tmtime.Now(), "startTime", cs.StartTime) 583 sleepDuration := rs.StartTime.Sub(tmtime.Now()) 584 cs.scheduleTimeout(sleepDuration, rs.Height, 0, cstypes.RoundStepNewHeight) 585 } 586 587 // Attempt to schedule a timeout (by sending timeoutInfo on the tickChan) 588 func (cs *State) scheduleTimeout(duration time.Duration, height int64, round int32, step cstypes.RoundStepType) { 589 cs.timeoutTicker.ScheduleTimeout(timeoutInfo{duration, height, round, step}) 590 } 591 592 // send a msg into the receiveRoutine regarding our own proposal, block part, or vote 593 func (cs *State) sendInternalMessage(mi msgInfo) { 594 select { 595 case cs.internalMsgQueue <- mi: 596 default: 597 // NOTE: using the go-routine means our votes can 598 // be processed out of order. 599 // TODO: use CList here for strict determinism and 600 // attempt push to internalMsgQueue in receiveRoutine 601 cs.Logger.Debug("internal msg queue is full; using a go-routine") 602 go func() { cs.internalMsgQueue <- mi }() 603 } 604 } 605 606 // Reconstruct LastCommit from SeenCommit, which we saved along with the block, 607 // (which happens even before saving the state) 608 func (cs *State) reconstructLastCommit(state sm.State) { 609 seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) 610 if seenCommit == nil { 611 panic(fmt.Sprintf( 612 "failed to reconstruct last commit; seen commit for height %v not found", 613 state.LastBlockHeight, 614 )) 615 } 616 617 lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastValidators) 618 if !lastPrecommits.HasTwoThirdsMajority() { 619 panic("failed to reconstruct last commit; does not have +2/3 maj") 620 } 621 622 cs.LastCommit = lastPrecommits 623 } 624 625 // Updates State and increments height to match that of state. 626 // The round becomes 0 and cs.Step becomes cstypes.RoundStepNewHeight. 627 func (cs *State) updateToState(state sm.State) { 628 if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight { 629 panic(fmt.Sprintf( 630 "updateToState() expected state height of %v but found %v", 631 cs.Height, state.LastBlockHeight, 632 )) 633 } 634 635 if !cs.state.IsEmpty() { 636 if cs.state.LastBlockHeight > 0 && cs.state.LastBlockHeight+1 != cs.Height { 637 // This might happen when someone else is mutating cs.state. 638 // Someone forgot to pass in state.Copy() somewhere?! 639 panic(fmt.Sprintf( 640 "inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v", 641 cs.state.LastBlockHeight+1, cs.Height, 642 )) 643 } 644 if cs.state.LastBlockHeight > 0 && cs.Height == cs.state.InitialHeight { 645 panic(fmt.Sprintf( 646 "inconsistent cs.state.LastBlockHeight %v, expected 0 for initial height %v", 647 cs.state.LastBlockHeight, cs.state.InitialHeight, 648 )) 649 } 650 651 // If state isn't further out than cs.state, just ignore. 652 // This happens when SwitchToConsensus() is called in the reactor. 653 // We don't want to reset e.g. the Votes, but we still want to 654 // signal the new round step, because other services (eg. txNotifier) 655 // depend on having an up-to-date peer state! 656 if state.LastBlockHeight <= cs.state.LastBlockHeight { 657 cs.Logger.Debug( 658 "ignoring updateToState()", 659 "new_height", state.LastBlockHeight+1, 660 "old_height", cs.state.LastBlockHeight+1, 661 ) 662 cs.newStep() 663 return 664 } 665 } 666 667 // Reset fields based on state. 668 validators := state.Validators 669 670 switch { 671 case state.LastBlockHeight == 0: // Very first commit should be empty. 672 cs.LastCommit = (*types.VoteSet)(nil) 673 case cs.CommitRound > -1 && cs.Votes != nil: // Otherwise, use cs.Votes 674 if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() { 675 panic(fmt.Sprintf( 676 "wanted to form a commit, but precommits (H/R: %d/%d) didn't have 2/3+: %v", 677 state.LastBlockHeight, cs.CommitRound, cs.Votes.Precommits(cs.CommitRound), 678 )) 679 } 680 681 cs.LastCommit = cs.Votes.Precommits(cs.CommitRound) 682 683 case cs.LastCommit == nil: 684 // NOTE: when Ostracon starts, it has no votes. reconstructLastCommit 685 // must be called to reconstruct LastCommit from SeenCommit. 686 panic(fmt.Sprintf( 687 "last commit cannot be empty after initial block (H:%d)", 688 state.LastBlockHeight+1, 689 )) 690 } 691 692 // Next desired block height 693 height := state.LastBlockHeight + 1 694 if height == 1 { 695 height = state.InitialHeight 696 } 697 698 // RoundState fields 699 cs.updateHeight(height) 700 cs.updateRoundStep(0, cstypes.RoundStepNewHeight) 701 702 if cs.CommitTime.IsZero() { 703 // "Now" makes it easier to sync up dev nodes. 704 // We add timeoutCommit to allow transactions 705 // to be gathered for the first block. 706 // And alternative solution that relies on clocks: 707 // cs.StartTime = state.LastBlockTime.Add(timeoutCommit) 708 cs.StartTime = cs.config.Commit(tmtime.Now()) 709 } else { 710 cs.StartTime = cs.config.Commit(cs.CommitTime) 711 } 712 713 cs.Validators = state.Validators.Copy() 714 cs.Proposal = nil 715 cs.ProposalBlock = nil 716 cs.ProposalBlockParts = nil 717 cs.LockedRound = -1 718 cs.LockedBlock = nil 719 cs.LockedBlockParts = nil 720 cs.ValidRound = -1 721 cs.ValidBlock = nil 722 cs.ValidBlockParts = nil 723 cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) 724 cs.CommitRound = -1 725 cs.LastValidators = state.LastValidators 726 cs.TriggeredTimeoutPrecommit = false 727 728 cs.state = state 729 730 // Finally, broadcast RoundState 731 cs.newStep() 732 } 733 734 func (cs *State) newStep() { 735 rs := cs.RoundStateEvent() 736 if err := cs.wal.Write(rs); err != nil { 737 cs.Logger.Error("failed writing to WAL", "err", err) 738 } 739 740 cs.nSteps++ 741 742 // newStep is called by updateToState in NewState before the eventBus is set! 743 if cs.eventBus != nil { 744 if err := cs.eventBus.PublishEventNewRoundStep(rs); err != nil { 745 cs.Logger.Error("failed publishing new round step", "err", err) 746 } 747 748 cs.evsw.FireEvent(types.EventNewRoundStep, &cs.RoundState) 749 } 750 } 751 752 //----------------------------------------- 753 // the main go routines 754 755 // receiveRoutine handles messages which may cause state transitions. 756 // it's argument (n) is the number of messages to process before exiting - use 0 to run forever 757 // It keeps the RoundState and is the only thing that updates it. 758 // Updates (state transitions) happen on timeouts, complete proposals, and 2/3 majorities. 759 // State must be locked before any internal state is updated. 760 func (cs *State) receiveRoutine(maxSteps int) { 761 onExit := func(cs *State) { 762 // NOTE: the internalMsgQueue may have signed messages from our 763 // priv_val that haven't hit the WAL, but its ok because 764 // priv_val tracks LastSig 765 766 // close wal now that we're done writing to it 767 if err := cs.wal.Stop(); err != nil { 768 cs.Logger.Error("failed trying to stop WAL", "error", err) 769 } 770 771 cs.wal.Wait() 772 close(cs.done) 773 } 774 775 defer func() { 776 if r := recover(); r != nil { 777 cs.Logger.Error("CONSENSUS FAILURE!!!", "err", r, "stack", string(debug.Stack())) 778 // stop gracefully 779 // 780 // NOTE: We most probably shouldn't be running any further when there is 781 // some unexpected panic. Some unknown error happened, and so we don't 782 // know if that will result in the validator signing an invalid thing. It 783 // might be worthwhile to explore a mechanism for manual resuming via 784 // some console or secure RPC system, but for now, halting the chain upon 785 // unexpected consensus bugs sounds like the better option. 786 onExit(cs) 787 } 788 }() 789 790 for { 791 if maxSteps > 0 { 792 if cs.nSteps >= maxSteps { 793 cs.Logger.Debug("reached max steps; exiting receive routine") 794 cs.nSteps = 0 795 return 796 } 797 } 798 799 rs := cs.RoundState 800 var mi msgInfo 801 802 select { 803 case <-cs.txNotifier.TxsAvailable(): 804 cs.handleTxsAvailable() 805 806 case mi = <-cs.peerMsgQueue: 807 if err := cs.wal.Write(mi); err != nil { 808 cs.Logger.Error("failed writing to WAL", "err", err) 809 } 810 811 // handles proposals, block parts, votes 812 // may generate internal events (votes, complete proposals, 2/3 majorities) 813 cs.handleMsg(mi) 814 815 case mi = <-cs.internalMsgQueue: 816 if err := cs.wal.Write(mi); err != nil { 817 cs.Logger.Error("failed writing to WAL", "err", err) 818 } 819 820 if _, ok := mi.Msg.(*VoteMessage); ok { 821 // we actually want to simulate failing during 822 // the previous WriteSync, but this isn't easy to do. 823 // Equivalent would be to fail here and manually remove 824 // some bytes from the end of the wal. 825 fail.Fail() // XXX 826 } 827 828 // handles proposals, block parts, votes 829 cs.handleMsg(mi) 830 831 case ti := <-cs.timeoutTicker.Chan(): // tockChan: 832 if err := cs.wal.Write(ti); err != nil { 833 cs.Logger.Error("failed writing to WAL", "err", err) 834 } 835 836 // if the timeout is relevant to the rs 837 // go to the next step 838 cs.handleTimeout(ti, rs) 839 840 case <-cs.Quit(): 841 onExit(cs) 842 return 843 } 844 } 845 } 846 847 // state transitions on complete-proposal, 2/3-any, 2/3-one 848 func (cs *State) handleMsg(mi msgInfo) { 849 cs.mtx.Lock() 850 defer cs.mtx.Unlock() 851 var ( 852 added bool 853 err error 854 ) 855 856 msg, peerID := mi.Msg, mi.PeerID 857 858 switch msg := msg.(type) { 859 case *ProposalMessage: 860 // will not cause transition. 861 // once proposal is set, we can receive block parts 862 err = cs.setProposal(msg.Proposal) 863 864 case *BlockPartMessage: 865 // if the proposal is complete, we'll enterPrevote or tryFinalizeCommit 866 added, err = cs.addProposalBlockPart(msg, peerID) 867 868 // We unlock here to yield to any routines that need to read the the RoundState. 869 // Previously, this code held the lock from the point at which the final block 870 // part was received until the block executed against the application. 871 // This prevented the reactor from being able to retrieve the most updated 872 // version of the RoundState. The reactor needs the updated RoundState to 873 // gossip the now completed block. 874 // 875 // This code can be further improved by either always operating on a copy 876 // of RoundState and only locking when switching out State's copy of 877 // RoundState with the updated copy or by emitting RoundState events in 878 // more places for routines depending on it to listen for. 879 cs.mtx.Unlock() 880 881 cs.mtx.Lock() 882 if added && cs.ProposalBlockParts.IsComplete() { 883 cs.handleCompleteProposal(msg.Height) 884 } 885 if added { 886 cs.statsMsgQueue <- mi 887 } 888 889 if err != nil && msg.Round != cs.Round { 890 cs.Logger.Debug( 891 "received block part from wrong round", 892 "height", cs.Height, 893 "cs_round", cs.Round, 894 "block_round", msg.Round, 895 ) 896 err = nil 897 } 898 899 case *VoteMessage: 900 // attempt to add the vote and dupeout the validator if its a duplicate signature 901 // if the vote gives us a 2/3-any or 2/3-one, we transition 902 added, err = cs.tryAddVote(msg.Vote, peerID) 903 if added { 904 cs.statsMsgQueue <- mi 905 } 906 907 // if err == ErrAddingVote { 908 // TODO: punish peer 909 // We probably don't want to stop the peer here. The vote does not 910 // necessarily comes from a malicious peer but can be just broadcasted by 911 // a typical peer. 912 // https://github.com/tendermint/tendermint/issues/1281 913 // } 914 915 // NOTE: the vote is broadcast to peers by the reactor listening 916 // for vote events 917 918 // TODO: If rs.Height == vote.Height && rs.Round < vote.Round, 919 // the peer is sending us CatchupCommit precommits. 920 // We could make note of this and help filter in broadcastHasVoteMessage(). 921 922 default: 923 cs.Logger.Error("unknown msg type", "type", fmt.Sprintf("%T", msg)) 924 return 925 } 926 927 if err != nil { 928 cs.Logger.Error( 929 "failed to process message", 930 "height", cs.Height, 931 "round", cs.Round, 932 "peer", peerID, 933 "msg_type", fmt.Sprintf("%T", msg), 934 "err", err, 935 ) 936 } 937 } 938 939 func (cs *State) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) { 940 cs.Logger.Debug("received tock", "timeout", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) 941 942 // timeouts must be for current height, round, step 943 if ti.Height != rs.Height || ti.Round < rs.Round || (ti.Round == rs.Round && ti.Step < rs.Step) { 944 cs.Logger.Debug("ignoring tock because we are ahead", "height", rs.Height, "round", rs.Round, "step", rs.Step) 945 return 946 } 947 948 // the timeout will now cause a state transition 949 cs.mtx.Lock() 950 defer cs.mtx.Unlock() 951 952 switch ti.Step { 953 case cstypes.RoundStepNewHeight: 954 // NewRound event fired from enterNewRound. 955 // XXX: should we fire timeout here (for timeout commit)? 956 cs.enterNewRound(ti.Height, 0) 957 958 case cstypes.RoundStepNewRound: 959 cs.enterPropose(ti.Height, 0) 960 961 case cstypes.RoundStepPropose: 962 if err := cs.eventBus.PublishEventTimeoutPropose(cs.RoundStateEvent()); err != nil { 963 cs.Logger.Error("failed publishing timeout propose", "err", err) 964 } 965 966 cs.enterPrevote(ti.Height, ti.Round) 967 968 case cstypes.RoundStepPrevoteWait: 969 if err := cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()); err != nil { 970 cs.Logger.Error("failed publishing timeout wait", "err", err) 971 } 972 973 cs.enterPrecommit(ti.Height, ti.Round) 974 975 case cstypes.RoundStepPrecommitWait: 976 if err := cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()); err != nil { 977 cs.Logger.Error("failed publishing timeout wait", "err", err) 978 } 979 980 cs.enterPrecommit(ti.Height, ti.Round) 981 cs.enterNewRound(ti.Height, ti.Round+1) 982 983 default: 984 panic(fmt.Sprintf("invalid timeout step: %v", ti.Step)) 985 } 986 } 987 988 func (cs *State) handleTxsAvailable() { 989 cs.mtx.Lock() 990 defer cs.mtx.Unlock() 991 992 // We only need to do this for round 0. 993 if cs.Round != 0 { 994 return 995 } 996 997 switch cs.Step { 998 case cstypes.RoundStepNewHeight: // timeoutCommit phase 999 if cs.needProofBlock(cs.Height) { 1000 // enterPropose will be called by enterNewRound 1001 return 1002 } 1003 1004 // +1ms to ensure RoundStepNewRound timeout always happens after RoundStepNewHeight 1005 timeoutCommit := cs.StartTime.Sub(tmtime.Now()) + 1*time.Millisecond 1006 cs.scheduleTimeout(timeoutCommit, cs.Height, 0, cstypes.RoundStepNewRound) 1007 1008 case cstypes.RoundStepNewRound: // after timeoutCommit 1009 cs.enterPropose(cs.Height, 0) 1010 } 1011 } 1012 1013 //----------------------------------------------------------------------------- 1014 // State functions 1015 // Used internally by handleTimeout and handleMsg to make state transitions 1016 1017 // Enter: `timeoutNewHeight` by startTime (commitTime+timeoutCommit), 1018 // 1019 // or, if SkipTimeoutCommit==true, after receiving all precommits from (height,round-1) 1020 // 1021 // Enter: `timeoutPrecommits` after any +2/3 precommits from (height,round-1) 1022 // Enter: +2/3 precommits for nil at (height,round-1) 1023 // Enter: +2/3 prevotes any or +2/3 precommits for block or any from (height, round) 1024 // NOTE: cs.StartTime was already set for height. 1025 func (cs *State) enterNewRound(height int64, round int32) { 1026 logger := cs.Logger.With("height", height, "round", round) 1027 1028 if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != cstypes.RoundStepNewHeight) { 1029 logger.Debug( 1030 "entering new round with invalid args", 1031 "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step), 1032 ) 1033 return 1034 } 1035 1036 now := cs.stepTimes.StartNewRound() 1037 if cs.StartTime.After(now) { 1038 logger.Debug("need to set a buffer and log message here for sanity", "start_time", cs.StartTime, "now", now) 1039 } 1040 1041 logger.Debug("entering new round", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) 1042 1043 // Select the current height and round Proposer 1044 cs.Proposer = cs.Validators.SelectProposer(cs.state.LastProofHash, height, round) 1045 1046 // Setup new round 1047 // we don't fire newStep for this step, 1048 // but we fire an event, so update the round step first 1049 cs.updateRoundStep(round, cstypes.RoundStepNewRound) 1050 if round == 0 { 1051 // We've already reset these upon new height, 1052 // and meanwhile we might have received a proposal 1053 // for round 0. 1054 } else { 1055 logger.Debug("resetting proposal info") 1056 cs.Proposal = nil 1057 cs.ProposalBlock = nil 1058 cs.ProposalBlockParts = nil 1059 } 1060 1061 cs.Votes.SetRound(tmmath.SafeAddInt32(round, 1)) // also track next round (round+1) to allow round-skipping 1062 cs.TriggeredTimeoutPrecommit = false 1063 1064 if err := cs.eventBus.PublishEventNewRound(cs.NewRoundEvent()); err != nil { 1065 cs.Logger.Error("failed publishing new round", "err", err) 1066 } 1067 1068 cs.metrics.Rounds.Set(float64(round)) 1069 1070 // Wait for txs to be available in the mempool 1071 // before we enterPropose in round 0. If the last block changed the app hash, 1072 // we may need an empty "proof" block, and enterPropose immediately. 1073 waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height) 1074 if waitForTxs { 1075 if cs.config.CreateEmptyBlocksInterval > 0 { 1076 cs.scheduleTimeout(cs.config.CreateEmptyBlocksInterval, height, round, 1077 cstypes.RoundStepNewRound) 1078 } 1079 } else { 1080 cs.enterPropose(height, round) 1081 } 1082 } 1083 1084 // needProofBlock returns true on the first height (so the genesis app hash is signed right away) 1085 // and where the last block (height-1) caused the app hash to change 1086 func (cs *State) needProofBlock(height int64) bool { 1087 if height == cs.state.InitialHeight { 1088 return true 1089 } 1090 1091 lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1) 1092 if lastBlockMeta == nil { 1093 panic(fmt.Sprintf("needProofBlock: last block meta for height %d not found", height-1)) 1094 } 1095 1096 return !bytes.Equal(cs.state.AppHash, lastBlockMeta.Header.AppHash) 1097 } 1098 1099 // Enter (CreateEmptyBlocks): from enterNewRound(height,round) 1100 // Enter (CreateEmptyBlocks, CreateEmptyBlocksInterval > 0 ): 1101 // 1102 // after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval 1103 // 1104 // Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool 1105 func (cs *State) enterPropose(height int64, round int32) { 1106 logger := cs.Logger.With("height", height, "round", round) 1107 1108 if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPropose <= cs.Step) { 1109 logger.Debug( 1110 "entering propose step with invalid args", 1111 "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step), 1112 ) 1113 return 1114 } 1115 1116 logger.Debug("entering propose step", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) 1117 1118 defer func() { 1119 // Done enterPropose: 1120 cs.updateRoundStep(round, cstypes.RoundStepPropose) 1121 cs.newStep() 1122 1123 // If we have the whole proposal + POL, then goto Prevote now. 1124 // else, we'll enterPrevote when the rest of the proposal is received (in AddProposalBlockPart), 1125 // or else after timeoutPropose 1126 if cs.isProposalComplete() { 1127 cs.enterPrevote(height, cs.Round) 1128 } 1129 }() 1130 1131 // If we don't get the proposal and all block parts quick enough, enterPrevote 1132 cs.scheduleTimeout(cs.config.Propose(round), height, round, cstypes.RoundStepPropose) 1133 1134 // Nothing more to do if we're not a validator 1135 if cs.privValidator == nil { 1136 logger.Debug("node is not a validator") 1137 return 1138 } 1139 1140 if cs.privValidatorPubKey == nil { 1141 // If this node is a validator & proposer in the current round, it will 1142 // miss the opportunity to create a block. 1143 logger.Error("propose step; empty priv validator public key", "err", errPubKeyIsNotSet) 1144 return 1145 } 1146 1147 address := cs.privValidatorPubKey.Address() 1148 1149 // if not a validator, we're done 1150 if !cs.Validators.HasAddress(address) { 1151 logger.Debug("node is not a validator", "addr", address, "vals", cs.Validators) 1152 return 1153 } 1154 1155 logger.Debug("node is a validator") 1156 1157 // I'm a proposer, but I might not be a validator 1158 if cs.isProposer(address) { 1159 logger.Debug("propose step; our turn to propose", "proposer", address) 1160 cs.decideProposal(height, round) 1161 } else { 1162 logger.Debug("propose step; not our turn to propose", "proposer", cs.Proposer.Address, 1163 "privValidator", cs.privValidator) 1164 } 1165 } 1166 1167 func (cs *State) isProposer(address []byte) bool { 1168 return bytes.Equal(cs.Proposer.Address, address) 1169 } 1170 1171 func (cs *State) defaultDecideProposal(height int64, round int32) { 1172 var block *types.Block 1173 var blockParts *types.PartSet 1174 1175 // Decide on block 1176 if cs.ValidBlock != nil { 1177 // If there is valid block, choose that. 1178 block, blockParts = cs.ValidBlock, cs.ValidBlockParts 1179 } else { 1180 // Create a new proposal block from state/txs from the mempool. 1181 block, blockParts = cs.createProposalBlock(round) 1182 if block == nil { // on error 1183 return 1184 } 1185 cs.Logger.Info("Create Block", "Height", height, "Round", round, 1186 "ProposerAddr", block.Header.ProposerAddress.String()) 1187 } 1188 1189 // Flush the WAL. Otherwise, we may not recompute the same proposal to sign, 1190 // and the privValidator will refuse to sign anything. 1191 if err := cs.wal.FlushAndSync(); err != nil { 1192 cs.Logger.Error("failed flushing WAL to disk") 1193 } 1194 1195 // Make proposal 1196 propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} 1197 proposal := types.NewProposal(height, round, cs.ValidRound, propBlockID) 1198 p := proposal.ToProto() 1199 if err := cs.privValidator.SignProposal(cs.state.ChainID, p); err == nil { 1200 proposal.Signature = p.Signature 1201 1202 // send proposal and block parts on internal msg queue 1203 cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""}) 1204 1205 for i := 0; i < int(blockParts.Total()); i++ { 1206 part := blockParts.GetPart(i) 1207 cs.sendInternalMessage(msgInfo{&BlockPartMessage{cs.Height, cs.Round, part}, ""}) 1208 } 1209 1210 cs.Logger.Debug("signed proposal", "height", height, "round", round, "proposal", proposal) 1211 } else if !cs.replayMode { 1212 cs.Logger.Error("propose step; failed signing proposal", "height", height, "round", round, "err", err) 1213 } 1214 } 1215 1216 // Returns true if the proposal block is complete && 1217 // (if POLRound was proposed, we have +2/3 prevotes from there). 1218 func (cs *State) isProposalComplete() bool { 1219 if cs.Proposal == nil || cs.ProposalBlock == nil { 1220 return false 1221 } 1222 // we have the proposal. if there's a POLRound, 1223 // make sure we have the prevotes from it too 1224 if cs.Proposal.POLRound < 0 { 1225 return true 1226 } 1227 // if this is false the proposer is lying or we haven't received the POL yet 1228 return cs.Votes.Prevotes(cs.Proposal.POLRound).HasTwoThirdsMajority() 1229 } 1230 1231 // Create the next block to propose and return it. Returns nil block upon error. 1232 // 1233 // We really only need to return the parts, but the block is returned for 1234 // convenience so we can log the proposal block. 1235 // 1236 // NOTE: keep it side-effect free for clarity. 1237 // CONTRACT: cs.privValidator is not nil. 1238 func (cs *State) createProposalBlock(round int32) (block *types.Block, blockParts *types.PartSet) { 1239 if cs.privValidator == nil { 1240 panic("entered createProposalBlock with privValidator being nil") 1241 } 1242 1243 var commit *types.Commit 1244 switch { 1245 case cs.Height == cs.state.InitialHeight: 1246 // We're creating a proposal for the first block. 1247 // The commit is empty, but not nil. 1248 commit = types.NewCommit(0, 0, types.BlockID{}, nil) 1249 1250 case cs.LastCommit.HasTwoThirdsMajority(): 1251 // Make the commit from LastCommit 1252 commit = cs.LastCommit.MakeCommit() 1253 default: // This shouldn't happen. 1254 cs.Logger.Error("propose step; cannot propose anything without commit for the previous block") 1255 return 1256 } 1257 1258 if cs.privValidatorPubKey == nil { 1259 // If this node is a validator & proposer in the current round, it will 1260 // miss the opportunity to create a block. 1261 cs.Logger.Error("propose step; empty priv validator public key", "err", errPubKeyIsNotSet) 1262 return 1263 } 1264 1265 proposerAddr := cs.privValidatorPubKey.Address() 1266 1267 message := cs.state.MakeHashMessage(round) 1268 1269 proof, err := cs.privValidator.GenerateVRFProof(message) 1270 if err != nil { 1271 cs.Logger.Error(fmt.Sprintf("enterPropose: Cannot generate vrf proof: %s", err.Error())) 1272 return 1273 } 1274 1275 return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, round, proof, cs.config.MaxTxs) 1276 } 1277 1278 // Enter: `timeoutPropose` after entering Propose. 1279 // Enter: proposal block and POL is ready. 1280 // Prevote for LockedBlock if we're locked, or ProposalBlock if valid. 1281 // Otherwise vote nil. 1282 func (cs *State) enterPrevote(height int64, round int32) { 1283 logger := cs.Logger.With("height", height, "round", round) 1284 1285 if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevote <= cs.Step) { 1286 logger.Debug( 1287 "entering prevote step with invalid args", 1288 "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step), 1289 ) 1290 return 1291 } 1292 1293 defer func() { 1294 // Done enterPrevote: 1295 cs.updateRoundStep(round, cstypes.RoundStepPrevote) 1296 cs.newStep() 1297 }() 1298 1299 logger.Debug("entering prevote step", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) 1300 1301 // Sign and broadcast vote as necessary 1302 cs.stepTimes.ToPrevoteStep() 1303 cs.doPrevote(height, round) 1304 1305 // Once `addVote` hits any +2/3 prevotes, we will go to PrevoteWait 1306 // (so we have more time to try and collect +2/3 prevotes for a single block) 1307 } 1308 1309 func (cs *State) defaultDoPrevote(height int64, round int32) { 1310 logger := cs.Logger.With("height", height, "round", round) 1311 1312 // If a block is locked, prevote that. 1313 if cs.LockedBlock != nil { 1314 logger.Debug("prevote step; already locked on a block; prevoting locked block") 1315 cs.signAddVote(tmproto.PrevoteType, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header()) 1316 return 1317 } 1318 1319 // If ProposalBlock is nil, prevote nil. 1320 if cs.ProposalBlock == nil { 1321 // if it already ends or not starts it will be ignored 1322 logger.Debug("prevote step: ProposalBlock is nil") 1323 cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{}) 1324 // increase missing proposal by one 1325 cs.metrics.MissingProposal.Add(1) 1326 return 1327 } 1328 1329 // Validate proposal block 1330 err := cs.blockExec.ValidateBlock(cs.state, round, cs.ProposalBlock) 1331 if err != nil { 1332 // ProposalBlock is invalid, prevote nil. 1333 logger.Error("prevote step: ProposalBlock is invalid", "err", err) 1334 cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{}) 1335 return 1336 } 1337 1338 // Prevote cs.ProposalBlock 1339 // NOTE: the proposal signature is validated when it is received, 1340 // and the proposal block parts are validated as they are received (against the merkle hash in the proposal) 1341 logger.Debug("prevote step: ProposalBlock is valid") 1342 cs.signAddVote(tmproto.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header()) 1343 } 1344 1345 // Enter: any +2/3 prevotes at next round. 1346 func (cs *State) enterPrevoteWait(height int64, round int32) { 1347 logger := cs.Logger.With("height", height, "round", round) 1348 1349 if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevoteWait <= cs.Step) { 1350 logger.Debug( 1351 "entering prevote wait step with invalid args", 1352 "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step), 1353 ) 1354 return 1355 } 1356 1357 if !cs.Votes.Prevotes(round).HasTwoThirdsAny() { 1358 panic(fmt.Sprintf( 1359 "entering prevote wait step (%v/%v), but prevotes does not have any +2/3 votes", 1360 height, round, 1361 )) 1362 } 1363 1364 logger.Debug("entering prevote wait step", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) 1365 1366 defer func() { 1367 // Done enterPrevoteWait: 1368 cs.updateRoundStep(round, cstypes.RoundStepPrevoteWait) 1369 cs.newStep() 1370 }() 1371 1372 // Wait for some more prevotes; enterPrecommit 1373 cs.scheduleTimeout(cs.config.Prevote(round), height, round, cstypes.RoundStepPrevoteWait) 1374 } 1375 1376 // Enter: `timeoutPrevote` after any +2/3 prevotes. 1377 // Enter: `timeoutPrecommit` after any +2/3 precommits. 1378 // Enter: +2/3 precomits for block or nil. 1379 // Lock & precommit the ProposalBlock if we have enough prevotes for it (a POL in this round) 1380 // else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil, 1381 // else, precommit nil otherwise. 1382 func (cs *State) enterPrecommit(height int64, round int32) { 1383 logger := cs.Logger.With("height", height, "round", round) 1384 1385 if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommit <= cs.Step) { 1386 logger.Debug( 1387 "entering precommit step with invalid args", 1388 "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step), 1389 ) 1390 return 1391 } 1392 1393 logger.Debug("entering precommit step", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) 1394 1395 defer func() { 1396 // Done enterPrecommit: 1397 cs.updateRoundStep(round, cstypes.RoundStepPrecommit) 1398 cs.newStep() 1399 }() 1400 1401 cs.stepTimes.ToPrecommitStep() 1402 1403 // check for a polka 1404 blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority() 1405 1406 // If we don't have a polka, we must precommit nil. 1407 if !ok { 1408 if cs.LockedBlock != nil { 1409 logger.Debug("precommit step; no +2/3 prevotes during enterPrecommit while we are locked; precommitting nil") 1410 } else { 1411 logger.Debug("precommit step; no +2/3 prevotes during enterPrecommit; precommitting nil") 1412 } 1413 1414 cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{}) 1415 return 1416 } 1417 1418 // At this point +2/3 prevoted for a particular block or nil. 1419 if err := cs.eventBus.PublishEventPolka(cs.RoundStateEvent()); err != nil { 1420 logger.Error("failed publishing polka", "err", err) 1421 } 1422 1423 // the latest POLRound should be this round. 1424 polRound, _ := cs.Votes.POLInfo() 1425 if polRound < round { 1426 panic(fmt.Sprintf("this POLRound should be %v but got %v", round, polRound)) 1427 } 1428 1429 // +2/3 prevoted nil. Unlock and precommit nil. 1430 if len(blockID.Hash) == 0 { 1431 if cs.LockedBlock == nil { 1432 logger.Debug("precommit step; +2/3 prevoted for nil") 1433 } else { 1434 logger.Debug("precommit step; +2/3 prevoted for nil; unlocking") 1435 cs.LockedRound = -1 1436 cs.LockedBlock = nil 1437 cs.LockedBlockParts = nil 1438 1439 if err := cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()); err != nil { 1440 logger.Error("failed publishing event unlock", "err", err) 1441 } 1442 } 1443 1444 cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{}) 1445 return 1446 } 1447 1448 // At this point, +2/3 prevoted for a particular block. 1449 1450 // If we're already locked on that block, precommit it, and update the LockedRound 1451 if cs.LockedBlock.HashesTo(blockID.Hash) && cs.LockedBlockParts.HasHeader(blockID.PartSetHeader) { 1452 logger.Debug("precommit step; +2/3 prevoted locked block; relocking") 1453 cs.LockedRound = round 1454 1455 if err := cs.eventBus.PublishEventRelock(cs.RoundStateEvent()); err != nil { 1456 logger.Error("failed publishing event relock", "err", err) 1457 } 1458 1459 cs.signAddVote(tmproto.PrecommitType, blockID.Hash, blockID.PartSetHeader) 1460 return 1461 } 1462 1463 // If +2/3 prevoted for proposal block, stage and precommit it 1464 if cs.ProposalBlock.HashesTo(blockID.Hash) && cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) { 1465 logger.Debug("precommit step; +2/3 prevoted proposal block; locking", "hash", blockID.Hash) 1466 1467 // Validate the block. 1468 if err := cs.blockExec.ValidateBlock(cs.state, round, cs.ProposalBlock); err != nil { 1469 cs.Logger.Error(fmt.Sprintf("%v; block=%v", err, cs.ProposalBlock)) 1470 panic(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err)) 1471 } 1472 1473 cs.LockedRound = round 1474 cs.LockedBlock = cs.ProposalBlock 1475 cs.LockedBlockParts = cs.ProposalBlockParts 1476 1477 if err := cs.eventBus.PublishEventLock(cs.RoundStateEvent()); err != nil { 1478 logger.Error("failed publishing event lock", "err", err) 1479 } 1480 1481 cs.signAddVote(tmproto.PrecommitType, blockID.Hash, blockID.PartSetHeader) 1482 return 1483 } 1484 1485 // There was a polka in this round for a block we don't have. 1486 // Fetch that block, unlock, and precommit nil. 1487 // The +2/3 prevotes for this round is the POL for our unlock. 1488 logger.Debug("precommit step; +2/3 prevotes for a block we do not have; voting nil", "block_id", blockID) 1489 1490 cs.LockedRound = -1 1491 cs.LockedBlock = nil 1492 cs.LockedBlockParts = nil 1493 1494 if !cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) { 1495 cs.ProposalBlock = nil 1496 cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader) 1497 } 1498 1499 if err := cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()); err != nil { 1500 logger.Error("failed publishing event unlock", "err", err) 1501 } 1502 1503 cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{}) 1504 } 1505 1506 // Enter: any +2/3 precommits for next round. 1507 func (cs *State) enterPrecommitWait(height int64, round int32) { 1508 logger := cs.Logger.With("height", height, "round", round) 1509 1510 if cs.Height != height || round < cs.Round || (cs.Round == round && cs.TriggeredTimeoutPrecommit) { 1511 logger.Debug( 1512 "entering precommit wait step with invalid args", 1513 "triggered_timeout", cs.TriggeredTimeoutPrecommit, 1514 "current", log.NewLazySprintf("%v/%v", cs.Height, cs.Round), 1515 ) 1516 return 1517 } 1518 1519 if !cs.Votes.Precommits(round).HasTwoThirdsAny() { 1520 panic(fmt.Sprintf( 1521 "entering precommit wait step (%v/%v), but precommits does not have any +2/3 votes", 1522 height, round, 1523 )) 1524 } 1525 1526 logger.Debug("entering precommit wait step", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) 1527 1528 defer func() { 1529 // Done enterPrecommitWait: 1530 cs.TriggeredTimeoutPrecommit = true 1531 cs.newStep() 1532 }() 1533 1534 // wait for some more precommits; enterNewRound 1535 cs.scheduleTimeout(cs.config.Precommit(round), height, round, cstypes.RoundStepPrecommitWait) 1536 } 1537 1538 // Enter: +2/3 precommits for block 1539 func (cs *State) enterCommit(height int64, commitRound int32) { 1540 logger := cs.Logger.With("height", height, "commit_round", commitRound) 1541 1542 if cs.Height != height || cstypes.RoundStepCommit <= cs.Step { 1543 logger.Debug( 1544 "entering commit step with invalid args", 1545 "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step), 1546 ) 1547 return 1548 } 1549 1550 logger.Debug("entering commit step", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) 1551 1552 defer func() { 1553 // Done enterCommit: 1554 // keep cs.Round the same, commitRound points to the right Precommits set. 1555 cs.updateRoundStep(cs.Round, cstypes.RoundStepCommit) 1556 cs.CommitRound = commitRound 1557 cs.CommitTime = tmtime.Now() 1558 cs.newStep() 1559 1560 // Maybe finalize immediately. 1561 cs.tryFinalizeCommit(height) 1562 }() 1563 1564 blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority() 1565 if !ok { 1566 panic("RunActionCommit() expects +2/3 precommits") 1567 } 1568 1569 // The Locked* fields no longer matter. 1570 // Move them over to ProposalBlock if they match the commit hash, 1571 // otherwise they'll be cleared in updateToState. 1572 if cs.LockedBlock.HashesTo(blockID.Hash) && cs.LockedBlockParts.HasHeader(blockID.PartSetHeader) { 1573 logger.Debug("commit is for a locked block; set ProposalBlock=LockedBlock", "block_hash", blockID.Hash) 1574 cs.ProposalBlock = cs.LockedBlock 1575 cs.ProposalBlockParts = cs.LockedBlockParts 1576 } 1577 1578 // If we don't have the block being committed, set up to get it. 1579 if !cs.ProposalBlock.HashesTo(blockID.Hash) { 1580 if !cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) { 1581 logger.Info( 1582 "commit is for a block we do not know about; set ProposalBlock=nil", 1583 "proposal", log.NewLazyBlockHash(cs.ProposalBlock), 1584 "commit", blockID.Hash, 1585 ) 1586 1587 // We're getting the wrong block. 1588 // Set up ProposalBlockParts and keep waiting. 1589 cs.ProposalBlock = nil 1590 cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader) 1591 1592 if err := cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent()); err != nil { 1593 logger.Error("failed publishing valid block", "err", err) 1594 } 1595 1596 cs.evsw.FireEvent(types.EventValidBlock, &cs.RoundState) 1597 } 1598 } 1599 } 1600 1601 // If we have the block AND +2/3 commits for it, finalize. 1602 func (cs *State) tryFinalizeCommit(height int64) { 1603 logger := cs.Logger.With("height", height) 1604 1605 if cs.Height != height { 1606 panic(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height)) 1607 } 1608 1609 blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() 1610 if !ok || len(blockID.Hash) == 0 { 1611 logger.Error("failed attempt to finalize commit; there was no +2/3 majority or +2/3 was for nil") 1612 return 1613 } 1614 1615 if !cs.ProposalBlock.HashesTo(blockID.Hash) || !cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) { 1616 // TODO: this happens every time if we're not a validator (ugly logs) 1617 // TODO: ^^ wait, why does it matter that we're a validator? 1618 logger.Debug( 1619 "failed attempt to finalize commit; we do not have the commit block", 1620 "proposal_block", log.NewLazyBlockHash(cs.ProposalBlock), 1621 "commit_block", blockID.Hash, 1622 ) 1623 return 1624 } 1625 1626 cs.finalizeCommit(height) 1627 } 1628 1629 // Increment height and goto cstypes.RoundStepNewHeight 1630 func (cs *State) finalizeCommit(height int64) { 1631 logger := cs.Logger.With("height", height) 1632 1633 if cs.Height != height || cs.Step != cstypes.RoundStepCommit { 1634 logger.Debug( 1635 "entering finalize commit step", 1636 "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step), 1637 ) 1638 return 1639 } 1640 1641 cs.calculatePrevoteMessageDelayMetrics() 1642 1643 blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() 1644 block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts 1645 1646 if !ok { 1647 panic("cannot finalize commit; commit does not have 2/3 majority") 1648 } 1649 if !blockParts.HasHeader(blockID.PartSetHeader) { 1650 panic("expected ProposalBlockParts header to be commit header") 1651 } 1652 if !block.HashesTo(blockID.Hash) { 1653 panic("cannot finalize commit; proposal block does not hash to commit hash") 1654 } 1655 if err := cs.blockExec.ValidateBlock(cs.state, cs.CommitRound, block); err != nil { 1656 panic(fmt.Sprintf("+2/3 committed an invalid block: %v", err)) 1657 } 1658 1659 logger.Info( 1660 "finalizing commit of block", 1661 "hash", log.NewLazyBlockHash(block), 1662 "root", block.AppHash, 1663 "num_txs", len(block.Txs), 1664 ) 1665 logger.Debug("committed block", "block", log.NewLazySprintf("%v", block)) 1666 1667 fail.Fail() // XXX 1668 1669 // Save to blockStore. 1670 if cs.blockStore.Height() < block.Height { 1671 // NOTE: the seenCommit is local justification to commit this block, 1672 // but may differ from the LastCommit included in the next block 1673 precommits := cs.Votes.Precommits(cs.CommitRound) 1674 seenCommit := precommits.MakeCommit() 1675 cs.blockStore.SaveBlock(block, blockParts, seenCommit) 1676 } else { 1677 // Happens during replay if we already saved the block but didn't commit 1678 logger.Debug("calling finalizeCommit on already stored block", "height", block.Height) 1679 } 1680 1681 fail.Fail() // XXX 1682 1683 // Write EndHeightMessage{} for this height, implying that the blockstore 1684 // has saved the block. 1685 // 1686 // If we crash before writing this EndHeightMessage{}, we will recover by 1687 // running ApplyBlock during the ABCI handshake when we restart. If we 1688 // didn't save the block to the blockstore before writing 1689 // EndHeightMessage{}, we'd have to change WAL replay -- currently it 1690 // complains about replaying for heights where an #ENDHEIGHT entry already 1691 // exists. 1692 // 1693 // Either way, the State should not be resumed until we 1694 // successfully call ApplyBlock (ie. later here, or in Handshake after 1695 // restart). 1696 endMsg := EndHeightMessage{height} 1697 if err := cs.wal.WriteSync(endMsg); err != nil { // NOTE: fsync 1698 panic(fmt.Sprintf( 1699 "failed to write %v msg to consensus WAL due to %v; check your file system and restart the node", 1700 endMsg, err, 1701 )) 1702 } 1703 1704 fail.Fail() // XXX 1705 1706 // Create a copy of the state for staging and an event cache for txs. 1707 stateCopy := cs.state.Copy() 1708 1709 // Execute and commit the block, update and save the state, and update the mempool. 1710 // NOTE The block.AppHash wont reflect these txs until the next block. 1711 var ( 1712 err error 1713 retainHeight int64 1714 ) 1715 1716 cs.stepTimes.ToCommitExecuting() 1717 stateCopy, retainHeight, err = cs.blockExec.ApplyBlock( 1718 stateCopy, 1719 types.BlockID{ 1720 Hash: block.Hash(), 1721 PartSetHeader: blockParts.Header(), 1722 }, 1723 block, 1724 &cs.stepTimes.CommitStepTimes, 1725 ) 1726 if err != nil { 1727 logger.Error("failed to apply block", "err", err) 1728 return 1729 } 1730 1731 fail.Fail() // XXX 1732 1733 // Prune old heights, if requested by ABCI app. 1734 if retainHeight > 0 { 1735 pruned, err := cs.pruneBlocks(retainHeight) 1736 if err != nil { 1737 logger.Error("failed to prune blocks", "retain_height", retainHeight, "err", err) 1738 } else { 1739 logger.Debug("pruned blocks", "pruned", pruned, "retain_height", retainHeight) 1740 } 1741 } 1742 1743 cs.stepTimes.EndRound() 1744 1745 // must be called before we update state 1746 cs.recordMetrics(height, block) 1747 1748 // NewHeightStep! 1749 cs.updateToState(stateCopy) 1750 fail.Fail() // XXX 1751 1752 // Private validator might have changed it's key pair => refetch pubkey. 1753 if err := cs.updatePrivValidatorPubKey(); err != nil { 1754 logger.Error("failed to get private validator pubkey", "err", err) 1755 } 1756 1757 // cs.StartTime is already set. 1758 // Schedule Round0 to start soon. 1759 cs.scheduleRound0(&cs.RoundState) 1760 1761 // By here, 1762 // * cs.Height has been increment to height+1 1763 // * cs.Step is now cstypes.RoundStepNewHeight 1764 // * cs.StartTime is set to when we will start round0. 1765 1766 cs.stepTimes.StartWaiting() 1767 } 1768 1769 func (cs *State) pruneBlocks(retainHeight int64) (uint64, error) { 1770 base := cs.blockStore.Base() 1771 if retainHeight <= base { 1772 return 0, nil 1773 } 1774 pruned, err := cs.blockStore.PruneBlocks(retainHeight) 1775 if err != nil { 1776 return 0, fmt.Errorf("failed to prune block store: %w", err) 1777 } 1778 err = cs.blockExec.Store().PruneStates(base, retainHeight) 1779 if err != nil { 1780 return 0, fmt.Errorf("failed to prune state database: %w", err) 1781 } 1782 return pruned, nil 1783 } 1784 1785 func (cs *State) recordMetrics(height int64, block *types.Block) { 1786 cs.metrics.Validators.Set(float64(cs.Validators.Size())) 1787 cs.metrics.ValidatorsPower.Set(float64(cs.Validators.TotalVotingPower())) 1788 1789 var ( 1790 missingValidators int 1791 missingValidatorsPower int64 1792 ) 1793 // height=0 -> MissingValidators and MissingValidatorsPower are both 0. 1794 // Remember that the first LastCommit is intentionally empty, so it's not 1795 // fair to increment missing validators number. 1796 if height > cs.state.InitialHeight { 1797 // Sanity check that commit size matches validator set size - only applies 1798 // after first block. 1799 var ( 1800 commitSize = block.LastCommit.Size() 1801 valSetLen = len(cs.LastValidators.Validators) 1802 address types.Address 1803 ) 1804 if commitSize != valSetLen { 1805 panic(fmt.Sprintf("commit size (%d) doesn't match valset length (%d) at height %d\n\n%v\n\n%v", 1806 commitSize, valSetLen, block.Height, block.LastCommit.Signatures, cs.LastValidators.Validators)) 1807 } 1808 1809 if cs.privValidator != nil { 1810 if cs.privValidatorPubKey == nil { 1811 // Metrics won't be updated, but it's not critical. 1812 cs.Logger.Error(fmt.Sprintf("recordMetrics: %v", errPubKeyIsNotSet)) 1813 } else { 1814 address = cs.privValidatorPubKey.Address() 1815 } 1816 } 1817 1818 if cs.privValidator != nil { 1819 pubkey, err := cs.privValidator.GetPubKey() 1820 if err != nil { 1821 // Metrics won't be updated, but it's not critical. 1822 cs.Logger.Error("Error on retrieval of pubkey", "err", err) 1823 } else { 1824 address = pubkey.Address() 1825 } 1826 } 1827 1828 for i, val := range cs.LastValidators.Validators { 1829 commitSig := block.LastCommit.Signatures[i] 1830 if commitSig.Absent() { 1831 missingValidators++ 1832 missingValidatorsPower += val.VotingPower 1833 } 1834 1835 if bytes.Equal(val.Address, address) { 1836 label := []string{ 1837 "validator_address", val.Address.String(), 1838 } 1839 cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower)) 1840 if commitSig.ForBlock() { 1841 cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height)) 1842 } else { 1843 cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1)) 1844 } 1845 } 1846 1847 } 1848 } 1849 cs.metrics.MissingValidators.Set(float64(missingValidators)) 1850 cs.metrics.MissingValidatorsPower.Set(float64(missingValidatorsPower)) 1851 1852 // NOTE: byzantine validators power and count is only for consensus evidence i.e. duplicate vote 1853 var ( 1854 byzantineValidatorsPower = int64(0) 1855 byzantineValidatorsCount = int64(0) 1856 ) 1857 for _, ev := range block.Evidence.Evidence { 1858 if dve, ok := ev.(*types.DuplicateVoteEvidence); ok { 1859 if _, val := cs.Validators.GetByAddress(dve.VoteA.ValidatorAddress); val != nil { 1860 byzantineValidatorsCount++ 1861 byzantineValidatorsPower += val.VotingPower 1862 } 1863 } 1864 } 1865 cs.metrics.ByzantineValidators.Set(float64(byzantineValidatorsCount)) 1866 cs.metrics.ByzantineValidatorsPower.Set(float64(byzantineValidatorsPower)) 1867 1868 if height > 1 { 1869 lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1) 1870 if lastBlockMeta != nil { 1871 cs.metrics.BlockIntervalSeconds.Set( 1872 block.Time.Sub(lastBlockMeta.Header.Time).Seconds(), 1873 ) 1874 } 1875 } 1876 1877 cs.metrics.NumTxs.Set(float64(len(block.Data.Txs))) 1878 cs.metrics.TotalTxs.Add(float64(len(block.Data.Txs))) 1879 cs.metrics.BlockSizeBytes.Set(float64(block.Size())) 1880 cs.metrics.CommittedHeight.Set(float64(block.Height)) 1881 1882 cs.metrics.RoundFailures.Observe(float64(cs.Round)) 1883 cs.metrics.DurationProposal.Observe(cs.stepTimes.Proposal.GetDuration()) 1884 cs.metrics.DurationPrevote.Observe(cs.stepTimes.Prevote.GetDuration()) 1885 cs.metrics.DurationPrecommit.Observe(cs.stepTimes.Precommit.GetDuration()) 1886 cs.metrics.DurationCommitExecuting.Observe(cs.stepTimes.CommitExecuting.GetDuration()) 1887 cs.metrics.DurationCommitCommitting.Observe(cs.stepTimes.CommitCommitting.GetDuration()) 1888 cs.metrics.DurationCommitRechecking.Observe(cs.stepTimes.CommitRechecking.GetDuration()) 1889 cs.metrics.DurationWaitingForNewRound.Observe(cs.stepTimes.WaitingForNewRound.GetDuration()) 1890 1891 cs.metrics.DurationGaugeProposal.Set(cs.stepTimes.Proposal.GetDuration()) 1892 cs.metrics.DurationGaugePrevote.Set(cs.stepTimes.Prevote.GetDuration()) 1893 cs.metrics.DurationGaugePrecommit.Set(cs.stepTimes.Precommit.GetDuration()) 1894 cs.metrics.DurationGaugeCommitExecuting.Set(cs.stepTimes.CommitExecuting.GetDuration()) 1895 cs.metrics.DurationGaugeCommitCommitting.Set(cs.stepTimes.CommitCommitting.GetDuration()) 1896 cs.metrics.DurationGaugeCommitRechecking.Set(cs.stepTimes.CommitRechecking.GetDuration()) 1897 cs.metrics.DurationGaugeWaitingForNewRound.Set(cs.stepTimes.WaitingForNewRound.GetDuration()) 1898 } 1899 1900 //----------------------------------------------------------------------------- 1901 1902 func (cs *State) defaultSetProposal(proposal *types.Proposal) error { 1903 // Already have one 1904 // TODO: possibly catch double proposals 1905 if cs.Proposal != nil { 1906 return nil 1907 } 1908 1909 // Does not apply 1910 if proposal.Height != cs.Height || proposal.Round != cs.Round { 1911 return nil 1912 } 1913 1914 // Verify POLRound, which must be -1 or in range [0, proposal.Round). 1915 if proposal.POLRound < -1 || 1916 (proposal.POLRound >= 0 && proposal.POLRound >= proposal.Round) { 1917 return ErrInvalidProposalPOLRound 1918 } 1919 1920 // If consensus does not enterNewRound yet, cs.Proposer may be nil or prior proposer, so don't use cs.Proposer 1921 proposer := cs.Validators.SelectProposer(cs.state.LastProofHash, proposal.Height, proposal.Round) 1922 1923 p := proposal.ToProto() 1924 // Verify signature 1925 if !proposer.PubKey.VerifySignature( 1926 types.ProposalSignBytes(cs.state.ChainID, p), proposal.Signature, 1927 ) { 1928 cs.Logger.Error(fmt.Sprintf("proposal signature verification failed: proposer=%X, bytes=%X, signature=%X", 1929 cs.Proposer.Address, types.ProposalSignBytes(cs.state.ChainID, p), 1930 proposal.Signature)) 1931 return ErrInvalidProposalSignature 1932 } 1933 1934 proposal.Signature = p.Signature 1935 cs.Proposal = proposal 1936 // We don't update cs.ProposalBlockParts if it is already set. 1937 // This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round. 1938 // TODO: We can check if Proposal is for a different block as this is a sign of misbehavior! 1939 if cs.ProposalBlockParts == nil { 1940 cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockID.PartSetHeader) 1941 } 1942 1943 cs.Logger.Info("received proposal", "proposal", proposal) 1944 return nil 1945 } 1946 1947 // NOTE: block is not necessarily valid. 1948 // Asynchronously triggers either enterPrevote (before we timeout of propose) or tryFinalizeCommit, 1949 // once we have the full block. 1950 func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.ID) (added bool, err error) { 1951 height, round, part := msg.Height, msg.Round, msg.Part 1952 1953 // Blocks might be reused, so round mismatch is OK 1954 if cs.Height != height { 1955 cs.Logger.Debug("received block part from wrong height", "height", height, "round", round) 1956 return false, nil 1957 } 1958 1959 // We're not expecting a block part. 1960 if cs.ProposalBlockParts == nil { 1961 // NOTE: this can happen when we've gone to a higher round and 1962 // then receive parts from the previous round - not necessarily a bad peer. 1963 cs.Logger.Debug( 1964 "received a block part when we are not expecting any", 1965 "height", height, 1966 "round", round, 1967 "index", part.Index, 1968 "peer", peerID, 1969 ) 1970 return false, nil 1971 } 1972 1973 added, err = cs.ProposalBlockParts.AddPart(part) 1974 if err != nil { 1975 return added, err 1976 } 1977 if cs.ProposalBlockParts.ByteSize() > cs.state.ConsensusParams.Block.MaxBytes { 1978 return added, fmt.Errorf("total size of proposal block parts exceeds maximum block bytes (%d > %d)", 1979 cs.ProposalBlockParts.ByteSize(), cs.state.ConsensusParams.Block.MaxBytes, 1980 ) 1981 } 1982 if added && cs.ProposalBlockParts.IsComplete() { 1983 bz, err := io.ReadAll(cs.ProposalBlockParts.GetReader()) 1984 if err != nil { 1985 return added, err 1986 } 1987 1988 pbb := new(ocproto.Block) 1989 err = proto.Unmarshal(bz, pbb) 1990 if err != nil { 1991 return added, err 1992 } 1993 1994 block, err := types.BlockFromProto(pbb) 1995 if err != nil { 1996 return added, err 1997 } 1998 1999 cs.ProposalBlock = block 2000 2001 // NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal 2002 cs.Logger.Info("received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) 2003 2004 if err := cs.eventBus.PublishEventCompleteProposal(cs.CompleteProposalEvent()); err != nil { 2005 cs.Logger.Error("failed publishing event complete proposal", "err", err) 2006 } 2007 } 2008 return added, nil 2009 } 2010 2011 func (cs *State) handleCompleteProposal(blockHeight int64) { 2012 // Update Valid* if we can. 2013 prevotes := cs.Votes.Prevotes(cs.Round) 2014 blockID, hasTwoThirds := prevotes.TwoThirdsMajority() 2015 if hasTwoThirds && !blockID.IsZero() && (cs.ValidRound < cs.Round) { 2016 if cs.ProposalBlock.HashesTo(blockID.Hash) && cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) { 2017 cs.Logger.Debug( 2018 "updating valid block to new proposal block", 2019 "valid_round", cs.Round, 2020 "valid_block_hash", log.NewLazyBlockHash(cs.ProposalBlock), 2021 ) 2022 2023 cs.ValidRound = cs.Round 2024 cs.ValidBlock = cs.ProposalBlock 2025 cs.ValidBlockParts = cs.ProposalBlockParts 2026 } 2027 // TODO: In case there is +2/3 majority in Prevotes set for some 2028 // block and cs.ProposalBlock contains different block, either 2029 // proposer is faulty or voting power of faulty processes is more 2030 // than 1/3. We should trigger in the future accountability 2031 // procedure at this point. 2032 } 2033 2034 if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() { 2035 // Move onto the next step 2036 cs.enterPrevote(blockHeight, cs.Round) 2037 if hasTwoThirds { // this is optimisation as this will be triggered when prevote is added 2038 cs.enterPrecommit(blockHeight, cs.Round) 2039 } 2040 } else if cs.Step == cstypes.RoundStepCommit { 2041 // If we're waiting on the proposal block... 2042 cs.tryFinalizeCommit(blockHeight) 2043 } 2044 } 2045 2046 // Attempt to add the vote. if its a duplicate signature, dupeout the validator 2047 func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) { 2048 added, err := cs.addVote(vote, peerID) 2049 if err != nil { 2050 // If the vote height is off, we'll just ignore it, 2051 // But if it's a conflicting sig, add it to the cs.evpool. 2052 // If it's otherwise invalid, punish peer. 2053 // nolint: gocritic 2054 if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok { 2055 if cs.privValidatorPubKey == nil { 2056 return false, errPubKeyIsNotSet 2057 } 2058 2059 if bytes.Equal(vote.ValidatorAddress, cs.privValidatorPubKey.Address()) { 2060 cs.Logger.Error( 2061 "found conflicting vote from ourselves; did you unsafe_reset a validator?", 2062 "height", vote.Height, 2063 "round", vote.Round, 2064 "type", vote.Type, 2065 ) 2066 2067 return added, err 2068 } 2069 2070 // report conflicting votes to the evidence pool 2071 cs.evpool.ReportConflictingVotes(voteErr.VoteA, voteErr.VoteB) 2072 cs.Logger.Debug( 2073 "found and sent conflicting votes to the evidence pool", 2074 "vote_a", voteErr.VoteA, 2075 "vote_b", voteErr.VoteB, 2076 ) 2077 2078 return added, err 2079 } else if errors.Is(err, types.ErrVoteNonDeterministicSignature) { 2080 cs.Logger.Debug("vote has non-deterministic signature", "err", err) 2081 } else { 2082 // Either 2083 // 1) bad peer OR 2084 // 2) not a bad peer? this can also err sometimes with "Unexpected step" OR 2085 // 3) tmkms use with multiple validators connecting to a single tmkms instance 2086 // (https://github.com/tendermint/tendermint/issues/3839). 2087 cs.Logger.Info("failed attempting to add vote", "err", err) 2088 return added, ErrAddingVote 2089 } 2090 } 2091 2092 return added, nil 2093 } 2094 2095 func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error) { 2096 cs.Logger.Debug( 2097 "adding vote", 2098 "vote_height", vote.Height, 2099 "vote_type", vote.Type, 2100 "val_index", vote.ValidatorIndex, 2101 "cs_height", cs.Height, 2102 ) 2103 2104 // A precommit for the previous height? 2105 // These come in while we wait timeoutCommit 2106 if vote.Height+1 == cs.Height && vote.Type == tmproto.PrecommitType { 2107 if cs.Step != cstypes.RoundStepNewHeight { 2108 // Late precommit at prior height is ignored 2109 cs.Logger.Debug("precommit vote came in after commit timeout and has been ignored", "vote", vote) 2110 return 2111 } 2112 2113 added, err = cs.LastCommit.AddVote(vote) 2114 if !added { 2115 return 2116 } 2117 2118 cs.Logger.Debug("added vote to last precommits", "last_commit", cs.LastCommit.StringShort()) 2119 if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil { 2120 return added, err 2121 } 2122 2123 cs.evsw.FireEvent(types.EventVote, vote) 2124 2125 // if we can skip timeoutCommit and have all the votes now, 2126 if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() { 2127 // go straight to new round (skip timeout commit) 2128 // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight) 2129 cs.enterNewRound(cs.Height, 0) 2130 } 2131 2132 return 2133 } 2134 2135 // Height mismatch is ignored. 2136 // Not necessarily a bad peer, but not favourable behaviour. 2137 if vote.Height != cs.Height { 2138 cs.Logger.Debug("vote ignored and not added", "vote_height", vote.Height, "cs_height", cs.Height, "peer", peerID) 2139 return 2140 } 2141 2142 height := cs.Height 2143 added, err = cs.Votes.AddVote(vote, peerID) 2144 if !added { 2145 // Either duplicate, or error upon cs.Votes.AddByIndex() 2146 return 2147 } 2148 2149 if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil { 2150 return added, err 2151 } 2152 cs.evsw.FireEvent(types.EventVote, vote) 2153 2154 switch vote.Type { 2155 case tmproto.PrevoteType: 2156 prevotes := cs.Votes.Prevotes(vote.Round) 2157 cs.Logger.Debug("added vote to prevote", "vote", vote, "prevotes", prevotes.StringShort()) 2158 2159 // If +2/3 prevotes for a block or nil for *any* round: 2160 if blockID, ok := prevotes.TwoThirdsMajority(); ok { 2161 // There was a polka! 2162 // If we're locked but this is a recent polka, unlock. 2163 // If it matches our ProposalBlock, update the ValidBlock 2164 2165 // Unlock if `cs.LockedRound < vote.Round <= cs.Round` 2166 // NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round 2167 if (cs.LockedBlock != nil) && 2168 (cs.LockedRound < vote.Round) && 2169 (vote.Round <= cs.Round) && 2170 !cs.LockedBlock.HashesTo(blockID.Hash) && 2171 !cs.LockedBlockParts.HasHeader(blockID.PartSetHeader) { 2172 2173 cs.Logger.Debug("unlocking because of POL", "locked_round", cs.LockedRound, "pol_round", vote.Round) 2174 2175 cs.LockedRound = -1 2176 cs.LockedBlock = nil 2177 cs.LockedBlockParts = nil 2178 2179 if err := cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()); err != nil { 2180 return added, err 2181 } 2182 } 2183 2184 // Update Valid* if we can. 2185 // NOTE: our proposal block may be nil or not what received a polka.. 2186 if len(blockID.Hash) != 0 && (cs.ValidRound < vote.Round) && (vote.Round == cs.Round) { 2187 if cs.ProposalBlock.HashesTo(blockID.Hash) { 2188 cs.Logger.Debug("updating valid block because of POL", "valid_round", cs.ValidRound, "pol_round", vote.Round) 2189 cs.ValidRound = vote.Round 2190 cs.ValidBlock = cs.ProposalBlock 2191 cs.ValidBlockParts = cs.ProposalBlockParts 2192 } else { 2193 cs.Logger.Debug( 2194 "valid block we do not know about; set ProposalBlock=nil", 2195 "proposal", log.NewLazyBlockHash(cs.ProposalBlock), 2196 "block_id", blockID.Hash, 2197 ) 2198 2199 // we're getting the wrong block 2200 cs.ProposalBlock = nil 2201 } 2202 2203 if !cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) { 2204 cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader) 2205 } 2206 2207 cs.evsw.FireEvent(types.EventValidBlock, &cs.RoundState) 2208 if err := cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent()); err != nil { 2209 return added, err 2210 } 2211 } 2212 } 2213 2214 // If +2/3 prevotes for *anything* for future round: 2215 switch { 2216 case cs.Round < vote.Round && prevotes.HasTwoThirdsAny(): 2217 // Round-skip if there is any 2/3+ of votes ahead of us 2218 cs.enterNewRound(height, vote.Round) 2219 2220 case cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step: // current round 2221 blockID, ok := prevotes.TwoThirdsMajority() 2222 if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) { 2223 cs.enterPrecommit(height, vote.Round) 2224 } else if prevotes.HasTwoThirdsAny() { 2225 cs.enterPrevoteWait(height, vote.Round) 2226 } 2227 2228 case cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round: 2229 // If the proposal is now complete, enter prevote of cs.Round. 2230 if cs.isProposalComplete() { 2231 cs.enterPrevote(height, cs.Round) 2232 } 2233 } 2234 2235 case tmproto.PrecommitType: 2236 precommits := cs.Votes.Precommits(vote.Round) 2237 cs.Logger.Debug("added vote to precommit", 2238 "height", vote.Height, 2239 "round", vote.Round, 2240 "validator", vote.ValidatorAddress.String(), 2241 "vote_timestamp", vote.Timestamp, 2242 "data", precommits.LogString()) 2243 2244 blockID, ok := precommits.TwoThirdsMajority() 2245 if ok { 2246 // Executed as TwoThirdsMajority could be from a higher round 2247 cs.enterNewRound(height, vote.Round) 2248 cs.enterPrecommit(height, vote.Round) 2249 2250 if len(blockID.Hash) != 0 { 2251 cs.enterCommit(height, vote.Round) 2252 if cs.config.SkipTimeoutCommit && precommits.HasAll() { 2253 cs.enterNewRound(cs.Height, 0) 2254 } 2255 } else { 2256 cs.enterPrecommitWait(height, vote.Round) 2257 } 2258 } else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { 2259 cs.enterNewRound(height, vote.Round) 2260 cs.enterPrecommitWait(height, vote.Round) 2261 } 2262 2263 default: 2264 panic(fmt.Sprintf("unexpected vote type %v", vote.Type)) 2265 } 2266 2267 return added, err 2268 } 2269 2270 // CONTRACT: cs.privValidator is not nil. 2271 func (cs *State) signVote( 2272 msgType tmproto.SignedMsgType, 2273 hash []byte, 2274 header types.PartSetHeader, 2275 ) (*types.Vote, error) { 2276 // Flush the WAL. Otherwise, we may not recompute the same vote to sign, 2277 // and the privValidator will refuse to sign anything. 2278 if err := cs.wal.FlushAndSync(); err != nil { 2279 return nil, err 2280 } 2281 2282 if cs.privValidatorPubKey == nil { 2283 return nil, errPubKeyIsNotSet 2284 } 2285 2286 addr := cs.privValidatorPubKey.Address() 2287 valIdx, _ := cs.Validators.GetByAddress(addr) 2288 2289 vote := &types.Vote{ 2290 ValidatorAddress: addr, 2291 ValidatorIndex: valIdx, 2292 Height: cs.Height, 2293 Round: cs.Round, 2294 Timestamp: cs.voteTime(), 2295 Type: msgType, 2296 BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, 2297 } 2298 2299 v := vote.ToProto() 2300 err := cs.privValidator.SignVote(cs.state.ChainID, v) 2301 vote.Signature = v.Signature 2302 vote.Timestamp = v.Timestamp 2303 2304 return vote, err 2305 } 2306 2307 func (cs *State) voteTime() time.Time { 2308 now := tmtime.Now() 2309 minVoteTime := now 2310 // TODO: We should remove next line in case we don't vote for v in case cs.ProposalBlock == nil, 2311 // even if cs.LockedBlock != nil. See https://github.com/tendermint/tendermint/tree/v0.34.x/spec/. 2312 timeIota := time.Duration(cs.state.ConsensusParams.Block.TimeIotaMs) * time.Millisecond 2313 if cs.LockedBlock != nil { 2314 // See the BFT time spec 2315 // https://github.com/tendermint/tendermint/blob/v0.34.x/spec/consensus/bft-time.md 2316 minVoteTime = cs.LockedBlock.Time.Add(timeIota) 2317 } else if cs.ProposalBlock != nil { 2318 minVoteTime = cs.ProposalBlock.Time.Add(timeIota) 2319 } 2320 2321 if now.After(minVoteTime) { 2322 return now 2323 } 2324 return minVoteTime 2325 } 2326 2327 // sign the vote and publish on internalMsgQueue 2328 func (cs *State) signAddVote(msgType tmproto.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote { 2329 if cs.privValidator == nil { // the node does not have a key 2330 return nil 2331 } 2332 2333 if cs.privValidatorPubKey == nil { 2334 // Vote won't be signed, but it's not critical. 2335 cs.Logger.Error(fmt.Sprintf("signAddVote: %v", errPubKeyIsNotSet)) 2336 return nil 2337 } 2338 2339 // If the node not in the validator set, do nothing. 2340 if !cs.Validators.HasAddress(cs.privValidatorPubKey.Address()) { 2341 return nil 2342 } 2343 2344 // TODO: pass pubKey to signVote 2345 vote, err := cs.signVote(msgType, hash, header) 2346 if err == nil { 2347 cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""}) 2348 cs.Logger.Debug("signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote) 2349 return vote 2350 } 2351 2352 cs.Logger.Error("failed signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err) 2353 return nil 2354 } 2355 2356 // updatePrivValidatorPubKey get's the private validator public key and 2357 // memoizes it. This func returns an error if the private validator is not 2358 // responding or responds with an error. 2359 func (cs *State) updatePrivValidatorPubKey() error { 2360 if cs.privValidator == nil { 2361 return nil 2362 } 2363 2364 pubKey, err := cs.privValidator.GetPubKey() 2365 if err != nil { 2366 return err 2367 } 2368 cs.privValidatorPubKey = pubKey 2369 return nil 2370 } 2371 2372 // look back to check existence of the node's consensus votes before joining consensus 2373 func (cs *State) checkDoubleSigningRisk(height int64) error { 2374 if cs.privValidator != nil && cs.privValidatorPubKey != nil && cs.config.DoubleSignCheckHeight > 0 && height > 0 { 2375 valAddr := cs.privValidatorPubKey.Address() 2376 doubleSignCheckHeight := cs.config.DoubleSignCheckHeight 2377 if doubleSignCheckHeight > height { 2378 doubleSignCheckHeight = height 2379 } 2380 2381 for i := int64(1); i < doubleSignCheckHeight; i++ { 2382 lastCommit := cs.blockStore.LoadSeenCommit(height - i) 2383 if lastCommit != nil { 2384 for sigIdx, s := range lastCommit.Signatures { 2385 if s.BlockIDFlag == types.BlockIDFlagCommit && bytes.Equal(s.ValidatorAddress, valAddr) { 2386 cs.Logger.Info("found signature from the same key", "sig", s, "idx", sigIdx, "height", height-i) 2387 return ErrSignatureFoundInPastBlocks 2388 } 2389 } 2390 } 2391 } 2392 } 2393 2394 return nil 2395 } 2396 2397 func (cs *State) calculatePrevoteMessageDelayMetrics() { 2398 if cs.Proposal == nil { 2399 return 2400 } 2401 2402 ps := cs.Votes.Prevotes(cs.Round) 2403 pl := ps.List() 2404 2405 sort.Slice(pl, func(i, j int) bool { 2406 return pl[i].Timestamp.Before(pl[j].Timestamp) 2407 }) 2408 2409 var votingPowerSeen int64 2410 for _, v := range pl { 2411 _, val := cs.Validators.GetByAddress(v.ValidatorAddress) 2412 votingPowerSeen += val.VotingPower 2413 if votingPowerSeen >= cs.Validators.TotalVotingPower()*2/3+1 { 2414 cs.metrics.QuorumPrevoteMessageDelay.Set(v.Timestamp.Sub(cs.Proposal.Timestamp).Seconds()) 2415 break 2416 } 2417 } 2418 if ps.HasAll() { 2419 cs.metrics.FullPrevoteMessageDelay.Set(pl[len(pl)-1].Timestamp.Sub(cs.Proposal.Timestamp).Seconds()) 2420 } 2421 } 2422 2423 //--------------------------------------------------------- 2424 2425 func CompareHRS(h1 int64, r1 int32, s1 cstypes.RoundStepType, h2 int64, r2 int32, s2 cstypes.RoundStepType) int { 2426 if h1 < h2 { 2427 return -1 2428 } else if h1 > h2 { 2429 return 1 2430 } 2431 if r1 < r2 { 2432 return -1 2433 } else if r1 > r2 { 2434 return 1 2435 } 2436 if s1 < s2 { 2437 return -1 2438 } else if s1 > s2 { 2439 return 1 2440 } 2441 return 0 2442 } 2443 2444 // repairWalFile decodes messages from src (until the decoder errors) and 2445 // writes them to dst. 2446 func repairWalFile(src, dst string) error { 2447 in, err := os.Open(src) 2448 if err != nil { 2449 return err 2450 } 2451 defer in.Close() 2452 2453 out, err := os.Create(dst) 2454 if err != nil { 2455 return err 2456 } 2457 defer out.Close() 2458 2459 var ( 2460 dec = NewWALDecoder(in) 2461 enc = NewWALEncoder(out) 2462 ) 2463 2464 // best-case repair (until first error is encountered) 2465 for { 2466 msg, err := dec.Decode() 2467 if err != nil { 2468 break 2469 } 2470 2471 err = enc.Encode(msg) 2472 if err != nil { 2473 return fmt.Errorf("failed to encode msg: %w", err) 2474 } 2475 } 2476 2477 return nil 2478 }