github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/consensus/reactor.go (about) 1 package consensus 2 3 import ( 4 "fmt" 5 "log/slog" 6 "reflect" 7 "sync" 8 "time" 9 10 "github.com/gnolang/gno/tm2/pkg/amino" 11 cstypes "github.com/gnolang/gno/tm2/pkg/bft/consensus/types" 12 sm "github.com/gnolang/gno/tm2/pkg/bft/state" 13 "github.com/gnolang/gno/tm2/pkg/bft/types" 14 tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time" 15 "github.com/gnolang/gno/tm2/pkg/bitarray" 16 "github.com/gnolang/gno/tm2/pkg/errors" 17 "github.com/gnolang/gno/tm2/pkg/events" 18 "github.com/gnolang/gno/tm2/pkg/log" 19 "github.com/gnolang/gno/tm2/pkg/p2p" 20 ) 21 22 const ( 23 StateChannel = byte(0x20) 24 DataChannel = byte(0x21) 25 VoteChannel = byte(0x22) 26 VoteSetBitsChannel = byte(0x23) 27 28 maxMsgSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes. 29 30 blocksToContributeToBecomeGoodPeer = 10000 31 votesToContributeToBecomeGoodPeer = 10000 32 ) 33 34 // ----------------------------------------------------------------------------- 35 36 // ConsensusReactor defines a reactor for the consensus service. 37 type ConsensusReactor struct { 38 p2p.BaseReactor // BaseService + p2p.Switch 39 40 conS *ConsensusState 41 42 mtx sync.RWMutex 43 fastSync bool 44 evsw events.EventSwitch 45 } 46 47 type ReactorOption func(*ConsensusReactor) 48 49 // NewConsensusReactor returns a new ConsensusReactor with the given 50 // consensusState. 51 func NewConsensusReactor(consensusState *ConsensusState, fastSync bool, options ...ReactorOption) *ConsensusReactor { 52 conR := &ConsensusReactor{ 53 conS: consensusState, 54 fastSync: fastSync, 55 evsw: events.NilEventSwitch(), 56 } 57 conR.BaseReactor = *p2p.NewBaseReactor("ConsensusReactor", conR) 58 59 for _, option := range options { 60 option(conR) 61 } 62 63 return conR 64 } 65 66 // OnStart implements BaseService by subscribing to events, which later will be 67 // broadcasted to other peers and starting state if we're not in fast sync. 68 func (conR *ConsensusReactor) OnStart() error { 69 conR.Logger.Info("ConsensusReactor ", "fastSync", conR.FastSync()) 70 71 // start routine that computes peer statistics for evaluating peer quality 72 go conR.peerStatsRoutine() 73 74 conR.subscribeToBroadcastEvents() 75 76 if !conR.FastSync() { 77 err := conR.conS.Start() 78 if err != nil { 79 return err 80 } 81 } 82 83 return nil 84 } 85 86 // OnStop implements BaseService by unsubscribing from events and stopping 87 // state. 88 func (conR *ConsensusReactor) OnStop() { 89 conR.unsubscribeFromBroadcastEvents() 90 conR.conS.Stop() 91 if !conR.FastSync() { 92 conR.conS.Wait() 93 } 94 } 95 96 // SwitchToConsensus switches from fast_sync mode to consensus mode. 97 // It resets the state, turns off fast_sync, and starts the consensus state-machine 98 func (conR *ConsensusReactor) SwitchToConsensus(state sm.State, blocksSynced int) { 99 conR.Logger.Info("SwitchToConsensus") 100 conR.conS.reconstructLastCommit(state) 101 // NOTE: The line below causes broadcastNewRoundStepRoutine() to 102 // broadcast a NewRoundStepMessage. 103 conR.conS.updateToState(state) 104 105 conR.mtx.Lock() 106 conR.fastSync = false 107 conR.mtx.Unlock() 108 109 if blocksSynced > 0 { 110 // dont bother with the WAL if we fast synced 111 conR.conS.doWALCatchup = false 112 } 113 err := conR.conS.Start() 114 if err != nil { 115 panic(fmt.Sprintf(`Failed to start consensus state: %v 116 117 conS: 118 %+v 119 120 conR: 121 %+v`, err, conR.conS, conR)) 122 } 123 } 124 125 // GetChannels implements Reactor 126 func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor { 127 // TODO optimize 128 return []*p2p.ChannelDescriptor{ 129 { 130 ID: StateChannel, 131 Priority: 5, 132 SendQueueCapacity: 100, 133 RecvMessageCapacity: maxMsgSize, 134 }, 135 { 136 ID: DataChannel, // maybe split between gossiping current block and catchup stuff 137 Priority: 10, // once we gossip the whole block there's nothing left to send until next height or round 138 SendQueueCapacity: 100, 139 RecvBufferCapacity: 50 * 4096, 140 RecvMessageCapacity: maxMsgSize, 141 }, 142 { 143 ID: VoteChannel, 144 Priority: 5, 145 SendQueueCapacity: 100, 146 RecvBufferCapacity: 100 * 100, 147 RecvMessageCapacity: maxMsgSize, 148 }, 149 { 150 ID: VoteSetBitsChannel, 151 Priority: 1, 152 SendQueueCapacity: 2, 153 RecvBufferCapacity: 1024, 154 RecvMessageCapacity: maxMsgSize, 155 }, 156 } 157 } 158 159 // InitPeer implements Reactor by creating a state for the peer. 160 func (conR *ConsensusReactor) InitPeer(peer p2p.Peer) p2p.Peer { 161 peerState := NewPeerState(peer).SetLogger(conR.Logger) 162 peer.Set(types.PeerStateKey, peerState) 163 return peer 164 } 165 166 // AddPeer implements Reactor by spawning multiple gossiping goroutines for the 167 // peer. 168 func (conR *ConsensusReactor) AddPeer(peer p2p.Peer) { 169 if !conR.IsRunning() { 170 return 171 } 172 173 peerState, ok := peer.Get(types.PeerStateKey).(*PeerState) 174 if !ok { 175 panic(fmt.Sprintf("peer %v has no state", peer)) 176 } 177 // Begin routines for this peer. 178 go conR.gossipDataRoutine(peer, peerState) 179 go conR.gossipVotesRoutine(peer, peerState) 180 go conR.queryMaj23Routine(peer, peerState) 181 182 // Send our state to peer. 183 // If we're fast_syncing, broadcast a RoundStepMessage later upon SwitchToConsensus(). 184 if !conR.FastSync() { 185 conR.sendNewRoundStepMessage(peer) 186 } 187 } 188 189 // RemovePeer is a noop. 190 func (conR *ConsensusReactor) RemovePeer(peer p2p.Peer, reason interface{}) { 191 if !conR.IsRunning() { 192 return 193 } 194 // TODO 195 // ps, ok := peer.Get(PeerStateKey).(*PeerState) 196 // if !ok { 197 // panic(fmt.Sprintf("Peer %v has no state", peer)) 198 // } 199 // ps.Disconnect() 200 } 201 202 // Receive implements Reactor 203 // NOTE: We process these messages even when we're fast_syncing. 204 // Messages affect either a peer state or the consensus state. 205 // Peer state updates can happen in parallel, but processing of 206 // proposals, block parts, and votes are ordered by the receiveRoutine 207 // NOTE: blocks on consensus state for proposals, block parts, and votes 208 func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { 209 if !conR.IsRunning() { 210 conR.Logger.Debug("Receive", "src", src, "chId", chID, "bytes", msgBytes) 211 return 212 } 213 214 msg, err := decodeMsg(msgBytes) 215 if err != nil { 216 conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) 217 conR.Switch.StopPeerForError(src, err) 218 return 219 } 220 221 if err = msg.ValidateBasic(); err != nil { 222 conR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err) 223 conR.Switch.StopPeerForError(src, err) 224 return 225 } 226 227 conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg) 228 229 // Get peer states 230 ps, ok := src.Get(types.PeerStateKey).(*PeerState) 231 if !ok { 232 panic(fmt.Sprintf("Peer %v has no state", src)) 233 } 234 235 switch chID { 236 case StateChannel: 237 switch msg := msg.(type) { 238 case *NewRoundStepMessage: 239 ps.ApplyNewRoundStepMessage(msg) 240 case *NewValidBlockMessage: 241 ps.ApplyNewValidBlockMessage(msg) 242 case *HasVoteMessage: 243 ps.ApplyHasVoteMessage(msg) 244 case *VoteSetMaj23Message: 245 cs := conR.conS 246 cs.mtx.Lock() 247 height, votes := cs.Height, cs.Votes 248 cs.mtx.Unlock() 249 if height != msg.Height { 250 return 251 } 252 // Peer claims to have a maj23 for some BlockID at H,R,S, 253 err := votes.SetPeerMaj23(msg.Round, msg.Type, ps.peer.ID(), msg.BlockID) 254 if err != nil { 255 conR.Switch.StopPeerForError(src, err) 256 return 257 } 258 // Respond with a VoteSetBitsMessage showing which votes we have. 259 // (and consequently shows which we don't have) 260 var ourVotes *bitarray.BitArray 261 switch msg.Type { 262 case types.PrevoteType: 263 ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID) 264 case types.PrecommitType: 265 ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID) 266 default: 267 panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?") 268 } 269 src.TrySend(VoteSetBitsChannel, amino.MustMarshalAny(&VoteSetBitsMessage{ 270 Height: msg.Height, 271 Round: msg.Round, 272 Type: msg.Type, 273 BlockID: msg.BlockID, 274 Votes: ourVotes, 275 })) 276 default: 277 conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) 278 } 279 280 case DataChannel: 281 if conR.FastSync() { 282 conR.Logger.Info("Ignoring message received during fastSync", "msg", msg) 283 return 284 } 285 switch msg := msg.(type) { 286 case *ProposalMessage: 287 ps.SetHasProposal(msg.Proposal) 288 conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()} 289 case *ProposalPOLMessage: 290 ps.ApplyProposalPOLMessage(msg) 291 case *BlockPartMessage: 292 ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index) 293 conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()} 294 default: 295 conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) 296 } 297 298 case VoteChannel: 299 if conR.FastSync() { 300 conR.Logger.Info("Ignoring message received during fastSync", "msg", msg) 301 return 302 } 303 switch msg := msg.(type) { 304 case *VoteMessage: 305 cs := conR.conS 306 cs.mtx.RLock() 307 height, valSize, lastCommitSize := cs.Height, cs.Validators.Size(), cs.LastCommit.Size() 308 cs.mtx.RUnlock() 309 ps.EnsureVoteBitArrays(height, valSize) 310 ps.EnsureVoteBitArrays(height-1, lastCommitSize) 311 ps.SetHasVote(msg.Vote) 312 313 cs.peerMsgQueue <- msgInfo{msg, src.ID()} 314 315 default: 316 // don't punish (leave room for soft upgrades) 317 conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) 318 } 319 320 case VoteSetBitsChannel: 321 if conR.FastSync() { 322 conR.Logger.Info("Ignoring message received during fastSync", "msg", msg) 323 return 324 } 325 switch msg := msg.(type) { 326 case *VoteSetBitsMessage: 327 cs := conR.conS 328 cs.mtx.Lock() 329 height, votes := cs.Height, cs.Votes 330 cs.mtx.Unlock() 331 332 if height == msg.Height { 333 var ourVotes *bitarray.BitArray 334 switch msg.Type { 335 case types.PrevoteType: 336 ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID) 337 case types.PrecommitType: 338 ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID) 339 default: 340 panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?") 341 } 342 ps.ApplyVoteSetBitsMessage(msg, ourVotes) 343 } else { 344 ps.ApplyVoteSetBitsMessage(msg, nil) 345 } 346 default: 347 // don't punish (leave room for soft upgrades) 348 conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) 349 } 350 351 default: 352 conR.Logger.Error(fmt.Sprintf("Unknown chId %X", chID)) 353 } 354 } 355 356 // SetEventSwitch sets event bus. 357 func (conR *ConsensusReactor) SetEventSwitch(evsw events.EventSwitch) { 358 conR.evsw = evsw 359 conR.conS.SetEventSwitch(evsw) 360 } 361 362 // FastSync returns whether the consensus reactor is in fast-sync mode. 363 func (conR *ConsensusReactor) FastSync() bool { 364 conR.mtx.RLock() 365 defer conR.mtx.RUnlock() 366 return conR.fastSync 367 } 368 369 // -------------------------------------- 370 371 // subscribeToBroadcastEvents subscribes for new round steps and votes 372 // using internal pubsub defined on state to broadcast 373 // them to peers upon receiving. 374 func (conR *ConsensusReactor) subscribeToBroadcastEvents() { 375 const subscriber = "consensus-reactor" 376 conR.conS.evsw.AddListener(subscriber, func(event events.Event) { 377 switch event := event.(type) { 378 case cstypes.EventNewRoundStep: 379 conR.broadcastNewRoundStepMessage(event) 380 case cstypes.EventNewValidBlock: 381 conR.broadcastNewValidBlockMessage(event) 382 case types.EventVote: 383 conR.broadcastHasVoteMessage(event.Vote) 384 } 385 }) 386 } 387 388 func (conR *ConsensusReactor) unsubscribeFromBroadcastEvents() { 389 const subscriber = "consensus-reactor" 390 conR.conS.evsw.RemoveListener(subscriber) 391 } 392 393 func (conR *ConsensusReactor) broadcastNewRoundStepMessage(event cstypes.EventNewRoundStep) { 394 nrsMsg := makeRoundStepMessage(event) 395 conR.Switch.Broadcast(StateChannel, amino.MustMarshalAny(nrsMsg)) 396 } 397 398 func (conR *ConsensusReactor) broadcastNewValidBlockMessage(event cstypes.EventNewValidBlock) { 399 csMsg := &NewValidBlockMessage{ 400 Height: event.Height, 401 Round: event.Round, 402 BlockPartsHeader: event.BlockPartsHeader, 403 BlockParts: event.BlockParts, 404 IsCommit: event.IsCommit, 405 } 406 conR.Switch.Broadcast(StateChannel, amino.MustMarshalAny(csMsg)) 407 } 408 409 // Broadcasts HasVoteMessage to peers that care. 410 func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) { 411 msg := &HasVoteMessage{ 412 Height: vote.Height, 413 Round: vote.Round, 414 Type: vote.Type, 415 Index: vote.ValidatorIndex, 416 } 417 conR.Switch.Broadcast(StateChannel, amino.MustMarshalAny(msg)) 418 /* 419 // TODO: Make this broadcast more selective. 420 for _, peer := range conR.Switch.Peers().List() { 421 ps, ok := peer.Get(PeerStateKey).(*PeerState) 422 if !ok { 423 panic(fmt.Sprintf("Peer %v has no state", peer)) 424 } 425 prs := ps.GetRoundState() 426 if prs.Height == vote.Height { 427 // TODO: Also filter on round? 428 peer.TrySend(StateChannel, struct{ ConsensusMessage }{msg}) 429 } else { 430 // Height doesn't match 431 // TODO: check a field, maybe CatchupCommitRound? 432 // TODO: But that requires changing the struct field comment. 433 } 434 } 435 */ 436 } 437 438 func makeRoundStepMessage(event cstypes.EventNewRoundStep) (nrsMsg *NewRoundStepMessage) { 439 nrsMsg = &NewRoundStepMessage{ 440 Height: event.Height, 441 Round: event.Round, 442 Step: event.Step, 443 SecondsSinceStartTime: event.SecondsSinceStartTime, 444 LastCommitRound: event.LastCommitRound, 445 } 446 return 447 } 448 449 func (conR *ConsensusReactor) sendNewRoundStepMessage(peer p2p.Peer) { 450 rs := conR.conS.GetRoundState() 451 nrsMsg := makeRoundStepMessage(rs.EventNewRoundStep()) 452 peer.Send(StateChannel, amino.MustMarshalAny(nrsMsg)) 453 } 454 455 func (conR *ConsensusReactor) gossipDataRoutine(peer p2p.Peer, ps *PeerState) { 456 logger := conR.Logger.With("peer", peer) 457 458 OUTER_LOOP: 459 for { 460 // Manage disconnects from self or peer. 461 if !peer.IsRunning() || !conR.IsRunning() { 462 logger.Info("Stopping gossipDataRoutine for peer") 463 return 464 } 465 rs := conR.conS.GetRoundState() 466 prs := ps.GetRoundState() 467 468 // Send proposal Block parts? 469 if rs.ProposalBlockParts.HasHeader(prs.ProposalBlockPartsHeader) { 470 if index, ok := rs.ProposalBlockParts.BitArray().Sub(prs.ProposalBlockParts.Copy()).PickRandom(); ok { 471 part := rs.ProposalBlockParts.GetPart(index) 472 msg := &BlockPartMessage{ 473 Height: rs.Height, // This tells peer that this part applies to us. 474 Round: rs.Round, // This tells peer that this part applies to us. 475 Part: part, 476 } 477 logger.Debug("Sending block part", "height", prs.Height, "round", prs.Round) 478 if peer.Send(DataChannel, amino.MustMarshalAny(msg)) { 479 ps.SetHasProposalBlockPart(prs.Height, prs.Round, index) 480 } 481 continue OUTER_LOOP 482 } 483 } 484 485 // If the peer is on a previous height, help catch up. 486 if (0 < prs.Height) && (prs.Height < rs.Height) { 487 heightLogger := logger.With("height", prs.Height) 488 489 // if we never received the commit message from the peer, the block parts wont be initialized 490 if prs.ProposalBlockParts == nil { 491 blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height) 492 if blockMeta == nil { 493 panic(fmt.Sprintf("Failed to load block %d when blockStore is at %d", 494 prs.Height, conR.conS.blockStore.Height())) 495 } 496 ps.InitProposalBlockParts(blockMeta.BlockID.PartsHeader) 497 // continue the loop since prs is a copy and not effected by this initialization 498 continue OUTER_LOOP 499 } 500 conR.gossipDataForCatchup(heightLogger, rs, prs, ps, peer) 501 continue OUTER_LOOP 502 } 503 504 // If height and round don't match, sleep. 505 if (rs.Height != prs.Height) || (rs.Round != prs.Round) { 506 // logger.Info("Peer Height|Round mismatch, sleeping", "peerHeight", prs.Height, "peerRound", prs.Round, "peer", peer) 507 time.Sleep(conR.conS.config.PeerGossipSleepDuration) 508 continue OUTER_LOOP 509 } 510 511 // By here, height and round match. 512 // Proposal block parts were already matched and sent if any were wanted. 513 // (These can match on hash so the round doesn't matter) 514 // Now consider sending other things, like the Proposal itself. 515 516 // Send Proposal && ProposalPOL BitArray? 517 if rs.Proposal != nil && !prs.Proposal { 518 // Proposal: share the proposal metadata with peer. 519 { 520 msg := &ProposalMessage{Proposal: rs.Proposal} 521 logger.Debug("Sending proposal", "height", prs.Height, "round", prs.Round) 522 if peer.Send(DataChannel, amino.MustMarshalAny(msg)) { 523 // NOTE[ZM]: A peer might have received different proposal msg so this Proposal msg will be rejected! 524 ps.SetHasProposal(rs.Proposal) 525 } 526 } 527 // ProposalPOL: lets peer know which POL votes we have so far. 528 // Peer must receive ProposalMessage first. 529 // rs.Proposal was validated, so rs.Proposal.POLRound <= rs.Round, 530 // so we definitely have rs.Votes.Prevotes(rs.Proposal.POLRound). 531 if 0 <= rs.Proposal.POLRound { 532 msg := &ProposalPOLMessage{ 533 Height: rs.Height, 534 ProposalPOLRound: rs.Proposal.POLRound, 535 ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(), 536 } 537 logger.Debug("Sending POL", "height", prs.Height, "round", prs.Round) 538 peer.Send(DataChannel, amino.MustMarshalAny(msg)) 539 } 540 continue OUTER_LOOP 541 } 542 543 // Nothing to do. Sleep. 544 time.Sleep(conR.conS.config.PeerGossipSleepDuration) 545 continue OUTER_LOOP 546 } 547 } 548 549 func (conR *ConsensusReactor) gossipDataForCatchup(logger *slog.Logger, rs *cstypes.RoundState, 550 prs *cstypes.PeerRoundState, ps *PeerState, peer p2p.Peer, 551 ) { 552 if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok { 553 // Ensure that the peer's PartSetHeader is correct 554 blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height) 555 if blockMeta == nil { 556 logger.Error("Failed to load block meta", 557 "ourHeight", rs.Height, "blockstoreHeight", conR.conS.blockStore.Height()) 558 time.Sleep(conR.conS.config.PeerGossipSleepDuration) 559 return 560 } else if !blockMeta.BlockID.PartsHeader.Equals(prs.ProposalBlockPartsHeader) { 561 logger.Info("Peer ProposalBlockPartsHeader mismatch, sleeping", 562 "blockPartsHeader", blockMeta.BlockID.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader) 563 time.Sleep(conR.conS.config.PeerGossipSleepDuration) 564 return 565 } 566 // Load the part 567 part := conR.conS.blockStore.LoadBlockPart(prs.Height, index) 568 if part == nil { 569 logger.Error("Could not load part", "index", index, 570 "blockPartsHeader", blockMeta.BlockID.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader) 571 time.Sleep(conR.conS.config.PeerGossipSleepDuration) 572 return 573 } 574 // Send the part 575 msg := &BlockPartMessage{ 576 Height: prs.Height, // Not our height, so it doesn't matter. 577 Round: prs.Round, // Not our height, so it doesn't matter. 578 Part: part, 579 } 580 logger.Debug("Sending block part for catchup", "round", prs.Round, "index", index) 581 if peer.Send(DataChannel, amino.MustMarshalAny(msg)) { 582 ps.SetHasProposalBlockPart(prs.Height, prs.Round, index) 583 } else { 584 logger.Debug("Sending block part for catchup failed") 585 } 586 return 587 } 588 // logger.Info("No parts to send in catch-up, sleeping") 589 time.Sleep(conR.conS.config.PeerGossipSleepDuration) 590 } 591 592 func (conR *ConsensusReactor) gossipVotesRoutine(peer p2p.Peer, ps *PeerState) { 593 logger := conR.Logger.With("peer", peer) 594 595 // Simple hack to throttle logs upon sleep. 596 sleeping := 0 597 598 OUTER_LOOP: 599 for { 600 // Manage disconnects from self or peer. 601 if !peer.IsRunning() || !conR.IsRunning() { 602 logger.Info("Stopping gossipVotesRoutine for peer") 603 return 604 } 605 rs := conR.conS.GetRoundState() 606 prs := ps.GetRoundState() 607 608 switch sleeping { 609 case 1: // First sleep 610 sleeping = 2 611 case 2: // No more sleep 612 sleeping = 0 613 } 614 615 // logger.Debug("gossipVotesRoutine", "rsHeight", rs.Height, "rsRound", rs.Round, 616 // "prsHeight", prs.Height, "prsRound", prs.Round, "prsStep", prs.Step) 617 618 // If height matches, then send LastCommit, Prevotes, Precommits. 619 if rs.Height == prs.Height { 620 heightLogger := logger.With("height", prs.Height) 621 if conR.gossipVotesForHeight(heightLogger, rs, prs, ps) { 622 continue OUTER_LOOP 623 } 624 } 625 626 // Special catchup logic. 627 // If peer is lagging by height 1, send LastCommit. 628 if prs.Height != 0 && rs.Height == prs.Height+1 { 629 if ps.PickSendVote(rs.LastCommit) { 630 logger.Debug("Picked rs.LastCommit to send", "height", prs.Height) 631 continue OUTER_LOOP 632 } 633 } 634 635 // Catchup logic 636 // If peer is lagging by more than 1, send Commit. 637 if prs.Height != 0 && rs.Height >= prs.Height+2 { 638 // Load the block commit for prs.Height, 639 // which contains precommit signatures for prs.Height. 640 commit := conR.conS.blockStore.LoadBlockCommit(prs.Height) 641 if ps.PickSendVote(commit) { 642 logger.Debug("Picked Catchup commit to send", "height", prs.Height) 643 continue OUTER_LOOP 644 } 645 } 646 647 if sleeping == 0 { 648 // We sent nothing. Sleep... 649 sleeping = 1 650 logger.Debug("No votes to send, sleeping", "rs.Height", rs.Height, "prs.Height", prs.Height, 651 "localPV", rs.Votes.Prevotes(rs.Round).BitArray(), "peerPV", prs.Prevotes, 652 "localPC", rs.Votes.Precommits(rs.Round).BitArray(), "peerPC", prs.Precommits) 653 } else if sleeping == 2 { 654 // Continued sleep... 655 sleeping = 1 656 } 657 658 time.Sleep(conR.conS.config.PeerGossipSleepDuration) 659 continue OUTER_LOOP 660 } 661 } 662 663 func (conR *ConsensusReactor) gossipVotesForHeight(logger *slog.Logger, rs *cstypes.RoundState, prs *cstypes.PeerRoundState, ps *PeerState) bool { 664 // If there are lastCommits to send... 665 if prs.Step == cstypes.RoundStepNewHeight { 666 if ps.PickSendVote(rs.LastCommit) { 667 logger.Debug("Picked rs.LastCommit to send") 668 return true 669 } 670 } 671 // If there are POL prevotes to send... 672 if prs.Step <= cstypes.RoundStepPropose && prs.Round != -1 && prs.Round <= rs.Round && prs.ProposalPOLRound != -1 { 673 if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil { 674 if ps.PickSendVote(polPrevotes) { 675 logger.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send", 676 "round", prs.ProposalPOLRound) 677 return true 678 } 679 } 680 } 681 // If there are prevotes to send... 682 if prs.Step <= cstypes.RoundStepPrevoteWait && prs.Round != -1 && prs.Round <= rs.Round { 683 if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) { 684 logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round) 685 return true 686 } 687 } 688 // If there are precommits to send... 689 if prs.Step <= cstypes.RoundStepPrecommitWait && prs.Round != -1 && prs.Round <= rs.Round { 690 if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) { 691 logger.Debug("Picked rs.Precommits(prs.Round) to send", "round", prs.Round) 692 return true 693 } 694 } 695 // If there are prevotes to send...Needed because of validBlock mechanism 696 if prs.Round != -1 && prs.Round <= rs.Round { 697 if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) { 698 logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round) 699 return true 700 } 701 } 702 // If there are POLPrevotes to send... 703 if prs.ProposalPOLRound != -1 { 704 if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil { 705 if ps.PickSendVote(polPrevotes) { 706 logger.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send", 707 "round", prs.ProposalPOLRound) 708 return true 709 } 710 } 711 } 712 713 return false 714 } 715 716 // NOTE: `queryMaj23Routine` has a simple crude design since it only comes 717 // into play for liveness when there's a signature DDoS attack happening. 718 func (conR *ConsensusReactor) queryMaj23Routine(peer p2p.Peer, ps *PeerState) { 719 logger := conR.Logger.With("peer", peer) 720 721 OUTER_LOOP: 722 for { 723 // Manage disconnects from self or peer. 724 if !peer.IsRunning() || !conR.IsRunning() { 725 logger.Info("Stopping queryMaj23Routine for peer") 726 return 727 } 728 729 // Maybe send Height/Round/Prevotes 730 { 731 rs := conR.conS.GetRoundState() 732 prs := ps.GetRoundState() 733 if rs.Height == prs.Height { 734 if maj23, ok := rs.Votes.Prevotes(prs.Round).TwoThirdsMajority(); ok { 735 peer.TrySend(StateChannel, amino.MustMarshalAny(&VoteSetMaj23Message{ 736 Height: prs.Height, 737 Round: prs.Round, 738 Type: types.PrevoteType, 739 BlockID: maj23, 740 })) 741 time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) 742 } 743 } 744 } 745 746 // Maybe send Height/Round/Precommits 747 { 748 rs := conR.conS.GetRoundState() 749 prs := ps.GetRoundState() 750 if rs.Height == prs.Height { 751 if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok { 752 peer.TrySend(StateChannel, amino.MustMarshalAny(&VoteSetMaj23Message{ 753 Height: prs.Height, 754 Round: prs.Round, 755 Type: types.PrecommitType, 756 BlockID: maj23, 757 })) 758 time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) 759 } 760 } 761 } 762 763 // Maybe send Height/Round/ProposalPOL 764 { 765 rs := conR.conS.GetRoundState() 766 prs := ps.GetRoundState() 767 if rs.Height == prs.Height && prs.ProposalPOLRound >= 0 { 768 if maj23, ok := rs.Votes.Prevotes(prs.ProposalPOLRound).TwoThirdsMajority(); ok { 769 peer.TrySend(StateChannel, amino.MustMarshalAny(&VoteSetMaj23Message{ 770 Height: prs.Height, 771 Round: prs.ProposalPOLRound, 772 Type: types.PrevoteType, 773 BlockID: maj23, 774 })) 775 time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) 776 } 777 } 778 } 779 780 // Little point sending LastCommitRound/LastCommit, 781 // These are fleeting and non-blocking. 782 783 // Maybe send Height/CatchupCommitRound/CatchupCommit. 784 { 785 prs := ps.GetRoundState() 786 if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= conR.conS.blockStore.Height() { 787 commit := conR.conS.LoadCommit(prs.Height) 788 peer.TrySend(StateChannel, amino.MustMarshalAny(&VoteSetMaj23Message{ 789 Height: prs.Height, 790 Round: commit.Round(), 791 Type: types.PrecommitType, 792 BlockID: commit.BlockID, 793 })) 794 time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) 795 } 796 } 797 798 time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) 799 800 continue OUTER_LOOP 801 } 802 } 803 804 func (conR *ConsensusReactor) peerStatsRoutine() { 805 for { 806 if !conR.IsRunning() { 807 conR.Logger.Info("Stopping peerStatsRoutine") 808 return 809 } 810 811 select { 812 case msg := <-conR.conS.statsMsgQueue: 813 // Get peer 814 peer := conR.Switch.Peers().Get(msg.PeerID) 815 if peer == nil { 816 conR.Logger.Debug("Attempt to update stats for non-existent peer", 817 "peer", msg.PeerID) 818 continue 819 } 820 // Get peer state 821 ps, ok := peer.Get(types.PeerStateKey).(*PeerState) 822 if !ok { 823 panic(fmt.Sprintf("Peer %v has no state", peer)) 824 } 825 switch msg.Msg.(type) { 826 case *VoteMessage: 827 if numVotes := ps.RecordVote(); numVotes%votesToContributeToBecomeGoodPeer == 0 { 828 // TODO: peer metrics. 829 // conR.Switch.MarkPeerAsGood(peer) 830 } 831 case *BlockPartMessage: 832 if numParts := ps.RecordBlockPart(); numParts%blocksToContributeToBecomeGoodPeer == 0 { 833 // TODO: peer metrics. 834 // conR.Switch.MarkPeerAsGood(peer) 835 } 836 } 837 case <-conR.conS.Quit(): 838 return 839 840 case <-conR.Quit(): 841 return 842 } 843 } 844 } 845 846 // String returns a string representation of the ConsensusReactor. 847 // NOTE: For now, it is just a hard-coded string to avoid accessing unprotected shared variables. 848 // TODO: improve! 849 func (conR *ConsensusReactor) String() string { 850 // better not to access shared variables 851 return "ConsensusReactor" // conR.StringIndented("") 852 } 853 854 // StringIndented returns an indented string representation of the ConsensusReactor 855 func (conR *ConsensusReactor) StringIndented(indent string) string { 856 s := "ConsensusReactor{\n" 857 s += indent + " " + conR.conS.StringIndented(indent+" ") + "\n" 858 for _, peer := range conR.Switch.Peers().List() { 859 ps, ok := peer.Get(types.PeerStateKey).(*PeerState) 860 if !ok { 861 panic(fmt.Sprintf("Peer %v has no state", peer)) 862 } 863 s += indent + " " + ps.StringIndented(indent+" ") + "\n" 864 } 865 s += indent + "}" 866 return s 867 } 868 869 // ----------------------------------------------------------------------------- 870 871 var ( 872 ErrPeerStateHeightRegression = errors.New("Error peer state height regression") 873 ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime") 874 ) 875 876 // PeerState contains the known state of a peer, including its connection and 877 // threadsafe access to its PeerRoundState. 878 // NOTE: PeerStateExposed gets dumped with rpc/core/consensus.go. 879 // Be mindful of what you Expose. 880 type PeerState struct { 881 peer p2p.Peer 882 logger *slog.Logger 883 884 mtx sync.Mutex // NOTE: Modify below using setters, never directly. 885 cstypes.PeerStateExposed 886 } 887 888 // NewPeerState returns a new PeerState for the given Peer 889 func NewPeerState(peer p2p.Peer) *PeerState { 890 return &PeerState{ 891 peer: peer, 892 logger: log.NewNoopLogger(), 893 PeerStateExposed: cstypes.PeerStateExposed{ 894 PRS: cstypes.PeerRoundState{ 895 Round: -1, 896 ProposalPOLRound: -1, 897 LastCommitRound: -1, 898 CatchupCommitRound: -1, 899 }, 900 Stats: &cstypes.PeerStateStats{}, 901 }, 902 } 903 } 904 905 // SetLogger allows to set a logger on the peer state. Returns the peer state 906 // itself. 907 func (ps *PeerState) SetLogger(logger *slog.Logger) *PeerState { 908 ps.logger = logger 909 return ps 910 } 911 912 // GetRoundState returns an shallow copy of the PeerRoundState. 913 // There's no point in mutating it since it won't change PeerState. 914 func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState { 915 ps.mtx.Lock() 916 defer ps.mtx.Unlock() 917 918 prs := ps.PRS // copy 919 return &prs 920 } 921 922 // GetExposed returns the PeerStateExposed. This method is type-asserted for 923 // working with rpc/core.go without an import. 924 // The returned value is read-only. 925 func (ps *PeerState) GetExposed() cstypes.PeerStateExposed { 926 ps.mtx.Lock() 927 defer ps.mtx.Unlock() 928 929 return ps.PeerStateExposed 930 } 931 932 // GetHeight returns an atomic snapshot of the PeerRoundState's height 933 // used by the mempool to ensure peers are caught up before broadcasting new txs 934 func (ps *PeerState) GetHeight() int64 { 935 ps.mtx.Lock() 936 defer ps.mtx.Unlock() 937 return ps.PRS.Height 938 } 939 940 // SetHasProposal sets the given proposal as known for the peer. 941 func (ps *PeerState) SetHasProposal(proposal *types.Proposal) { 942 ps.mtx.Lock() 943 defer ps.mtx.Unlock() 944 945 if ps.PRS.Height != proposal.Height || ps.PRS.Round != proposal.Round { 946 return 947 } 948 949 if ps.PRS.Proposal { 950 return 951 } 952 953 ps.PRS.Proposal = true 954 955 // ps.PRS.ProposalBlockParts is set due to NewValidBlockMessage 956 if ps.PRS.ProposalBlockParts != nil { 957 return 958 } 959 960 ps.PRS.ProposalBlockPartsHeader = proposal.BlockID.PartsHeader 961 ps.PRS.ProposalBlockParts = bitarray.NewBitArray(proposal.BlockID.PartsHeader.Total) 962 ps.PRS.ProposalPOLRound = proposal.POLRound 963 ps.PRS.ProposalPOL = nil // Nil until ProposalPOLMessage received. 964 } 965 966 // InitProposalBlockParts initializes the peer's proposal block parts header and bit array. 967 func (ps *PeerState) InitProposalBlockParts(partsHeader types.PartSetHeader) { 968 ps.mtx.Lock() 969 defer ps.mtx.Unlock() 970 971 if ps.PRS.ProposalBlockParts != nil { 972 return 973 } 974 975 ps.PRS.ProposalBlockPartsHeader = partsHeader 976 ps.PRS.ProposalBlockParts = bitarray.NewBitArray(partsHeader.Total) 977 } 978 979 // SetHasProposalBlockPart sets the given block part index as known for the peer. 980 func (ps *PeerState) SetHasProposalBlockPart(height int64, round int, index int) { 981 ps.mtx.Lock() 982 defer ps.mtx.Unlock() 983 984 if ps.PRS.Height != height || ps.PRS.Round != round { 985 return 986 } 987 988 ps.PRS.ProposalBlockParts.SetIndex(index, true) 989 } 990 991 // PickSendVote picks a vote and sends it to the peer. 992 // Returns true if vote was sent. 993 func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool { 994 if vote, ok := ps.PickVoteToSend(votes); ok { 995 msg := &VoteMessage{vote} 996 ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote) 997 if ps.peer.Send(VoteChannel, amino.MustMarshalAny(msg)) { 998 ps.SetHasVote(vote) 999 return true 1000 } 1001 return false 1002 } 1003 return false 1004 } 1005 1006 // PickVoteToSend picks a vote to send to the peer. 1007 // Returns true if a vote was picked. 1008 // NOTE: `votes` must be the correct Size() for the Height(). 1009 func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote, ok bool) { 1010 ps.mtx.Lock() 1011 defer ps.mtx.Unlock() 1012 1013 if votes.Size() == 0 { 1014 return nil, false 1015 } 1016 1017 height, round, type_, size := votes.Height(), votes.Round(), types.SignedMsgType(votes.Type()), votes.Size() 1018 1019 // Lazily set data using 'votes'. 1020 if votes.IsCommit() { 1021 ps.ensureCatchupCommitRound(height, round, size) 1022 } 1023 ps.ensureVoteBitArrays(height, size) 1024 1025 psVotes := ps.getVoteBitArray(height, round, type_) 1026 if psVotes == nil { 1027 return nil, false // Not something worth sending 1028 } 1029 if index, ok := votes.BitArray().Sub(psVotes).PickRandom(); ok { 1030 return votes.GetByIndex(index), true 1031 } 1032 return nil, false 1033 } 1034 1035 func (ps *PeerState) getVoteBitArray(height int64, round int, type_ types.SignedMsgType) *bitarray.BitArray { 1036 if !types.IsVoteTypeValid(type_) { 1037 return nil 1038 } 1039 1040 if ps.PRS.Height == height { 1041 if ps.PRS.Round == round { 1042 switch type_ { 1043 case types.PrevoteType: 1044 return ps.PRS.Prevotes 1045 case types.PrecommitType: 1046 return ps.PRS.Precommits 1047 } 1048 } 1049 if ps.PRS.CatchupCommitRound == round { 1050 switch type_ { 1051 case types.PrevoteType: 1052 return nil 1053 case types.PrecommitType: 1054 return ps.PRS.CatchupCommit 1055 } 1056 } 1057 if ps.PRS.ProposalPOLRound == round { 1058 switch type_ { 1059 case types.PrevoteType: 1060 return ps.PRS.ProposalPOL 1061 case types.PrecommitType: 1062 return nil 1063 } 1064 } 1065 return nil 1066 } 1067 if ps.PRS.Height == height+1 { 1068 if ps.PRS.LastCommitRound == round { 1069 switch type_ { 1070 case types.PrevoteType: 1071 return nil 1072 case types.PrecommitType: 1073 return ps.PRS.LastCommit 1074 } 1075 } 1076 return nil 1077 } 1078 return nil 1079 } 1080 1081 // 'round': A round for which we have a +2/3 commit. 1082 func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValidators int) { 1083 if ps.PRS.Height != height { 1084 return 1085 } 1086 /* 1087 NOTE: This is wrong, 'round' could change. 1088 e.g. if orig round is not the same as block LastCommit round. 1089 if ps.CatchupCommitRound != -1 && ps.CatchupCommitRound != round { 1090 panic(fmt.Sprintf("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round)) 1091 } 1092 */ 1093 if ps.PRS.CatchupCommitRound == round { 1094 return // Nothing to do! 1095 } 1096 ps.PRS.CatchupCommitRound = round 1097 if round == ps.PRS.Round { 1098 ps.PRS.CatchupCommit = ps.PRS.Precommits 1099 } else { 1100 ps.PRS.CatchupCommit = bitarray.NewBitArray(numValidators) 1101 } 1102 } 1103 1104 // EnsureVoteBitArrays ensures the bit-arrays have been allocated for tracking 1105 // what votes this peer has received. 1106 // NOTE: It's important to make sure that numValidators actually matches 1107 // what the node sees as the number of validators for height. 1108 func (ps *PeerState) EnsureVoteBitArrays(height int64, numValidators int) { 1109 ps.mtx.Lock() 1110 defer ps.mtx.Unlock() 1111 ps.ensureVoteBitArrays(height, numValidators) 1112 } 1113 1114 func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) { 1115 if ps.PRS.Height == height { 1116 if ps.PRS.Prevotes == nil { 1117 ps.PRS.Prevotes = bitarray.NewBitArray(numValidators) 1118 } 1119 if ps.PRS.Precommits == nil { 1120 ps.PRS.Precommits = bitarray.NewBitArray(numValidators) 1121 } 1122 if ps.PRS.CatchupCommit == nil { 1123 ps.PRS.CatchupCommit = bitarray.NewBitArray(numValidators) 1124 } 1125 if ps.PRS.ProposalPOL == nil { 1126 ps.PRS.ProposalPOL = bitarray.NewBitArray(numValidators) 1127 } 1128 } else if ps.PRS.Height == height+1 { 1129 if ps.PRS.LastCommit == nil { 1130 ps.PRS.LastCommit = bitarray.NewBitArray(numValidators) 1131 } 1132 } 1133 } 1134 1135 // RecordVote increments internal votes related statistics for this peer. 1136 // It returns the total number of added votes. 1137 func (ps *PeerState) RecordVote() int { 1138 ps.mtx.Lock() 1139 defer ps.mtx.Unlock() 1140 1141 ps.Stats.Votes++ 1142 1143 return ps.Stats.Votes 1144 } 1145 1146 // VotesSent returns the number of blocks for which peer has been sending us 1147 // votes. 1148 func (ps *PeerState) VotesSent() int { 1149 ps.mtx.Lock() 1150 defer ps.mtx.Unlock() 1151 1152 return ps.Stats.Votes 1153 } 1154 1155 // RecordBlockPart increments internal block part related statistics for this peer. 1156 // It returns the total number of added block parts. 1157 func (ps *PeerState) RecordBlockPart() int { 1158 ps.mtx.Lock() 1159 defer ps.mtx.Unlock() 1160 1161 ps.Stats.BlockParts++ 1162 return ps.Stats.BlockParts 1163 } 1164 1165 // BlockPartsSent returns the number of useful block parts the peer has sent us. 1166 func (ps *PeerState) BlockPartsSent() int { 1167 ps.mtx.Lock() 1168 defer ps.mtx.Unlock() 1169 1170 return ps.Stats.BlockParts 1171 } 1172 1173 // SetHasVote sets the given vote as known by the peer 1174 func (ps *PeerState) SetHasVote(vote *types.Vote) { 1175 ps.mtx.Lock() 1176 defer ps.mtx.Unlock() 1177 1178 ps.setHasVote(vote.Height, vote.Round, vote.Type, vote.ValidatorIndex) 1179 } 1180 1181 func (ps *PeerState) setHasVote(height int64, round int, type_ types.SignedMsgType, index int) { 1182 logger := ps.logger.With("peerH/R", fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", fmt.Sprintf("%d/%d", height, round)) 1183 logger.Debug("setHasVote", "type", type_, "index", index) 1184 1185 // NOTE: some may be nil BitArrays -> no side effects. 1186 psVotes := ps.getVoteBitArray(height, round, type_) 1187 if psVotes != nil { 1188 psVotes.SetIndex(index, true) 1189 } 1190 } 1191 1192 // ApplyNewRoundStepMessage updates the peer state for the new round. 1193 func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) { 1194 ps.mtx.Lock() 1195 defer ps.mtx.Unlock() 1196 1197 // Ignore duplicates or decreases 1198 if CompareHRS(msg.Height, msg.Round, msg.Step, ps.PRS.Height, ps.PRS.Round, ps.PRS.Step) <= 0 { 1199 return 1200 } 1201 1202 // Just remember these values. 1203 psHeight := ps.PRS.Height 1204 psRound := ps.PRS.Round 1205 psCatchupCommitRound := ps.PRS.CatchupCommitRound 1206 psCatchupCommit := ps.PRS.CatchupCommit 1207 1208 startTime := tmtime.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second) 1209 ps.PRS.Height = msg.Height 1210 ps.PRS.Round = msg.Round 1211 ps.PRS.Step = msg.Step 1212 ps.PRS.StartTime = startTime 1213 if psHeight != msg.Height || psRound != msg.Round { 1214 ps.PRS.Proposal = false 1215 ps.PRS.ProposalBlockPartsHeader = types.PartSetHeader{} 1216 ps.PRS.ProposalBlockParts = nil 1217 ps.PRS.ProposalPOLRound = -1 1218 ps.PRS.ProposalPOL = nil 1219 // We'll update the BitArray capacity later. 1220 ps.PRS.Prevotes = nil 1221 ps.PRS.Precommits = nil 1222 } 1223 if psHeight == msg.Height && psRound != msg.Round && msg.Round == psCatchupCommitRound { 1224 // Peer caught up to CatchupCommitRound. 1225 // Preserve psCatchupCommit! 1226 // NOTE: We prefer to use prs.Precommits if 1227 // pr.Round matches pr.CatchupCommitRound. 1228 ps.PRS.Precommits = psCatchupCommit 1229 } 1230 if psHeight != msg.Height { 1231 // Shift Precommits to LastCommit. 1232 if psHeight+1 == msg.Height && psRound == msg.LastCommitRound { 1233 ps.PRS.LastCommitRound = msg.LastCommitRound 1234 ps.PRS.LastCommit = ps.PRS.Precommits 1235 } else { 1236 ps.PRS.LastCommitRound = msg.LastCommitRound 1237 ps.PRS.LastCommit = nil 1238 } 1239 // We'll update the BitArray capacity later. 1240 ps.PRS.CatchupCommitRound = -1 1241 ps.PRS.CatchupCommit = nil 1242 } 1243 } 1244 1245 // ApplyNewValidBlockMessage updates the peer state for the new valid block. 1246 func (ps *PeerState) ApplyNewValidBlockMessage(msg *NewValidBlockMessage) { 1247 ps.mtx.Lock() 1248 defer ps.mtx.Unlock() 1249 1250 if ps.PRS.Height != msg.Height { 1251 return 1252 } 1253 1254 if ps.PRS.Round != msg.Round && !msg.IsCommit { 1255 return 1256 } 1257 1258 ps.PRS.ProposalBlockPartsHeader = msg.BlockPartsHeader 1259 ps.PRS.ProposalBlockParts = msg.BlockParts 1260 } 1261 1262 // ApplyProposalPOLMessage updates the peer state for the new proposal POL. 1263 func (ps *PeerState) ApplyProposalPOLMessage(msg *ProposalPOLMessage) { 1264 ps.mtx.Lock() 1265 defer ps.mtx.Unlock() 1266 1267 if ps.PRS.Height != msg.Height { 1268 return 1269 } 1270 if ps.PRS.ProposalPOLRound != msg.ProposalPOLRound { 1271 return 1272 } 1273 1274 // TODO: Merge onto existing ps.PRS.ProposalPOL? 1275 // We might have sent some prevotes in the meantime. 1276 ps.PRS.ProposalPOL = msg.ProposalPOL 1277 } 1278 1279 // ApplyHasVoteMessage updates the peer state for the new vote. 1280 func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) { 1281 ps.mtx.Lock() 1282 defer ps.mtx.Unlock() 1283 1284 if ps.PRS.Height != msg.Height { 1285 return 1286 } 1287 1288 ps.setHasVote(msg.Height, msg.Round, msg.Type, msg.Index) 1289 } 1290 1291 // ApplyVoteSetBitsMessage updates the peer state for the bit-array of votes 1292 // it claims to have for the corresponding BlockID. 1293 // `ourVotes` is a BitArray of votes we have for msg.BlockID 1294 // NOTE: if ourVotes is nil (e.g. msg.Height < rs.Height), 1295 // we conservatively overwrite ps's votes w/ msg.Votes. 1296 func (ps *PeerState) ApplyVoteSetBitsMessage(msg *VoteSetBitsMessage, ourVotes *bitarray.BitArray) { 1297 ps.mtx.Lock() 1298 defer ps.mtx.Unlock() 1299 1300 votes := ps.getVoteBitArray(msg.Height, msg.Round, msg.Type) 1301 if votes != nil { 1302 if ourVotes == nil { 1303 votes.Update(msg.Votes) 1304 } else { 1305 otherVotes := votes.Sub(ourVotes) 1306 hasVotes := otherVotes.Or(msg.Votes) 1307 votes.Update(hasVotes) 1308 } 1309 } 1310 } 1311 1312 // String returns a string representation of the PeerState 1313 func (ps *PeerState) String() string { 1314 return ps.StringIndented("") 1315 } 1316 1317 // StringIndented returns a string representation of the PeerState 1318 func (ps *PeerState) StringIndented(indent string) string { 1319 ps.mtx.Lock() 1320 defer ps.mtx.Unlock() 1321 return fmt.Sprintf(`PeerState{ 1322 %s Key %v 1323 %s RoundState %v 1324 %s Stats %v 1325 %s}`, 1326 indent, ps.peer.ID(), 1327 indent, ps.PRS.StringIndented(indent+" "), 1328 indent, ps.Stats, 1329 indent) 1330 } 1331 1332 // ----------------------------------------------------------------------------- 1333 // Messages 1334 1335 // ConsensusMessage is a message that can be sent and received on the ConsensusReactor 1336 type ConsensusMessage interface { 1337 ValidateBasic() error 1338 } 1339 1340 func decodeMsg(bz []byte) (msg ConsensusMessage, err error) { 1341 if len(bz) > maxMsgSize { 1342 return msg, fmt.Errorf("msg exceeds max size (%d > %d)", len(bz), maxMsgSize) 1343 } 1344 err = amino.Unmarshal(bz, &msg) 1345 return 1346 } 1347 1348 // ------------------------------------- 1349 1350 // NewRoundStepMessage is sent for every step taken in the ConsensusState. 1351 // For every height/round/step transition 1352 type NewRoundStepMessage struct { 1353 Height int64 1354 Round int 1355 Step cstypes.RoundStepType 1356 SecondsSinceStartTime int 1357 LastCommitRound int 1358 } 1359 1360 // ValidateBasic performs basic validation. 1361 func (m *NewRoundStepMessage) ValidateBasic() error { 1362 if m.Height < 0 { 1363 return errors.New("Negative Height") 1364 } 1365 if m.Round < 0 { 1366 return errors.New("Negative Round") 1367 } 1368 if !m.Step.IsValid() { 1369 return errors.New("Invalid Step") 1370 } 1371 1372 // NOTE: SecondsSinceStartTime may be negative 1373 1374 if (m.Height == 1 && m.LastCommitRound != -1) || 1375 (m.Height > 1 && m.LastCommitRound < -1) { // TODO: #2737 LastCommitRound should always be >= 0 for heights > 1 1376 return errors.New("Invalid LastCommitRound (for 1st block: -1, for others: >= 0)") 1377 } 1378 return nil 1379 } 1380 1381 // String returns a string representation. 1382 func (m *NewRoundStepMessage) String() string { 1383 return fmt.Sprintf("[NewRoundStep H:%v R:%v S:%v LCR:%v]", 1384 m.Height, m.Round, m.Step, m.LastCommitRound) 1385 } 1386 1387 // ------------------------------------- 1388 1389 // NewValidBlockMessage is sent when a validator observes a valid block B in some round r, 1390 // i.e., there is a Proposal for block B and 2/3+ prevotes for the block B in the round r. 1391 // In case the block is also committed, then IsCommit flag is set to true. 1392 type NewValidBlockMessage struct { 1393 Height int64 1394 Round int 1395 BlockPartsHeader types.PartSetHeader 1396 BlockParts *bitarray.BitArray 1397 IsCommit bool 1398 } 1399 1400 // ValidateBasic performs basic validation. 1401 func (m *NewValidBlockMessage) ValidateBasic() error { 1402 if m.Height < 0 { 1403 return errors.New("Negative Height") 1404 } 1405 if m.Round < 0 { 1406 return errors.New("Negative Round") 1407 } 1408 if err := m.BlockPartsHeader.ValidateBasic(); err != nil { 1409 return fmt.Errorf("wrong BlockPartsHeader: %w", err) 1410 } 1411 if m.BlockParts.Size() == 0 { 1412 return errors.New("Empty BlockParts") 1413 } 1414 if m.BlockParts.Size() != m.BlockPartsHeader.Total { 1415 return fmt.Errorf("BlockParts bit array size %d not equal to BlockPartsHeader.Total %d", 1416 m.BlockParts.Size(), 1417 m.BlockPartsHeader.Total) 1418 } 1419 if m.BlockParts.Size() > types.MaxBlockPartsCount { 1420 return errors.New("BlockParts bit array is too big: %d, max: %d", m.BlockParts.Size(), types.MaxBlockPartsCount) 1421 } 1422 return nil 1423 } 1424 1425 // String returns a string representation. 1426 func (m *NewValidBlockMessage) String() string { 1427 return fmt.Sprintf("[ValidBlockMessage H:%v R:%v BP:%v BA:%v IsCommit:%v]", 1428 m.Height, m.Round, m.BlockPartsHeader, m.BlockParts, m.IsCommit) 1429 } 1430 1431 // ------------------------------------- 1432 1433 // ProposalMessage is sent when a new block is proposed. 1434 type ProposalMessage struct { 1435 Proposal *types.Proposal 1436 } 1437 1438 // ValidateBasic performs basic validation. 1439 func (m *ProposalMessage) ValidateBasic() error { 1440 return m.Proposal.ValidateBasic() 1441 } 1442 1443 // String returns a string representation. 1444 func (m *ProposalMessage) String() string { 1445 return fmt.Sprintf("[Proposal %v]", m.Proposal) 1446 } 1447 1448 // ------------------------------------- 1449 1450 // ProposalPOLMessage is sent when a previous proposal is re-proposed. 1451 type ProposalPOLMessage struct { 1452 Height int64 1453 ProposalPOLRound int 1454 ProposalPOL *bitarray.BitArray 1455 } 1456 1457 // ValidateBasic performs basic validation. 1458 func (m *ProposalPOLMessage) ValidateBasic() error { 1459 if m.Height < 0 { 1460 return errors.New("Negative Height") 1461 } 1462 if m.ProposalPOLRound < 0 { 1463 return errors.New("Negative ProposalPOLRound") 1464 } 1465 if m.ProposalPOL.Size() == 0 { 1466 return errors.New("Empty ProposalPOL bit array") 1467 } 1468 if m.ProposalPOL.Size() > types.MaxVotesCount { 1469 return errors.New("ProposalPOL bit array is too big: %d, max: %d", m.ProposalPOL.Size(), types.MaxVotesCount) 1470 } 1471 return nil 1472 } 1473 1474 // String returns a string representation. 1475 func (m *ProposalPOLMessage) String() string { 1476 return fmt.Sprintf("[ProposalPOL H:%v POLR:%v POL:%v]", m.Height, m.ProposalPOLRound, m.ProposalPOL) 1477 } 1478 1479 // ------------------------------------- 1480 1481 // BlockPartMessage is sent when gossipping a piece of the proposed block. 1482 type BlockPartMessage struct { 1483 Height int64 1484 Round int 1485 Part *types.Part 1486 } 1487 1488 // ValidateBasic performs basic validation. 1489 func (m *BlockPartMessage) ValidateBasic() error { 1490 if m.Height < 0 { 1491 return errors.New("Negative Height") 1492 } 1493 if m.Round < 0 { 1494 return errors.New("Negative Round") 1495 } 1496 if err := m.Part.ValidateBasic(); err != nil { 1497 return fmt.Errorf("wrong Part: %w", err) 1498 } 1499 return nil 1500 } 1501 1502 // String returns a string representation. 1503 func (m *BlockPartMessage) String() string { 1504 return fmt.Sprintf("[BlockPart H:%v R:%v P:%v]", m.Height, m.Round, m.Part) 1505 } 1506 1507 // ------------------------------------- 1508 1509 // VoteMessage is sent when voting for a proposal (or lack thereof). 1510 type VoteMessage struct { 1511 Vote *types.Vote 1512 } 1513 1514 // ValidateBasic performs basic validation. 1515 func (m *VoteMessage) ValidateBasic() error { 1516 return m.Vote.ValidateBasic() 1517 } 1518 1519 // String returns a string representation. 1520 func (m *VoteMessage) String() string { 1521 return fmt.Sprintf("[Vote %v]", m.Vote) 1522 } 1523 1524 // ------------------------------------- 1525 1526 // HasVoteMessage is sent to indicate that a particular vote has been received. 1527 type HasVoteMessage struct { 1528 Height int64 1529 Round int 1530 Type types.SignedMsgType 1531 Index int 1532 } 1533 1534 // ValidateBasic performs basic validation. 1535 func (m *HasVoteMessage) ValidateBasic() error { 1536 if m.Height < 0 { 1537 return errors.New("Negative Height") 1538 } 1539 if m.Round < 0 { 1540 return errors.New("Negative Round") 1541 } 1542 if !types.IsVoteTypeValid(m.Type) { 1543 return errors.New("Invalid Type") 1544 } 1545 if m.Index < 0 { 1546 return errors.New("Negative Index") 1547 } 1548 return nil 1549 } 1550 1551 // String returns a string representation. 1552 func (m *HasVoteMessage) String() string { 1553 return fmt.Sprintf("[HasVote VI:%v V:{%v/%02d/%v}]", m.Index, m.Height, m.Round, m.Type) 1554 } 1555 1556 // ------------------------------------- 1557 1558 // VoteSetMaj23Message is sent to indicate that a given BlockID has seen +2/3 votes. 1559 type VoteSetMaj23Message struct { 1560 Height int64 1561 Round int 1562 Type types.SignedMsgType 1563 BlockID types.BlockID 1564 } 1565 1566 // ValidateBasic performs basic validation. 1567 func (m *VoteSetMaj23Message) ValidateBasic() error { 1568 if m.Height < 0 { 1569 return errors.New("Negative Height") 1570 } 1571 if m.Round < 0 { 1572 return errors.New("Negative Round") 1573 } 1574 if !types.IsVoteTypeValid(m.Type) { 1575 return errors.New("Invalid Type") 1576 } 1577 if err := m.BlockID.ValidateBasic(); err != nil { 1578 return fmt.Errorf("wrong BlockID: %w", err) 1579 } 1580 return nil 1581 } 1582 1583 // String returns a string representation. 1584 func (m *VoteSetMaj23Message) String() string { 1585 return fmt.Sprintf("[VSM23 %v/%02d/%v %v]", m.Height, m.Round, m.Type, m.BlockID) 1586 } 1587 1588 // ------------------------------------- 1589 1590 // VoteSetBitsMessage is sent to communicate the bit-array of votes seen for the BlockID. 1591 type VoteSetBitsMessage struct { 1592 Height int64 1593 Round int 1594 Type types.SignedMsgType 1595 BlockID types.BlockID 1596 Votes *bitarray.BitArray 1597 } 1598 1599 // ValidateBasic performs basic validation. 1600 func (m *VoteSetBitsMessage) ValidateBasic() error { 1601 if m.Height < 0 { 1602 return errors.New("Negative Height") 1603 } 1604 if m.Round < 0 { 1605 return errors.New("Negative Round") 1606 } 1607 if !types.IsVoteTypeValid(m.Type) { 1608 return errors.New("Invalid Type") 1609 } 1610 if err := m.BlockID.ValidateBasic(); err != nil { 1611 return fmt.Errorf("wrong BlockID: %w", err) 1612 } 1613 // NOTE: Votes.Size() can be zero if the node does not have any 1614 if m.Votes.Size() > types.MaxVotesCount { 1615 return fmt.Errorf("votes bit array is too big: %d, max: %d", m.Votes.Size(), types.MaxVotesCount) 1616 } 1617 return nil 1618 } 1619 1620 // String returns a string representation. 1621 func (m *VoteSetBitsMessage) String() string { 1622 return fmt.Sprintf("[VSB %v/%02d/%v %v %v]", m.Height, m.Round, m.Type, m.BlockID, m.Votes) 1623 } 1624 1625 // -------------------------------------