func (conR *ConsensusReactor) gossipVotesRoutine(peer *p2p.Peer, ps *PeerState) { // Simple hack to throttle logs upon sleep. var sleeping = 0 OUTER_LOOP: for { // Manage disconnects from self or peer. if !peer.IsRunning() || !conR.IsRunning() { log.Info(Fmt("Stopping gossipVotesRoutine for %v.", peer)) return } rs := conR.conS.GetRoundState() prs := ps.GetRoundState() switch sleeping { case 1: // First sleep sleeping = 2 case 2: // No more sleep sleeping = 0 } // Returns true when useful work was done. trySendVote := func(height uint, voteSet *VoteSet, peerVoteSet *BitArray) (sent bool) { if voteSet == nil { return false } else if peerVoteSet == nil { ps.EnsureVoteBitArrays(height, voteSet.Size()) return true } // TODO: give priority to our vote. if index, ok := voteSet.BitArray().Sub(peerVoteSet.Copy()).PickRandom(); ok { vote := voteSet.GetByIndex(index) // NOTE: vote may be a commit. msg := &VoteMessage{index, vote} peer.Send(VoteChannel, msg) ps.SetHasVote(vote, index) return true } return false } // Returns true when useful work was done. trySendCommitFromValidation := func(blockMeta *types.BlockMeta, validation *types.Validation, peerVoteSet *BitArray) (sent bool) { if validation == nil { return false } else if peerVoteSet == nil { ps.EnsureVoteBitArrays(blockMeta.Header.Height, uint(len(validation.Commits))) return true } if index, ok := validation.BitArray().Sub(prs.Commits.Copy()).PickRandom(); ok { commit := validation.Commits[index] log.Debug("Picked commit to send", "index", index, "commit", commit) // Reconstruct vote. vote := &types.Vote{ Height: prs.Height, Round: commit.Round, Type: types.VoteTypeCommit, BlockHash: blockMeta.Hash, BlockParts: blockMeta.Parts, Signature: commit.Signature, } msg := &VoteMessage{index, vote} peer.Send(VoteChannel, msg) ps.SetHasVote(vote, index) return true } return false } // If height matches, then send LastCommits, Prevotes, Precommits, or Commits. if rs.Height == prs.Height { // If there are lastcommits to send... if prs.Round == 0 && prs.Step == RoundStepNewHeight { if trySendVote(rs.Height-1, rs.LastCommits, prs.LastCommits) { continue OUTER_LOOP } } // If there are prevotes to send... if rs.Round == prs.Round && prs.Step <= RoundStepPrevote { if trySendVote(rs.Height, rs.Prevotes, prs.Prevotes) { continue OUTER_LOOP } } // If there are precommits to send... if rs.Round == prs.Round && prs.Step <= RoundStepPrecommit { if trySendVote(rs.Height, rs.Precommits, prs.Precommits) { continue OUTER_LOOP } } // If there are any commits to send... if trySendVote(rs.Height, rs.Commits, prs.Commits) { continue OUTER_LOOP } } // Catchup logic if prs.Height != 0 && !prs.HasAllCatchupCommits { // If peer is lagging by height 1, match our LastCommits or SeenValidation to peer's Commits. if rs.Height == prs.Height+1 && rs.LastCommits.Size() > 0 { // If there are lastcommits to send... if trySendVote(prs.Height, rs.LastCommits, prs.Commits) { continue OUTER_LOOP } else { ps.SetHasAllCatchupCommits(prs.Height) } } // Or, if peer is lagging by 1 and we don't have LastCommits, send SeenValidation. if rs.Height == prs.Height+1 && rs.LastCommits.Size() == 0 { // Load the blockMeta for block at prs.Height blockMeta := conR.blockStore.LoadBlockMeta(prs.Height) // Load the seen validation for prs.Height validation := conR.blockStore.LoadSeenValidation(prs.Height) log.Debug("Loaded SeenValidation for catch-up", "height", prs.Height, "blockMeta", blockMeta, "validation", validation) if trySendCommitFromValidation(blockMeta, validation, prs.Commits) { continue OUTER_LOOP } else { ps.SetHasAllCatchupCommits(prs.Height) } } // If peer is lagging by more than 1, send Validation. if rs.Height >= prs.Height+2 { // Load the blockMeta for block at prs.Height blockMeta := conR.blockStore.LoadBlockMeta(prs.Height) // Load the block validation for prs.Height+1, // which contains commit signatures for prs.Height. validation := conR.blockStore.LoadBlockValidation(prs.Height + 1) log.Debug("Loaded BlockValidation for catch-up", "height", prs.Height+1, "blockMeta", blockMeta, "validation", validation) if trySendCommitFromValidation(blockMeta, validation, prs.Commits) { continue OUTER_LOOP } else { ps.SetHasAllCatchupCommits(prs.Height) } } } if sleeping == 0 { // We sent nothing. Sleep... sleeping = 1 log.Debug("No votes to send, sleeping", "peer", peer, "localPV", rs.Prevotes.BitArray(), "peerPV", prs.Prevotes, "localPC", rs.Precommits.BitArray(), "peerPC", prs.Precommits, "localCM", rs.Commits.BitArray(), "peerCM", prs.Commits) } else if sleeping == 2 { // Continued sleep... sleeping = 1 } time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } }
func (conR *ConsensusReactor) gossipVotesRoutine(peer *p2p.Peer, ps *PeerState) { log := log.New("peer", peer.Key) // Simple hack to throttle logs upon sleep. var sleeping = 0 OUTER_LOOP: for { // Manage disconnects from self or peer. if !peer.IsRunning() || !conR.IsRunning() { log.Notice(Fmt("Stopping gossipVotesRoutine for %v.", peer)) return } rs := conR.conS.GetRoundState() prs := ps.GetRoundState() switch sleeping { case 1: // First sleep sleeping = 2 case 2: // No more sleep sleeping = 0 } log.Debug("gossipVotesRoutine", "rsHeight", rs.Height, "rsRound", rs.Round, "prsHeight", prs.Height, "prsRound", prs.Round, "prsStep", prs.Step) // If height matches, then send LastCommit, Prevotes, Precommits. if rs.Height == prs.Height { // If there are lastCommits to send... if prs.Step == RoundStepNewHeight { if ps.PickSendVote(rs.LastCommit) { log.Info("Picked rs.LastCommit to send") continue OUTER_LOOP } } // If there are prevotes to send... if rs.Round == prs.Round && prs.Step <= RoundStepPrevote { if ps.PickSendVote(rs.Votes.Prevotes(rs.Round)) { log.Info("Picked rs.Prevotes(rs.Round) to send") continue OUTER_LOOP } } // If there are precommits to send... if rs.Round == prs.Round && prs.Step <= RoundStepPrecommit { if ps.PickSendVote(rs.Votes.Precommits(rs.Round)) { log.Info("Picked rs.Precommits(rs.Round) to send") continue OUTER_LOOP } } // If there are prevotes to send for the last round... if rs.Round == prs.Round+1 && prs.Step <= RoundStepPrevote { if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) { log.Info("Picked rs.Prevotes(prs.Round) to send") continue OUTER_LOOP } } // If there are precommits to send for the last round... if rs.Round == prs.Round+1 && prs.Step <= RoundStepPrecommit { if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) { log.Info("Picked rs.Precommits(prs.Round) to send") continue OUTER_LOOP } } // If there are POLPrevotes to send... if 0 <= prs.ProposalPOLRound { if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil { if ps.PickSendVote(polPrevotes) { log.Info("Picked rs.Prevotes(prs.ProposalPOLRound) to send") continue OUTER_LOOP } } } } // Special catchup logic. // If peer is lagging by height 1, send LastCommit. if prs.Height != 0 && rs.Height == prs.Height+1 { if ps.PickSendVote(rs.LastCommit) { log.Info("Picked rs.LastCommit to send") continue OUTER_LOOP } } // Catchup logic // If peer is lagging by more than 1, send Validation. if prs.Height != 0 && rs.Height >= prs.Height+2 { // Load the block validation for prs.Height, // which contains precommit signatures for prs.Height. validation := conR.blockStore.LoadBlockValidation(prs.Height) log.Info("Loaded BlockValidation for catch-up", "height", prs.Height, "validation", validation) if ps.PickSendVote(validation) { log.Info("Picked Catchup validation to send") continue OUTER_LOOP } } if sleeping == 0 { // We sent nothing. Sleep... sleeping = 1 log.Info("No votes to send, sleeping", "peer", peer, "localPV", rs.Votes.Prevotes(rs.Round).BitArray(), "peerPV", prs.Prevotes, "localPC", rs.Votes.Precommits(rs.Round).BitArray(), "peerPC", prs.Precommits) } else if sleeping == 2 { // Continued sleep... sleeping = 1 } time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } }
func (conR *ConsensusReactor) gossipDataRoutine(peer *p2p.Peer, ps *PeerState) { OUTER_LOOP: for { // Manage disconnects from self or peer. if !peer.IsRunning() || !conR.IsRunning() { log.Info(Fmt("Stopping gossipDataRoutine for %v.", peer)) return } rs := conR.conS.GetRoundState() prs := ps.GetRoundState() // Send proposal Block parts? // NOTE: if we or peer is at RoundStepCommit*, the round // won't necessarily match, but that's OK. if rs.ProposalBlockParts.HasHeader(prs.ProposalBlockParts) { //log.Debug("ProposalBlockParts matched", "blockParts", prs.ProposalBlockParts) if index, ok := rs.ProposalBlockParts.BitArray().Sub(prs.ProposalBlockBitArray.Copy()).PickRandom(); ok { part := rs.ProposalBlockParts.GetPart(index) msg := &PartMessage{ Height: rs.Height, Round: rs.Round, Type: partTypeProposalBlock, Part: part, } peer.Send(DataChannel, msg) ps.SetHasProposalBlockPart(rs.Height, rs.Round, index) continue OUTER_LOOP } } // If the peer is on a previous height, help catch up. if 0 < prs.Height && prs.Height < rs.Height { //log.Debug("Data catchup", "height", rs.Height, "peerHeight", prs.Height, "peerProposalBlockBitArray", prs.ProposalBlockBitArray) if index, ok := prs.ProposalBlockBitArray.Not().PickRandom(); ok { // Ensure that the peer's PartSetHeader is correct blockMeta := conR.blockStore.LoadBlockMeta(prs.Height) if !blockMeta.Parts.Equals(prs.ProposalBlockParts) { log.Debug("Peer ProposalBlockParts mismatch, sleeping", "peerHeight", prs.Height, "blockParts", blockMeta.Parts, "peerBlockParts", prs.ProposalBlockParts) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } // Load the part part := conR.blockStore.LoadBlockPart(prs.Height, index) if part == nil { log.Warn("Could not load part", "index", index, "peerHeight", prs.Height, "blockParts", blockMeta.Parts, "peerBlockParts", prs.ProposalBlockParts) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } // Send the part msg := &PartMessage{ Height: prs.Height, Round: prs.Round, Type: partTypeProposalBlock, Part: part, } peer.Send(DataChannel, msg) ps.SetHasProposalBlockPart(prs.Height, prs.Round, index) continue OUTER_LOOP } else { //log.Debug("No parts to send in catch-up, sleeping") time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } } // If height and round don't match, sleep. if rs.Height != prs.Height || rs.Round != prs.Round { //log.Debug("Peer Height|Round mismatch, sleeping", "peerHeight", prs.Height, "peerRound", prs.Round, "peer", peer) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } // Send proposal? if rs.Proposal != nil && !prs.Proposal { msg := &ProposalMessage{Proposal: rs.Proposal} peer.Send(DataChannel, msg) ps.SetHasProposal(rs.Proposal) continue OUTER_LOOP } // Send proposal POL parts? if rs.ProposalPOLParts.HasHeader(prs.ProposalPOLParts) { if index, ok := rs.ProposalPOLParts.BitArray().Sub(prs.ProposalPOLBitArray.Copy()).PickRandom(); ok { msg := &PartMessage{ Height: rs.Height, Round: rs.Round, Type: partTypeProposalPOL, Part: rs.ProposalPOLParts.GetPart(index), } peer.Send(DataChannel, msg) ps.SetHasProposalPOLPart(rs.Height, rs.Round, index) continue OUTER_LOOP } } // Nothing to do. Sleep. time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } }
func (conR *ConsensusReactor) gossipDataRoutine(peer *p2p.Peer, ps *PeerState) { log := log.New("peer", peer.Key) OUTER_LOOP: for { // Manage disconnects from self or peer. if !peer.IsRunning() || !conR.IsRunning() { log.Notice(Fmt("Stopping gossipDataRoutine for %v.", peer)) return } rs := conR.conS.GetRoundState() prs := ps.GetRoundState() // Send proposal Block parts? if rs.ProposalBlockParts.HasHeader(prs.ProposalBlockPartsHeader) { //log.Info("ProposalBlockParts matched", "blockParts", prs.ProposalBlockParts) if index, ok := rs.ProposalBlockParts.BitArray().Sub(prs.ProposalBlockParts.Copy()).PickRandom(); ok { part := rs.ProposalBlockParts.GetPart(index) msg := &BlockPartMessage{ Height: rs.Height, // This tells peer that this part applies to us. Round: rs.Round, // This tells peer that this part applies to us. Part: part, } peer.Send(DataChannel, msg) ps.SetHasProposalBlockPart(prs.Height, prs.Round, index) continue OUTER_LOOP } } // If the peer is on a previous height, help catch up. if (0 < prs.Height) && (prs.Height < rs.Height) { //log.Info("Data catchup", "height", rs.Height, "peerHeight", prs.Height, "peerProposalBlockParts", prs.ProposalBlockParts) if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok { // Ensure that the peer's PartSetHeader is correct blockMeta := conR.blockStore.LoadBlockMeta(prs.Height) if !blockMeta.PartsHeader.Equals(prs.ProposalBlockPartsHeader) { log.Info("Peer ProposalBlockPartsHeader mismatch, sleeping", "peerHeight", prs.Height, "blockPartsHeader", blockMeta.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } // Load the part part := conR.blockStore.LoadBlockPart(prs.Height, index) if part == nil { log.Warn("Could not load part", "index", index, "peerHeight", prs.Height, "blockPartsHeader", blockMeta.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } // Send the part msg := &BlockPartMessage{ Height: prs.Height, // Not our height, so it doesn't matter. Round: prs.Round, // Not our height, so it doesn't matter. Part: part, } peer.Send(DataChannel, msg) ps.SetHasProposalBlockPart(prs.Height, prs.Round, index) continue OUTER_LOOP } else { //log.Info("No parts to send in catch-up, sleeping") time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } } // If height and round don't match, sleep. if (rs.Height != prs.Height) || (rs.Round != prs.Round) { //log.Info("Peer Height|Round mismatch, sleeping", "peerHeight", prs.Height, "peerRound", prs.Round, "peer", peer) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } // By here, height and round match. // Proposal block parts were already matched and sent if any were wanted. // (These can match on hash so the round doesn't matter) // Now consider sending other things, like the Proposal itself. // Send Proposal && ProposalPOL BitArray? if rs.Proposal != nil && !prs.Proposal { // Proposal { msg := &ProposalMessage{Proposal: rs.Proposal} peer.Send(DataChannel, msg) ps.SetHasProposal(rs.Proposal) } // ProposalPOL. // Peer must receive ProposalMessage first. // rs.Proposal was validated, so rs.Proposal.POLRound <= rs.Round, // so we definitely have rs.Votes.Prevotes(rs.Proposal.POLRound). if 0 <= rs.Proposal.POLRound { msg := &ProposalPOLMessage{ Height: rs.Height, ProposalPOLRound: rs.Proposal.POLRound, ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(), } peer.Send(DataChannel, msg) } continue OUTER_LOOP } // Nothing to do. Sleep. time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } }