Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
508 changes: 249 additions & 259 deletions abci/types/types.pb.go

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions proto/tendermint/abci/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ option go_package = "github.com/cometbft/cometbft/abci/types";
import "tendermint/crypto/proof.proto";
import "tendermint/crypto/keys.proto";
import "tendermint/types/params.proto";
import "tendermint/types/validator.proto";
import "google/protobuf/timestamp.proto";
import "gogoproto/gogo.proto";

Expand Down Expand Up @@ -434,18 +435,22 @@ message ValidatorUpdate {

message VoteInfo {
Validator validator = 1 [(gogoproto.nullable) = false];
bool signed_last_block = 2;
tendermint.types.BlockIDFlag block_id_flag = 3;

reserved 2; // signed_last_block
}

message ExtendedVoteInfo {
// The validator that sent the vote.
Validator validator = 1 [(gogoproto.nullable) = false];
// Indicates whether the validator signed the last block, allowing for rewards based on validator availability.
bool signed_last_block = 2;
// Non-deterministic extension provided by the sending validator's application.
bytes vote_extension = 3;
// Vote extension signature created by CometBFT
bytes extension_signature = 4;
// block_id_flag indicates whether the validator voted for a block, nil, or did not vote at all
tendermint.types.BlockIDFlag block_id_flag = 5;

reserved 2; // signed_last_block
}

enum MisbehaviorType {
Expand Down
207 changes: 84 additions & 123 deletions proto/tendermint/types/types.pb.go

Large diffs are not rendered by default.

23 changes: 6 additions & 17 deletions proto/tendermint/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,6 @@ import "tendermint/crypto/proof.proto";
import "tendermint/version/types.proto";
import "tendermint/types/validator.proto";

// BlockIdFlag indicates which BlockID the signature is for
enum BlockIDFlag {
option (gogoproto.goproto_enum_stringer) = true;
option (gogoproto.goproto_enum_prefix) = false;

BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; // indicates an error condition
BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; // the vote was not received
BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; // voted for the block that received the majority
BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; // voted for nil
}

// SignedMsgType is a type of signed message in the consensus.
enum SignedMsgType {
option (gogoproto.goproto_enum_stringer) = true;
Expand Down Expand Up @@ -123,9 +112,9 @@ message Commit {

// CommitSig is a part of the Vote included in a Commit.
message CommitSig {
BlockIDFlag block_id_flag = 1;
bytes validator_address = 2;
google.protobuf.Timestamp timestamp = 3
tendermint.types.BlockIDFlag block_id_flag = 1;
bytes validator_address = 2;
google.protobuf.Timestamp timestamp = 3
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
bytes signature = 4;
}
Expand All @@ -142,9 +131,9 @@ message ExtendedCommit {
// extension-related fields. We use two signatures to ensure backwards compatibility.
// That is the digest of the original signature is still the same in prior versions
message ExtendedCommitSig {
BlockIDFlag block_id_flag = 1;
bytes validator_address = 2;
google.protobuf.Timestamp timestamp = 3
tendermint.types.BlockIDFlag block_id_flag = 1;
bytes validator_address = 2;
google.protobuf.Timestamp timestamp = 3
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
bytes signature = 4;
// Vote extension data
Expand Down
90 changes: 66 additions & 24 deletions proto/tendermint/types/validator.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions proto/tendermint/types/validator.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ option go_package = "github.com/cometbft/cometbft/proto/tendermint/types";
import "gogoproto/gogo.proto";
import "tendermint/crypto/keys.proto";

// BlockIdFlag indicates which BlockID the signature is for
enum BlockIDFlag {
option (gogoproto.goproto_enum_stringer) = true;
option (gogoproto.goproto_enum_prefix) = false;

BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; // indicates an error condition
BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; // the vote was not received
BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; // voted for the block that received the majority
BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; // voted for nil
}


message ValidatorSet {
repeated Validator validators = 1;
Validator proposer = 2;
Expand Down
7 changes: 4 additions & 3 deletions state/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/cometbft/cometbft/libs/fail"
"github.com/cometbft/cometbft/libs/log"
"github.com/cometbft/cometbft/mempool"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cometbft/cometbft/proxy"
"github.com/cometbft/cometbft/types"
)
Expand Down Expand Up @@ -421,8 +422,8 @@ func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) a
for i, val := range lastValSet.Validators {
commitSig := block.LastCommit.Signatures[i]
votes[i] = abci.VoteInfo{
Validator: types.TM2PB.Validator(val),
SignedLastBlock: commitSig.BlockIDFlag != types.BlockIDFlagAbsent,
Validator: types.TM2PB.Validator(val),
BlockIdFlag: cmtproto.BlockIDFlag(commitSig.BlockIDFlag),
}
}

Expand Down Expand Up @@ -494,7 +495,7 @@ func buildExtendedCommitInfo(ec *types.ExtendedCommit, store Store, initialHeigh

votes[i] = abci.ExtendedVoteInfo{
Validator: types.TM2PB.Validator(val),
SignedLastBlock: ecs.BlockIDFlag != types.BlockIDFlagAbsent,
BlockIdFlag: cmtproto.BlockIDFlag(ecs.BlockIDFlag),
VoteExtension: ext,
ExtensionSignature: extSig,
}
Expand Down
9 changes: 5 additions & 4 deletions state/execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/cometbft/cometbft/internal/test"
"github.com/cometbft/cometbft/libs/log"
mpmocks "github.com/cometbft/cometbft/mempool/mocks"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
cmtversion "github.com/cometbft/cometbft/proto/tendermint/version"
"github.com/cometbft/cometbft/proxy"
pmocks "github.com/cometbft/cometbft/proxy/mocks"
Expand Down Expand Up @@ -149,7 +150,7 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
// -> app receives a list of validators with a bool indicating if they signed
for i, v := range app.CommitVotes {
_, absent := tc.absentCommitSigs[i]
assert.Equal(t, !absent, v.SignedLastBlock)
assert.Equal(t, !absent, v.BlockIdFlag != cmtproto.BlockIDFlagAbsent)
}
})
}
Expand Down Expand Up @@ -228,10 +229,10 @@ func TestFinalizeBlockValidators(t *testing.T) {
if ctr < len(tc.expectedAbsentValidators) &&
tc.expectedAbsentValidators[ctr] == i {

assert.False(t, v.SignedLastBlock)
assert.Equal(t, v.BlockIdFlag, cmtproto.BlockIDFlagAbsent)
ctr++
} else {
assert.True(t, v.SignedLastBlock)
assert.NotEqual(t, v.BlockIdFlag, cmtproto.BlockIDFlagAbsent)
}
}
}
Expand Down Expand Up @@ -398,7 +399,7 @@ func TestProcessProposal(t *testing.T) {
addr := pk.Address()
voteInfos = append(voteInfos,
abci.VoteInfo{
SignedLastBlock: true,
BlockIdFlag: cmtproto.BlockIDFlagCommit,
Validator: abci.Validator{
Address: addr,
Power: 1000,
Expand Down
38 changes: 28 additions & 10 deletions test/e2e/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,30 +568,48 @@ func parseTx(tx []byte) (string, string, error) {
return string(parts[0]), string(parts[1]), nil
}

func (app *Application) verifyAndSum(areExtensionsEnabled bool, currentHeight int64, extCommit *abci.ExtendedCommitInfo, callsite string) (int64, error) {
func (app *Application) verifyAndSum(
areExtensionsEnabled bool,
currentHeight int64,
extCommit *abci.ExtendedCommitInfo,
callsite string,
) (int64, error) {
var sum int64
var extCount int
for _, vote := range extCommit.Votes {
if !vote.SignedLastBlock {
continue
if vote.BlockIdFlag == cmtproto.BlockIDFlagUnknown || vote.BlockIdFlag > cmtproto.BlockIDFlagNil {
return 0, fmt.Errorf("vote with bad blockID flag value at height %d; blockID flag %d", currentHeight, vote.BlockIdFlag)
}
if !areExtensionsEnabled {
if vote.BlockIdFlag == cmtproto.BlockIDFlagAbsent || vote.BlockIdFlag == cmtproto.BlockIDFlagNil {
if len(vote.VoteExtension) != 0 {
return 0, fmt.Errorf("non-empty vote extension at height %d, which has extensions disabled", currentHeight)
return 0, fmt.Errorf("non-empty vote extension at height %d, for a vote with blockID flag %d",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially, if this validator had voted correctly, and there is an extension, the flag had to have been Commit?

Copy link
Collaborator Author

@sergio-mena sergio-mena Mar 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly:

  • BlockIDFlagAbsent means it didn't precommit so, how come we got a vote extension?
  • BlockIDFlagNil means it precommitted for nil, but nil precommits don't have vote extensions

In both cases the info we get is contradictory, so we should reject it.

Copy link
Collaborator Author

@sergio-mena sergio-mena Mar 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, did you mean that we could simplify the condition in line 583 like this?
if vote.BlockIdFlag != cmtproto.BlockIDFlagCommit {

currentHeight, vote.BlockIdFlag)
}
if len(vote.ExtensionSignature) != 0 {
return 0, fmt.Errorf("non-empty vote extension signature at height %d, for a vote with blockID flag %d",
currentHeight, vote.BlockIdFlag)
}
// Only interested in votes that can have extensions
continue
}
if len(vote.VoteExtension) == 0 {
app.logger.Info("received empty vote extension form %X at height %d (extensions enabled), must represent nil-precommit",
vote.Validator, currentHeight)
if !areExtensionsEnabled {
if len(vote.VoteExtension) != 0 {
return 0, fmt.Errorf("non-empty vote extension at height %d, which has extensions disabled",
currentHeight)
}
if len(vote.ExtensionSignature) != 0 {
return 0, errors.New("non-empty vote extension signature with empty vote extension")
return 0, fmt.Errorf("non-empty vote extension signature at height %d, which has extensions disabled",
currentHeight)
}
continue
}
if len(vote.VoteExtension) == 0 {
return 0, fmt.Errorf("received empty vote extension from %X at height %d (extensions enabled); "+
"e2e app's logic does not allow it", vote.Validator, currentHeight)
}
// Vote extension signatures are always provided. Apps can use them to verify the integrity of extensions
if len(vote.ExtensionSignature) == 0 {
return 0, errors.New("non-empty vote extension with empty signature")
return 0, fmt.Errorf("empty vote extension signature at height %d (extensions enabled)", currentHeight)
}

// Reconstruct vote extension's signed bytes...
Expand Down