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