@ -73,7 +73,7 @@ func (b *Block) ValidateBasic() error {
return errors . New ( "nil LastCommit" )
return errors . New ( "nil LastCommit" )
}
}
if err := b . LastCommit . ValidateBasic ( ) ; err != nil {
if err := b . LastCommit . ValidateBasic ( ) ; err != nil {
return fmt . Errorf ( "wrong LastCommit" )
return fmt . Errorf ( "wrong LastCommit: %v " , err )
}
}
}
}
if err := ValidateHash ( b . LastCommitHash ) ; err != nil {
if err := ValidateHash ( b . LastCommitHash ) ; err != nil {
@ -434,27 +434,112 @@ func (h *Header) StringIndented(indent string) string {
//-------------------------------------
//-------------------------------------
// CommitSig is a vote included in a Commit.
// For now, it is identical to a vote,
// but in the future it will contain fewer fields
// to eliminate the redundancy in commits.
// See https://github.com/tendermint/tendermint/issues/1648.
type CommitSig Vote
// BlockIDFlag indicates which BlockID the signature is for.
type BlockIDFlag byte
// String returns the underlying Vote.String()
func ( cs * CommitSig ) String ( ) string {
return cs . toVote ( ) . String ( )
const (
// BlockIDFlagAbsent - no vote was received from a validator.
BlockIDFlagAbsent BlockIDFlag = iota + 1
// BlockIDFlagCommit - voted for the Commit.BlockID.
BlockIDFlagCommit
// BlockIDFlagNil - voted for nil.
BlockIDFlagNil
)
// CommitSig is a part of the Vote included in a Commit.
type CommitSig struct {
BlockIDFlag BlockIDFlag ` json:"block_id_flag" `
ValidatorAddress Address ` json:"validator_address" `
Timestamp time . Time ` json:"timestamp" `
Signature [ ] byte ` json:"signature" `
}
}
// toVote converts the CommitSig to a vote.
// TODO: deprecate for #1648. Converting to Vote will require
// access to ValidatorSet.
func ( cs * CommitSig ) toVote ( ) * Vote {
if cs == nil {
return nil
// NewCommitSigForBlock returns new CommitSig with BlockIDFlagCommit.
func NewCommitSigForBlock ( signature [ ] byte , valAddr Address , ts time . Time ) CommitSig {
return CommitSig {
BlockIDFlag : BlockIDFlagCommit ,
ValidatorAddress : valAddr ,
Timestamp : ts ,
Signature : signature ,
}
}
v := Vote ( * cs )
return & v
}
// NewCommitSigAbsent returns new CommitSig with BlockIDFlagAbsent. Other
// fields are all empty.
func NewCommitSigAbsent ( ) CommitSig {
return CommitSig {
BlockIDFlag : BlockIDFlagAbsent ,
}
}
// Absent returns true if CommitSig is absent.
func ( cs CommitSig ) Absent ( ) bool {
return cs . BlockIDFlag == BlockIDFlagAbsent
}
func ( cs CommitSig ) String ( ) string {
return fmt . Sprintf ( "CommitSig{%X by %X on %v @ %s}" ,
cmn . Fingerprint ( cs . Signature ) ,
cmn . Fingerprint ( cs . ValidatorAddress ) ,
cs . BlockIDFlag ,
CanonicalTime ( cs . Timestamp ) )
}
// BlockID returns the Commit's BlockID if CommitSig indicates signing,
// otherwise - empty BlockID.
func ( cs CommitSig ) BlockID ( commitBlockID BlockID ) BlockID {
var blockID BlockID
switch cs . BlockIDFlag {
case BlockIDFlagAbsent :
blockID = BlockID { }
case BlockIDFlagCommit :
blockID = commitBlockID
case BlockIDFlagNil :
blockID = BlockID { }
default :
panic ( fmt . Sprintf ( "Unknown BlockIDFlag: %v" , cs . BlockIDFlag ) )
}
return blockID
}
// ValidateBasic performs basic validation.
func ( cs CommitSig ) ValidateBasic ( ) error {
switch cs . BlockIDFlag {
case BlockIDFlagAbsent :
case BlockIDFlagCommit :
case BlockIDFlagNil :
default :
return fmt . Errorf ( "unknown BlockIDFlag: %v" , cs . BlockIDFlag )
}
switch cs . BlockIDFlag {
case BlockIDFlagAbsent :
if len ( cs . ValidatorAddress ) != 0 {
return errors . New ( "validator address is present" )
}
if ! cs . Timestamp . IsZero ( ) {
return errors . New ( "time is present" )
}
if len ( cs . Signature ) != 0 {
return errors . New ( "signature is present" )
}
default :
if len ( cs . ValidatorAddress ) != crypto . AddressSize {
return fmt . Errorf ( "expected ValidatorAddress size to be %d bytes, got %d bytes" ,
crypto . AddressSize ,
len ( cs . ValidatorAddress ) ,
)
}
// NOTE: Timestamp validation is subtle and handled elsewhere.
if len ( cs . Signature ) == 0 {
return errors . New ( "signature is missing" )
}
if len ( cs . Signature ) > MaxSignatureSize {
return fmt . Errorf ( "signature is too big (max: %d)" , MaxSignatureSize )
}
}
return nil
}
}
//-------------------------------------
//-------------------------------------
@ -462,40 +547,40 @@ func (cs *CommitSig) toVote() *Vote {
// Commit contains the evidence that a block was committed by a set of validators.
// Commit contains the evidence that a block was committed by a set of validators.
// NOTE: Commit is empty for height 1, but never nil.
// NOTE: Commit is empty for height 1, but never nil.
type Commit struct {
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 [ ] * CommitSig ` json:"precommits" `
// memoized in first call to corresponding method
// NOTE: can't memoize in constructor because constructor
// isn't used for unmarshaling
height int64
round int
// NOTE: The signatures are in order of address to preserve the bonded
// ValidatorSet order.
// Any peer with a block can gossip signatures by index with a peer without
// recalculating the active ValidatorSet.
Height int64 ` json:"height" `
Round int ` json:"round" `
BlockID BlockID ` json:"block_id" `
Signatures [ ] CommitSig ` json:"signatures" `
// Memoized in first call to corresponding method.
// NOTE: can't memoize in constructor because constructor isn't used for
// unmarshaling.
hash cmn . HexBytes
hash cmn . HexBytes
bitArray * cmn . BitArray
bitArray * cmn . BitArray
}
}
// NewCommit returns a new Commit with the given blockID and precommits.
// TODO: memoize ValidatorSet in constructor so votes can be easily reconstructed
// from CommitSig after #1648.
func NewCommit ( blockID BlockID , precommits [ ] * CommitSig ) * Commit {
// NewCommit returns a new Commit.
func NewCommit ( height int64 , round int , blockID BlockID , commitSigs [ ] CommitSig ) * Commit {
return & Commit {
return & Commit {
Height : height ,
Round : round ,
BlockID : blockID ,
BlockID : blockID ,
Precommits : precommits ,
Signature s: commitSig s ,
}
}
}
}
// Construct a VoteSet from the Commit and validator set. Panics
// if precommit s from the commit can't be added to the voteset.
// CommitToVoteSet co nstructs a VoteSet from the Commit and validator set.
// Panics if signature s from the commit can't be added to the voteset.
// Inverse of VoteSet.MakeCommit().
// Inverse of VoteSet.MakeCommit().
func CommitToVoteSet ( chainID string , commit * Commit , vals * ValidatorSet ) * VoteSet {
func CommitToVoteSet ( chainID string , commit * Commit , vals * ValidatorSet ) * VoteSet {
height , round , typ := commit . Height ( ) , commit . Round ( ) , PrecommitType
voteSet := NewVoteSet ( chainID , height , round , typ , vals )
for idx , precommit := range commit . Precommits {
if precommit == nil {
continue
voteSet := NewVoteSet ( chainID , commit . Height , commit . Round , PrecommitType , vals )
for idx , commitSig := range commit . Signatures {
if commitSig . Absent ( ) {
continue // OK, some precommits can be missing.
}
}
added , err := voteSet . AddVote ( commit . GetVote ( idx ) )
added , err := voteSet . AddVote ( commit . GetVote ( idx ) )
if ! added || err != nil {
if ! added || err != nil {
@ -509,21 +594,12 @@ func CommitToVoteSet(chainID string, commit *Commit, vals *ValidatorSet) *VoteSe
// Returns nil if the precommit at valIdx is nil.
// Returns nil if the precommit at valIdx is nil.
// Panics if valIdx >= commit.Size().
// Panics if valIdx >= commit.Size().
func ( commit * Commit ) GetVote ( valIdx int ) * Vote {
func ( commit * Commit ) GetVote ( valIdx int ) * Vote {
commitSig := commit . Precommits [ valIdx ]
if commitSig == nil {
return nil
}
// NOTE: this commitSig might be for a nil blockID,
// so we can't just use commit.BlockID here.
// For #1648, CommitSig will need to indicate what BlockID it's for !
blockID := commitSig . BlockID
commit . memoizeHeightRound ( )
commitSig := commit . Signatures [ valIdx ]
return & Vote {
return & Vote {
Type : PrecommitType ,
Type : PrecommitType ,
Height : commit . h eight,
Round : commit . r ound,
BlockID : blockID ,
Height : commit . Height ,
Round : commit . Round ,
BlockID : commitSig . BlockID ( commit . BlockID ) ,
Timestamp : commitSig . Timestamp ,
Timestamp : commitSig . Timestamp ,
ValidatorAddress : commitSig . ValidatorAddress ,
ValidatorAddress : commitSig . ValidatorAddress ,
ValidatorIndex : valIdx ,
ValidatorIndex : valIdx ,
@ -539,58 +615,42 @@ func (commit *Commit) VoteSignBytes(chainID string, valIdx int) []byte {
return commit . GetVote ( valIdx ) . SignBytes ( chainID )
return commit . GetVote ( valIdx ) . SignBytes ( chainID )
}
}
// memoizeHeightRound memoizes the height and round of the commit using
// the first non-nil vote.
// Should be called before any attempt to access `commit.height` or `commit.round`.
func ( commit * Commit ) memoizeHeightRound ( ) {
if len ( commit . Precommits ) == 0 {
return
}
if commit . height > 0 {
return
}
for _ , precommit := range commit . Precommits {
if precommit != nil {
commit . height = precommit . Height
commit . round = precommit . Round
return
}
}
}
// Height returns the height of the commit
func ( commit * Commit ) Height ( ) int64 {
commit . memoizeHeightRound ( )
return commit . height
// Type returns the vote type of the commit, which is always VoteTypePrecommit
// Implements VoteSetReader.
func ( commit * Commit ) Type ( ) byte {
return byte ( PrecommitType )
}
}
// Round returns the round of the commit
func ( commit * Commit ) Round ( ) int {
commit . memoizeHeightRound ( )
return commit . round
// GetHeight returns height of the commit.
// Implements VoteSetReader.
func ( commit * Commit ) GetHeight ( ) int64 {
return commit . Height
}
}
// Type returns the vote type of the commit, which is always VoteTypePrecommit
func ( commit * Commit ) Type ( ) byte {
return byte ( PrecommitType )
// GetRound returns height of the commit.
// Implements VoteSetReader.
func ( commit * Commit ) GetRound ( ) int {
return commit . Round
}
}
// Size returns the number of votes in the commit
// Size returns the number of signatures in the commit.
// Implements VoteSetReader.
func ( commit * Commit ) Size ( ) int {
func ( commit * Commit ) Size ( ) int {
if commit == nil {
if commit == nil {
return 0
return 0
}
}
return len ( commit . Precommit s)
return len ( commit . Signature s)
}
}
// BitArray returns a BitArray of which validators voted in this commit
// BitArray returns a BitArray of which validators voted for BlockID or nil in this commit.
// Implements VoteSetReader.
func ( commit * Commit ) BitArray ( ) * cmn . BitArray {
func ( commit * Commit ) BitArray ( ) * cmn . BitArray {
if commit . bitArray == nil {
if commit . bitArray == nil {
commit . bitArray = cmn . NewBitArray ( len ( commit . Precommit s) )
for i , pre commit := range commit . Precommit s {
commit . bitArray = cmn . NewBitArray ( len ( commit . Signature s) )
for i , commitSig := range commit . Signature s {
// TODO: need to check the BlockID otherwise we could be counting conflicts,
// TODO: need to check the BlockID otherwise we could be counting conflicts,
// not just the one with +2/3 !
// not just the one with +2/3 !
commit . bitArray . SetIndex ( i , precommit != nil )
commit . bitArray . SetIndex ( i , ! commitSig . Absent ( ) )
}
}
}
}
return commit . bitArray
return commit . bitArray
@ -603,44 +663,35 @@ func (commit *Commit) GetByIndex(valIdx int) *Vote {
return commit . GetVote ( valIdx )
return commit . GetVote ( valIdx )
}
}
// IsCommit returns true if there is at least one vote.
// IsCommit returns true if there is at least one signature.
// Implements VoteSetReader.
func ( commit * Commit ) IsCommit ( ) bool {
func ( commit * Commit ) IsCommit ( ) bool {
return len ( commit . Precommit s) != 0
return len ( commit . Signature s) != 0
}
}
// ValidateBasic performs basic validation that doesn't involve state data.
// ValidateBasic performs basic validation that doesn't involve state data.
// Does not actually check the cryptographic signatures.
// Does not actually check the cryptographic signatures.
func ( commit * Commit ) ValidateBasic ( ) error {
func ( commit * Commit ) ValidateBasic ( ) error {
if commit . Height < 0 {
return errors . New ( "negative Height" )
}
if commit . Round < 0 {
return errors . New ( "negative Round" )
}
if commit . BlockID . IsZero ( ) {
if commit . BlockID . IsZero ( ) {
return errors . New ( "commit cannot be for nil block" )
return errors . New ( "commit cannot be for nil block" )
}
}
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 != PrecommitType {
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 )
if len ( commit . Signatures ) == 0 {
return errors . New ( "no signatures in commit" )
}
for i , commitSig := range commit . Signatures {
if err := commitSig . ValidateBasic ( ) ; err != nil {
return fmt . Errorf ( "wrong CommitSig #%d: %v" , i , err )
}
}
}
}
return nil
return nil
}
}
@ -650,9 +701,9 @@ func (commit *Commit) Hash() cmn.HexBytes {
return nil
return nil
}
}
if commit . hash == nil {
if commit . hash == nil {
bs := make ( [ ] [ ] byte , len ( commit . Precommit s) )
for i , pre commit := range commit . Precommit s {
bs [ i ] = cdcEncode ( pre commit)
bs := make ( [ ] [ ] byte , len ( commit . Signature s) )
for i , commitSig := range commit . Signature s {
bs [ i ] = cdcEncode ( commitSig )
}
}
commit . hash = merkle . SimpleHashFromByteSlices ( bs )
commit . hash = merkle . SimpleHashFromByteSlices ( bs )
}
}
@ -664,18 +715,22 @@ func (commit *Commit) StringIndented(indent string) string {
if commit == nil {
if commit == nil {
return "nil-Commit"
return "nil-Commit"
}
}
pre commitStrings := make ( [ ] string , len ( commit . Precommit s) )
for i , pre commit := range commit . Precommit s {
pre commitStrings[ i ] = pre commit. String ( )
commitSig Strings := make ( [ ] string , len ( commit . Signature s) )
for i , commitSig := range commit . Signature s {
commitSig Strings [ i ] = commitSig . String ( )
}
}
return fmt . Sprintf ( ` Commit {
return fmt . Sprintf ( ` Commit {
% s Height : % d
% s Round : % d
% s BlockID : % v
% s BlockID : % v
% s Precommits :
% s Signature s:
% s % v
% s % v
% s } # % v ` ,
% s } # % v ` ,
indent , commit . Height ,
indent , commit . Round ,
indent , commit . BlockID ,
indent , commit . BlockID ,
indent ,
indent ,
indent , strings . Join ( pre commitStrings, "\n" + indent + " " ) ,
indent , strings . Join ( commitSig Strings , "\n" + indent + " " ) ,
indent , commit . hash )
indent , commit . hash )
}
}
@ -695,7 +750,6 @@ type SignedHeader struct {
// sure to use a Verifier to validate the signatures actually provide a
// sure to use a Verifier to validate the signatures actually provide a
// significantly strong proof for this header's validity.
// significantly strong proof for this header's validity.
func ( sh SignedHeader ) ValidateBasic ( chainID string ) error {
func ( sh SignedHeader ) ValidateBasic ( chainID string ) error {
// Make sure the header is consistent with the commit.
// Make sure the header is consistent with the commit.
if sh . Header == nil {
if sh . Header == nil {
return errors . New ( "signedHeader missing header" )
return errors . New ( "signedHeader missing header" )
@ -710,9 +764,9 @@ func (sh SignedHeader) ValidateBasic(chainID string) error {
sh . ChainID , chainID )
sh . ChainID , chainID )
}
}
// Check Height.
// Check Height.
if sh . Commit . Height ( ) != sh . Height {
if sh . Commit . Height != sh . Height {
return fmt . Errorf ( "signedHeader header and commit height mismatch: %v vs %v" ,
return fmt . Errorf ( "signedHeader header and commit height mismatch: %v vs %v" ,
sh . Height , sh . Commit . Height ( ) )
sh . Height , sh . Commit . Height )
}
}
// Check Hash.
// Check Hash.
hhash := sh . Hash ( )
hhash := sh . Hash ( )