WIP: exploring changes related to proposed ADR:

https://github.com/tendermint/tendermint/pull/2445

- still contains a lot of failing tests etc
This commit is contained in:
Ismail Khoffi
2018-09-21 10:49:43 +02:00
parent 6ddc0199ef
commit d54c09dd67
31 changed files with 256 additions and 234 deletions

View File

@@ -332,18 +332,18 @@ type Commit struct {
// 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.
BlockID BlockID `json:"block_id"`
Precommits []*Vote `json:"precommits"`
BlockID BlockID `json:"block_id"`
Precommits []*SignedVote `json:"precommits"`
// Volatile
firstPrecommit *Vote
firstPrecommit *SignedVote
hash cmn.HexBytes
bitArray *cmn.BitArray
}
// FirstPrecommit returns the first non-nil precommit in the commit.
// If all precommits are nil, it returns an empty precommit with height 0.
func (commit *Commit) FirstPrecommit() *Vote {
func (commit *Commit) FirstPrecommit() *SignedVote {
if len(commit.Precommits) == 0 {
return nil
}
@@ -356,8 +356,12 @@ func (commit *Commit) FirstPrecommit() *Vote {
return precommit
}
}
return &Vote{
Type: VoteTypePrecommit,
return &SignedVote{
// TODO(ismail): That is confusing; does the caller expect a signed pre-commit or not?
// looks like this is always unsigned (check how this is reflected in the spec)
Vote: &UnsignedVote{
Type: VoteTypePrecommit,
},
}
}
@@ -366,7 +370,7 @@ func (commit *Commit) Height() int64 {
if len(commit.Precommits) == 0 {
return 0
}
return commit.FirstPrecommit().Height
return commit.FirstPrecommit().Vote.Height
}
// Round returns the round of the commit
@@ -374,7 +378,7 @@ func (commit *Commit) Round() int {
if len(commit.Precommits) == 0 {
return 0
}
return commit.FirstPrecommit().Round
return commit.FirstPrecommit().Vote.Round
}
// Type returns the vote type of the commit, which is always VoteTypePrecommit
@@ -404,7 +408,7 @@ func (commit *Commit) BitArray() *cmn.BitArray {
}
// GetByIndex returns the vote corresponding to a given validator index
func (commit *Commit) GetByIndex(index int) *Vote {
func (commit *Commit) GetByIndex(index int) *SignedVote {
return commit.Precommits[index]
}
@@ -427,23 +431,23 @@ func (commit *Commit) ValidateBasic() error {
// Validate the precommits.
for _, precommit := range commit.Precommits {
// It's OK for precommits to be missing.
if precommit == nil {
if precommit == nil || precommit.Vote == nil {
continue
}
// Ensure that all votes are precommits.
if precommit.Type != VoteTypePrecommit {
if precommit.Vote.Type != VoteTypePrecommit {
return fmt.Errorf("Invalid commit vote. Expected precommit, got %v",
precommit.Type)
precommit.Vote.Type)
}
// Ensure that all heights are the same.
if precommit.Height != height {
if precommit.Vote.Height != height {
return fmt.Errorf("Invalid commit precommit height. Expected %v, got %v",
height, precommit.Height)
height, precommit.Vote.Height)
}
// Ensure that all rounds are the same.
if precommit.Round != round {
if precommit.Vote.Round != round {
return fmt.Errorf("Invalid commit precommit round. Expected %v, got %v",
round, precommit.Round)
round, precommit.Vote.Round)
}
}
return nil
@@ -471,7 +475,7 @@ func (commit *Commit) StringIndented(indent string) string {
}
precommitStrings := make([]string, len(commit.Precommits))
for i, precommit := range commit.Precommits {
precommitStrings[i] = precommit.String()
precommitStrings[i] = precommit.Vote.String()
}
return fmt.Sprintf(`Commit{
%s BlockID: %v

View File

@@ -221,17 +221,17 @@ func TestCommitValidateBasic(t *testing.T) {
// tamper with types
commit = randCommit()
commit.Precommits[0].Type = VoteTypePrevote
commit.Precommits[0].Vote.Type = VoteTypePrevote
assert.Error(t, commit.ValidateBasic())
// tamper with height
commit = randCommit()
commit.Precommits[0].Height = int64(100)
commit.Precommits[0].Vote.Height = int64(100)
assert.Error(t, commit.ValidateBasic())
// tamper with round
commit = randCommit()
commit.Precommits[0].Round = 100
commit.Precommits[0].Vote.Round = 100
assert.Error(t, commit.ValidateBasic())
}

View File

@@ -24,7 +24,7 @@ const (
EventTx = "Tx"
EventUnlock = "Unlock"
EventValidatorSetUpdates = "ValidatorSetUpdates"
EventVote = "Vote"
EventVote = "UnsignedVote"
)
///////////////////////////////////////////////////////////////////////////////
@@ -42,7 +42,7 @@ func RegisterEventDatas(cdc *amino.Codec) {
cdc.RegisterConcrete(EventDataNewBlockHeader{}, "tendermint/event/NewBlockHeader", nil)
cdc.RegisterConcrete(EventDataTx{}, "tendermint/event/Tx", nil)
cdc.RegisterConcrete(EventDataRoundState{}, "tendermint/event/RoundState", nil)
cdc.RegisterConcrete(EventDataVote{}, "tendermint/event/Vote", nil)
cdc.RegisterConcrete(EventDataVote{}, "tendermint/event/UnsignedVote", nil)
cdc.RegisterConcrete(EventDataProposalHeartbeat{}, "tendermint/event/ProposalHeartbeat", nil)
cdc.RegisterConcrete(EventDataValidatorSetUpdates{}, "tendermint/event/ValidatorSetUpdates", nil)
cdc.RegisterConcrete(EventDataString(""), "tendermint/event/ProposalString", nil)
@@ -80,7 +80,7 @@ type EventDataRoundState struct {
}
type EventDataVote struct {
Vote *Vote
Vote *UnsignedVote
}
type EventDataString string

View File

@@ -34,11 +34,11 @@ func (err *ErrEvidenceInvalid) Error() string {
// Evidence represents any provable malicious activity by a validator
type Evidence interface {
Height() int64 // height of the equivocation
Address() []byte // address of the equivocating validator
Hash() []byte // hash of the evidence
Verify(chainID string, pubKey crypto.PubKey) error // verify the evidence
Equal(Evidence) bool // check equality of evidence
Height() int64 // height of the equivocation
Address() []byte // address of the equivocating validator
Hash() []byte // hash of the evidence
Verify(pubKey crypto.PubKey) error // verify the evidence
Equal(Evidence) bool // check equality of evidence
String() string
}
@@ -63,8 +63,8 @@ func MaxEvidenceBytesPerBlock(blockMaxBytes int) int {
type DuplicateVoteEvidence struct {
PubKey crypto.PubKey
// TODO(ismail): this probably need to be `SignedVoteReply`s
VoteA *SignVoteReply
VoteB *SignVoteReply
VoteA *SignedVote
VoteB *SignedVote
}
// String returns a string representation of the evidence.
@@ -90,7 +90,7 @@ func (dve *DuplicateVoteEvidence) Hash() []byte {
// Verify returns an error if the two votes aren't conflicting.
// To be conflicting, they must be from the same validator, for the same H/R/S, but for different blocks.
func (dve *DuplicateVoteEvidence) Verify(chainID string, pubKey crypto.PubKey) error {
func (dve *DuplicateVoteEvidence) Verify(pubKey crypto.PubKey) error {
// H/R/S must be the same
if dve.VoteA.Vote.Height != dve.VoteB.Vote.Height ||
dve.VoteA.Vote.Round != dve.VoteB.Vote.Round ||
@@ -161,7 +161,7 @@ func (e MockGoodEvidence) Address() []byte { return e.Address_ }
func (e MockGoodEvidence) Hash() []byte {
return []byte(fmt.Sprintf("%d-%x", e.Height_, e.Address_))
}
func (e MockGoodEvidence) Verify(chainID string, pubKey crypto.PubKey) error { return nil }
func (e MockGoodEvidence) Verify(pubKey crypto.PubKey) error { return nil }
func (e MockGoodEvidence) Equal(ev Evidence) bool {
e2 := ev.(MockGoodEvidence)
return e.Height_ == e2.Height_ &&
@@ -176,7 +176,7 @@ type MockBadEvidence struct {
MockGoodEvidence
}
func (e MockBadEvidence) Verify(chainID string, pubKey crypto.PubKey) error {
func (e MockBadEvidence) Verify(pubKey crypto.PubKey) error {
return fmt.Errorf("MockBadEvidence")
}
func (e MockBadEvidence) Equal(ev Evidence) bool {

View File

@@ -11,25 +11,26 @@ import (
)
type voteData struct {
vote1 *Vote
vote2 *Vote
vote1 *SignedVote
vote2 *SignedVote
valid bool
}
func makeVote(val PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID BlockID) *Vote {
v := &Vote{
func makeVote(val PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID BlockID) *SignedVote {
v := &UnsignedVote{
ValidatorAddress: val.GetAddress(),
ValidatorIndex: valIndex,
Height: height,
Round: round,
Type: byte(step),
BlockID: blockID,
ChainID: chainID,
}
err := val.SignVote(chainID, v)
sv, err := val.SignVote(v)
if err != nil {
panic(err)
}
return v
return sv
}
func TestEvidence(t *testing.T) {
@@ -45,7 +46,7 @@ func TestEvidence(t *testing.T) {
vote1 := makeVote(val, chainID, 0, 10, 2, 1, blockID)
badVote := makeVote(val, chainID, 0, 10, 2, 1, blockID)
err := val2.SignVote(chainID, badVote)
signedBadVote, err := val2.SignVote(badVote.Vote)
if err != nil {
panic(err)
}
@@ -61,19 +62,21 @@ func TestEvidence(t *testing.T) {
{vote1, makeVote(val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round
{vote1, makeVote(val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step
{vote1, makeVote(val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator
{vote1, badVote, false}, // signed by wrong key
{vote1, signedBadVote, false}, // signed by wrong key
}
pubKey := val.GetPubKey()
for _, c := range cases {
for idx, c := range cases {
//fmt.Println(idx)
ev := &DuplicateVoteEvidence{
VoteA: c.vote1,
VoteB: c.vote2,
}
if c.valid {
assert.Nil(t, ev.Verify(chainID, pubKey), "evidence should be valid")
assert.NoError(t, ev.Verify(pubKey), "evidence should be valid")
} else {
assert.NotNil(t, ev.Verify(chainID, pubKey), "evidence should be invalid")
t.Logf("idx=%v, err=%s", idx, ev.Verify(pubKey))
assert.Error(t, ev.Verify(pubKey), "evidence should be invalid")
}
}
}

View File

@@ -14,7 +14,7 @@ type PrivValidator interface {
GetAddress() Address // redundant since .PubKey().Address()
GetPubKey() crypto.PubKey
SignVote(vote *Vote) (SignVoteReply, error)
SignVote(vote *UnsignedVote) (*SignedVote, error)
SignProposal(chainID string, proposal *Proposal) error
SignHeartbeat(chainID string, heartbeat *Heartbeat) error
}
@@ -62,16 +62,15 @@ func (pv *MockPV) GetPubKey() crypto.PubKey {
}
// Implements PrivValidator.
func (pv *MockPV) SignVote(vote *Vote) (SignVoteReply, error) {
func (pv *MockPV) SignVote(vote *UnsignedVote) (*SignedVote, error) {
signBytes := vote.SignBytes()
sig, err := pv.privKey.Sign(signBytes)
if err != nil {
// TODO(ismail): encapsulate error into reply!
return SignVoteReply{}, err
return nil, err
}
return SignVoteReply{
Vote: *vote,
return &SignedVote{
Vote: vote,
Signature: sig,
}, nil
}

View File

@@ -11,7 +11,7 @@ func MakeCommit(blockID BlockID, height int64, round int,
// all sign
for i := 0; i < len(validators); i++ {
vote := &Vote{
vote := &UnsignedVote{
ValidatorAddress: validators[i].GetAddress(),
ValidatorIndex: i,
Height: height,
@@ -30,11 +30,11 @@ func MakeCommit(blockID BlockID, height int64, round int,
return voteSet.MakeCommit(), nil
}
func signAddVote(privVal PrivValidator, vote *Vote, voteSet *VoteSet) (signed bool, err error) {
func signAddVote(privVal PrivValidator, vote *UnsignedVote, voteSet *VoteSet) (signed bool, err error) {
vote.ChainID = voteSet.ChainID()
repl, err := privVal.SignVote(vote)
if err != nil {
return false, err
}
return voteSet.AddVote(vote)
return voteSet.AddVote(repl)
}

View File

@@ -273,26 +273,31 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height i
round := commit.Round()
for idx, precommit := range commit.Precommits {
if precommit == nil {
if precommit == nil || precommit.Vote == nil {
continue // OK, some precommits can be missing.
}
if precommit.Height != height {
return fmt.Errorf("Invalid commit -- wrong height: want %v got %v", height, precommit.Height)
if precommit.Vote.Height != height {
return fmt.Errorf("invalid commit -- wrong height: want %v got %v", height, precommit.Vote.Height)
}
if precommit.Round != round {
return fmt.Errorf("Invalid commit -- wrong round: want %v got %v", round, precommit.Round)
if precommit.Vote.Round != round {
return fmt.Errorf("invalid commit -- wrong round: want %v got %v", round, precommit.Vote.Round)
}
if precommit.Type != VoteTypePrecommit {
return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx)
if precommit.Vote.Type != VoteTypePrecommit {
return fmt.Errorf("invalid commit -- not precommit @ index %v", idx)
}
_, val := vals.GetByIndex(idx)
// Validate signature.
precommitSignBytes := precommit.SignBytes()
precommitSignBytes := precommit.Vote.SignBytes()
if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
return fmt.Errorf("Invalid commit -- invalid signature: %v", precommit)
return fmt.Errorf("invalid commit -- invalid signature: %v", precommit)
}
if precommit.Vote.ChainID != chainID {
return fmt.Errorf("invalid commit -- chainId does not match: expected %v, got %v", chainID, precommit.Vote.ChainID)
}
// Good precommit!
if blockID.Equals(precommit.BlockID) {
if blockID.Equals(precommit.Vote.BlockID) {
talliedVotingPower += val.VotingPower
} else {
// It's OK that the BlockID doesn't match. We include stray
@@ -303,7 +308,7 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height i
if talliedVotingPower > vals.TotalVotingPower()*2/3 {
return nil
}
return fmt.Errorf("Invalid commit -- insufficient voting power: got %v, needed %v",
return fmt.Errorf("invalid commit -- insufficient voting power: got %v, needed %v",
talliedVotingPower, (vals.TotalVotingPower()*2/3 + 1))
}
@@ -352,32 +357,32 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin
round := commit.Round()
for idx, precommit := range commit.Precommits {
if precommit == nil {
if precommit == nil || precommit.Vote == nil {
continue
}
if precommit.Height != height {
return cmn.NewError("Blocks don't match - %d vs %d", round, precommit.Round)
if precommit.Vote.Height != height {
return cmn.NewError("Blocks don't match - %d vs %d", round, precommit.Vote.Round)
}
if precommit.Round != round {
return cmn.NewError("Invalid commit -- wrong round: %v vs %v", round, precommit.Round)
if precommit.Vote.Round != round {
return cmn.NewError("Invalid commit -- wrong round: %v vs %v", round, precommit.Vote.Round)
}
if precommit.Type != VoteTypePrecommit {
if precommit.Vote.Type != VoteTypePrecommit {
return cmn.NewError("Invalid commit -- not precommit @ index %v", idx)
}
// See if this validator is in oldVals.
idx, val := oldVals.GetByAddress(precommit.ValidatorAddress)
idx, val := oldVals.GetByAddress(precommit.Vote.ValidatorAddress)
if val == nil || seen[idx] {
continue // missing or double vote...
}
seen[idx] = true
// Validate signature.
precommitSignBytes := precommit.SignBytes(chainID)
precommitSignBytes := precommit.Vote.SignBytes()
if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
return cmn.NewError("Invalid commit -- invalid signature: %v", precommit)
}
// Good precommit!
if blockID.Equals(precommit.BlockID) {
if blockID.Equals(precommit.Vote.BlockID) {
oldVotingPower += val.VotingPower
} else {
// It's OK that the BlockID doesn't match. We include stray

View File

@@ -379,7 +379,7 @@ func TestValidatorSetVerifyCommit(t *testing.T) {
chainID := "mychainID"
blockID := BlockID{Hash: []byte("hello")}
height := int64(5)
vote := &Vote{
vote := &UnsignedVote{
ValidatorAddress: v1.Address,
ValidatorIndex: 0,
Height: height,
@@ -387,13 +387,14 @@ func TestValidatorSetVerifyCommit(t *testing.T) {
Timestamp: tmtime.Now(),
Type: VoteTypePrecommit,
BlockID: blockID,
ChainID: chainID,
}
sig, err := privKey.Sign(vote.SignBytes(chainID))
sig, err := privKey.Sign(vote.SignBytes())
assert.NoError(t, err)
vote.Signature = sig
commit := &Commit{
BlockID: blockID,
Precommits: []*Vote{vote},
Precommits: []*SignedVote{&SignedVote{Vote: vote, Signature: sig}},
}
badChainID := "notmychainID"
@@ -401,7 +402,7 @@ func TestValidatorSetVerifyCommit(t *testing.T) {
badHeight := height + 1
badCommit := &Commit{
BlockID: blockID,
Precommits: []*Vote{nil},
Precommits: []*SignedVote{nil},
}
// test some error cases
@@ -420,7 +421,7 @@ func TestValidatorSetVerifyCommit(t *testing.T) {
for i, c := range cases {
err := vset.VerifyCommit(c.chainID, c.blockID, c.height, c.commit)
assert.NotNil(t, err, i)
assert.NotNil(t, err, "test-case: %d", i)
}
// test a good one

View File

@@ -33,7 +33,7 @@ func (err *ErrVoteConflictingVotes) Error() string {
return fmt.Sprintf("Conflicting votes from validator %v", err.PubKey.Address())
}
func NewConflictingVoteError(val *Validator, voteA, voteB *Vote) *ErrVoteConflictingVotes {
func NewConflictingVoteError(val *Validator, voteA, voteB *SignedVote) *ErrVoteConflictingVotes {
return &ErrVoteConflictingVotes{
&DuplicateVoteEvidence{
PubKey: val.PubKey,
@@ -65,7 +65,7 @@ func IsVoteTypeValid(type_ byte) bool {
type Address = cmn.HexBytes
// Represents a prevote, precommit, or commit vote from validators for consensus.
type Vote struct {
type UnsignedVote struct {
ValidatorAddress Address `json:"validator_address"`
ValidatorIndex int `json:"validator_index"`
Height int64 `json:"height"`
@@ -76,14 +76,9 @@ type Vote struct {
ChainID string `json:"chain_id"`
}
type SignVoteRequest struct {
Vote Vote
}
type SignVoteReply struct {
Vote Vote
type SignedVote struct {
Vote *UnsignedVote
Signature []byte
Err Error
}
type Error struct {
@@ -91,7 +86,7 @@ type Error struct {
Description string
}
func (vote *Vote) SignBytes() []byte {
func (vote *UnsignedVote) SignBytes() []byte {
bz, err := cdc.MarshalBinary(vote)
if err != nil {
panic(err)
@@ -99,14 +94,14 @@ func (vote *Vote) SignBytes() []byte {
return bz
}
func (vote *Vote) Copy() *Vote {
func (vote *UnsignedVote) Copy() *UnsignedVote {
voteCopy := *vote
return &voteCopy
}
func (vote *Vote) String() string {
func (vote *UnsignedVote) String() string {
if vote == nil {
return "nil-Vote"
return "nil-UnsignedVote"
}
var typeString string
switch vote.Type {
@@ -118,20 +113,24 @@ func (vote *Vote) String() string {
cmn.PanicSanity("Unknown vote type")
}
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}",
return fmt.Sprintf("UnsignedVote{%v:%X %v/%02d/%v(%v) %X @ %s}",
vote.ValidatorIndex, cmn.Fingerprint(vote.ValidatorAddress),
vote.Height, vote.Round, vote.Type, typeString,
cmn.Fingerprint(vote.BlockID.Hash),
cmn.Fingerprint(vote.Signature),
// TODO(ismail): add corresponding
//cmn.Fingerprint(vote.Signature),
CanonicalTime(vote.Timestamp))
}
func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) {
func (vote *SignedVote) Verify(pubKey crypto.PubKey) error {
if vote == nil || vote.Vote == nil {
return errors.New("called verify on nil/empty SignedVote")
}
if !bytes.Equal(pubKey.Address(), vote.Vote.ValidatorAddress) {
return ErrVoteInvalidValidatorAddress
}
if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) {
if !pubKey.VerifyBytes(vote.Vote.SignBytes(), vote.Signature) {
return ErrVoteInvalidSignature
}
return nil

View File

@@ -60,7 +60,7 @@ type VoteSet struct {
mtx sync.Mutex
votesBitArray *cmn.BitArray
votes []*Vote // Primary votes to share
votes []*SignedVote // Primary votes to share
sum int64 // Sum of voting power for seen votes, discounting conflicts
maj23 *BlockID // First 2/3 majority seen
votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes
@@ -79,7 +79,7 @@ func NewVoteSet(chainID string, height int64, round int, type_ byte, valSet *Val
type_: type_,
valSet: valSet,
votesBitArray: cmn.NewBitArray(valSet.Size()),
votes: make([]*Vote, valSet.Size()),
votes: make([]*SignedVote, valSet.Size()),
sum: 0,
maj23: nil,
votesByBlock: make(map[string]*blockVotes, valSet.Size()),
@@ -127,8 +127,8 @@ func (voteSet *VoteSet) Size() int {
// Conflicting votes return added=*, err=ErrVoteConflictingVotes.
// NOTE: vote should not be mutated after adding.
// NOTE: VoteSet must not be nil
// NOTE: Vote must not be nil
func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) {
// NOTE: UnsignedVote must not be nil
func (voteSet *VoteSet) AddVote(vote *SignedVote) (added bool, err error) {
if voteSet == nil {
cmn.PanicSanity("AddVote() on nil VoteSet")
}
@@ -139,13 +139,13 @@ func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) {
}
// NOTE: Validates as much as possible before attempting to verify the signature.
func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
if vote == nil {
func (voteSet *VoteSet) addVote(vote *SignedVote) (added bool, err error) {
if vote == nil || vote.Vote == nil {
return false, ErrVoteNil
}
valIndex := vote.ValidatorIndex
valAddr := vote.ValidatorAddress
blockKey := vote.BlockID.Key()
valIndex := vote.Vote.ValidatorIndex
valAddr := vote.Vote.ValidatorAddress
blockKey := vote.Vote.BlockID.Key()
// Ensure that validator index was set
if valIndex < 0 {
@@ -155,12 +155,12 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
}
// Make sure the step matches.
if (vote.Height != voteSet.height) ||
(vote.Round != voteSet.round) ||
(vote.Type != voteSet.type_) {
if (vote.Vote.Height != voteSet.height) ||
(vote.Vote.Round != voteSet.round) ||
(vote.Vote.Type != voteSet.type_) {
return false, errors.Wrapf(ErrVoteUnexpectedStep, "Got %d/%d/%d, expected %d/%d/%d",
voteSet.height, voteSet.round, voteSet.type_,
vote.Height, vote.Round, vote.Type)
vote.Vote.Height, vote.Vote.Round, vote.Vote.Type)
}
// Ensure that signer is a validator.
@@ -186,7 +186,7 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
}
// Check signature.
if err := vote.Verify(voteSet.chainID, val.PubKey); err != nil {
if err := vote.Verify(val.PubKey); err != nil {
return false, errors.Wrapf(err, "Failed to verify vote with ChainID %s and PubKey %s", voteSet.chainID, val.PubKey)
}
@@ -202,8 +202,8 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
}
// Returns (vote, true) if vote exists for valIndex and blockKey.
func (voteSet *VoteSet) getVote(valIndex int, blockKey string) (vote *Vote, ok bool) {
if existing := voteSet.votes[valIndex]; existing != nil && existing.BlockID.Key() == blockKey {
func (voteSet *VoteSet) getVote(valIndex int, blockKey string) (vote *SignedVote, ok bool) {
if existing := voteSet.votes[valIndex]; existing != nil && existing.Vote != nil && existing.Vote.BlockID.Key() == blockKey {
return existing, true
}
if existing := voteSet.votesByBlock[blockKey].getByIndex(valIndex); existing != nil {
@@ -214,12 +214,12 @@ func (voteSet *VoteSet) getVote(valIndex int, blockKey string) (vote *Vote, ok b
// Assumes signature is valid.
// If conflicting vote exists, returns it.
func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower int64) (added bool, conflicting *Vote) {
valIndex := vote.ValidatorIndex
func (voteSet *VoteSet) addVerifiedVote(vote *SignedVote, blockKey string, votingPower int64) (added bool, conflicting *SignedVote) {
valIndex := vote.Vote.ValidatorIndex
// Already exists in voteSet.votes?
if existing := voteSet.votes[valIndex]; existing != nil {
if existing.BlockID.Equals(vote.BlockID) {
if existing.Vote.BlockID.Equals(vote.Vote.BlockID) {
cmn.PanicSanity("addVerifiedVote does not expect duplicate votes")
} else {
conflicting = existing
@@ -269,7 +269,7 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower
if origSum < quorum && quorum <= votesByBlock.sum {
// Only consider the first quorum reached
if voteSet.maj23 == nil {
maj23BlockID := vote.BlockID
maj23BlockID := vote.Vote.BlockID
voteSet.maj23 = &maj23BlockID
// And also copy votes over to voteSet.votes
for i, vote := range votesByBlock.votes {
@@ -346,7 +346,7 @@ func (voteSet *VoteSet) BitArrayByBlockID(blockID BlockID) *cmn.BitArray {
}
// NOTE: if validator has conflicting votes, returns "canonical" vote
func (voteSet *VoteSet) GetByIndex(valIndex int) *Vote {
func (voteSet *VoteSet) GetByIndex(valIndex int) *SignedVote {
if voteSet == nil {
return nil
}
@@ -355,7 +355,7 @@ func (voteSet *VoteSet) GetByIndex(valIndex int) *Vote {
return voteSet.votes[valIndex]
}
func (voteSet *VoteSet) GetByAddress(address []byte) *Vote {
func (voteSet *VoteSet) GetByAddress(address []byte) *SignedVote {
if voteSet == nil {
return nil
}
@@ -433,10 +433,10 @@ func (voteSet *VoteSet) StringIndented(indent string) string {
defer voteSet.mtx.Unlock()
voteStrings := make([]string, len(voteSet.votes))
for i, vote := range voteSet.votes {
if vote == nil {
voteStrings[i] = "nil-Vote"
if vote == nil || vote.Vote == nil {
voteStrings[i] = "nil-UnsignedVote"
} else {
voteStrings[i] = vote.String()
voteStrings[i] = vote.Vote.String()
}
}
return fmt.Sprintf(`VoteSet{
@@ -498,10 +498,10 @@ func (voteSet *VoteSet) VoteStrings() []string {
func (voteSet *VoteSet) voteStrings() []string {
voteStrings := make([]string, len(voteSet.votes))
for i, vote := range voteSet.votes {
if vote == nil {
if vote == nil || vote.Vote == nil {
voteStrings[i] = "nil-Vote"
} else {
voteStrings[i] = vote.String()
voteStrings[i] = vote.Vote.String()
}
}
return voteStrings
@@ -541,7 +541,7 @@ func (voteSet *VoteSet) MakeCommit() *Commit {
}
// For every validator, get the precommit
votesCopy := make([]*Vote, len(voteSet.votes))
votesCopy := make([]*SignedVote, len(voteSet.votes))
copy(votesCopy, voteSet.votes)
return &Commit{
BlockID: *voteSet.maj23,
@@ -560,7 +560,7 @@ func (voteSet *VoteSet) MakeCommit() *Commit {
type blockVotes struct {
peerMaj23 bool // peer claims to have maj23
bitArray *cmn.BitArray // valIndex -> hasVote?
votes []*Vote // valIndex -> *Vote
votes []*SignedVote // valIndex -> *UnsignedVote
sum int64 // vote sum
}
@@ -568,13 +568,13 @@ func newBlockVotes(peerMaj23 bool, numValidators int) *blockVotes {
return &blockVotes{
peerMaj23: peerMaj23,
bitArray: cmn.NewBitArray(numValidators),
votes: make([]*Vote, numValidators),
votes: make([]*SignedVote, numValidators),
sum: 0,
}
}
func (vs *blockVotes) addVerifiedVote(vote *Vote, votingPower int64) {
valIndex := vote.ValidatorIndex
func (vs *blockVotes) addVerifiedVote(vote *SignedVote, votingPower int64) {
valIndex := vote.Vote.ValidatorIndex
if existing := vs.votes[valIndex]; existing == nil {
vs.bitArray.SetIndex(valIndex, true)
vs.votes[valIndex] = vote
@@ -582,7 +582,7 @@ func (vs *blockVotes) addVerifiedVote(vote *Vote, votingPower int64) {
}
}
func (vs *blockVotes) getByIndex(index int) *Vote {
func (vs *blockVotes) getByIndex(index int) *SignedVote {
if vs == nil {
return nil
}
@@ -598,6 +598,6 @@ type VoteSetReader interface {
Type() byte
Size() int
BitArray() *cmn.BitArray
GetByIndex(int) *Vote
GetByIndex(int) *UnsignedVote
IsCommit() bool
}

View File

@@ -17,7 +17,7 @@ func randVoteSet(height int64, round int, type_ byte, numValidators int, votingP
}
// Convenience: Return new vote with different validator address/index
func withValidator(vote *Vote, addr []byte, idx int) *Vote {
func withValidator(vote *UnsignedVote, addr []byte, idx int) *UnsignedVote {
vote = vote.Copy()
vote.ValidatorAddress = addr
vote.ValidatorIndex = idx
@@ -25,35 +25,35 @@ func withValidator(vote *Vote, addr []byte, idx int) *Vote {
}
// Convenience: Return new vote with different height
func withHeight(vote *Vote, height int64) *Vote {
func withHeight(vote *UnsignedVote, height int64) *UnsignedVote {
vote = vote.Copy()
vote.Height = height
return vote
}
// Convenience: Return new vote with different round
func withRound(vote *Vote, round int) *Vote {
func withRound(vote *UnsignedVote, round int) *UnsignedVote {
vote = vote.Copy()
vote.Round = round
return vote
}
// Convenience: Return new vote with different type
func withType(vote *Vote, type_ byte) *Vote {
func withType(vote *UnsignedVote, type_ byte) *UnsignedVote {
vote = vote.Copy()
vote.Type = type_
return vote
}
// Convenience: Return new vote with different blockHash
func withBlockHash(vote *Vote, blockHash []byte) *Vote {
func withBlockHash(vote *UnsignedVote, blockHash []byte) *UnsignedVote {
vote = vote.Copy()
vote.BlockID.Hash = blockHash
return vote
}
// Convenience: Return new vote with different blockParts
func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
func withBlockPartsHeader(vote *UnsignedVote, blockPartsHeader PartSetHeader) *UnsignedVote {
vote = vote.Copy()
vote.BlockID.PartsHeader = blockPartsHeader
return vote
@@ -77,7 +77,7 @@ func TestAddVote(t *testing.T) {
t.Errorf("There should be no 2/3 majority")
}
vote := &Vote{
vote := &UnsignedVote{
ValidatorAddress: val0.GetAddress(),
ValidatorIndex: 0, // since privValidators are in order
Height: height,
@@ -107,7 +107,7 @@ func Test2_3Majority(t *testing.T) {
height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
voteProto := &Vote{
voteProto := &UnsignedVote{
ValidatorAddress: nil, // NOTE: must fill in
ValidatorIndex: -1, // NOTE: must fill in
Height: height,
@@ -164,7 +164,7 @@ func Test2_3MajorityRedux(t *testing.T) {
blockPartsTotal := 123
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
voteProto := &Vote{
voteProto := &UnsignedVote{
ValidatorAddress: nil, // NOTE: must fill in
ValidatorIndex: -1, // NOTE: must fill in
Height: height,
@@ -259,7 +259,7 @@ func TestBadVotes(t *testing.T) {
height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
voteProto := &Vote{
voteProto := &UnsignedVote{
ValidatorAddress: nil,
ValidatorIndex: -1,
Height: height,
@@ -321,7 +321,7 @@ func TestConflicts(t *testing.T) {
blockHash1 := cmn.RandBytes(32)
blockHash2 := cmn.RandBytes(32)
voteProto := &Vote{
voteProto := &UnsignedVote{
ValidatorAddress: nil,
ValidatorIndex: -1,
Height: height,
@@ -450,7 +450,7 @@ func TestMakeCommit(t *testing.T) {
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrecommit, 10, 1)
blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
voteProto := &Vote{
voteProto := &UnsignedVote{
ValidatorAddress: nil,
ValidatorIndex: -1,
Height: height,

View File

@@ -12,21 +12,21 @@ import (
tmtime "github.com/tendermint/tendermint/types/time"
)
func examplePrevote() *Vote {
func examplePrevote() *UnsignedVote {
return exampleVote(VoteTypePrevote)
}
func examplePrecommit() *Vote {
func examplePrecommit() *UnsignedVote {
return exampleVote(VoteTypePrecommit)
}
func exampleVote(t byte) *Vote {
func exampleVote(t byte) *UnsignedVote {
var stamp, err = time.Parse(TimeFormat, "2017-12-25T03:00:01.234Z")
if err != nil {
panic(err)
}
return &Vote{
return &UnsignedVote{
ValidatorAddress: tmhash.Sum([]byte("validator_address")),
ValidatorIndex: 56789,
Height: 12345,
@@ -40,18 +40,20 @@ func exampleVote(t byte) *Vote {
Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")),
},
},
ChainID: "test_chain_id",
}
}
func TestVoteSignable(t *testing.T) {
t.Skip("TODO(ismail): switch to amino")
vote := examplePrecommit()
signBytes := vote.SignBytes("test_chain_id")
signBytes := vote.SignBytes()
signStr := string(signBytes)
expected := `{"@chain_id":"test_chain_id","@type":"vote","block_id":{"hash":"8B01023386C371778ECB6368573E539AFC3CC860","parts":{"hash":"72DB3D959635DFF1BB567BEDAA70573392C51596","total":"1000000"}},"height":"12345","round":"2","timestamp":"2017-12-25T03:00:01.234Z","type":2}`
if signStr != expected {
// NOTE: when this fails, you probably want to fix up consensus/replay_test too
t.Errorf("Got unexpected sign string for Vote. Expected:\n%v\nGot:\n%v", expected, signStr)
t.Errorf("Got unexpected sign string for UnsignedVote. Expected:\n%v\nGot:\n%v", expected, signStr)
}
}
@@ -60,25 +62,25 @@ func TestVoteVerifySignature(t *testing.T) {
pubkey := privVal.GetPubKey()
vote := examplePrecommit()
signBytes := vote.SignBytes("test_chain_id")
signBytes := vote.SignBytes()
// sign it
err := privVal.SignVote("test_chain_id", vote)
signedVote, err := privVal.SignVote(vote)
require.NoError(t, err)
// verify the same vote
valid := pubkey.VerifyBytes(vote.SignBytes("test_chain_id"), vote.Signature)
valid := pubkey.VerifyBytes(vote.SignBytes(), signedVote.Signature)
require.True(t, valid)
// serialize, deserialize and verify again....
precommit := new(Vote)
bs, err := cdc.MarshalBinary(vote)
precommit := new(SignedVote)
bs, err := cdc.MarshalBinary(signedVote)
require.NoError(t, err)
err = cdc.UnmarshalBinary(bs, &precommit)
require.NoError(t, err)
// verify the transmitted vote
newSignBytes := precommit.SignBytes("test_chain_id")
newSignBytes := precommit.Vote.SignBytes()
require.Equal(t, string(signBytes), string(newSignBytes))
valid = pubkey.VerifyBytes(newSignBytes, precommit.Signature)
require.True(t, valid)
@@ -99,7 +101,7 @@ func TestIsVoteTypeValid(t *testing.T) {
tt := tt
t.Run(tt.name, func(st *testing.T) {
if rs := IsVoteTypeValid(tt.in); rs != tt.out {
t.Errorf("Got unexpected Vote type. Expected:\n%v\nGot:\n%v", rs, tt.out)
t.Errorf("Got unexpected UnsignedVote type. Expected:\n%v\nGot:\n%v", rs, tt.out)
}
})
}
@@ -111,20 +113,21 @@ func TestVoteVerify(t *testing.T) {
vote := examplePrevote()
vote.ValidatorAddress = pubkey.Address()
sv := &SignedVote{Vote: vote}
err := vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey())
err := sv.Verify(ed25519.GenPrivKey().PubKey())
if assert.Error(t, err) {
assert.Equal(t, ErrVoteInvalidValidatorAddress, err)
}
err = vote.Verify("test_chain_id", pubkey)
err = sv.Verify(pubkey)
if assert.Error(t, err) {
assert.Equal(t, ErrVoteInvalidSignature, err)
}
}
func TestMaxVoteBytes(t *testing.T) {
vote := &Vote{
vote := &UnsignedVote{
ValidatorAddress: tmhash.Sum([]byte("validator_address")),
ValidatorIndex: math.MaxInt64,
Height: math.MaxInt64,
@@ -141,11 +144,12 @@ func TestMaxVoteBytes(t *testing.T) {
}
privVal := NewMockPV()
err := privVal.SignVote("test_chain_id", vote)
sv, err := privVal.SignVote(vote)
require.NoError(t, err)
bz, err := cdc.MarshalBinary(vote)
bz, err := cdc.MarshalBinary(sv)
require.NoError(t, err)
// TODO(ismail): if we include the chainId in the vote this varies ...
// we need a max size for chainId too
assert.Equal(t, MaxVoteBytes, len(bz))
}