tendermint/consensus/reactor.go

747 lines
20 KiB
Go
Raw Normal View History

2014-08-10 16:35:08 -07:00
package consensus
import (
"bytes"
"errors"
"fmt"
"sync"
"sync/atomic"
"time"
. "github.com/tendermint/tendermint/binary"
2014-12-17 01:37:13 -08:00
. "github.com/tendermint/tendermint/block"
2014-08-10 16:35:08 -07:00
. "github.com/tendermint/tendermint/common"
2014-12-17 01:37:13 -08:00
. "github.com/tendermint/tendermint/consensus/types"
"github.com/tendermint/tendermint/mempool"
2014-08-10 16:35:08 -07:00
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/state"
2014-08-10 16:35:08 -07:00
)
const (
2014-09-14 15:37:32 -07:00
StateCh = byte(0x20)
DataCh = byte(0x21)
VoteCh = byte(0x22)
peerStateKey = "ConsensusReactor.peerState"
2014-08-10 16:35:08 -07:00
2014-09-14 15:37:32 -07:00
peerGossipSleepDuration = 50 * time.Millisecond // Time to sleep if there's nothing to send.
2014-08-10 16:35:08 -07:00
)
//-----------------------------------------------------------------------------
2014-09-14 15:37:32 -07:00
type ConsensusReactor struct {
sw *p2p.Switch
started uint32
stopped uint32
2014-10-30 03:32:09 -07:00
quit chan struct{}
2014-08-10 16:35:08 -07:00
2014-10-30 03:32:09 -07:00
conS *ConsensusState
2014-08-10 16:35:08 -07:00
}
func NewConsensusReactor(blockStore *BlockStore, mempoolReactor *mempool.MempoolReactor, state *state.State) *ConsensusReactor {
conS := NewConsensusState(state, blockStore, mempoolReactor)
2014-09-14 15:37:32 -07:00
conR := &ConsensusReactor{
quit: make(chan struct{}),
2014-10-30 03:32:09 -07:00
conS: conS,
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
return conR
2014-08-10 16:35:08 -07:00
}
2014-10-22 17:20:44 -07:00
// Implements Reactor
func (conR *ConsensusReactor) Start(sw *p2p.Switch) {
2014-09-14 15:37:32 -07:00
if atomic.CompareAndSwapUint32(&conR.started, 0, 1) {
log.Info("Starting ConsensusReactor")
2014-10-22 17:20:44 -07:00
conR.sw = sw
2014-10-30 03:32:09 -07:00
conR.conS.Start()
go conR.broadcastNewRoundStepRoutine()
2014-09-14 15:37:32 -07:00
}
}
2014-10-22 17:20:44 -07:00
// Implements Reactor
2014-09-14 15:37:32 -07:00
func (conR *ConsensusReactor) Stop() {
if atomic.CompareAndSwapUint32(&conR.stopped, 0, 1) {
log.Info("Stopping ConsensusReactor")
2014-10-30 03:32:09 -07:00
conR.conS.Stop()
2014-09-14 15:37:32 -07:00
close(conR.quit)
2014-08-10 16:35:08 -07:00
}
}
2014-10-30 03:32:09 -07:00
func (conR *ConsensusReactor) IsStopped() bool {
return atomic.LoadUint32(&conR.stopped) == 1
}
2014-09-14 15:37:32 -07:00
// Implements Reactor
func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor {
// TODO optimize
return []*p2p.ChannelDescriptor{
&p2p.ChannelDescriptor{
2014-10-04 19:16:49 -07:00
Id: StateCh,
Priority: 5,
2014-09-14 15:37:32 -07:00
},
&p2p.ChannelDescriptor{
2014-10-04 19:16:49 -07:00
Id: DataCh,
Priority: 5,
2014-09-14 15:37:32 -07:00
},
&p2p.ChannelDescriptor{
2014-10-04 19:16:49 -07:00
Id: VoteCh,
Priority: 5,
2014-09-14 15:37:32 -07:00
},
2014-08-10 16:35:08 -07:00
}
}
2014-09-14 15:37:32 -07:00
// Implements Reactor
func (conR *ConsensusReactor) AddPeer(peer *p2p.Peer) {
// Create peerState for peer
peerState := NewPeerState(peer)
peer.Data.Set(peerStateKey, peerState)
// Begin gossip routines for this peer.
go conR.gossipDataRoutine(peer, peerState)
go conR.gossipVotesRoutine(peer, peerState)
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
// Implements Reactor
func (conR *ConsensusReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
//peer.Data.Get(peerStateKey).(*PeerState).Disconnect()
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
// Implements Reactor
func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte) {
2014-08-10 16:35:08 -07:00
2014-09-14 15:37:32 -07:00
// Get round state
rs := conR.conS.GetRoundState()
ps := peer.Data.Get(peerStateKey).(*PeerState)
_, msg_, err := DecodeMessage(msgBytes)
2014-12-29 15:14:54 -08:00
if err != nil {
2014-12-29 18:09:06 -08:00
log.Warn("Error decoding message", "channel", chId, "peer", peer, "msg", msg_, "error", err, "bytes", msgBytes)
2014-12-29 15:14:54 -08:00
return
}
2014-12-29 18:39:19 -08:00
log.Debug("Receive", "channel", chId, "peer", peer, "msg", msg_, "bytes", msgBytes)
2014-10-25 14:27:53 -07:00
2014-09-14 15:37:32 -07:00
switch chId {
case StateCh:
2014-08-10 16:35:08 -07:00
switch msg_.(type) {
2014-09-14 15:37:32 -07:00
case *NewRoundStepMessage:
msg := msg_.(*NewRoundStepMessage)
ps.ApplyNewRoundStepMessage(msg, rs)
2014-08-10 16:35:08 -07:00
case *CommitStepMessage:
msg := msg_.(*CommitStepMessage)
ps.ApplyCommitStepMessage(msg)
case *HasVoteMessage:
msg := msg_.(*HasVoteMessage)
ps.ApplyHasVoteMessage(msg)
2014-08-10 16:35:08 -07:00
2014-09-14 15:37:32 -07:00
default:
// Ignore unknown message
}
2014-08-10 16:35:08 -07:00
2014-09-14 15:37:32 -07:00
case DataCh:
switch msg_.(type) {
case *Proposal:
proposal := msg_.(*Proposal)
ps.SetHasProposal(proposal)
2014-09-14 15:37:32 -07:00
err = conR.conS.SetProposal(proposal)
case *PartMessage:
msg := msg_.(*PartMessage)
if msg.Type == partTypeProposalBlock {
ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index)
_, err = conR.conS.AddProposalBlockPart(msg.Height, msg.Round, msg.Part)
} else if msg.Type == partTypeProposalPOL {
ps.SetHasProposalPOLPart(msg.Height, msg.Round, msg.Part.Index)
_, err = conR.conS.AddProposalPOLPart(msg.Height, msg.Round, msg.Part)
} else {
// Ignore unknown part type
}
2014-09-14 15:37:32 -07:00
default:
// Ignore unknown message
}
2014-09-14 15:37:32 -07:00
case VoteCh:
switch msg_.(type) {
case *VoteMessage:
voteMessage := msg_.(*VoteMessage)
vote := voteMessage.Vote
if rs.Height != vote.Height {
return // Wrong height. Not necessarily a bad peer.
}
validatorIndex := voteMessage.ValidatorIndex
address, _ := rs.Validators.GetByIndex(validatorIndex)
added, index, err := conR.conS.AddVote(address, vote)
2014-09-14 15:37:32 -07:00
if err != nil {
// Probably an invalid signature. Bad peer.
2014-12-29 18:39:19 -08:00
log.Warn("Error attempting to add vote", "error", err)
2014-09-14 15:37:32 -07:00
}
// Initialize Prevotes/Precommits/Commits if needed
ps.EnsureVoteBitArrays(rs.Height, rs.Validators.Size())
ps.SetHasVote(vote, index)
2014-09-14 15:37:32 -07:00
if added {
msg := &HasVoteMessage{
Height: vote.Height,
Round: vote.Round,
Type: vote.Type,
Index: index,
2014-08-10 16:35:08 -07:00
}
conR.sw.Broadcast(StateCh, msg)
2014-08-10 16:35:08 -07:00
}
default:
// Ignore unknown message
}
2014-09-14 15:37:32 -07:00
default:
// Ignore unknown channel
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
if err != nil {
2014-12-29 18:39:19 -08:00
log.Warn("Error in Receive()", "error", err)
2014-09-14 15:37:32 -07:00
}
2014-08-10 16:35:08 -07:00
}
2014-10-22 17:20:44 -07:00
// Sets our private validator account for signing votes.
2014-12-17 01:37:13 -08:00
func (conR *ConsensusReactor) SetPrivValidator(priv *state.PrivValidator) {
2014-10-22 17:20:44 -07:00
conR.conS.SetPrivValidator(priv)
}
2014-10-21 23:30:18 -07:00
//--------------------------------------
2014-10-30 03:32:09 -07:00
// Listens for changes to the ConsensusState.Step by pulling
// on conR.conS.NewStepCh().
func (conR *ConsensusReactor) broadcastNewRoundStepRoutine() {
for {
2014-10-30 03:32:09 -07:00
// Get RoundState with new Step or quit.
var rs *RoundState
select {
case rs = <-conR.conS.NewStepCh():
case <-conR.quit:
return
}
2014-10-30 03:32:09 -07:00
// Get seconds since beginning of height.
// Due to the condition documented, this is safe.
2014-12-29 15:14:54 -08:00
timeElapsed := time.Now().Sub(rs.StartTime)
2014-10-30 03:32:09 -07:00
// Broadcast NewRoundStepMessage
{
msg := &NewRoundStepMessage{
Height: rs.Height,
Round: rs.Round,
Step: rs.Step,
SecondsSinceStartTime: uint(timeElapsed.Seconds()),
}
conR.sw.Broadcast(StateCh, msg)
}
// If the step is commit, then also broadcast a CommitStepMessage.
if rs.Step == RoundStepCommit {
msg := &CommitStepMessage{
Height: rs.Height,
BlockParts: rs.ProposalBlockParts.Header(),
BlockBitArray: rs.ProposalBlockParts.BitArray(),
}
conR.sw.Broadcast(StateCh, msg)
}
}
2014-10-21 23:30:18 -07:00
}
2014-09-14 15:37:32 -07:00
func (conR *ConsensusReactor) gossipDataRoutine(peer *p2p.Peer, ps *PeerState) {
2014-08-10 16:35:08 -07:00
OUTER_LOOP:
for {
2014-09-14 15:37:32 -07:00
// Manage disconnects from self or peer.
if peer.IsStopped() || conR.IsStopped() {
2014-12-29 18:09:06 -08:00
log.Info(Fmt("Stopping gossipDataRoutine for %v.", peer))
2014-09-14 15:37:32 -07:00
return
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
rs := conR.conS.GetRoundState()
prs := ps.GetRoundState()
2014-08-10 16:35:08 -07:00
2014-10-26 13:26:27 -07:00
// Send proposal Block parts?
// NOTE: if we or peer is at RoundStepCommit*, the round
// won't necessarily match, but that's OK.
2014-11-03 15:50:23 -08:00
if rs.ProposalBlockParts.HasHeader(prs.ProposalBlockParts) {
2014-10-26 13:26:27 -07:00
if index, ok := rs.ProposalBlockParts.BitArray().Sub(
prs.ProposalBlockBitArray).PickRandom(); ok {
msg := &PartMessage{
Height: rs.Height,
Round: rs.Round,
Type: partTypeProposalBlock,
Part: rs.ProposalBlockParts.GetPart(index),
}
peer.Send(DataCh, msg)
ps.SetHasProposalBlockPart(rs.Height, rs.Round, index)
continue OUTER_LOOP
}
}
2014-09-14 15:37:32 -07:00
// If height and round doesn't match, sleep.
if rs.Height != prs.Height || rs.Round != prs.Round {
time.Sleep(peerGossipSleepDuration)
2014-08-10 16:35:08 -07:00
continue OUTER_LOOP
}
2014-09-14 15:37:32 -07:00
// Send proposal?
if rs.Proposal != nil && !prs.Proposal {
msg := p2p.TypedMessage{msgTypeProposal, rs.Proposal}
peer.Send(DataCh, msg)
ps.SetHasProposal(rs.Proposal)
2014-09-14 15:37:32 -07:00
continue OUTER_LOOP
2014-08-10 16:35:08 -07:00
}
2014-10-26 13:26:27 -07:00
// Send proposal POL parts?
2014-11-03 15:50:23 -08:00
if rs.ProposalPOLParts.HasHeader(prs.ProposalPOLParts) {
2014-10-26 13:26:27 -07:00
if index, ok := rs.ProposalPOLParts.BitArray().Sub(
2014-10-25 14:27:53 -07:00
prs.ProposalPOLBitArray).PickRandom(); ok {
msg := &PartMessage{
Height: rs.Height,
Round: rs.Round,
Type: partTypeProposalPOL,
Part: rs.ProposalPOLParts.GetPart(index),
2014-10-25 14:27:53 -07:00
}
peer.Send(DataCh, msg)
ps.SetHasProposalPOLPart(rs.Height, rs.Round, index)
2014-10-25 14:27:53 -07:00
continue OUTER_LOOP
2014-09-14 15:37:32 -07:00
}
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
// Nothing to do. Sleep.
time.Sleep(peerGossipSleepDuration)
continue OUTER_LOOP
2014-08-10 16:35:08 -07:00
}
}
2014-09-14 15:37:32 -07:00
func (conR *ConsensusReactor) gossipVotesRoutine(peer *p2p.Peer, ps *PeerState) {
OUTER_LOOP:
for {
// Manage disconnects from self or peer.
if peer.IsStopped() || conR.IsStopped() {
2014-12-29 18:09:06 -08:00
log.Info(Fmt("Stopping gossipVotesRoutine for %v.", peer))
2014-09-14 15:37:32 -07:00
return
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
rs := conR.conS.GetRoundState()
prs := ps.GetRoundState()
2014-10-30 03:32:09 -07:00
trySendVote := func(voteSet *VoteSet, peerVoteSet BitArray) (sent bool) {
// TODO: give priority to our vote.
2014-12-28 17:10:03 -08:00
// peerVoteSet BitArray is being accessed concurrently with
// writes from Receive() routines. We must lock like so here:
ps.mtx.Lock()
2014-10-30 03:32:09 -07:00
index, ok := voteSet.BitArray().Sub(peerVoteSet).PickRandom()
2014-12-28 17:10:03 -08:00
ps.mtx.Unlock()
2014-10-30 03:32:09 -07:00
if ok {
vote := voteSet.GetByIndex(index)
// NOTE: vote may be a commit.
msg := &VoteMessage{index, vote}
2014-10-30 03:32:09 -07:00
peer.Send(VoteCh, msg)
ps.SetHasVote(vote, index)
2014-10-30 03:32:09 -07:00
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 prs.LastCommits.Size() == rs.LastCommits.Size() {
if trySendVote(rs.LastCommits, prs.LastCommits) {
continue OUTER_LOOP
}
}
}
// Initialize Prevotes/Precommits/Commits if needed
ps.EnsureVoteBitArrays(rs.Height, rs.Validators.Size())
// If there are prevotes to send...
if rs.Round == prs.Round && prs.Step <= RoundStepPrevote {
if trySendVote(rs.Prevotes, prs.Prevotes) {
continue OUTER_LOOP
}
}
// If there are precommits to send...
if rs.Round == prs.Round && prs.Step <= RoundStepPrecommit {
if trySendVote(rs.Precommits, prs.Precommits) {
continue OUTER_LOOP
}
}
// If there are any commits to send...
if trySendVote(rs.Commits, prs.Commits) {
2014-10-30 03:32:09 -07:00
continue OUTER_LOOP
2014-09-14 15:37:32 -07:00
}
}
// If peer is lagging by height 1, match our LastCommits to peer's Commits.
if rs.Height == prs.Height+1 {
// Initialize Commits if needed
ps.EnsureVoteBitArrays(rs.Height-1, rs.LastCommits.Size())
// If there are lastcommits to send...
if trySendVote(rs.LastCommits, prs.Commits) {
2014-10-30 03:32:09 -07:00
continue OUTER_LOOP
2014-08-10 16:35:08 -07:00
}
2014-08-10 16:35:08 -07:00
}
// If peer is lagging by more than 1, load and send Validation and send Commits.
if rs.Height >= prs.Height+2 {
// Load the block header and validation for prs.Height+1,
// which contains commit signatures for prs.Height.
header, validation := conR.conS.LoadHeaderValidation(prs.Height + 1)
size := uint(len(validation.Commits))
log.Debug("Loaded HeaderValidation for catchup", "height", prs.Height+1, "header", header, "validation", validation, "size", size)
// Initialize Commits if needed
ps.EnsureVoteBitArrays(prs.Height, size)
index, ok := validation.BitArray().Sub(prs.Commits).PickRandom()
if ok {
commit := validation.Commits[index]
log.Debug("Picked commit to send", "index", index, "commit", commit)
// Reconstruct vote.
vote := &Vote{
Height: prs.Height,
Round: commit.Round,
Type: VoteTypeCommit,
BlockHash: header.LastBlockHash,
BlockParts: header.LastBlockParts,
Signature: commit.Signature,
}
msg := &VoteMessage{index, vote}
peer.Send(VoteCh, msg)
ps.SetHasVote(vote, index)
continue OUTER_LOOP
} else {
log.Debug("No commits to send", "ours", validation.BitArray(), "theirs", prs.Commits)
}
2014-09-14 15:37:32 -07:00
}
2014-08-10 16:35:08 -07:00
2014-09-14 15:37:32 -07:00
// We sent nothing. Sleep...
time.Sleep(peerGossipSleepDuration)
continue OUTER_LOOP
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
}
2014-08-10 16:35:08 -07:00
//-----------------------------------------------------------------------------
2014-09-14 15:37:32 -07:00
// Read only when returned by PeerState.GetRoundState().
type PeerRoundState struct {
Height uint // Height peer is at
Round uint // Round peer is at
2014-10-30 03:32:09 -07:00
Step RoundStep // Step peer is at
StartTime time.Time // Estimated start of round 0 at this height
Proposal bool // True if peer has proposal for this round
ProposalBlockParts PartSetHeader //
ProposalBlockBitArray BitArray // True bit -> has part
ProposalPOLParts PartSetHeader //
ProposalPOLBitArray BitArray // True bit -> has part
Prevotes BitArray // All votes peer has for this round
Precommits BitArray // All precommits peer has for this round
Commits BitArray // All commits peer has for this height
LastCommits BitArray // All commits peer has for last height
2014-09-14 15:37:32 -07:00
}
//-----------------------------------------------------------------------------
2014-08-10 16:35:08 -07:00
var (
ErrPeerStateHeightRegression = errors.New("Error peer state height regression")
ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime")
)
type PeerState struct {
2014-09-14 15:37:32 -07:00
mtx sync.Mutex
PeerRoundState
2014-08-10 16:35:08 -07:00
}
func NewPeerState(peer *p2p.Peer) *PeerState {
2014-09-14 15:37:32 -07:00
return &PeerState{}
2014-08-10 16:35:08 -07:00
}
2014-09-14 15:37:32 -07:00
// Returns an atomic snapshot of the PeerRoundState.
// There's no point in mutating it since it won't change PeerState.
func (ps *PeerState) GetRoundState() *PeerRoundState {
ps.mtx.Lock()
defer ps.mtx.Unlock()
2014-09-14 15:37:32 -07:00
prs := ps.PeerRoundState // copy
return &prs
}
func (ps *PeerState) SetHasProposal(proposal *Proposal) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != proposal.Height || ps.Round != proposal.Round {
return
}
if ps.Proposal {
return
}
ps.Proposal = true
2014-10-30 03:32:09 -07:00
ps.ProposalBlockParts = proposal.BlockParts
ps.ProposalBlockBitArray = NewBitArray(uint(proposal.BlockParts.Total))
ps.ProposalPOLParts = proposal.POLParts
ps.ProposalPOLBitArray = NewBitArray(uint(proposal.POLParts.Total))
2014-09-14 15:37:32 -07:00
}
func (ps *PeerState) SetHasProposalBlockPart(height uint, round uint, index uint) {
2014-08-10 16:35:08 -07:00
ps.mtx.Lock()
defer ps.mtx.Unlock()
2014-09-14 15:37:32 -07:00
if ps.Height != height || ps.Round != round {
return
2014-08-10 16:35:08 -07:00
}
ps.ProposalBlockBitArray.SetIndex(uint(index), true)
2014-08-10 16:35:08 -07:00
}
func (ps *PeerState) SetHasProposalPOLPart(height uint, round uint, index uint) {
2014-08-10 16:35:08 -07:00
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != height || ps.Round != round {
return
}
ps.ProposalPOLBitArray.SetIndex(uint(index), true)
}
func (ps *PeerState) EnsureVoteBitArrays(height uint, numValidators uint) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != height {
return
}
if ps.Prevotes.IsZero() {
ps.Prevotes = NewBitArray(numValidators)
}
if ps.Precommits.IsZero() {
ps.Precommits = NewBitArray(numValidators)
}
if ps.Commits.IsZero() {
ps.Commits = NewBitArray(numValidators)
}
2014-08-10 16:35:08 -07:00
}
func (ps *PeerState) SetHasVote(vote *Vote, index uint) {
2014-08-10 16:35:08 -07:00
ps.mtx.Lock()
defer ps.mtx.Unlock()
ps.setHasVote(vote.Height, vote.Round, vote.Type, index)
}
func (ps *PeerState) setHasVote(height uint, round uint, type_ byte, index uint) {
if ps.Height == height+1 && type_ == VoteTypeCommit {
// Special case for LastCommits.
ps.LastCommits.SetIndex(index, true)
return
} else if ps.Height != height {
// Does not apply.
return
}
switch type_ {
case VoteTypePrevote:
ps.Prevotes.SetIndex(index, true)
case VoteTypePrecommit:
ps.Precommits.SetIndex(index, true)
case VoteTypeCommit:
if round < ps.Round {
2014-10-30 03:32:09 -07:00
ps.Prevotes.SetIndex(index, true)
ps.Precommits.SetIndex(index, true)
}
ps.Commits.SetIndex(index, true)
default:
panic("Invalid vote type")
2014-08-10 16:35:08 -07:00
}
}
func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage, rs *RoundState) {
2014-08-10 16:35:08 -07:00
ps.mtx.Lock()
defer ps.mtx.Unlock()
2014-09-14 15:37:32 -07:00
// Just remember these values.
psHeight := ps.Height
psRound := ps.Round
//psStep := ps.Step
2014-09-14 15:37:32 -07:00
startTime := time.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
ps.Height = msg.Height
ps.Round = msg.Round
ps.Step = msg.Step
ps.StartTime = startTime
if psHeight != msg.Height || psRound != msg.Round {
2014-10-25 14:27:53 -07:00
ps.Proposal = false
2014-10-30 03:32:09 -07:00
ps.ProposalBlockParts = PartSetHeader{}
2014-10-25 14:27:53 -07:00
ps.ProposalBlockBitArray = BitArray{}
2014-10-30 03:32:09 -07:00
ps.ProposalPOLParts = PartSetHeader{}
2014-10-25 14:27:53 -07:00
ps.ProposalPOLBitArray = BitArray{}
// We'll update the BitArray capacity later.
ps.Prevotes = BitArray{}
ps.Precommits = BitArray{}
2014-10-25 14:27:53 -07:00
}
if psHeight != msg.Height {
// Shift Commits to LastCommits
if psHeight+1 == msg.Height {
ps.LastCommits = ps.Commits
} else {
ps.LastCommits = BitArray{}
}
// We'll update the BitArray capacity later.
ps.Commits = BitArray{}
2014-09-14 15:37:32 -07:00
}
2014-08-10 16:35:08 -07:00
}
func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != msg.Height {
return
}
ps.ProposalBlockParts = msg.BlockParts
ps.ProposalBlockBitArray = msg.BlockBitArray
}
func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
// Special case for LastCommits
if ps.Height == msg.Height+1 && msg.Type == VoteTypeCommit {
ps.LastCommits.SetIndex(msg.Index, true)
return
} else if ps.Height != msg.Height {
return
}
ps.setHasVote(msg.Height, msg.Round, msg.Type, msg.Index)
}
2014-08-10 16:35:08 -07:00
//-----------------------------------------------------------------------------
// Messages
const (
msgTypeUnknown = byte(0x00)
2014-09-14 15:37:32 -07:00
msgTypeNewRoundStep = byte(0x01)
msgTypeCommitStep = byte(0x02)
msgTypeProposal = byte(0x11)
msgTypePart = byte(0x12) // both block & POL
msgTypeVote = byte(0x13)
msgTypeHasVote = byte(0x14)
2014-08-10 16:35:08 -07:00
)
// TODO: check for unnecessary extra bytes at the end.
func DecodeMessage(bz []byte) (msgType byte, msg interface{}, err error) {
2014-12-29 15:14:54 -08:00
n := new(int64)
2014-12-29 18:09:06 -08:00
// log.Debug(Fmt("decoding msg bytes: %X", bz))
msgType = bz[0]
r := bytes.NewReader(bz)
switch msgType {
2014-09-14 15:37:32 -07:00
// Messages for communicating state changes
case msgTypeNewRoundStep:
2014-12-29 15:14:54 -08:00
msg = ReadBinary(&NewRoundStepMessage{}, r, n, &err)
case msgTypeCommitStep:
2014-12-29 15:14:54 -08:00
msg = ReadBinary(&CommitStepMessage{}, r, n, &err)
2014-09-14 15:37:32 -07:00
// Messages of data
case msgTypeProposal:
2014-12-29 19:41:46 -08:00
r.ReadByte() // Consume the byte
2014-12-29 15:14:54 -08:00
msg = ReadBinary(&Proposal{}, r, n, &err)
2014-09-14 15:37:32 -07:00
case msgTypePart:
2014-12-29 15:14:54 -08:00
msg = ReadBinary(&PartMessage{}, r, n, &err)
2014-08-10 16:35:08 -07:00
case msgTypeVote:
2014-12-29 15:14:54 -08:00
msg = ReadBinary(&VoteMessage{}, r, n, &err)
case msgTypeHasVote:
2014-12-29 15:14:54 -08:00
msg = ReadBinary(&HasVoteMessage{}, r, n, &err)
2014-08-10 16:35:08 -07:00
default:
msg = nil
2014-08-10 16:35:08 -07:00
}
return
2014-08-10 16:35:08 -07:00
}
//-------------------------------------
2014-09-14 15:37:32 -07:00
type NewRoundStepMessage struct {
Height uint
Round uint
Step RoundStep
SecondsSinceStartTime uint
2014-08-10 16:35:08 -07:00
}
func (m *NewRoundStepMessage) TypeByte() byte { return msgTypeNewRoundStep }
2014-08-10 16:35:08 -07:00
2014-09-14 15:37:32 -07:00
func (m *NewRoundStepMessage) String() string {
return fmt.Sprintf("[NewRoundStep %v/%v/%X]", m.Height, m.Round, m.Step)
2014-08-10 16:35:08 -07:00
}
//-------------------------------------
type CommitStepMessage struct {
Height uint
BlockParts PartSetHeader
BlockBitArray BitArray
}
func (m *CommitStepMessage) TypeByte() byte { return msgTypeCommitStep }
func (m *CommitStepMessage) String() string {
return fmt.Sprintf("[CommitStep %v %v %v]", m.Height, m.BlockParts, m.BlockBitArray)
}
//-------------------------------------
2014-09-14 15:37:32 -07:00
const (
partTypeProposalBlock = byte(0x01)
partTypeProposalPOL = byte(0x02)
)
type PartMessage struct {
Height uint
Round uint
2014-09-14 15:37:32 -07:00
Type byte
Part *Part
2014-08-10 16:35:08 -07:00
}
func (m *PartMessage) TypeByte() byte { return msgTypePart }
func (m *PartMessage) String() string {
return fmt.Sprintf("[Part %v/%v T:%X %v]", m.Height, m.Round, m.Type, m.Part)
2014-08-10 16:35:08 -07:00
}
//-------------------------------------
type VoteMessage struct {
ValidatorIndex uint
Vote *Vote
2014-08-10 16:35:08 -07:00
}
func (m *VoteMessage) TypeByte() byte { return msgTypeVote }
func (m *VoteMessage) String() string {
return fmt.Sprintf("[Vote ValidatorIndex:%v Vote:%v]", m.ValidatorIndex, m.Vote)
2014-08-10 16:35:08 -07:00
}
//-------------------------------------
type HasVoteMessage struct {
Height uint
Round uint
Type byte
Index uint
}
func (m *HasVoteMessage) TypeByte() byte { return msgTypeHasVote }
func (m *HasVoteMessage) String() string {
2014-11-03 16:43:37 -08:00
return fmt.Sprintf("[HasVote %v/%v T:%X]", m.Height, m.Round, m.Type)
}