mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-20 06:54:41 +00:00
Change Commit to hold signatures+timestamps instead of votes
This commit is contained in:
+32
-58
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user