Refactor Tx, Validator, and Account structure

This commit is contained in:
Jae Kwon
2014-12-16 05:45:40 -08:00
parent 4424a85fbd
commit 83d313cbe5
56 changed files with 1917 additions and 2022 deletions
+42 -72
View File
@@ -2,111 +2,81 @@ package consensus
import (
"fmt"
"io"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/state"
)
// Each signature of a POL (proof-of-lock, see whitepaper) is
// either a prevote or a commit.
// Commits require an additional round which is strictly less than
// the POL round. Prevote rounds are equal to the POL round.
type POLVoteSignature struct {
Round uint
Signature SignatureEd25519
}
// Proof of lock.
// +2/3 of validators' prevotes for a given blockhash (or nil)
type POL struct {
Height uint32
Round uint16
BlockHash []byte // Could be nil, which makes this a proof of unlock.
BlockParts PartSetHeader // When BlockHash is nil, this is zero.
Votes []Signature // Vote signatures for height/round/hash
Commits []RoundSignature // Commit signatures for height/hash
}
func ReadPOL(r io.Reader, n *int64, err *error) *POL {
return &POL{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
BlockHash: ReadByteSlice(r, n, err),
BlockParts: ReadPartSetHeader(r, n, err),
Votes: ReadSignatures(r, n, err),
Commits: ReadRoundSignatures(r, n, err),
}
}
func (pol *POL) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt32(w, pol.Height, &n, &err)
WriteUInt16(w, pol.Round, &n, &err)
WriteByteSlice(w, pol.BlockHash, &n, &err)
WriteBinary(w, pol.BlockParts, &n, &err)
WriteSignatures(w, pol.Votes, &n, &err)
WriteRoundSignatures(w, pol.Commits, &n, &err)
return
Height uint
Round uint
BlockHash []byte // Could be nil, which makes this a proof of unlock.
BlockParts PartSetHeader // When BlockHash is nil, this is zero.
Votes []POLVoteSignature // Prevote and commit signatures in ValidatorSet order.
}
// Returns whether +2/3 have voted/committed for BlockHash.
func (pol *POL) Verify(vset *state.ValidatorSet) error {
func (pol *POL) Verify(valSet *state.ValidatorSet) error {
if uint(len(pol.Votes)) != valSet.Size() {
return Errorf("Invalid POL votes count: Expected %v, got %v",
valSet.Size(), len(pol.Votes))
}
talliedVotingPower := uint64(0)
voteDoc := BinaryBytes(&Vote{
prevoteDoc := SignBytes(&Vote{
Height: pol.Height, Round: pol.Round, Type: VoteTypePrevote,
BlockHash: pol.BlockHash,
BlockParts: pol.BlockParts,
})
seenValidators := map[uint64]struct{}{}
seenValidators := map[string]struct{}{}
for _, sig := range pol.Votes {
for idx, sig := range pol.Votes {
voteDoc := prevoteDoc
_, val := valSet.GetByIndex(uint(idx))
// Commit signature?
if sig.Round < pol.Round {
voteDoc = SignBytes(&Vote{
Height: pol.Height, Round: sig.Round, Type: VoteTypeCommit,
BlockHash: pol.BlockHash,
BlockParts: pol.BlockParts,
})
} else if sig.Round > pol.Round {
return Errorf("Invalid commit round %v for POL %v", sig.Round, pol)
}
// Validate
if _, seen := seenValidators[sig.SignerId]; seen {
if _, seen := seenValidators[string(val.Address)]; seen {
return Errorf("Duplicate validator for vote %v for POL %v", sig, pol)
}
_, val := vset.GetById(sig.SignerId)
if val == nil {
return Errorf("Invalid validator for vote %v for POL %v", sig, pol)
}
if !val.VerifyBytes(voteDoc, sig) {
if !val.PubKey.VerifyBytes(voteDoc, sig.Signature.Bytes) {
return Errorf("Invalid signature for vote %v for POL %v", sig, pol)
}
// Tally
seenValidators[val.Id] = struct{}{}
seenValidators[string(val.Address)] = struct{}{}
talliedVotingPower += val.VotingPower
}
for _, rsig := range pol.Commits {
round := rsig.Round
sig := rsig.Signature
// Validate
if _, seen := seenValidators[sig.SignerId]; seen {
return Errorf("Duplicate validator for commit %v for POL %v", sig, pol)
}
_, val := vset.GetById(sig.SignerId)
if val == nil {
return Errorf("Invalid validator for commit %v for POL %v", sig, pol)
}
if round >= pol.Round {
return Errorf("Invalid commit round %v for POL %v", round, pol)
}
commitDoc := BinaryBytes(&Vote{
Height: pol.Height, Round: round, Type: VoteTypeCommit,
BlockHash: pol.BlockHash,
BlockParts: pol.BlockParts,
})
if !val.VerifyBytes(commitDoc, sig) {
return Errorf("Invalid signature for commit %v for POL %v", sig, pol)
}
// Tally
seenValidators[val.Id] = struct{}{}
talliedVotingPower += val.VotingPower
}
if talliedVotingPower > vset.TotalVotingPower()*2/3 {
if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
return nil
} else {
return Errorf("Invalid POL, insufficient voting power %v, needed %v",
talliedVotingPower, (vset.TotalVotingPower()*2/3 + 1))
talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
}
}
+152 -16
View File
@@ -1,30 +1,166 @@
package consensus
// TODO: This logic is crude. Should be more transactional.
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
db_ "github.com/tendermint/tendermint/db"
"github.com/tendermint/tendermint/state"
. "github.com/tendermint/tendermint/common"
. "github.com/tendermint/tendermint/config"
"github.com/tendermint/go-ed25519"
)
//-----------------------------------------------------------------------------
const (
stepNone = 0 // Used to distinguish the initial state
stepPropose = 1
stepPrevote = 2
stepPrecommit = 3
stepCommit = 4
)
func voteToStep(vote *Vote) uint8 {
switch vote.Type {
case VoteTypePrevote:
return stepPrevote
case VoteTypePrecommit:
return stepPrecommit
case VoteTypeCommit:
return stepCommit
default:
panic("Unknown vote type")
}
}
type PrivValidator struct {
db db_.DB
state.PrivAccount
Address []byte
PubKey PubKeyEd25519
PrivKey PrivKeyEd25519
LastHeight uint
LastRound uint
LastStep uint8
}
// Generates a new validator with private key.
func GenPrivValidator() *PrivValidator {
privKeyBytes := CRandBytes(32)
pubKeyBytes := ed25519.MakePubKey(privKeyBytes)
pubKey := PubKeyEd25519{pubKeyBytes}
privKey := PrivKeyEd25519{pubKeyBytes, privKeyBytes}
return &PrivValidator{
Address: pubKey.Address(),
PubKey: pubKey,
PrivKey: privKey,
LastHeight: 0,
LastRound: 0,
LastStep: stepNone,
}
}
type PrivValidatorJSON struct {
Address string
PubKey string
PrivKey string
LastHeight uint
LastRound uint
LastStep uint8
}
func LoadPrivValidator() *PrivValidator {
privValJSONBytes, err := ioutil.ReadFile(PrivValidatorFile())
if err != nil {
panic(err)
}
privValJSON := PrivValidatorJSON{}
err = json.Unmarshal(privValJSONBytes, &privValJSON)
if err != nil {
panic(err)
}
address, err := base64.StdEncoding.DecodeString(privValJSON.Address)
if err != nil {
panic(err)
}
pubKeyBytes, err := base64.StdEncoding.DecodeString(privValJSON.PubKey)
if err != nil {
panic(err)
}
privKeyBytes, err := base64.StdEncoding.DecodeString(privValJSON.PrivKey)
if err != nil {
panic(err)
}
n := new(int64)
privVal := &PrivValidator{
Address: address,
PubKey: ReadBinary(PubKeyEd25519{}, bytes.NewReader(pubKeyBytes), n, &err).(PubKeyEd25519),
PrivKey: ReadBinary(PrivKeyEd25519{}, bytes.NewReader(privKeyBytes), n, &err).(PrivKeyEd25519),
LastHeight: privValJSON.LastHeight,
LastRound: privValJSON.LastRound,
LastStep: privValJSON.LastStep,
}
if err != nil {
panic(err)
}
return privVal
}
func NewPrivValidator(db db_.DB, priv *state.PrivAccount) *PrivValidator {
return &PrivValidator{db, *priv}
func (privVal *PrivValidator) Save() {
privValJSON := PrivValidatorJSON{
Address: base64.StdEncoding.EncodeToString(privVal.Address),
PubKey: base64.StdEncoding.EncodeToString(BinaryBytes(privVal.PubKey)),
PrivKey: base64.StdEncoding.EncodeToString(BinaryBytes(privVal.PrivKey)),
LastHeight: privVal.LastHeight,
LastRound: privVal.LastRound,
LastStep: privVal.LastStep,
}
privValJSONBytes, err := json.Marshal(privValJSON)
if err != nil {
panic(err)
}
err = ioutil.WriteFile(PrivValidatorFile(), privValJSONBytes, 0700)
if err != nil {
panic(err)
}
}
func (privVal *PrivValidator) SignVote(vote *Vote) SignatureEd25519 {
if privVal.LastHeight < vote.Height ||
privVal.LastHeight == vote.Height && privVal.LastRound < vote.Round ||
privVal.LastHeight == vote.Height && privVal.LastRound == vote.Round && privVal.LastStep < voteToStep(vote) {
// Persist height/round/step
privVal.LastHeight = vote.Height
privVal.LastRound = vote.Round
privVal.LastStep = voteToStep(vote)
privVal.Save()
// Sign
return privVal.PrivKey.Sign(SignBytes(vote)).(SignatureEd25519)
} else {
panic(fmt.Sprintf("Attempt of duplicate signing of vote: Height %v, Round %v, Type %v", vote.Height, vote.Round, vote.Type))
}
}
func (privVal *PrivValidator) SignProposal(proposal *Proposal) SignatureEd25519 {
if privVal.LastHeight < proposal.Height ||
privVal.LastHeight == proposal.Height && privVal.LastRound < proposal.Round ||
privVal.LastHeight == 0 && privVal.LastRound == 0 && privVal.LastStep == stepNone {
// Persist height/round/step
privVal.LastHeight = proposal.Height
privVal.LastRound = proposal.Round
privVal.LastStep = stepPropose
privVal.Save()
// Double signing results in a panic.
func (pv *PrivValidator) Sign(o Signable) {
switch o.(type) {
case *Proposal:
//TODO: prevent double signing && test.
pv.PrivAccount.Sign(o.(*Proposal))
case *Vote:
//TODO: prevent double signing && test.
pv.PrivAccount.Sign(o.(*Vote))
// Sign
return privVal.PrivKey.Sign(SignBytes(proposal)).(SignatureEd25519)
} else {
panic(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v, Type %v", proposal.Height, proposal.Round))
}
}
+12 -32
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
)
@@ -15,15 +16,14 @@ var (
)
type Proposal struct {
Height uint32
Round uint16
Height uint
Round uint
BlockParts PartSetHeader
POLParts PartSetHeader
Signature Signature
Signature SignatureEd25519
}
func NewProposal(height uint32, round uint16, blockParts, polParts PartSetHeader) *Proposal {
func NewProposal(height uint, round uint, blockParts, polParts PartSetHeader) *Proposal {
return &Proposal{
Height: height,
Round: round,
@@ -32,34 +32,14 @@ func NewProposal(height uint32, round uint16, blockParts, polParts PartSetHeader
}
}
func ReadProposal(r io.Reader, n *int64, err *error) *Proposal {
return &Proposal{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
BlockParts: ReadPartSetHeader(r, n, err),
POLParts: ReadPartSetHeader(r, n, err),
Signature: ReadSignature(r, n, err),
}
}
func (p *Proposal) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt32(w, p.Height, &n, &err)
WriteUInt16(w, p.Round, &n, &err)
WriteBinary(w, p.BlockParts, &n, &err)
WriteBinary(w, p.POLParts, &n, &err)
WriteBinary(w, p.Signature, &n, &err)
return
}
func (p *Proposal) GetSignature() Signature {
return p.Signature
}
func (p *Proposal) SetSignature(sig Signature) {
p.Signature = sig
}
func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v %v %v %v}", p.Height, p.Round,
p.BlockParts, p.POLParts, p.Signature)
}
func (p *Proposal) WriteSignBytes(w io.Writer, n *int64, err *error) {
WriteUVarInt(p.Height, w, n, err)
WriteUVarInt(p.Round, w, n, err)
WriteBinary(p.BlockParts, w, n, err)
WriteBinary(p.POLParts, w, n, err)
}
+70 -115
View File
@@ -4,7 +4,6 @@ import (
"bytes"
"errors"
"fmt"
"io"
"sync"
"sync/atomic"
"time"
@@ -123,9 +122,9 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte
msg := msg_.(*NewRoundStepMessage)
ps.ApplyNewRoundStepMessage(msg, rs)
case *CommitMessage:
msg := msg_.(*CommitMessage)
ps.ApplyCommitMessage(msg)
case *CommitStepMessage:
msg := msg_.(*CommitStepMessage)
ps.ApplyCommitStepMessage(msg)
case *HasVoteMessage:
msg := msg_.(*HasVoteMessage)
@@ -160,10 +159,17 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte
case VoteCh:
switch msg_.(type) {
case *Vote:
vote := msg_.(*Vote)
added, index, err := conR.conS.AddVote(vote)
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)
if err != nil {
// Probably an invalid signature. Bad peer.
log.Warning("Error attempting to add vote: %v", err)
}
// Initialize Prevotes/Precommits/Commits if needed
@@ -220,14 +226,14 @@ func (conR *ConsensusReactor) broadcastNewRoundStepRoutine() {
Height: rs.Height,
Round: rs.Round,
Step: rs.Step,
SecondsSinceStartTime: uint32(timeElapsed.Seconds()),
SecondsSinceStartTime: uint(timeElapsed.Seconds()),
}
conR.sw.Broadcast(StateCh, msg)
}
// If the step is commit, then also broadcast a CommitMessage.
// If the step is commit, then also broadcast a CommitStepMessage.
if rs.Step == RoundStepCommit {
msg := &CommitMessage{
msg := &CommitStepMessage{
Height: rs.Height,
BlockParts: rs.ProposalBlockParts.Header(),
BlockBitArray: rs.ProposalBlockParts.BitArray(),
@@ -259,10 +265,10 @@ OUTER_LOOP:
Height: rs.Height,
Round: rs.Round,
Type: partTypeProposalBlock,
Part: rs.ProposalBlockParts.GetPart(uint16(index)),
Part: rs.ProposalBlockParts.GetPart(index),
}
peer.Send(DataCh, msg)
ps.SetHasProposalBlockPart(rs.Height, rs.Round, uint16(index))
ps.SetHasProposalBlockPart(rs.Height, rs.Round, index)
continue OUTER_LOOP
}
}
@@ -289,10 +295,10 @@ OUTER_LOOP:
Height: rs.Height,
Round: rs.Round,
Type: partTypeProposalPOL,
Part: rs.ProposalPOLParts.GetPart(uint16(index)),
Part: rs.ProposalPOLParts.GetPart(index),
}
peer.Send(DataCh, msg)
ps.SetHasProposalPOLPart(rs.Height, rs.Round, uint16(index))
ps.SetHasProposalPOLPart(rs.Height, rs.Round, index)
continue OUTER_LOOP
}
}
@@ -320,7 +326,7 @@ OUTER_LOOP:
if ok {
vote := voteSet.GetByIndex(index)
// NOTE: vote may be a commit.
msg := p2p.TypedMessage{msgTypeVote, vote}
msg := &VoteMessage{index, vote}
peer.Send(VoteCh, msg)
ps.SetHasVote(vote, index)
return true
@@ -399,7 +405,7 @@ OUTER_LOOP:
BlockParts: header.LastBlockParts,
Signature: rsig.Signature,
}
msg := p2p.TypedMessage{msgTypeVote, vote}
msg := &VoteMessage{index, vote}
peer.Send(VoteCh, msg)
ps.SetHasVote(vote, index)
continue OUTER_LOOP
@@ -416,8 +422,8 @@ OUTER_LOOP:
// Read only when returned by PeerState.GetRoundState().
type PeerRoundState struct {
Height uint32 // Height peer is at
Round uint16 // Round peer is at
Height uint // Height peer is at
Round uint // Round peer is at
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
@@ -474,7 +480,7 @@ func (ps *PeerState) SetHasProposal(proposal *Proposal) {
ps.ProposalPOLBitArray = NewBitArray(uint(proposal.POLParts.Total))
}
func (ps *PeerState) SetHasProposalBlockPart(height uint32, round uint16, index uint16) {
func (ps *PeerState) SetHasProposalBlockPart(height uint, round uint, index uint) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
@@ -485,7 +491,7 @@ func (ps *PeerState) SetHasProposalBlockPart(height uint32, round uint16, index
ps.ProposalBlockBitArray.SetIndex(uint(index), true)
}
func (ps *PeerState) SetHasProposalPOLPart(height uint32, round uint16, index uint16) {
func (ps *PeerState) SetHasProposalPOLPart(height uint, round uint, index uint) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
@@ -496,7 +502,7 @@ func (ps *PeerState) SetHasProposalPOLPart(height uint32, round uint16, index ui
ps.ProposalPOLBitArray.SetIndex(uint(index), true)
}
func (ps *PeerState) EnsureVoteBitArrays(height uint32, numValidators uint) {
func (ps *PeerState) EnsureVoteBitArrays(height uint, numValidators uint) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
@@ -521,7 +527,7 @@ func (ps *PeerState) SetHasVote(vote *Vote, index uint) {
ps.setHasVote(vote.Height, vote.Round, vote.Type, index)
}
func (ps *PeerState) setHasVote(height uint32, round uint16, type_ byte, index uint) {
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)
@@ -583,7 +589,7 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage, rs *Roun
}
}
func (ps *PeerState) ApplyCommitMessage(msg *CommitMessage) {
func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
@@ -614,15 +620,13 @@ func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) {
// Messages
const (
msgTypeUnknown = byte(0x00)
// Messages for communicating state changes
msgTypeUnknown = byte(0x00)
msgTypeNewRoundStep = byte(0x01)
msgTypeCommit = byte(0x02)
// Messages of data
msgTypeProposal = byte(0x11)
msgTypePart = byte(0x12) // both block & POL
msgTypeVote = byte(0x13)
msgTypeHasVote = byte(0x14)
msgTypeCommitStep = byte(0x02)
msgTypeProposal = byte(0x11)
msgTypePart = byte(0x12) // both block & POL
msgTypeVote = byte(0x13)
msgTypeHasVote = byte(0x14)
)
// TODO: check for unnecessary extra bytes at the end.
@@ -634,18 +638,18 @@ func decodeMessage(bz []byte) (msgType byte, msg interface{}) {
switch msgType {
// Messages for communicating state changes
case msgTypeNewRoundStep:
msg = readNewRoundStepMessage(r, n, err)
case msgTypeCommit:
msg = readCommitMessage(r, n, err)
msg = ReadBinary(&NewRoundStepMessage{}, r, n, err)
case msgTypeCommitStep:
msg = ReadBinary(&CommitStepMessage{}, r, n, err)
// Messages of data
case msgTypeProposal:
msg = ReadProposal(r, n, err)
msg = ReadBinary(&Proposal{}, r, n, err)
case msgTypePart:
msg = readPartMessage(r, n, err)
msg = ReadBinary(&PartMessage{}, r, n, err)
case msgTypeVote:
msg = ReadVote(r, n, err)
msg = ReadBinary(&VoteMessage{}, r, n, err)
case msgTypeHasVote:
msg = readHasVoteMessage(r, n, err)
msg = ReadBinary(&HasVoteMessage{}, r, n, err)
default:
msg = nil
}
@@ -655,29 +659,13 @@ func decodeMessage(bz []byte) (msgType byte, msg interface{}) {
//-------------------------------------
type NewRoundStepMessage struct {
Height uint32
Round uint16
Height uint
Round uint
Step RoundStep
SecondsSinceStartTime uint32
SecondsSinceStartTime uint
}
func readNewRoundStepMessage(r io.Reader, n *int64, err *error) *NewRoundStepMessage {
return &NewRoundStepMessage{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
Step: RoundStep(ReadUInt8(r, n, err)),
SecondsSinceStartTime: ReadUInt32(r, n, err),
}
}
func (m *NewRoundStepMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypeNewRoundStep, &n, &err)
WriteUInt32(w, m.Height, &n, &err)
WriteUInt16(w, m.Round, &n, &err)
WriteUInt8(w, uint8(m.Step), &n, &err)
WriteUInt32(w, m.SecondsSinceStartTime, &n, &err)
return
}
func (m *NewRoundStepMessage) TypeByte() byte { return msgTypeNewRoundStep }
func (m *NewRoundStepMessage) String() string {
return fmt.Sprintf("[NewRoundStep %v/%v/%X]", m.Height, m.Round, m.Step)
@@ -685,30 +673,16 @@ func (m *NewRoundStepMessage) String() string {
//-------------------------------------
type CommitMessage struct {
Height uint32
type CommitStepMessage struct {
Height uint
BlockParts PartSetHeader
BlockBitArray BitArray
}
func readCommitMessage(r io.Reader, n *int64, err *error) *CommitMessage {
return &CommitMessage{
Height: ReadUInt32(r, n, err),
BlockParts: ReadPartSetHeader(r, n, err),
BlockBitArray: ReadBitArray(r, n, err),
}
}
func (m *CommitStepMessage) TypeByte() byte { return msgTypeCommitStep }
func (m *CommitMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypeCommit, &n, &err)
WriteUInt32(w, m.Height, &n, &err)
WriteBinary(w, m.BlockParts, &n, &err)
WriteBinary(w, m.BlockBitArray, &n, &err)
return
}
func (m *CommitMessage) String() string {
return fmt.Sprintf("[Commit %v %v %v]", m.Height, m.BlockParts, m.BlockBitArray)
func (m *CommitStepMessage) String() string {
return fmt.Sprintf("[CommitStep %v %v %v]", m.Height, m.BlockParts, m.BlockBitArray)
}
//-------------------------------------
@@ -719,29 +693,13 @@ const (
)
type PartMessage struct {
Height uint32
Round uint16
Height uint
Round uint
Type byte
Part *Part
}
func readPartMessage(r io.Reader, n *int64, err *error) *PartMessage {
return &PartMessage{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
Type: ReadByte(r, n, err),
Part: ReadPart(r, n, err),
}
}
func (m *PartMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypePart, &n, &err)
WriteUInt32(w, m.Height, &n, &err)
WriteUInt16(w, m.Round, &n, &err)
WriteByte(w, m.Type, &n, &err)
WriteBinary(w, m.Part, &n, &err)
return
}
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)
@@ -749,30 +707,27 @@ func (m *PartMessage) String() string {
//-------------------------------------
type VoteMessage struct {
ValidatorIndex uint
Vote *Vote
}
func (m *VoteMessage) TypeByte() byte { return msgTypeVote }
func (m *VoteMessage) String() string {
return fmt.Sprintf("[Vote ValidatorIndex:%v Vote:%v]", m.ValidatorIndex, m.Vote)
}
//-------------------------------------
type HasVoteMessage struct {
Height uint32
Round uint16
Height uint
Round uint
Type byte
Index uint
}
func readHasVoteMessage(r io.Reader, n *int64, err *error) *HasVoteMessage {
return &HasVoteMessage{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
Type: ReadByte(r, n, err),
Index: ReadUVarInt(r, n, err),
}
}
func (m *HasVoteMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypeHasVote, &n, &err)
WriteUInt32(w, m.Height, &n, &err)
WriteUInt16(w, m.Round, &n, &err)
WriteByte(w, m.Type, &n, &err)
WriteUVarInt(w, m.Index, &n, &err)
return
}
func (m *HasVoteMessage) TypeByte() byte { return msgTypeHasVote }
func (m *HasVoteMessage) String() string {
return fmt.Sprintf("[HasVote %v/%v T:%X]", m.Height, m.Round, m.Type)
+57 -43
View File
@@ -52,6 +52,7 @@ Consensus State Machine Overview:
package consensus
import (
"bytes"
"errors"
"fmt"
"math"
@@ -59,6 +60,7 @@ import (
"sync/atomic"
"time"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common"
@@ -82,7 +84,7 @@ const (
RoundActionPrevote = RoundActionType(0xA1) // Prevote and goto RoundStepPrevote
RoundActionPrecommit = RoundActionType(0xA2) // Precommit and goto RoundStepPrecommit
RoundActionTryCommit = RoundActionType(0xC0) // Goto RoundStepCommit, or RoundStepPropose for next round.
RoundActionCommit = RoundActionType(0xC1) // Goto RoundStepCommit
RoundActionCommit = RoundActionType(0xC1) // Goto RoundStepCommit upon +2/3 commits
RoundActionTryFinalize = RoundActionType(0xC2) // Maybe goto RoundStepPropose for next round.
roundDuration0 = 60 * time.Second // The first round is 60 seconds long.
@@ -97,8 +99,8 @@ var (
)
type RoundAction struct {
Height uint32 // The block height for which consensus is reaching for.
Round uint16 // The round number at given height.
Height uint // The block height for which consensus is reaching for.
Round uint // The round number at given height.
Action RoundActionType // Action to perform.
}
@@ -106,8 +108,8 @@ type RoundAction struct {
// Immutable when returned from ConsensusState.GetRoundState()
type RoundState struct {
Height uint32 // Height we are working on
Round uint16
Height uint // Height we are working on
Round uint
Step RoundStep
StartTime time.Time
CommitTime time.Time // Time when +2/3 commits were found
@@ -347,7 +349,6 @@ ACTION_LOOP:
if rs.Precommits.HasTwoThirdsMajority() {
// Enter RoundStepCommit and commit.
cs.RunActionCommit(rs.Height)
cs.queueAction(RoundAction{rs.Height, rs.Round, RoundActionTryFinalize})
continue ACTION_LOOP
} else {
// Could not commit, move onto next round.
@@ -363,7 +364,6 @@ ACTION_LOOP:
}
// Enter RoundStepCommit and commit.
cs.RunActionCommit(rs.Height)
cs.queueAction(RoundAction{rs.Height, rs.Round, RoundActionTryFinalize})
continue ACTION_LOOP
case RoundActionTryFinalize:
@@ -435,7 +435,7 @@ func (cs *ConsensusState) updateToState(state *state.State) {
}
// After the call cs.Step becomes RoundStepNewRound.
func (cs *ConsensusState) setupNewRound(round uint16) {
func (cs *ConsensusState) setupNewRound(round uint) {
// Sanity check
if round == 0 {
panic("setupNewRound() should never be called for round 0")
@@ -470,7 +470,7 @@ func (cs *ConsensusState) SetPrivValidator(priv *PrivValidator) {
//-----------------------------------------------------------------------------
// Set up the round to desired round and set step to RoundStepNewRound
func (cs *ConsensusState) SetupNewRound(height uint32, desiredRound uint16) bool {
func (cs *ConsensusState) SetupNewRound(height uint, desiredRound uint) bool {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.Height != height {
@@ -485,7 +485,7 @@ func (cs *ConsensusState) SetupNewRound(height uint32, desiredRound uint16) bool
return true
}
func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
func (cs *ConsensusState) RunActionPropose(height uint, round uint) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.Height != height || cs.Round != round {
@@ -497,7 +497,7 @@ func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
}()
// Nothing to do if it's not our turn.
if cs.PrivValidator == nil || cs.Validators.Proposer().Id != cs.PrivValidator.Id {
if cs.PrivValidator == nil || !bytes.Equal(cs.Validators.Proposer().Address, cs.PrivValidator.Address) {
return
}
@@ -560,7 +560,7 @@ func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
// Make proposal
proposal := NewProposal(cs.Height, cs.Round, blockParts.Header(), polParts.Header())
cs.PrivValidator.Sign(proposal)
proposal.Signature = cs.PrivValidator.SignProposal(proposal)
// Set fields
cs.Proposal = proposal
@@ -572,7 +572,7 @@ func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
// Prevote for LockedBlock if we're locked, or ProposealBlock if valid.
// Otherwise vote nil.
func (cs *ConsensusState) RunActionPrevote(height uint32, round uint16) {
func (cs *ConsensusState) RunActionPrevote(height uint, round uint) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.Height != height || cs.Round != round {
@@ -612,7 +612,7 @@ func (cs *ConsensusState) RunActionPrevote(height uint32, round uint16) {
// Lock & Precommit the ProposalBlock if we have enough prevotes for it,
// or unlock an existing lock if +2/3 of prevotes were nil.
func (cs *ConsensusState) RunActionPrecommit(height uint32, round uint16) {
func (cs *ConsensusState) RunActionPrecommit(height uint, round uint) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.Height != height || cs.Round != round {
@@ -668,7 +668,11 @@ func (cs *ConsensusState) RunActionPrecommit(height uint32, round uint16) {
}
// Enter commit step. See the diagram for details.
func (cs *ConsensusState) RunActionCommit(height uint32) {
// There are two ways to enter this step:
// * After the Precommit step with +2/3 precommits, or,
// * Upon +2/3 commits regardless of current step
// Either way this action is run at most once per round.
func (cs *ConsensusState) RunActionCommit(height uint) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.Height != height {
@@ -679,6 +683,7 @@ func (cs *ConsensusState) RunActionCommit(height uint32) {
cs.newStepCh <- cs.getRoundState()
}()
// Sanity check.
// There are two ways to enter:
// 1. +2/3 precommits at the end of RoundStepPrecommit
// 2. +2/3 commits at any time
@@ -712,15 +717,20 @@ func (cs *ConsensusState) RunActionCommit(height uint32) {
}
} else {
// We have the block, so save/stage/sign-commit-vote.
cs.saveCommitVoteBlock(cs.ProposalBlock, cs.ProposalBlockParts)
}
cs.processBlockForCommit(cs.ProposalBlock, cs.ProposalBlockParts)
// If we have the block AND +2/3 commits, queue RoundActionTryFinalize.
// Round will immediately become finalized.
if cs.ProposalBlock.HashesTo(hash) && cs.Commits.HasTwoThirdsMajority() {
cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionTryFinalize})
}
}
// Returns true if Finalize happened, which increments height && sets
// the step to RoundStepNewHeight (or RoundStepNewRound, but probably not).
func (cs *ConsensusState) TryFinalizeCommit(height uint32) bool {
func (cs *ConsensusState) TryFinalizeCommit(height uint) bool {
cs.mtx.Lock()
defer cs.mtx.Unlock()
@@ -754,6 +764,7 @@ func (cs *ConsensusState) TryFinalizeCommit(height uint32) bool {
return true
} else {
// Prevent zombies.
// TODO: Does this ever happen?
Panicf("+2/3 committed an invalid block: %v", err)
}
}
@@ -782,7 +793,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
}
// Verify signature
if !cs.Validators.Proposer().Verify(proposal) {
if !cs.Validators.Proposer().PubKey.VerifyBytes(SignBytes(proposal), proposal.Signature) {
return ErrInvalidProposalSignature
}
@@ -794,7 +805,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
// NOTE: block is not necessarily valid.
// NOTE: This function may increment the height.
func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part *Part) (added bool, err error) {
func (cs *ConsensusState) AddProposalBlockPart(height uint, round uint, part *Part) (added bool, err error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
@@ -815,8 +826,11 @@ func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part
if added && cs.ProposalBlockParts.IsComplete() {
var n int64
var err error
cs.ProposalBlock = ReadBlock(cs.ProposalBlockParts.GetReader(), &n, &err)
cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionTryFinalize})
cs.ProposalBlock = ReadBinary(&Block{}, cs.ProposalBlockParts.GetReader(), &n, &err).(*Block)
// If we're already in the commit step, try to finalize round.
if cs.Step == RoundStepCommit {
cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionTryFinalize})
}
// XXX If POL is valid, consider unlocking.
return true, err
}
@@ -824,7 +838,7 @@ func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part
}
// NOTE: POL is not necessarily valid.
func (cs *ConsensusState) AddProposalPOLPart(height uint32, round uint16, part *Part) (added bool, err error) {
func (cs *ConsensusState) AddProposalPOLPart(height uint, round uint, part *Part) (added bool, err error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
@@ -844,21 +858,21 @@ func (cs *ConsensusState) AddProposalPOLPart(height uint32, round uint16, part *
if added && cs.ProposalPOLParts.IsComplete() {
var n int64
var err error
cs.ProposalPOL = ReadPOL(cs.ProposalPOLParts.GetReader(), &n, &err)
cs.ProposalPOL = ReadBinary(&POL{}, cs.ProposalPOLParts.GetReader(), &n, &err).(*POL)
return true, err
}
return true, nil
}
func (cs *ConsensusState) AddVote(vote *Vote) (added bool, index uint, err error) {
func (cs *ConsensusState) AddVote(address []byte, vote *Vote) (added bool, index uint, err error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cs.addVote(vote)
return cs.addVote(address, vote)
}
// TODO: Maybe move this out of here?
func (cs *ConsensusState) LoadHeaderValidation(height uint32) (*Header, *Validation) {
func (cs *ConsensusState) LoadHeaderValidation(height uint) (*Header, *Validation) {
meta := cs.blockStore.LoadBlockMeta(height)
if meta == nil {
return nil, nil
@@ -869,21 +883,21 @@ func (cs *ConsensusState) LoadHeaderValidation(height uint32) (*Header, *Validat
//-----------------------------------------------------------------------------
func (cs *ConsensusState) addVote(vote *Vote) (added bool, index uint, err error) {
func (cs *ConsensusState) addVote(address []byte, vote *Vote) (added bool, index uint, err error) {
switch vote.Type {
case VoteTypePrevote:
// Prevotes checks for height+round match.
return cs.Prevotes.Add(vote)
return cs.Prevotes.Add(address, vote)
case VoteTypePrecommit:
// Precommits checks for height+round match.
return cs.Precommits.Add(vote)
return cs.Precommits.Add(address, vote)
case VoteTypeCommit:
if vote.Height == cs.Height {
// No need to check if vote.Round < cs.Round ...
// Prevotes && Precommits already checks that.
cs.Prevotes.Add(vote)
cs.Precommits.Add(vote)
added, index, err = cs.Commits.Add(vote)
cs.Prevotes.Add(address, vote)
cs.Precommits.Add(address, vote)
added, index, err = cs.Commits.Add(address, vote)
if added && cs.Commits.HasTwoThirdsMajority() && cs.CommitTime.IsZero() {
cs.CommitTime = time.Now()
log.Debug("Set CommitTime to %v", cs.CommitTime)
@@ -896,7 +910,7 @@ func (cs *ConsensusState) addVote(vote *Vote) (added bool, index uint, err error
return added, index, err
}
if vote.Height+1 == cs.Height {
return cs.LastCommits.Add(vote)
return cs.LastCommits.Add(address, vote)
}
return false, 0, nil
default:
@@ -930,7 +944,7 @@ func (cs *ConsensusState) stageBlock(block *Block, blockParts *PartSet) error {
}
func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header PartSetHeader) *Vote {
if cs.PrivValidator == nil || !cs.Validators.HasId(cs.PrivValidator.Id) {
if cs.PrivValidator == nil || !cs.Validators.HasAddress(cs.PrivValidator.Address) {
return nil
}
vote := &Vote{
@@ -940,12 +954,12 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header PartSetHea
BlockHash: hash,
BlockParts: header,
}
cs.PrivValidator.Sign(vote)
cs.addVote(vote)
vote.Signature = cs.PrivValidator.SignVote(vote)
cs.addVote(cs.PrivValidator.Address, vote)
return vote
}
func (cs *ConsensusState) processBlockForCommit(block *Block, blockParts *PartSet) {
func (cs *ConsensusState) saveCommitVoteBlock(block *Block, blockParts *PartSet) {
// The proposal must be valid.
if err := cs.stageBlock(block, blockParts); err != nil {
@@ -969,19 +983,19 @@ func (cs *ConsensusState) processBlockForCommit(block *Block, blockParts *PartSe
//-----------------------------------------------------------------------------
// total duration of given round
func calcRoundDuration(round uint16) time.Duration {
func calcRoundDuration(round uint) time.Duration {
return roundDuration0 + roundDurationDelta*time.Duration(round)
}
// startTime is when round zero started.
func calcRoundStartTime(round uint16, startTime time.Time) time.Time {
func calcRoundStartTime(round uint, startTime time.Time) time.Time {
return startTime.Add(roundDuration0*time.Duration(round) +
roundDurationDelta*(time.Duration((int64(round)*int64(round)-int64(round))/2)))
}
// calculates the current round given startTime of round zero.
// NOTE: round is zero if startTime is in the future.
func calcRound(startTime time.Time) uint16 {
func calcRound(startTime time.Time) uint {
now := time.Now()
if now.Before(startTime) {
return 0
@@ -997,18 +1011,18 @@ func calcRound(startTime time.Time) uint16 {
if math.IsNaN(R) {
panic("Could not calc round, should not happen")
}
if R > math.MaxInt16 {
if R > math.MaxInt32 {
Panicf("Could not calc round, round overflow: %v", R)
}
if R < 0 {
return 0
}
return uint16(R)
return uint(R)
}
// convenience
// NOTE: elapsedRatio can be negative if startTime is in the future.
func calcRoundInfo(startTime time.Time) (round uint16, roundStartTime time.Time, roundDuration time.Duration,
func calcRoundInfo(startTime time.Time) (round uint, roundStartTime time.Time, roundDuration time.Duration,
roundElapsed time.Duration, elapsedRatio float64) {
round = calcRound(startTime)
roundStartTime = calcRoundStartTime(round, startTime)
+15 -11
View File
@@ -6,23 +6,27 @@ import (
// Common test methods
func makeValidator(id uint64, votingPower uint64) (*state.Validator, *state.PrivAccount) {
privAccount := state.GenPrivAccount()
privAccount.Id = id
func makeValidator(votingPower uint64) (*state.Validator, *PrivValidator) {
privValidator := GenPrivValidator()
return &state.Validator{
Account: privAccount.Account,
VotingPower: votingPower,
}, privAccount
Address: privValidator.Address,
PubKey: privValidator.PubKey,
BondHeight: 0,
UnbondHeight: 0,
LastCommitHeight: 0,
VotingPower: votingPower,
Accum: 0,
}, privValidator
}
func makeVoteSet(height uint32, round uint16, type_ byte, numValidators int, votingPower uint64) (*VoteSet, *state.ValidatorSet, []*state.PrivAccount) {
func makeVoteSet(height uint, round uint, type_ byte, numValidators int, votingPower uint64) (*VoteSet, *state.ValidatorSet, []*PrivValidator) {
vals := make([]*state.Validator, numValidators)
privAccounts := make([]*state.PrivAccount, numValidators)
privValidators := make([]*PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
val, privAccount := makeValidator(uint64(i), votingPower)
val, privValidator := makeValidator(votingPower)
vals[i] = val
privAccounts[i] = privAccount
privValidators[i] = privValidator
}
valSet := state.NewValidatorSet(vals)
return NewVoteSet(height, round, type_, valSet), valSet, privAccounts
return NewVoteSet(height, round, type_, valSet), valSet, privValidators
}
+112 -128
View File
@@ -6,6 +6,7 @@ import (
"strings"
"sync"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common"
@@ -16,17 +17,16 @@ import (
// for a predefined vote type.
// Note that there three kinds of votes: prevotes, precommits, and commits.
// A commit of prior rounds can be added added in lieu of votes/precommits.
// TODO: test majority calculations etc.
// NOTE: assumes that the sum total of voting power does not exceed MaxUInt64.
// NOTE: Assumes that the sum total of voting power does not exceed MaxUInt64.
type VoteSet struct {
height uint32
round uint16
height uint
round uint
type_ byte
mtx sync.Mutex
vset *state.ValidatorSet
votes map[uint64]*Vote
votesBitArray BitArray
valSet *state.ValidatorSet
votes []*Vote // validator index -> vote
votesBitArray BitArray // validator index -> has vote?
votesByBlock map[string]uint64 // string(blockHash)+string(blockParts) -> vote sum.
totalVotes uint64
maj23Hash []byte
@@ -35,7 +35,7 @@ type VoteSet struct {
}
// Constructs a new VoteSet struct used to accumulate votes for each round.
func NewVoteSet(height uint32, round uint16, type_ byte, vset *state.ValidatorSet) *VoteSet {
func NewVoteSet(height uint, round uint, type_ byte, valSet *state.ValidatorSet) *VoteSet {
if height == 0 {
panic("Cannot make VoteSet for height == 0, doesn't make sense.")
}
@@ -46,55 +46,55 @@ func NewVoteSet(height uint32, round uint16, type_ byte, vset *state.ValidatorSe
height: height,
round: round,
type_: type_,
vset: vset,
votes: make(map[uint64]*Vote, vset.Size()),
votesBitArray: NewBitArray(vset.Size()),
valSet: valSet,
votes: make([]*Vote, valSet.Size()),
votesBitArray: NewBitArray(valSet.Size()),
votesByBlock: make(map[string]uint64),
totalVotes: 0,
}
}
func (vs *VoteSet) Size() uint {
if vs == nil {
func (voteSet *VoteSet) Size() uint {
if voteSet == nil {
return 0
} else {
return vs.vset.Size()
return voteSet.valSet.Size()
}
}
// True if added, false if not.
// Returns ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
// NOTE: vote should not be mutated after adding.
func (vs *VoteSet) Add(vote *Vote) (bool, uint, error) {
vs.mtx.Lock()
defer vs.mtx.Unlock()
func (voteSet *VoteSet) Add(address []byte, vote *Vote) (bool, uint, error) {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
// Make sure the step matches. (or that vote is commit && round < vs.round)
if vote.Height != vs.height ||
(vote.Type != VoteTypeCommit && vote.Round != vs.round) ||
(vote.Type != VoteTypeCommit && vote.Type != vs.type_) ||
(vote.Type == VoteTypeCommit && vs.type_ != VoteTypeCommit && vote.Round >= vs.round) {
// Make sure the step matches. (or that vote is commit && round < voteSet.round)
if vote.Height != voteSet.height ||
(vote.Type != VoteTypeCommit && vote.Round != voteSet.round) ||
(vote.Type != VoteTypeCommit && vote.Type != voteSet.type_) ||
(vote.Type == VoteTypeCommit && voteSet.type_ != VoteTypeCommit && vote.Round >= voteSet.round) {
return false, 0, ErrVoteUnexpectedStep
}
// Ensure that signer is a validator.
_, val := vs.vset.GetById(vote.SignerId)
valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil {
return false, 0, ErrVoteInvalidAccount
}
// Check signature.
if !val.Verify(vote) {
if !val.PubKey.VerifyBytes(SignBytes(vote), vote.Signature) {
// Bad signature.
return false, 0, ErrVoteInvalidSignature
}
return vs.addVote(vote)
return voteSet.addVote(valIndex, vote)
}
func (vs *VoteSet) addVote(vote *Vote) (bool, uint, error) {
func (voteSet *VoteSet) addVote(valIndex uint, vote *Vote) (bool, uint, error) {
// If vote already exists, return false.
if existingVote, ok := vs.votes[vote.SignerId]; ok {
if existingVote := voteSet.votes[valIndex]; existingVote != nil {
if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
return false, 0, nil
} else {
@@ -103,170 +103,154 @@ func (vs *VoteSet) addVote(vote *Vote) (bool, uint, error) {
}
// Add vote.
vs.votes[vote.SignerId] = vote
voterIndex, val := vs.vset.GetById(vote.SignerId)
_, val := voteSet.valSet.GetByIndex(valIndex)
if val == nil {
return false, 0, ErrVoteInvalidAccount
panic(fmt.Sprintf("Missing validator for index %v", valIndex))
}
vs.votesBitArray.SetIndex(uint(voterIndex), true)
voteSet.votes[valIndex] = vote
voteSet.votesBitArray.SetIndex(valIndex, true)
blockKey := string(vote.BlockHash) + string(BinaryBytes(vote.BlockParts))
totalBlockHashVotes := vs.votesByBlock[blockKey] + val.VotingPower
vs.votesByBlock[blockKey] = totalBlockHashVotes
vs.totalVotes += val.VotingPower
totalBlockHashVotes := voteSet.votesByBlock[blockKey] + val.VotingPower
voteSet.votesByBlock[blockKey] = totalBlockHashVotes
voteSet.totalVotes += val.VotingPower
// If we just nudged it up to two thirds majority, add it.
if totalBlockHashVotes > vs.vset.TotalVotingPower()*2/3 &&
(totalBlockHashVotes-val.VotingPower) <= vs.vset.TotalVotingPower()*2/3 {
vs.maj23Hash = vote.BlockHash
vs.maj23Parts = vote.BlockParts
vs.maj23Exists = true
if totalBlockHashVotes > voteSet.valSet.TotalVotingPower()*2/3 &&
(totalBlockHashVotes-val.VotingPower) <= voteSet.valSet.TotalVotingPower()*2/3 {
voteSet.maj23Hash = vote.BlockHash
voteSet.maj23Parts = vote.BlockParts
voteSet.maj23Exists = true
}
return true, voterIndex, nil
return true, valIndex, nil
}
// Assumes that commits VoteSet is valid.
func (vs *VoteSet) AddFromCommits(commits *VoteSet) {
commitVotes := commits.AllVotes()
for _, commit := range commitVotes {
if commit.Round < vs.round {
vs.addVote(commit)
func (voteSet *VoteSet) AddFromCommits(commits *VoteSet) {
for valIndex, commit := range commits.votes {
if commit.Round < voteSet.round {
voteSet.addVote(uint(valIndex), commit)
}
}
}
func (vs *VoteSet) BitArray() BitArray {
if vs == nil {
func (voteSet *VoteSet) BitArray() BitArray {
if voteSet == nil {
return BitArray{}
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
return vs.votesBitArray.Copy()
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.votesBitArray.Copy()
}
func (vs *VoteSet) GetByIndex(index uint) *Vote {
vs.mtx.Lock()
defer vs.mtx.Unlock()
func (voteSet *VoteSet) GetByIndex(valIndex uint) *Vote {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.votes[valIndex]
}
id, val := vs.vset.GetByIndex(index)
func (voteSet *VoteSet) GetByAddress(address []byte) *Vote {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil {
panic("GetByIndex(index) returned nil")
panic("GetByAddress(address) returned nil")
}
return vs.votes[id]
return voteSet.votes[valIndex]
}
func (vs *VoteSet) GetById(id uint64) *Vote {
vs.mtx.Lock()
defer vs.mtx.Unlock()
return vs.votes[id]
}
func (vs *VoteSet) AllVotes() []*Vote {
vs.mtx.Lock()
defer vs.mtx.Unlock()
votes := []*Vote{}
for _, vote := range vs.votes {
votes = append(votes, vote)
}
return votes
}
func (vs *VoteSet) HasTwoThirdsMajority() bool {
if vs == nil {
func (voteSet *VoteSet) HasTwoThirdsMajority() bool {
if voteSet == nil {
return false
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
return vs.maj23Exists
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.maj23Exists
}
// Returns either a blockhash (or nil) that received +2/3 majority.
// If there exists no such majority, returns (nil, false).
func (vs *VoteSet) TwoThirdsMajority() (hash []byte, parts PartSetHeader, ok bool) {
vs.mtx.Lock()
defer vs.mtx.Unlock()
if vs.maj23Exists {
return vs.maj23Hash, vs.maj23Parts, true
func (voteSet *VoteSet) TwoThirdsMajority() (hash []byte, parts PartSetHeader, ok bool) {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if voteSet.maj23Exists {
return voteSet.maj23Hash, voteSet.maj23Parts, true
} else {
return nil, PartSetHeader{}, false
}
}
func (vs *VoteSet) MakePOL() *POL {
if vs.type_ != VoteTypePrevote {
func (voteSet *VoteSet) MakePOL() *POL {
if voteSet.type_ != VoteTypePrevote {
panic("Cannot MakePOL() unless VoteSet.Type is VoteTypePrevote")
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
if !vs.maj23Exists {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if !voteSet.maj23Exists {
return nil
}
pol := &POL{
Height: vs.height,
Round: vs.round,
BlockHash: vs.maj23Hash,
BlockParts: vs.maj23Parts,
Height: voteSet.height,
Round: voteSet.round,
BlockHash: voteSet.maj23Hash,
BlockParts: voteSet.maj23Parts,
Votes: make([]POLVoteSignature, voteSet.valSet.Size()),
}
for _, vote := range vs.votes {
if !bytes.Equal(vote.BlockHash, vs.maj23Hash) {
for valIndex, vote := range voteSet.votes {
if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) {
continue
}
if !vote.BlockParts.Equals(vs.maj23Parts) {
if !vote.BlockParts.Equals(voteSet.maj23Parts) {
continue
}
if vote.Type == VoteTypePrevote {
pol.Votes = append(pol.Votes, vote.Signature)
} else if vote.Type == VoteTypeCommit {
pol.Commits = append(pol.Commits, RoundSignature{vote.Round, vote.Signature})
} else {
Panicf("Unexpected vote type %X", vote.Type)
pol.Votes[valIndex] = POLVoteSignature{
Round: vote.Round,
Signature: vote.Signature,
}
}
return pol
}
func (vs *VoteSet) MakeValidation() *Validation {
if vs.type_ != VoteTypeCommit {
func (voteSet *VoteSet) MakeValidation() *Validation {
if voteSet.type_ != VoteTypeCommit {
panic("Cannot MakeValidation() unless VoteSet.Type is VoteTypeCommit")
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
if len(vs.maj23Hash) == 0 {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if len(voteSet.maj23Hash) == 0 {
panic("Cannot MakeValidation() unless a blockhash has +2/3")
}
rsigs := make([]RoundSignature, vs.vset.Size())
vs.vset.Iterate(func(index uint, val *state.Validator) bool {
vote := vs.votes[val.Id]
commits := make([]Commit, voteSet.valSet.Size())
voteSet.valSet.Iterate(func(valIndex uint, val *state.Validator) bool {
vote := voteSet.votes[valIndex]
if vote == nil {
return false
}
if !bytes.Equal(vote.BlockHash, vs.maj23Hash) {
if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) {
return false
}
if !vote.BlockParts.Equals(vs.maj23Parts) {
if !vote.BlockParts.Equals(voteSet.maj23Parts) {
return false
}
rsigs[index] = RoundSignature{vote.Round, vote.Signature}
commits[valIndex] = Commit{vote.Round, vote.Signature}
return false
})
return &Validation{
Commits: rsigs,
Commits: commits,
}
}
func (vs *VoteSet) String() string {
return vs.StringWithIndent("")
func (voteSet *VoteSet) String() string {
return voteSet.StringWithIndent("")
}
func (vs *VoteSet) StringWithIndent(indent string) string {
vs.mtx.Lock()
defer vs.mtx.Unlock()
func (voteSet *VoteSet) StringWithIndent(indent string) string {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
voteStrings := make([]string, len(vs.votes))
voteStrings := make([]string, len(voteSet.votes))
counter := 0
for _, vote := range vs.votes {
for _, vote := range voteSet.votes {
voteStrings[counter] = vote.String()
counter++
}
@@ -275,18 +259,18 @@ func (vs *VoteSet) StringWithIndent(indent string) string {
%s %v
%s %v
%s}`,
indent, vs.height, vs.round, vs.type_,
indent, voteSet.height, voteSet.round, voteSet.type_,
indent, strings.Join(voteStrings, "\n"+indent+" "),
indent, vs.votesBitArray,
indent, voteSet.votesBitArray,
indent)
}
func (vs *VoteSet) Description() string {
if vs == nil {
func (voteSet *VoteSet) Description() string {
if voteSet == nil {
return "nil-VoteSet"
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return fmt.Sprintf(`VoteSet{H:%v R:%v T:%v %v}`,
vs.height, vs.round, vs.type_, vs.votesBitArray)
voteSet.height, voteSet.round, voteSet.type_, voteSet.votesBitArray)
}