Change Commit to hold signatures+timestamps instead of votes

This commit is contained in:
Jeremiah Andrews
2018-08-18 21:38:02 -07:00
parent debe56326f
commit e47a8939f9
13 changed files with 141 additions and 156 deletions
+32 -58
View File
@@ -296,49 +296,38 @@ 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 []*CommitSig `json:"precommits"`
RoundNum int
HeightNum int64
// Volatile
firstPrecommit *Vote
hash cmn.HexBytes
bitArray *cmn.BitArray
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 {
if len(commit.Precommits) == 0 {
return nil
}
if commit.firstPrecommit != nil {
return commit.firstPrecommit
}
for _, precommit := range commit.Precommits {
if precommit != nil {
commit.firstPrecommit = precommit
return precommit
}
}
return &Vote{
Type: VoteTypePrecommit,
}
type CommitSig struct {
Signature []byte
Timestamp time.Time
}
func (cs *CommitSig) String(index int, address Address, height int64, round int, blockID BlockID) string {
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}",
index, cmn.Fingerprint(address),
height, round, VoteTypePrecommit, "Precommit",
cmn.Fingerprint(blockID.Hash),
cmn.Fingerprint(cs.Signature),
CanonicalTime(cs.Timestamp))
}
// Height returns the height of the commit
func (commit *Commit) Height() int64 {
if len(commit.Precommits) == 0 {
return 0
}
return commit.FirstPrecommit().Height
return commit.HeightNum
}
// Round returns the round of the commit
func (commit *Commit) Round() int {
if len(commit.Precommits) == 0 {
return 0
}
return commit.FirstPrecommit().Round
return commit.RoundNum
}
// Type returns the vote type of the commit, which is always VoteTypePrecommit
@@ -368,8 +357,16 @@ func (commit *Commit) BitArray() *cmn.BitArray {
}
// GetByIndex returns the vote corresponding to a given validator index
func (commit *Commit) GetByIndex(index int) *Vote {
return commit.Precommits[index]
func (com *Commit) GetByIndex(index int) *Vote {
return &Vote{
ValidatorIndex: index,
Height: com.HeightNum,
Round: com.RoundNum,
Timestamp: com.Precommits[index].Timestamp,
Type: VoteTypePrecommit,
BlockID: com.BlockID,
Signature: com.Precommits[index].Signature,
}
}
// IsCommit returns true if there is at least one vote
@@ -386,30 +383,6 @@ func (commit *Commit) ValidateBasic() error {
if len(commit.Precommits) == 0 {
return errors.New("No precommits in commit")
}
height, round := commit.Height(), commit.Round()
// Validate the precommits.
for _, precommit := range commit.Precommits {
// It's OK for precommits to be missing.
if precommit == nil {
continue
}
// Ensure that all votes are precommits.
if precommit.Type != VoteTypePrecommit {
return fmt.Errorf("Invalid commit vote. Expected precommit, got %v",
precommit.Type)
}
// Ensure that all heights are the same.
if precommit.Height != height {
return fmt.Errorf("Invalid commit precommit height. Expected %v, got %v",
height, precommit.Height)
}
// Ensure that all rounds are the same.
if precommit.Round != round {
return fmt.Errorf("Invalid commit precommit round. Expected %v, got %v",
round, precommit.Round)
}
}
return nil
}
@@ -435,7 +408,8 @@ 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.String(i, []byte("---"),
commit.HeightNum, commit.RoundNum, commit.BlockID)
}
return fmt.Sprintf(`Commit{
%s BlockID: %v
+4 -18
View File
@@ -118,7 +118,7 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) {
partSet := MakeBlock(h, txs, commit, evList).MakePartSet(1024)
assert.NotNil(t, partSet)
assert.Equal(t, 3, partSet.Total())
assert.Equal(t, 2, partSet.Total())
}
func TestBlockHashesTo(t *testing.T) {
@@ -193,7 +193,6 @@ func TestCommit(t *testing.T) {
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err)
assert.NotNil(t, commit.FirstPrecommit())
assert.Equal(t, h-1, commit.Height())
assert.Equal(t, 1, commit.Round())
assert.Equal(t, VoteTypePrecommit, commit.Type())
@@ -204,7 +203,9 @@ func TestCommit(t *testing.T) {
require.NotNil(t, commit.BitArray())
assert.Equal(t, cmn.NewBitArray(10).Size(), commit.BitArray().Size())
assert.Equal(t, voteSet.GetByIndex(0), commit.GetByIndex(0))
cv := commit.GetByIndex(0)
cv.ValidatorAddress = voteSet.GetByIndex(0).ValidatorAddress
assert.Equal(t, voteSet.GetByIndex(0), cv)
assert.True(t, commit.IsCommit())
}
@@ -216,21 +217,6 @@ func TestCommitValidateBasic(t *testing.T) {
commit = randCommit()
commit.Precommits[0] = nil
assert.NoError(t, commit.ValidateBasic())
// tamper with types
commit = randCommit()
commit.Precommits[0].Type = VoteTypePrevote
assert.Error(t, commit.ValidateBasic())
// tamper with height
commit = randCommit()
commit.Precommits[0].Height = int64(100)
assert.Error(t, commit.ValidateBasic())
// tamper with round
commit = randCommit()
commit.Precommits[0].Round = 100
assert.Error(t, commit.ValidateBasic())
}
func randCommit() *Commit {
+21 -35
View File
@@ -270,34 +270,27 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height i
}
talliedVotingPower := int64(0)
round := commit.Round()
baseVote := Vote{
Height: height,
Round: commit.Round(),
Type: VoteTypePrecommit,
BlockID: blockID,
}
for idx, precommit := range commit.Precommits {
if precommit == 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.Round != round {
return fmt.Errorf("Invalid commit -- wrong round: want %v got %v", round, precommit.Round)
}
if precommit.Type != VoteTypePrecommit {
return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx)
}
_, val := vals.GetByIndex(idx)
baseVote.Timestamp = precommit.Timestamp
// Validate signature.
precommitSignBytes := precommit.SignBytes(chainID)
precommitSignBytes := baseVote.SignBytes(chainID)
if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
return fmt.Errorf("Invalid commit -- invalid signature: %v", precommit)
}
// Good precommit!
if blockID.Equals(precommit.BlockID) {
talliedVotingPower += val.VotingPower
} else {
// It's OK that the BlockID doesn't match. We include stray
// precommits to measure validator availability.
}
talliedVotingPower += val.VotingPower
}
if talliedVotingPower > vals.TotalVotingPower()*2/3 {
@@ -349,40 +342,33 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin
// Check old voting power.
oldVotingPower := int64(0)
seen := map[int]bool{}
round := commit.Round()
baseVote := Vote{
Height: height,
Round: commit.Round(),
Type: VoteTypePrecommit,
BlockID: blockID,
}
for idx, precommit := range commit.Precommits {
if precommit == nil {
continue
}
if precommit.Height != height {
return cmn.NewError("Blocks don't match - %d vs %d", round, precommit.Round)
}
if precommit.Round != round {
return cmn.NewError("Invalid commit -- wrong round: %v vs %v", round, precommit.Round)
}
if precommit.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)
adr, _ := newSet.GetByIndex(idx)
idx, val := oldVals.GetByAddress(adr)
if val == nil || seen[idx] {
continue // missing or double vote...
}
seen[idx] = true
// Validate signature.
precommitSignBytes := precommit.SignBytes(chainID)
baseVote.Timestamp = precommit.Timestamp
precommitSignBytes := baseVote.SignBytes(chainID)
if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
return cmn.NewError("Invalid commit -- invalid signature: %v", precommit)
}
// Good precommit!
if blockID.Equals(precommit.BlockID) {
oldVotingPower += val.VotingPower
} else {
// It's OK that the BlockID doesn't match. We include stray
// precommits to measure validator availability.
}
oldVotingPower += val.VotingPower
}
if oldVotingPower <= oldVals.TotalVotingPower()*2/3 {
+12 -3
View File
@@ -392,8 +392,15 @@ func TestValidatorSetVerifyCommit(t *testing.T) {
assert.NoError(t, err)
vote.Signature = sig
commit := &Commit{
BlockID: blockID,
Precommits: []*Vote{vote},
BlockID: blockID,
Precommits: []*CommitSig{
&CommitSig{
Signature: sig,
Timestamp: vote.Timestamp,
},
},
HeightNum: height,
RoundNum: 0,
}
badChainID := "notmychainID"
@@ -401,7 +408,9 @@ func TestValidatorSetVerifyCommit(t *testing.T) {
badHeight := height + 1
badCommit := &Commit{
BlockID: blockID,
Precommits: []*Vote{nil},
Precommits: []*CommitSig{nil},
HeightNum: height,
RoundNum: 0,
}
// test some error cases
+12 -1
View File
@@ -543,9 +543,20 @@ func (voteSet *VoteSet) MakeCommit() *Commit {
// For every validator, get the precommit
votesCopy := make([]*Vote, len(voteSet.votes))
copy(votesCopy, voteSet.votes)
precommits := make([]*CommitSig, len(voteSet.votes))
for i, v := range votesCopy {
if v != nil {
precommits[i] = &CommitSig{
Signature: v.Signature,
Timestamp: v.Timestamp,
}
}
}
return &Commit{
BlockID: *voteSet.maj23,
Precommits: votesCopy,
Precommits: precommits,
RoundNum: voteSet.round,
HeightNum: voteSet.height,
}
}