Skip to content
This repository has been archived by the owner on Aug 24, 2022. It is now read-only.

Separate timestamp from vote signature #216

Merged
Merged
Show file tree
Hide file tree
Changes from 2 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
58 changes: 24 additions & 34 deletions privval/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,13 @@ func (pvKey FilePVKey) Save() {

// FilePVLastSignState stores the mutable part of PrivValidator.
type FilePVLastSignState struct {
Height int64 `json:"height"`
Round int `json:"round"`
Step int8 `json:"step"`
Signature []byte `json:"signature,omitempty"`
SignBytes tmbytes.HexBytes `json:"signbytes,omitempty"`
Height int64 `json:"height"`
Round int `json:"round"`
Step int8 `json:"step"`
Signature []byte `json:"signature,omitempty"`
SignBytes tmbytes.HexBytes `json:"signbytes,omitempty"`
TimestampSignature []byte `json:"timestamp_signature,omitempty"`
Timestamp time.Time `json:"timestamp,omitempty"`

filePath string
}
Expand Down Expand Up @@ -309,6 +311,8 @@ func (pv *FilePV) Reset() {
pv.LastSignState.Step = 0
pv.LastSignState.Signature = sig
pv.LastSignState.SignBytes = nil
pv.LastSignState.Timestamp = time.Time{}
pv.LastSignState.TimestampSignature = nil
pv.Save()
}

Expand Down Expand Up @@ -346,11 +350,13 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
// If they only differ by timestamp, use last timestamp and signature
// Otherwise, return error
if sameHRS {
if bytes.Equal(signBytes, lss.SignBytes) {
if bytes.Equal(signBytes, lss.SignBytes) && vote.Timestamp.Equal(lss.Timestamp) {
vote.Signature = lss.Signature
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok {
vote.Timestamp = timestamp
vote.TimestampSignature = lss.TimestampSignature
} else if bytes.Equal(signBytes, lss.SignBytes) && !vote.Timestamp.Equal(lss.Timestamp) {
vote.Signature = lss.Signature
vote.Timestamp = lss.Timestamp
vote.TimestampSignature = lss.TimestampSignature
} else {
err = fmt.Errorf("conflicting data")
}
Expand All @@ -362,8 +368,13 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error {
if err != nil {
return err
}
pv.saveSigned(height, round, step, signBytes, sig)
timestampSig, err := pv.Key.PrivKey.Sign(vote.SignTimestamp(chainID))
if err != nil {
return err
}
pv.saveSigned(height, round, step, signBytes, sig, vote.Timestamp, timestampSig)
vote.Signature = sig
vote.TimestampSignature = timestampSig
return nil
}

Expand Down Expand Up @@ -404,48 +415,27 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error {
if err != nil {
return err
}
pv.saveSigned(height, round, step, signBytes, sig)
pv.saveSigned(height, round, step, signBytes, sig, proposal.Timestamp, nil)
proposal.Signature = sig
return nil
}

// Persist height/round/step and signature
func (pv *FilePV) saveSigned(height int64, round int, step int8,
signBytes []byte, sig []byte) {
signBytes []byte, sig []byte, timestamp time.Time, timestampSig []byte) {

pv.LastSignState.Height = height
pv.LastSignState.Round = round
pv.LastSignState.Step = step
pv.LastSignState.Signature = sig
pv.LastSignState.SignBytes = signBytes
pv.LastSignState.Timestamp = timestamp
pv.LastSignState.TimestampSignature = timestampSig
pv.LastSignState.Save()
}

//-----------------------------------------------------------------------------------------

// returns the timestamp from the lastSignBytes.
// returns true if the only difference in the votes is their timestamp.
func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
var lastVote, newVote types.CanonicalVote
if err := cdc.UnmarshalBinaryLengthPrefixed(lastSignBytes, &lastVote); err != nil {
panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err))
}
if err := cdc.UnmarshalBinaryLengthPrefixed(newSignBytes, &newVote); err != nil {
panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err))
}

lastTime := lastVote.Timestamp

// set the times to the same value and check equality
now := tmtime.Now()
lastVote.Timestamp = now
newVote.Timestamp = now
lastVoteBytes, _ := cdc.MarshalJSON(lastVote)
newVoteBytes, _ := cdc.MarshalJSON(newVote)

return lastTime, bytes.Equal(newVoteBytes, lastVoteBytes)
}

// returns the timestamp from the lastSignBytes.
// returns true if the only difference in the proposals is their timestamp
func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
Expand Down
179 changes: 98 additions & 81 deletions proto/types/types.pb.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions proto/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ message Vote {
bytes validator_address = 6;
int64 validator_index = 7;
bytes signature = 8;
bytes timestamp_signature = 9;
}

// Commit contains the evidence that a block was committed by a set of validators.
Expand All @@ -120,6 +121,7 @@ message CommitSig {
google.protobuf.Timestamp timestamp = 3
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
bytes signature = 4;
bytes timestamp_signature = 5;
}

message Proposal {
Expand Down
5 changes: 4 additions & 1 deletion rpc/client/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,13 @@ func deepcpVote(vote *types.Vote) (res *types.Vote) {
Hash: make([]byte, len(vote.BlockID.Hash)),
PartsHeader: vote.BlockID.PartsHeader,
},
Signature: make([]byte, len(vote.Signature)),
Signature: make([]byte, len(vote.Signature)),
TimestampSignature: make([]byte, len(vote.TimestampSignature)),
}
copy(res.ValidatorAddress, vote.ValidatorAddress)
copy(res.BlockID.Hash, vote.BlockID.Hash)
copy(res.Signature, vote.Signature)
copy(res.TimestampSignature, vote.TimestampSignature)
return
}

Expand Down Expand Up @@ -612,6 +614,7 @@ func makeEvidences(

var err error
vote.Signature, err = val.Key.PrivKey.Sign(vote.SignBytes(chainID))
vote.TimestampSignature, err = val.Key.PrivKey.Sign(vote.SignTimestamp(chainID))
require.NoError(t, err)

vote2 := deepcpVote(vote)
Expand Down
8 changes: 4 additions & 4 deletions state/tx_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ func TestTxFilter(t *testing.T) {
isErr bool
}{
{types.Tx(tmrand.Bytes(250)), false},
{types.Tx(tmrand.Bytes(1811)), false},
{types.Tx(tmrand.Bytes(1831)), false},
{types.Tx(tmrand.Bytes(1838)), true},
{types.Tx(tmrand.Bytes(1839)), true},
{types.Tx(tmrand.Bytes(1755)), false},
{types.Tx(tmrand.Bytes(1765)), false},
{types.Tx(tmrand.Bytes(1766)), true},
{types.Tx(tmrand.Bytes(1767)), true},
{types.Tx(tmrand.Bytes(3000)), true},
}

Expand Down
50 changes: 30 additions & 20 deletions types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func MaxDataBytes(maxBytes int64, valsCount, evidenceCount int) int64 {
maxDataBytes := maxBytes -
MaxAminoOverheadForBlock -
MaxHeaderBytes -
int64(valsCount)*MaxVoteBytes -
int64(valsCount)*MaxBlockVoteBytes -
int64(evidenceCount)*MaxEvidenceBytes

if maxDataBytes < 0 {
Expand Down Expand Up @@ -536,10 +536,11 @@ const (

// 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"`
BlockIDFlag BlockIDFlag `json:"block_id_flag"`
ValidatorAddress Address `json:"validator_address"`
Timestamp time.Time `json:"timestamp"`
Signature []byte `json:"signature"`
TimestampSignature []byte `json:"timestamp_signature"`
}

// NewCommitSigForBlock returns new CommitSig with BlockIDFlagCommit.
Expand Down Expand Up @@ -571,11 +572,12 @@ func (cs CommitSig) Absent() bool {
}

func (cs CommitSig) String() string {
return fmt.Sprintf("CommitSig{%X by %X on %v @ %s}",
return fmt.Sprintf("CommitSig{%X by %X on %v @ %s %X}",
tmbytes.Fingerprint(cs.Signature),
tmbytes.Fingerprint(cs.ValidatorAddress),
cs.BlockIDFlag,
CanonicalTime(cs.Timestamp))
CanonicalTime(cs.Timestamp),
cs.TimestampSignature)
}

// BlockID returns the Commit's BlockID if CommitSig indicates signing,
Expand Down Expand Up @@ -630,6 +632,9 @@ func (cs CommitSig) ValidateBasic() error {
if len(cs.Signature) > MaxSignatureSize {
return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
}
if len(cs.TimestampSignature) > MaxSignatureSize {
return fmt.Errorf("timestamp signature is too big (max: %d)", MaxSignatureSize)
}
}

return nil
Expand All @@ -642,10 +647,11 @@ func (cs *CommitSig) ToProto() *tmproto.CommitSig {
}

return &tmproto.CommitSig{
BlockIdFlag: tmproto.BlockIDFlag(cs.BlockIDFlag),
ValidatorAddress: cs.ValidatorAddress,
Timestamp: cs.Timestamp,
Signature: cs.Signature,
BlockIdFlag: tmproto.BlockIDFlag(cs.BlockIDFlag),
ValidatorAddress: cs.ValidatorAddress,
Timestamp: cs.Timestamp,
Signature: cs.Signature,
TimestampSignature: cs.TimestampSignature,
}
}

Expand All @@ -657,6 +663,7 @@ func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error {
cs.ValidatorAddress = csp.ValidatorAddress
cs.Timestamp = csp.Timestamp
cs.Signature = csp.Signature
cs.TimestampSignature = csp.TimestampSignature

return cs.ValidateBasic()
}
Expand Down Expand Up @@ -715,14 +722,15 @@ func CommitToVoteSet(chainID string, commit *Commit, vals *ValidatorSet) *VoteSe
func (commit *Commit) GetVote(valIdx int) *Vote {
commitSig := commit.Signatures[valIdx]
return &Vote{
Type: PrecommitType,
Height: commit.Height,
Round: commit.Round,
BlockID: commitSig.BlockID(commit.BlockID),
Timestamp: commitSig.Timestamp,
ValidatorAddress: commitSig.ValidatorAddress,
ValidatorIndex: valIdx,
Signature: commitSig.Signature,
Type: PrecommitType,
Height: commit.Height,
Round: commit.Round,
BlockID: commitSig.BlockID(commit.BlockID),
Timestamp: commitSig.Timestamp,
ValidatorAddress: commitSig.ValidatorAddress,
ValidatorIndex: valIdx,
Signature: commitSig.Signature,
TimestampSignature: commitSig.TimestampSignature,
}
}

Expand Down Expand Up @@ -815,14 +823,16 @@ func (commit *Commit) ValidateBasic() error {
return nil
}

// Hash returns the hash of the commit
// Hash returns the hash of the commit. Don't include timestamp signature in
// hash as this signature is not included in the block
func (commit *Commit) Hash() tmbytes.HexBytes {
if commit == nil {
return nil
}
if commit.hash == nil {
bs := make([][]byte, len(commit.Signatures))
for i, commitSig := range commit.Signatures {
commitSig.TimestampSignature = nil
bs[i] = cdcEncode(commitSig)
}
commit.hash = merkle.SimpleHashFromByteSlices(bs)
Expand Down
6 changes: 3 additions & 3 deletions types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,9 @@ func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) {
}{
0: {-10, 1, true, 0},
1: {10, 1, true, 0},
2: {961, 1, true, 0},
3: {962, 1, false, 0},
4: {963, 1, false, 1},
2: {1034, 1, true, 0},
3: {1035, 1, false, 0},
4: {1036, 1, false, 1},
}

for i, tc := range testCases {
Expand Down
28 changes: 19 additions & 9 deletions types/canonical.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ type CanonicalProposal struct {
}

type CanonicalVote struct {
Type SignedMsgType // type alias for byte
Height int64 `binary:"fixed64"`
Round int64 `binary:"fixed64"`
BlockID CanonicalBlockID
Type SignedMsgType // type alias for byte
Height int64 `binary:"fixed64"`
Round int64 `binary:"fixed64"`
BlockID CanonicalBlockID
ChainID string
}

type CanonicalTimestamp struct {
Timestamp time.Time
ChainID string
}
Expand Down Expand Up @@ -72,11 +76,17 @@ func CanonicalizeProposal(chainID string, proposal *Proposal) CanonicalProposal

func CanonicalizeVote(chainID string, vote *Vote) CanonicalVote {
return CanonicalVote{
Type: vote.Type,
Height: vote.Height,
Round: int64(vote.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int)
BlockID: CanonicalizeBlockID(vote.BlockID),
Timestamp: vote.Timestamp,
Type: vote.Type,
Height: vote.Height,
Round: int64(vote.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int)
BlockID: CanonicalizeBlockID(vote.BlockID),
ChainID: chainID,
}
}

func CanonicalizeTimestamp(chainID string, timestamp time.Time) CanonicalTimestamp {
return CanonicalTimestamp{
Timestamp: timestamp,
ChainID: chainID,
}
}
Expand Down
8 changes: 7 additions & 1 deletion types/evidence.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

const (
// MaxEvidenceBytes is a maximum size of any evidence (including amino overhead).
MaxEvidenceBytes int64 = 484
MaxEvidenceBytes int64 = 616
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Increased because votes now contain an extra signature

)

// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid.
Expand Down Expand Up @@ -388,9 +388,15 @@ func (dve *DuplicateVoteEvidence) Verify(chainID string, pubKey crypto.PubKey) e
if !pubKey.VerifyBytes(dve.VoteA.SignBytes(chainID), dve.VoteA.Signature) {
return fmt.Errorf("duplicateVoteEvidence Error verifying VoteA: %v", ErrVoteInvalidSignature)
}
if !pubKey.VerifyBytes(dve.VoteA.SignTimestamp(chainID), dve.VoteA.TimestampSignature) {
return fmt.Errorf("duplicateVoteEvidence Error verifying VoteA: %v", ErrVoteInvalidTimestampSignature)
}
if !pubKey.VerifyBytes(dve.VoteB.SignBytes(chainID), dve.VoteB.Signature) {
return fmt.Errorf("duplicateVoteEvidence Error verifying VoteB: %v", ErrVoteInvalidSignature)
}
if !pubKey.VerifyBytes(dve.VoteB.SignTimestamp(chainID), dve.VoteB.TimestampSignature) {
return fmt.Errorf("duplicateVoteEvidence Error verifying VoteB: %v", ErrVoteInvalidTimestampSignature)
}

return nil
}
Expand Down
7 changes: 7 additions & 0 deletions types/priv_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ func (pv MockPV) SignVote(chainID string, vote *Vote) error {
return err
}
vote.Signature = sig
// Sign timestamp
signBytes = vote.SignTimestamp(useChainID)
timestampSig, err := pv.PrivKey.Sign(signBytes)
if err != nil {
return err
}
vote.TimestampSignature = timestampSig
return nil
}

Expand Down
6 changes: 6 additions & 0 deletions types/test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ func MakeBlock(height int64, txs []Tx, lastCommit *Commit, evidence []Evidence)
Evidence: EvidenceData{Evidence: evidence},
LastCommit: lastCommit,
}
// Remove timestamp signatures from commit when making block
if block.LastCommit != nil {
for index := range block.LastCommit.Signatures {
block.LastCommit.Signatures[index].TimestampSignature = nil
}
}
block.fillHeader()
return block
}
Loading