Browse Source

consensus/state is 2-step asynchronous

pull/96/head
Jae Kwon 10 years ago
parent
commit
01b5540ffe
16 changed files with 682 additions and 1443 deletions
  1. +7
    -9
      blockchain/store.go
  2. +23
    -35
      consensus/height_vote_set.go
  3. +0
    -101
      consensus/pol.go
  4. +0
    -213
      consensus/pol_test.go
  5. +545
    -692
      consensus/state.go
  6. +1
    -170
      consensus/state_test.go
  7. +5
    -6
      consensus/types/proposal.go
  8. +18
    -58
      consensus/vote_set.go
  9. +5
    -62
      consensus/vote_set_test.go
  10. +20
    -27
      state/execution.go
  11. +0
    -10
      state/priv_validator.go
  12. +1
    -1
      state/state.go
  13. +13
    -13
      state/state_test.go
  14. +9
    -9
      state/validator_set.go
  15. +35
    -32
      types/block.go
  16. +0
    -5
      types/vote.go

+ 7
- 9
blockchain/store.go View File

@ -18,9 +18,9 @@ Simple low level store for blocks.
There are three types of information stored:
- BlockMeta: Meta information about each block
- Block part: Parts of each block, aggregated w/ PartSet
- Validation: The Validation part of each block, for gossiping commit votes
- Validation: The Validation part of each block, for gossiping precommit votes
Currently the commit signatures are duplicated in the Block parts as
Currently the precommit signatures are duplicated in the Block parts as
well as the Validation. In the future this may change, perhaps by moving
the Validation data outside the Block.
*/
@ -101,7 +101,7 @@ func (bs *BlockStore) LoadBlockMeta(height uint) *types.BlockMeta {
return meta
}
// NOTE: the Commit-vote heights are for the block at `height-1`
// NOTE: the Precommit-vote heights are for the block at `height-1`
// Since these are included in the subsequent block, the height
// is off by 1.
func (bs *BlockStore) LoadBlockValidation(height uint) *types.Validation {
@ -118,7 +118,7 @@ func (bs *BlockStore) LoadBlockValidation(height uint) *types.Validation {
return validation
}
// NOTE: the Commit-vote heights are for the block at `height`
// NOTE: the Precommit-vote heights are for the block at `height`
func (bs *BlockStore) LoadSeenValidation(height uint) *types.Validation {
var n int64
var err error
@ -134,12 +134,10 @@ func (bs *BlockStore) LoadSeenValidation(height uint) *types.Validation {
}
// blockParts: Must be parts of the block
// seenValidation: The +2/3 commits that were seen which finalized the height.
// seenValidation: The +2/3 precommits that were seen which committed at height.
// If all the nodes restart after committing a block,
// we need this to reload the commits to catch-up nodes to the
// we need this to reload the precommits to catch-up nodes to the
// most recent height. Otherwise they'd stall at H-1.
// Also good to have to debug consensus issues & punish wrong-signers
// whose commits weren't included in the block.
func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenValidation *types.Validation) {
height := block.Height
if height != bs.height+1 {
@ -163,7 +161,7 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
blockValidationBytes := binary.BinaryBytes(block.Validation)
bs.db.Set(calcBlockValidationKey(height), blockValidationBytes)
// Save seen validation (seen +2/3 commits)
// Save seen validation (seen +2/3 precommits for block)
seenValidationBytes := binary.BinaryBytes(seenValidation)
bs.db.Set(calcSeenValidationKey(height), seenValidationBytes)


+ 23
- 35
consensus/height_vote_set.go View File

@ -15,9 +15,6 @@ type RoundVoteSet struct {
}
// Keeps track of VoteSets for all the rounds of a height.
// We add the commit votes to all the affected rounds,
// and for new rounds carry over the commit set. Commits have
// an associated round, so the performance hit won't be O(rounds).
type HeightVoteSet struct {
height uint
valSet *sm.ValidatorSet
@ -25,7 +22,6 @@ type HeightVoteSet struct {
mtx sync.Mutex
round uint // max tracked round
roundVoteSets map[uint]RoundVoteSet // keys: [0...round]
commits *VoteSet // all commits for height
}
func NewHeightVoteSet(height uint, valSet *sm.ValidatorSet) *HeightVoteSet {
@ -33,7 +29,6 @@ func NewHeightVoteSet(height uint, valSet *sm.ValidatorSet) *HeightVoteSet {
height: height,
valSet: valSet,
roundVoteSets: make(map[uint]RoundVoteSet),
commits: NewVoteSet(height, 0, types.VoteTypeCommit, valSet),
}
hvs.SetRound(0)
return hvs
@ -49,7 +44,7 @@ func (hvs *HeightVoteSet) Round() uint {
return hvs.round
}
// Create more RoundVoteSets up to round with all commits carried over.
// Create more RoundVoteSets up to round.
func (hvs *HeightVoteSet) SetRound(round uint) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
@ -58,9 +53,7 @@ func (hvs *HeightVoteSet) SetRound(round uint) {
}
for r := hvs.round + 1; r <= round; r++ {
prevotes := NewVoteSet(hvs.height, r, types.VoteTypePrevote, hvs.valSet)
prevotes.AddFromCommits(hvs.commits)
precommits := NewVoteSet(hvs.height, r, types.VoteTypePrecommit, hvs.valSet)
precommits.AddFromCommits(hvs.commits)
hvs.roundVoteSets[r] = RoundVoteSet{
Prevotes: prevotes,
Precommits: precommits,
@ -78,39 +71,35 @@ func (hvs *HeightVoteSet) AddByAddress(address []byte, vote *types.Vote) (added
return
}
added, index, err = voteSet.AddByAddress(address, vote)
if err != nil {
return
}
// If vote is commit, also add to all prevote/precommit for future rounds.
if vote.Type == types.VoteTypeCommit {
for round := vote.Round + 1; round <= hvs.round; round++ {
voteSet := hvs.getVoteSet(round, types.VoteTypePrevote)
_, _, err = voteSet.AddByAddress(address, vote)
if err != nil {
// TODO slash for prevote after commit
log.Warn("Prevote after commit", "address", address, "vote", vote)
}
voteSet = hvs.getVoteSet(round, types.VoteTypePrecommit)
_, _, err = voteSet.AddByAddress(address, vote)
if err != nil {
// TODO slash for prevote after commit
log.Warn("Prevote after commit", "address", address, "vote", vote)
}
}
}
return
}
func (hvs *HeightVoteSet) GetVoteSet(round uint, type_ byte) *VoteSet {
func (hvs *HeightVoteSet) Prevotes(round uint) *VoteSet {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
return hvs.getVoteSet(round, type_)
return hvs.getVoteSet(round, types.VoteTypePrevote)
}
func (hvs *HeightVoteSet) getVoteSet(round uint, type_ byte) *VoteSet {
if type_ == types.VoteTypeCommit {
return hvs.commits
func (hvs *HeightVoteSet) Precommits(round uint) *VoteSet {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
return hvs.getVoteSet(round, types.VoteTypePrecommit)
}
// Last round that has +2/3 prevotes for a particular block or nik.
// Returns -1 if no such round exists.
func (hvs *HeightVoteSet) POLRound() int {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
for r := hvs.round; r >= 0; r-- {
if hvs.getVoteSet(r, types.VoteTypePrevote).HasTwoThirdsMajority() {
return int(r)
}
}
return -1
}
func (hvs *HeightVoteSet) getVoteSet(round uint, type_ byte) *VoteSet {
rvs, ok := hvs.roundVoteSets[round]
if !ok {
return nil
@ -130,8 +119,7 @@ func (hvs *HeightVoteSet) String() string {
}
func (hvs *HeightVoteSet) StringIndented(indent string) string {
vsStrings := make([]string, 0, hvs.round*2+1)
vsStrings = append(vsStrings, hvs.commits.StringShort())
vsStrings := make([]string, 0, hvs.round*2)
for round := uint(0); round <= hvs.round; round++ {
voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort()
vsStrings = append(vsStrings, voteSetString)


+ 0
- 101
consensus/pol.go View File

@ -1,101 +0,0 @@
package consensus
import (
"fmt"
"github.com/tendermint/tendermint/account"
"github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
// 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 `json:"round"`
Signature account.SignatureEd25519 `json:"signature"`
}
// Proof of lock.
// +2/3 of validators' prevotes for a given blockhash (or nil)
type POL struct {
Height uint `json:"height"`
Round uint `json:"round"`
BlockHash []byte `json:"block_hash"` // Could be nil, which makes this a proof of unlock.
BlockParts types.PartSetHeader `json:"block_parts"` // When BlockHash is nil, this is zero.
Votes []POLVoteSignature `json:"votes"` // Prevote and commit signatures in ValidatorSet order.
}
// Returns whether +2/3 have prevoted/committed for BlockHash.
func (pol *POL) Verify(valSet *sm.ValidatorSet) error {
if uint(len(pol.Votes)) != valSet.Size() {
return fmt.Errorf("Invalid POL votes count: Expected %v, got %v",
valSet.Size(), len(pol.Votes))
}
talliedVotingPower := uint64(0)
prevoteDoc := account.SignBytes(config.GetString("chain_id"), &types.Vote{
Height: pol.Height, Round: pol.Round, Type: types.VoteTypePrevote,
BlockHash: pol.BlockHash,
BlockParts: pol.BlockParts,
})
seenValidators := map[string]struct{}{}
for idx, vote := range pol.Votes {
// vote may be zero, in which case skip.
if vote.Signature.IsZero() {
continue
}
voteDoc := prevoteDoc
_, val := valSet.GetByIndex(uint(idx))
// Commit vote?
if vote.Round < pol.Round {
voteDoc = account.SignBytes(config.GetString("chain_id"), &types.Vote{
Height: pol.Height, Round: vote.Round, Type: types.VoteTypeCommit,
BlockHash: pol.BlockHash,
BlockParts: pol.BlockParts,
})
} else if vote.Round > pol.Round {
return fmt.Errorf("Invalid commit round %v for POL %v", vote.Round, pol)
}
// Validate
if _, seen := seenValidators[string(val.Address)]; seen {
return fmt.Errorf("Duplicate validator for vote %v for POL %v", vote, pol)
}
if !val.PubKey.VerifyBytes(voteDoc, vote.Signature) {
return fmt.Errorf("Invalid signature for vote %v for POL %v", vote, pol)
}
// Tally
seenValidators[string(val.Address)] = struct{}{}
talliedVotingPower += val.VotingPower
}
if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
return nil
} else {
return fmt.Errorf("Invalid POL, insufficient voting power %v, needed %v",
talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
}
}
func (pol *POL) StringShort() string {
if pol == nil {
return "nil-POL"
} else {
return fmt.Sprintf("POL{H:%v R:%v BH:%X}", pol.Height, pol.Round,
Fingerprint(pol.BlockHash), pol.BlockParts)
}
}
func (pol *POL) MakePartSet() *types.PartSet {
return types.NewPartSetFromData(binary.BinaryBytes(pol))
}

+ 0
- 213
consensus/pol_test.go View File

@ -1,213 +0,0 @@
package consensus
import (
"github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
_ "github.com/tendermint/tendermint/config/tendermint_test"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
"bytes"
"testing"
)
// NOTE: see consensus/test.go for common test methods.
// Convenience method.
// Signs the vote and sets the POL's vote at the desired index
// Returns the POLVoteSignature pointer, so you can modify it afterwards.
func signAddPOLVoteSignature(val *sm.PrivValidator, valSet *sm.ValidatorSet, vote *types.Vote, pol *POL) *POLVoteSignature {
vote = vote.Copy()
err := val.SignVote(config.GetString("chain_id"), vote)
if err != nil {
panic(err)
}
idx, _ := valSet.GetByAddress(val.Address) // now we have the index
pol.Votes[idx] = POLVoteSignature{vote.Round, vote.Signature}
return &pol.Votes[idx]
}
func TestVerifyVotes(t *testing.T) {
height, round := uint(1), uint(0)
_, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1)
// Make a POL with -2/3 votes.
blockHash := RandBytes(32)
pol := &POL{
Height: height, Round: round, BlockHash: blockHash,
Votes: make([]POLVoteSignature, valSet.Size()),
}
voteProto := &types.Vote{
Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: blockHash,
}
for i := 0; i < 6; i++ {
signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol)
}
// Check that validation fails.
if err := pol.Verify(valSet); err == nil {
t.Errorf("Expected POL.Verify() to fail, not enough votes.")
}
// Insert another vote to make +2/3
signAddPOLVoteSignature(privValidators[7], valSet, voteProto, pol)
// Check that validation succeeds.
if err := pol.Verify(valSet); err != nil {
t.Errorf("POL.Verify() failed: %v", err)
}
}
func TestVerifyInvalidVote(t *testing.T) {
height, round := uint(1), uint(0)
_, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1)
// Make a POL with +2/3 votes with the wrong signature.
blockHash := RandBytes(32)
pol := &POL{
Height: height, Round: round, BlockHash: blockHash,
Votes: make([]POLVoteSignature, valSet.Size()),
}
voteProto := &types.Vote{
Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: blockHash,
}
for i := 0; i < 7; i++ {
polVoteSig := signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol)
polVoteSig.Signature[0] += byte(0x01) // mutated!
}
// Check that validation fails.
if err := pol.Verify(valSet); err == nil {
t.Errorf("Expected POL.Verify() to fail, wrong signatures.")
}
}
func TestVerifyCommits(t *testing.T) {
height, round := uint(1), uint(2)
_, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1)
// Make a POL with +2/3 votes.
blockHash := RandBytes(32)
pol := &POL{
Height: height, Round: round, BlockHash: blockHash,
Votes: make([]POLVoteSignature, valSet.Size()),
}
voteProto := &types.Vote{
Height: height, Round: round - 1, Type: types.VoteTypeCommit, BlockHash: blockHash,
}
for i := 0; i < 7; i++ {
signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol)
}
// Check that validation succeeds.
if err := pol.Verify(valSet); err != nil {
t.Errorf("POL.Verify() failed: %v", err)
}
}
func TestVerifyInvalidCommits(t *testing.T) {
height, round := uint(1), uint(2)
_, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1)
// Make a POL with +2/3 votes with the wrong signature.
blockHash := RandBytes(32)
pol := &POL{
Height: height, Round: round, BlockHash: blockHash,
Votes: make([]POLVoteSignature, valSet.Size()),
}
voteProto := &types.Vote{
Height: height, Round: round - 1, Type: types.VoteTypeCommit, BlockHash: blockHash,
}
for i := 0; i < 7; i++ {
polVoteSig := signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol)
polVoteSig.Signature[0] += byte(0x01)
}
// Check that validation fails.
if err := pol.Verify(valSet); err == nil {
t.Errorf("Expected POL.Verify() to fail, wrong signatures.")
}
}
func TestVerifyInvalidCommitRounds(t *testing.T) {
height, round := uint(1), uint(2)
_, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1)
// Make a POL with +2/3 commits for the current round.
blockHash := RandBytes(32)
pol := &POL{
Height: height, Round: round, BlockHash: blockHash,
Votes: make([]POLVoteSignature, valSet.Size()),
}
voteProto := &types.Vote{
Height: height, Round: round, Type: types.VoteTypeCommit, BlockHash: blockHash,
}
for i := 0; i < 7; i++ {
signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol)
}
// Check that validation fails.
if err := pol.Verify(valSet); err == nil {
t.Errorf("Expected POL.Verify() to fail, same round.")
}
}
func TestVerifyInvalidCommitRounds2(t *testing.T) {
height, round := uint(1), uint(2)
_, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1)
// Make a POL with +2/3 commits for future round.
blockHash := RandBytes(32)
pol := &POL{
Height: height, Round: round, BlockHash: blockHash,
Votes: make([]POLVoteSignature, valSet.Size()),
}
voteProto := &types.Vote{
Height: height, Round: round + 1, Type: types.VoteTypeCommit, BlockHash: blockHash,
}
for i := 0; i < 7; i++ {
polVoteSig := signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol)
polVoteSig.Round += 1 // mutate round
}
// Check that validation fails.
if err := pol.Verify(valSet); err == nil {
t.Errorf("Expected POL.Verify() to fail, future round.")
}
}
func TestReadWrite(t *testing.T) {
height, round := uint(1), uint(2)
_, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1)
// Make a POL with +2/3 votes.
blockHash := RandBytes(32)
pol := &POL{
Height: height, Round: round, BlockHash: blockHash,
Votes: make([]POLVoteSignature, valSet.Size()),
}
voteProto := &types.Vote{
Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: blockHash,
}
for i := 0; i < 7; i++ {
signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol)
}
// Write it to a buffer.
buf, n, err := new(bytes.Buffer), new(int64), new(error)
binary.WriteBinary(pol, buf, n, err)
if *err != nil {
t.Fatalf("Failed to write POL: %v", *err)
}
// Read from buffer.
pol2 := binary.ReadBinary(&POL{}, buf, n, err).(*POL)
if *err != nil {
t.Fatalf("Failed to read POL: %v", *err)
}
// Check that validation succeeds.
if err := pol2.Verify(valSet); err != nil {
t.Errorf("POL.Verify() failed: %v", err)
}
}

+ 545
- 692
consensus/state.go
File diff suppressed because it is too large
View File


+ 1
- 170
consensus/state_test.go View File

@ -8,51 +8,6 @@ import (
"github.com/tendermint/tendermint/types"
)
func TestSetupRound(t *testing.T) {
cs, privValidators := randConsensusState()
val0 := privValidators[0]
// Add a vote, precommit, and commit by val0.
voteTypes := []byte{types.VoteTypePrevote, types.VoteTypePrecommit, types.VoteTypeCommit}
for _, voteType := range voteTypes {
vote := &types.Vote{Height: 1, Round: 0, Type: voteType} // nil vote
err := val0.SignVote(cs.state.ChainID, vote)
if err != nil {
t.Error("Error signing vote: %v", err)
}
cs.AddVote(val0.Address, vote)
}
// Ensure that vote appears in RoundState.
rs0 := cs.GetRoundState()
if vote := rs0.Prevotes.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypePrevote {
t.Errorf("Expected to find prevote but got %v", vote)
}
if vote := rs0.Precommits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypePrecommit {
t.Errorf("Expected to find precommit but got %v", vote)
}
if vote := rs0.Commits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit {
t.Errorf("Expected to find commit but got %v", vote)
}
// Setup round 1 (next round)
cs.SetupNewRound(1, 1)
<-cs.NewStepCh()
// Now the commit should be copied over to prevotes and precommits.
rs1 := cs.GetRoundState()
if vote := rs1.Prevotes.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit {
t.Errorf("Expected to find commit but got %v", vote)
}
if vote := rs1.Precommits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit {
t.Errorf("Expected to find commit but got %v", vote)
}
if vote := rs1.Commits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit {
t.Errorf("Expected to find commit but got %v", vote)
}
}
func TestRunActionProposeNoPrivValidator(t *testing.T) {
cs, _ := randConsensusState()
cs.RunActionPropose(1, 0)
@ -82,128 +37,4 @@ func TestRunActionPropose(t *testing.T) {
}
}
func checkRoundState(t *testing.T, rs *RoundState,
height uint, round uint, step RoundStepType) {
if rs.Height != height {
t.Errorf("rs.Height should be %v, got %v", height, rs.Height)
}
if rs.Round != round {
t.Errorf("rs.Round should be %v, got %v", round, rs.Round)
}
if rs.Step != step {
t.Errorf("rs.Step should be %v, got %v", step, rs.Step)
}
}
func TestRunActionPrecommitCommitFinalize(t *testing.T) {
cs, privValidators := randConsensusState()
val0 := privValidators[0]
cs.SetPrivValidator(val0)
cs.RunActionPrecommit(1, 0)
<-cs.NewStepCh() // TODO: test this value too.
if cs.Precommits.GetByAddress(val0.Address) != nil {
t.Errorf("RunActionPrecommit should return nil without a proposal")
}
cs.RunActionPropose(1, 0)
<-cs.NewStepCh() // TODO: test this value too.
cs.RunActionPrecommit(1, 0)
<-cs.NewStepCh() // TODO: test this value too.
if cs.Precommits.GetByAddress(val0.Address) != nil {
t.Errorf("RunActionPrecommit should return nil, not enough prevotes")
}
// Add at least +2/3 prevotes.
for i := 0; i < 7; i++ {
vote := &types.Vote{
Height: 1,
Round: 0,
Type: types.VoteTypePrevote,
BlockHash: cs.ProposalBlock.Hash(),
BlockParts: cs.ProposalBlockParts.Header(),
}
err := privValidators[i].SignVote(cs.state.ChainID, vote)
if err != nil {
t.Error("Error signing vote: %v", err)
}
cs.AddVote(privValidators[i].Address, vote)
}
// Test RunActionPrecommit success:
cs.RunActionPrecommit(1, 0)
<-cs.NewStepCh() // TODO: test this value too.
if cs.Precommits.GetByAddress(val0.Address) == nil {
t.Errorf("RunActionPrecommit should have succeeded")
}
checkRoundState(t, cs.GetRoundState(), 1, 0, RoundStepPrecommit)
// Add at least +2/3 precommits.
for i := 0; i < 7; i++ {
if bytes.Equal(privValidators[i].Address, val0.Address) {
if cs.Precommits.GetByAddress(val0.Address) == nil {
t.Errorf("Proposer should already have signed a precommit vote")
}
continue
}
vote := &types.Vote{
Height: 1,
Round: 0,
Type: types.VoteTypePrecommit,
BlockHash: cs.ProposalBlock.Hash(),
BlockParts: cs.ProposalBlockParts.Header(),
}
err := privValidators[i].SignVote(cs.state.ChainID, vote)
if err != nil {
t.Error("Error signing vote: %v", err)
}
added, _, err := cs.AddVote(privValidators[i].Address, vote)
if !added || err != nil {
t.Errorf("Error adding precommit: %v", err)
}
}
// Test RunActionCommit success:
cs.RunActionCommit(1)
<-cs.NewStepCh() // TODO: test this value too.
if cs.Commits.GetByAddress(val0.Address) == nil {
t.Errorf("RunActionCommit should have succeeded")
}
checkRoundState(t, cs.GetRoundState(), 1, 0, RoundStepCommit)
// cs.CommitTime should still be zero
if !cs.CommitTime.IsZero() {
t.Errorf("Expected CommitTime to yet be zero")
}
// Add at least +2/3 commits.
for i := 0; i < 7; i++ {
if bytes.Equal(privValidators[i].Address, val0.Address) {
if cs.Commits.GetByAddress(val0.Address) == nil {
t.Errorf("Proposer should already have signed a commit vote")
}
continue
}
vote := &types.Vote{
Height: 1,
Round: uint(i), // Doesn't matter what round
Type: types.VoteTypeCommit,
BlockHash: cs.ProposalBlock.Hash(),
BlockParts: cs.ProposalBlockParts.Header(),
}
err := privValidators[i].SignVote(cs.state.ChainID, vote)
if err != nil {
t.Error("Error signing vote: %v", err)
}
added, _, err := cs.AddVote(privValidators[i].Address, vote)
if !added || err != nil {
t.Errorf("Error adding commit: %v", err)
}
}
// Test TryFinalizeCommit:
cs.TryFinalizeCommit(1)
<-cs.NewStepCh() // TODO: test this value too.
checkRoundState(t, cs.GetRoundState(), 2, 0, RoundStepNewHeight)
}
// TODO write better consensus state tests

+ 5
- 6
consensus/types/proposal.go View File

@ -20,29 +20,28 @@ type Proposal struct {
Height uint `json:"height"`
Round uint `json:"round"`
BlockParts types.PartSetHeader `json:"block_parts"`
POLParts types.PartSetHeader `json:"pol_parts"`
POLRound int `json:"pol_round"` // -1 if null.
Signature account.SignatureEd25519 `json:"signature"`
}
func NewProposal(height uint, round uint, blockParts, polParts types.PartSetHeader) *Proposal {
func NewProposal(height uint, round uint, blockParts types.PartSetHeader, polRound int) *Proposal {
return &Proposal{
Height: height,
Round: round,
BlockParts: blockParts,
POLParts: polParts,
POLRound: polRound,
}
}
func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v %v %v %v}", p.Height, p.Round,
p.BlockParts, p.POLParts, p.Signature)
p.BlockParts, p.POLRound, p.Signature)
}
func (p *Proposal) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
binary.WriteTo([]byte(Fmt(`{"chain_id":"%s"`, chainID)), w, n, err)
binary.WriteTo([]byte(`,"proposal":{"block_parts":`), w, n, err)
p.BlockParts.WriteSignBytes(w, n, err)
binary.WriteTo([]byte(Fmt(`,"height":%v,"pol_parts":`, p.Height)), w, n, err)
p.POLParts.WriteSignBytes(w, n, err)
binary.WriteTo([]byte(Fmt(`,"height":%v,"pol_round":%v`, p.Height, p.POLRound)), w, n, err)
binary.WriteTo([]byte(Fmt(`,"round":%v}}`, p.Round)), w, n, err)
}

+ 18
- 58
consensus/vote_set.go View File

@ -39,9 +39,6 @@ func NewVoteSet(height uint, round uint, type_ byte, valSet *sm.ValidatorSet) *V
if height == 0 {
panic("Cannot make VoteSet for height == 0, doesn't make sense.")
}
if type_ == types.VoteTypeCommit && round != 0 {
panic("Expected round 0 for commit vote set")
}
return &VoteSet{
height: height,
round: round,
@ -111,10 +108,9 @@ func (voteSet *VoteSet) addByIndex(valIndex uint, vote *types.Vote) (bool, uint,
func (voteSet *VoteSet) addVote(val *sm.Validator, valIndex uint, vote *types.Vote) (bool, uint, error) {
// Make sure the step matches. (or that vote is commit && round < voteSet.round)
if vote.Height != voteSet.height ||
(vote.Type != types.VoteTypeCommit && vote.Round != voteSet.round) ||
(vote.Type != types.VoteTypeCommit && vote.Type != voteSet.type_) ||
(vote.Type == types.VoteTypeCommit && voteSet.type_ != types.VoteTypeCommit && vote.Round >= voteSet.round) {
if (vote.Height != voteSet.height) ||
(vote.Round != voteSet.round) ||
(vote.Type != voteSet.type_) {
return false, 0, types.ErrVoteUnexpectedStep
}
@ -155,18 +151,6 @@ func (voteSet *VoteSet) addVote(val *sm.Validator, valIndex uint, vote *types.Vo
return true, valIndex, nil
}
// Assumes that commits VoteSet is valid.
func (voteSet *VoteSet) AddFromCommits(commits *VoteSet) {
for valIndex, commit := range commits.votes {
if commit == nil {
continue
}
if commit.Round < voteSet.round {
voteSet.addByIndex(uint(valIndex), commit)
}
}
}
func (voteSet *VoteSet) BitArray() *BitArray {
if voteSet == nil {
return nil
@ -201,6 +185,15 @@ func (voteSet *VoteSet) HasTwoThirdsMajority() bool {
return voteSet.maj23Exists
}
func (voteSet *VoteSet) HasTwoThirdsAny() bool {
if voteSet == nil {
return false
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.totalBlockHashVotes > voteSet.valSet.TotalVotingPower()*2/3
}
// Returns either a blockhash (or nil) that received +2/3 majority.
// If there exists no such majority, returns (nil, false).
func (voteSet *VoteSet) TwoThirdsMajority() (hash []byte, parts types.PartSetHeader, ok bool) {
@ -213,50 +206,16 @@ func (voteSet *VoteSet) TwoThirdsMajority() (hash []byte, parts types.PartSetHea
}
}
func (voteSet *VoteSet) MakePOL() *POL {
if voteSet.type_ != types.VoteTypePrevote {
panic("Cannot MakePOL() unless VoteSet.Type is types.VoteTypePrevote")
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if !voteSet.maj23Exists {
return nil
}
pol := &POL{
Height: voteSet.height,
Round: voteSet.round,
BlockHash: voteSet.maj23Hash,
BlockParts: voteSet.maj23Parts,
Votes: make([]POLVoteSignature, voteSet.valSet.Size()),
}
for valIndex, vote := range voteSet.votes {
if vote == nil {
continue
}
if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) {
continue
}
if !vote.BlockParts.Equals(voteSet.maj23Parts) {
continue
}
pol.Votes[valIndex] = POLVoteSignature{
Round: vote.Round,
Signature: vote.Signature,
}
}
return pol
}
func (voteSet *VoteSet) MakeValidation() *types.Validation {
if voteSet.type_ != types.VoteTypeCommit {
panic("Cannot MakeValidation() unless VoteSet.Type is types.VoteTypeCommit")
if voteSet.type_ != types.VoteTypePrecommit {
panic("Cannot MakeValidation() unless VoteSet.Type is types.VoteTypePrecommit")
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if len(voteSet.maj23Hash) == 0 {
panic("Cannot MakeValidation() unless a blockhash has +2/3")
}
commits := make([]types.Commit, voteSet.valSet.Size())
precommits := make([]types.Precommit, voteSet.valSet.Size())
voteSet.valSet.Iterate(func(valIndex uint, val *sm.Validator) bool {
vote := voteSet.votes[valIndex]
if vote == nil {
@ -268,11 +227,12 @@ func (voteSet *VoteSet) MakeValidation() *types.Validation {
if !vote.BlockParts.Equals(voteSet.maj23Parts) {
return false
}
commits[valIndex] = types.Commit{val.Address, vote.Round, vote.Signature}
precommits[valIndex] = types.Precommit{val.Address, vote.Signature}
return false
})
return &types.Validation{
Commits: commits,
Round: voteSet.round,
Precommits: precommits,
}
}


+ 5
- 62
consensus/vote_set_test.go View File

@ -225,69 +225,12 @@ func TestBadVotes(t *testing.T) {
}
}
func TestAddCommitsToPrevoteVotes(t *testing.T) {
height, round := uint(2), uint(5)
voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1)
// val0, val1, val2, val3, val4, val5 vote for nil.
vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: nil}
for i := 0; i < 6; i++ {
signAddVote(privValidators[i], vote, voteSet)
}
hash, header, ok := voteSet.TwoThirdsMajority()
if hash != nil || !header.IsZero() || ok {
t.Errorf("There should be no 2/3 majority")
}
// Attempt to add a commit from val6 at a previous height
vote = &types.Vote{Height: height - 1, Round: round, Type: types.VoteTypeCommit, BlockHash: nil}
added, _ := signAddVote(privValidators[6], vote, voteSet)
if added {
t.Errorf("Expected VoteSet.Add to fail, wrong height.")
}
// Attempt to add a commit from val6 at a later round
vote = &types.Vote{Height: height, Round: round + 1, Type: types.VoteTypeCommit, BlockHash: nil}
added, _ = signAddVote(privValidators[6], vote, voteSet)
if added {
t.Errorf("Expected VoteSet.Add to fail, cannot add future round vote.")
}
// Attempt to add a commit from val6 for currrent height/round.
vote = &types.Vote{Height: height, Round: round, Type: types.VoteTypeCommit, BlockHash: nil}
added, err := signAddVote(privValidators[6], vote, voteSet)
if added || err == nil {
t.Errorf("Expected VoteSet.Add to fail, only prior round commits can be added.")
}
// Add commit from val6 at a previous round
vote = &types.Vote{Height: height, Round: round - 1, Type: types.VoteTypeCommit, BlockHash: nil}
added, err = signAddVote(privValidators[6], vote, voteSet)
if !added || err != nil {
t.Errorf("Expected VoteSet.Add to succeed, commit for prior rounds are relevant.")
}
// Also add commit from val7 for previous round.
vote = &types.Vote{Height: height, Round: round - 2, Type: types.VoteTypeCommit, BlockHash: nil}
added, err = signAddVote(privValidators[7], vote, voteSet)
if !added || err != nil {
t.Errorf("Expected VoteSet.Add to succeed. err: %v", err)
}
// We should have 2/3 majority
hash, header, ok = voteSet.TwoThirdsMajority()
if hash != nil || !header.IsZero() || !ok {
t.Errorf("There should be 2/3 majority for nil")
}
}
func TestMakeValidation(t *testing.T) {
height, round := uint(1), uint(0)
voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypeCommit, 10, 1)
voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypePrecommit, 10, 1)
blockHash, blockParts := CRandBytes(32), types.PartSetHeader{123, CRandBytes(32)}
vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypeCommit,
vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypePrecommit,
BlockHash: blockHash, BlockParts: blockParts}
// 6 out of 10 voted for some block.
@ -313,11 +256,11 @@ func TestMakeValidation(t *testing.T) {
validation := voteSet.MakeValidation()
// Validation should have 10 elements
if len(validation.Commits) != 10 {
t.Errorf("Validation Commits should have the same number of commits as validators")
if len(validation.Precommits) != 10 {
t.Errorf("Validation Precommits should have the same number of precommits as validators")
}
// Ensure that Validation commits are ordered.
// Ensure that Validation precommits are ordered.
if err := validation.ValidateBasic(); err != nil {
t.Errorf("Error in Validation.ValidateBasic(): %v", err)
}


+ 20
- 27
state/execution.go View File

@ -40,28 +40,28 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade
// Validate block Validation.
if block.Height == 1 {
if len(block.Validation.Commits) != 0 {
return errors.New("Block at height 1 (first block) should have no Validation commits")
if len(block.Validation.Precommits) != 0 {
return errors.New("Block at height 1 (first block) should have no Validation precommits")
}
} else {
if uint(len(block.Validation.Commits)) != s.LastBondedValidators.Size() {
if uint(len(block.Validation.Precommits)) != s.LastBondedValidators.Size() {
return errors.New(Fmt("Invalid block validation size. Expected %v, got %v",
s.LastBondedValidators.Size(), len(block.Validation.Commits)))
s.LastBondedValidators.Size(), len(block.Validation.Precommits)))
}
var sumVotingPower uint64
s.LastBondedValidators.Iterate(func(index uint, val *Validator) bool {
commit := block.Validation.Commits[index]
if commit.IsZero() {
precommit := block.Validation.Precommits[index]
if precommit.IsZero() {
return false
} else {
vote := &types.Vote{
Height: block.Height - 1,
Round: commit.Round,
Type: types.VoteTypeCommit,
Round: block.Validation.Round,
Type: types.VoteTypePrecommit,
BlockHash: block.LastBlockHash,
BlockParts: block.LastBlockParts,
}
if val.PubKey.VerifyBytes(account.SignBytes(s.ChainID, vote), commit.Signature) {
if val.PubKey.VerifyBytes(account.SignBytes(s.ChainID, vote), precommit.Signature) {
sumVotingPower += val.VotingPower
return false
} else {
@ -80,8 +80,8 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade
}
// Update Validator.LastCommitHeight as necessary.
for i, commit := range block.Validation.Commits {
if commit.IsZero() {
for i, precommit := range block.Validation.Precommits {
if precommit.IsZero() {
continue
}
_, val := s.LastBondedValidators.GetByIndex(uint(i))
@ -111,7 +111,7 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade
// Create BlockCache to cache changes to state.
blockCache := NewBlockCache(s)
// Commit each tx
// Execute each tx
for _, tx := range block.Data.Txs {
err := ExecTx(blockCache, tx, true, s.evc)
if err != nil {
@ -726,21 +726,14 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
if tx.VoteA.Height != tx.VoteB.Height {
return errors.New("DupeoutTx heights don't match")
}
if tx.VoteA.Type == types.VoteTypeCommit && tx.VoteA.Round < tx.VoteB.Round {
// Check special case (not an error, validator must be slashed!)
// Validators should not sign another vote after committing.
} else if tx.VoteB.Type == types.VoteTypeCommit && tx.VoteB.Round < tx.VoteA.Round {
// We need to check both orderings of the votes
} else {
if tx.VoteA.Round != tx.VoteB.Round {
return errors.New("DupeoutTx rounds don't match")
}
if tx.VoteA.Type != tx.VoteB.Type {
return errors.New("DupeoutTx types don't match")
}
if bytes.Equal(tx.VoteA.BlockHash, tx.VoteB.BlockHash) {
return errors.New("DupeoutTx blockhashes shouldn't match")
}
if tx.VoteA.Round != tx.VoteB.Round {
return errors.New("DupeoutTx rounds don't match")
}
if tx.VoteA.Type != tx.VoteB.Type {
return errors.New("DupeoutTx types don't match")
}
if bytes.Equal(tx.VoteA.BlockHash, tx.VoteB.BlockHash) {
return errors.New("DupeoutTx blockhashes shouldn't match")
}
// Good! (Bad validator!)


+ 0
- 10
state/priv_validator.go View File

@ -1,7 +1,5 @@
package state
// TODO: This logic is crude. Should be more transactional.
import (
"errors"
"fmt"
@ -23,7 +21,6 @@ const (
stepPropose = 1
stepPrevote = 2
stepPrecommit = 3
stepCommit = 4
)
func voteToStep(vote *types.Vote) uint8 {
@ -32,8 +29,6 @@ func voteToStep(vote *types.Vote) uint8 {
return stepPrevote
case types.VoteTypePrecommit:
return stepPrecommit
case types.VoteTypeCommit:
return stepCommit
default:
panic("Unknown vote type")
}
@ -108,7 +103,6 @@ func (privVal *PrivValidator) save() {
}
}
// TODO: test
func (privVal *PrivValidator) SignVote(chainID string, vote *types.Vote) error {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
@ -119,10 +113,6 @@ func (privVal *PrivValidator) SignVote(chainID string, vote *types.Vote) error {
}
// More cases for when the height matches
if privVal.LastHeight == vote.Height {
// If attempting any sign after commit, panic
if privVal.LastStep == stepCommit {
return errors.New("SignVote on matching height after a commit")
}
// If round regression, panic
if privVal.LastRound > vote.Round {
return errors.New("Round regression in SignVote")


+ 1
- 1
state/state.go View File

@ -130,7 +130,7 @@ func (s *State) Hash() []byte {
}
// Mutates the block in place and updates it with new state hash.
func (s *State) SetBlockStateHash(block *types.Block) error {
func (s *State) ComputeBlockStateHash(block *types.Block) error {
sCopy := s.Copy()
// sCopy has no event cache in it, so this won't fire events
err := execBlock(sCopy, block, types.PartSetHeader{})


+ 13
- 13
state/state_test.go View File

@ -71,7 +71,7 @@ func TestCopyState(t *testing.T) {
}
}
func makeBlock(t *testing.T, state *State, commits []types.Commit, txs []types.Tx) *types.Block {
func makeBlock(t *testing.T, state *State, validation *types.Validation, txs []types.Tx) *types.Block {
block := &types.Block{
Header: &types.Header{
ChainID: state.ChainID,
@ -83,16 +83,14 @@ func makeBlock(t *testing.T, state *State, commits []types.Commit, txs []types.T
LastBlockParts: state.LastBlockParts,
StateHash: nil,
},
Validation: &types.Validation{
Commits: commits,
},
Validation: validation,
Data: &types.Data{
Txs: txs,
},
}
// Fill in block StateHash
err := state.SetBlockStateHash(block)
err := state.ComputeBlockStateHash(block)
if err != nil {
t.Error("Error appending initial block:", err)
}
@ -620,21 +618,23 @@ func TestAddValidator(t *testing.T) {
// The validation for the next block should only require 1 signature
// (the new validator wasn't active for block0)
commit0 := &types.Vote{
precommit0 := &types.Vote{
Height: 1,
Round: 0,
Type: types.VoteTypeCommit,
Type: types.VoteTypePrecommit,
BlockHash: block0.Hash(),
BlockParts: block0Parts.Header(),
}
privValidators[0].SignVote(s0.ChainID, commit0)
privValidators[0].SignVote(s0.ChainID, precommit0)
block1 := makeBlock(t, s0,
[]types.Commit{
types.Commit{
Address: privValidators[0].Address,
Round: 0,
Signature: commit0.Signature,
types.Validation{
Round: 0,
Precommits: []types.Precommit{
types.Precommit{
Address: privValidators[0].Address,
Signature: precommit0.Signature,
},
},
}, nil,
)


+ 9
- 9
state/validator_set.go View File

@ -202,33 +202,33 @@ func (valSet *ValidatorSet) Iterate(fn func(index uint, val *Validator) bool) {
// Verify that +2/3 of the set had signed the given signBytes
func (valSet *ValidatorSet) VerifyValidation(chainID string, hash []byte, parts types.PartSetHeader, height uint, v *types.Validation) error {
if valSet.Size() != uint(len(v.Commits)) {
if valSet.Size() != uint(len(v.Precommits)) {
return errors.New(Fmt("Invalid validation -- wrong set size: %v vs %v",
valSet.Size(), len(v.Commits)))
valSet.Size(), len(v.Precommits)))
}
talliedVotingPower := uint64(0)
seenValidators := map[string]struct{}{}
for idx, commit := range v.Commits {
for idx, precommit := range v.Precommits {
// may be zero, in which case skip.
if commit.Signature.IsZero() {
if precommit.Signature.IsZero() {
continue
}
_, val := valSet.GetByIndex(uint(idx))
commitSignBytes := account.SignBytes(chainID, &types.Vote{
Height: height, Round: commit.Round, Type: types.VoteTypeCommit,
precommitSignBytes := account.SignBytes(chainID, &types.Vote{
Height: height, Round: v.Round, Type: types.VoteTypePrecommit,
BlockHash: hash,
BlockParts: parts,
})
// Validate
if _, seen := seenValidators[string(val.Address)]; seen {
return fmt.Errorf("Duplicate validator for commit %v for Validation %v", commit, v)
return fmt.Errorf("Duplicate validator for precommit %v for Validation %v", precommit, v)
}
if !val.PubKey.VerifyBytes(commitSignBytes, commit.Signature) {
return fmt.Errorf("Invalid signature for commit %v for Validation %v", commit, v)
if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
return fmt.Errorf("Invalid signature for precommit %v for Validation %v", precommit, v)
}
// Tally


+ 35
- 32
types/block.go View File

@ -176,27 +176,27 @@ func (h *Header) StringIndented(indent string) string {
//-----------------------------------------------------------------------------
type Commit struct {
type Precommit struct {
Address []byte `json:"address"`
Round uint `json:"round"`
Signature account.SignatureEd25519 `json:"signature"`
}
func (commit Commit) IsZero() bool {
return commit.Round == 0 && commit.Signature.IsZero()
func (pc Precommit) IsZero() bool {
return pc.Signature.IsZero()
}
func (commit Commit) String() string {
return fmt.Sprintf("Commit{A:%X R:%v %X}", commit.Address, commit.Round, Fingerprint(commit.Signature))
func (pc Precommit) String() string {
return fmt.Sprintf("Precommit{A:%X %X}", pc.Address, Fingerprint(pc.Signature))
}
//-------------------------------------
// NOTE: The Commits are in order of address to preserve the bonded ValidatorSet order.
// Any peer with a block can gossip commits by index with a peer without recalculating the
// NOTE: The Precommits are in order of address to preserve the bonded ValidatorSet order.
// Any peer with a block can gossip precommits by index with a peer without recalculating the
// active ValidatorSet.
type Validation struct {
Commits []Commit `json:"commits"` // Commits (or nil) of all active validators in address order.
Round uint `json:"round"` // Round for all precommits
Precommits []Precommit `json:"precommits"` // Precommits (or nil) of all active validators in address order.
// Volatile
hash []byte
@ -204,24 +204,24 @@ type Validation struct {
}
func (v *Validation) ValidateBasic() error {
if len(v.Commits) == 0 {
return errors.New("No commits in validation")
if len(v.Precommits) == 0 {
return errors.New("No precommits in validation")
}
lastAddress := []byte{}
for i := 0; i < len(v.Commits); i++ {
commit := v.Commits[i]
if commit.IsZero() {
if len(commit.Address) > 0 {
return errors.New("Zero commits should not have an address")
for i := 0; i < len(v.Precommits); i++ {
precommit := v.Precommits[i]
if precommit.IsZero() {
if len(precommit.Address) > 0 {
return errors.New("Zero precommits should not have an address")
}
} else {
if len(commit.Address) == 0 {
return errors.New("Nonzero commits should have an address")
if len(precommit.Address) == 0 {
return errors.New("Nonzero precommits should have an address")
}
if len(lastAddress) > 0 && bytes.Compare(lastAddress, commit.Address) != -1 {
return errors.New("Invalid commit order")
if len(lastAddress) > 0 && bytes.Compare(lastAddress, precommit.Address) != -1 {
return errors.New("Invalid precommit order")
}
lastAddress = commit.Address
lastAddress = precommit.Address
}
}
return nil
@ -229,9 +229,10 @@ func (v *Validation) ValidateBasic() error {
func (v *Validation) Hash() []byte {
if v.hash == nil {
bs := make([]interface{}, len(v.Commits))
for i, commit := range v.Commits {
bs[i] = commit
bs := make([]interface{}, 1+len(v.Precommits))
bs[0] = v.Round
for i, precommit := range v.Precommits {
bs[1+i] = precommit
}
v.hash = merkle.SimpleHashFromBinaries(bs)
}
@ -242,22 +243,24 @@ func (v *Validation) StringIndented(indent string) string {
if v == nil {
return "nil-Validation"
}
commitStrings := make([]string, len(v.Commits))
for i, commit := range v.Commits {
commitStrings[i] = commit.String()
precommitStrings := make([]string, len(v.Precommits))
for i, precommit := range v.Precommits {
precommitStrings[i] = precommit.String()
}
return fmt.Sprintf(`Validation{
%s %v
%s Round: %v
%s Precommits: %v
%s}#%X`,
indent, strings.Join(commitStrings, "\n"+indent+" "),
indent, v.Round,
indent, strings.Join(precommitStrings, "\n"+indent+" "),
indent, v.hash)
}
func (v *Validation) BitArray() *BitArray {
if v.bitArray == nil {
v.bitArray = NewBitArray(uint(len(v.Commits)))
for i, commit := range v.Commits {
v.bitArray.SetIndex(uint(i), !commit.IsZero())
v.bitArray = NewBitArray(uint(len(v.Precommits)))
for i, precommit := range v.Precommits {
v.bitArray.SetIndex(uint(i), !precommit.IsZero())
}
}
return v.bitArray


+ 0
- 5
types/vote.go View File

@ -27,8 +27,6 @@ func (err *ErrVoteConflictingSignature) Error() string {
}
// Represents a prevote, precommit, or commit vote from validators for consensus.
// Commit votes get aggregated into the next block's Validaiton.
// See the whitepaper for details.
type Vote struct {
Height uint `json:"height"`
Round uint `json:"round"`
@ -42,7 +40,6 @@ type Vote struct {
const (
VoteTypePrevote = byte(0x01)
VoteTypePrecommit = byte(0x02)
VoteTypeCommit = byte(0x03)
)
func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) {
@ -63,8 +60,6 @@ func (vote *Vote) String() string {
typeString = "Prevote"
case VoteTypePrecommit:
typeString = "Precommit"
case VoteTypeCommit:
typeString = "Commit"
default:
panic("Unknown vote type")
}


Loading…
Cancel
Save