From 599673690c8d99e43cf9e75589a1a14748d49b00 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 11 Dec 2017 18:34:46 +0100 Subject: [PATCH] Add timestamp to vote canonical encoding --- types/canonical_json.go | 29 ++++++++++++++++++++--------- types/proposal.go | 10 +--------- types/proposal_test.go | 2 +- types/vote.go | 7 +++++-- types/vote_test.go | 9 ++++++++- 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/types/canonical_json.go b/types/canonical_json.go index be371f242..22576247c 100644 --- a/types/canonical_json.go +++ b/types/canonical_json.go @@ -1,11 +1,16 @@ package types import ( + "time" + "github.com/tendermint/go-wire/data" ) // canonical json is go-wire's json for structs with fields in alphabetical order +// timeFormat is RFC3339Millis, used for generating the sigs +const timeFormat = "2006-01-02T15:04:05.999Z07:00" + type CanonicalJSONBlockID struct { Hash data.Bytes `json:"hash,omitempty"` PartsHeader CanonicalJSONPartSetHeader `json:"parts,omitempty"` @@ -26,10 +31,11 @@ type CanonicalJSONProposal struct { } type CanonicalJSONVote struct { - BlockID CanonicalJSONBlockID `json:"block_id"` - Height int64 `json:"height"` - Round int `json:"round"` - Type byte `json:"type"` + BlockID CanonicalJSONBlockID `json:"block_id"` + Height int64 `json:"height"` + Round int `json:"round"` + Timestamp string `json:"timestamp"` + Type byte `json:"type"` } type CanonicalJSONHeartbeat struct { @@ -79,7 +85,7 @@ func CanonicalProposal(proposal *Proposal) CanonicalJSONProposal { return CanonicalJSONProposal{ BlockPartsHeader: CanonicalPartSetHeader(proposal.BlockPartsHeader), Height: proposal.Height, - Timestamp: proposal.TimeString(), + Timestamp: CanonicalTime(proposal.Timestamp), POLBlockID: CanonicalBlockID(proposal.POLBlockID), POLRound: proposal.POLRound, Round: proposal.Round, @@ -88,10 +94,11 @@ func CanonicalProposal(proposal *Proposal) CanonicalJSONProposal { func CanonicalVote(vote *Vote) CanonicalJSONVote { return CanonicalJSONVote{ - CanonicalBlockID(vote.BlockID), - vote.Height, - vote.Round, - vote.Type, + BlockID: CanonicalBlockID(vote.BlockID), + Height: vote.Height, + Round: vote.Round, + Timestamp: CanonicalTime(vote.Timestamp), + Type: vote.Type, } } @@ -104,3 +111,7 @@ func CanonicalHeartbeat(heartbeat *Heartbeat) CanonicalJSONHeartbeat { heartbeat.ValidatorIndex, } } + +func CanonicalTime(t time.Time) string { + return t.Format(timeFormat) +} diff --git a/types/proposal.go b/types/proposal.go index 58dfa0944..98600681a 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -15,9 +15,6 @@ var ( ErrInvalidBlockPartHash = errors.New("Error invalid block part hash") ) -// TimeFormat is RFC3339Millis, used for generating the sigs -const TimeFormat = "2006-01-02T15:04:05.999Z07:00" - // Proposal defines a block proposal for the consensus. // It refers to the block only by its PartSetHeader. // It must be signed by the correct proposer for the given Height/Round @@ -46,16 +43,11 @@ func NewProposal(height int64, round int, blockPartsHeader PartSetHeader, polRou } } -// TimeString returns the canonical encoding of timestamp -func (p *Proposal) TimeString() string { - return p.Timestamp.Format(TimeFormat) -} - // String returns a string representation of the Proposal. func (p *Proposal) String() string { return fmt.Sprintf("Proposal{%v/%v %v (%v,%v) %v @ %s}", p.Height, p.Round, p.BlockPartsHeader, p.POLRound, - p.POLBlockID, p.Signature, p.TimeString()) + p.POLBlockID, p.Signature, CanonicalTime(p.Timestamp)) } // WriteSignBytes writes the Proposal bytes for signing diff --git a/types/proposal_test.go b/types/proposal_test.go index c2e62f258..a45ae873c 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -8,7 +8,7 @@ import ( var testProposal *Proposal func init() { - var stamp, err = time.Parse(TimeFormat, "2018-02-11T07:09:22.765Z") + var stamp, err = time.Parse(timeFormat, "2018-02-11T07:09:22.765Z") if err != nil { panic(err) } diff --git a/types/vote.go b/types/vote.go index bb8679f46..aa993e33f 100644 --- a/types/vote.go +++ b/types/vote.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "time" "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" @@ -53,6 +54,7 @@ type Vote struct { ValidatorIndex int `json:"validator_index"` Height int64 `json:"height"` Round int `json:"round"` + Timestamp time.Time `json:"timestamp"` Type byte `json:"type"` BlockID BlockID `json:"block_id"` // zero if vote is nil. Signature crypto.Signature `json:"signature"` @@ -84,8 +86,9 @@ func (vote *Vote) String() string { cmn.PanicSanity("Unknown vote type") } - return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %v}", + return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %v @ %s}", vote.ValidatorIndex, cmn.Fingerprint(vote.ValidatorAddress), vote.Height, vote.Round, vote.Type, typeString, - cmn.Fingerprint(vote.BlockID.Hash), vote.Signature) + cmn.Fingerprint(vote.BlockID.Hash), vote.Signature, + CanonicalTime(vote.Timestamp)) } diff --git a/types/vote_test.go b/types/vote_test.go index 353acfaf5..a8ea11bb2 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -2,14 +2,21 @@ package types import ( "testing" + "time" ) func TestVoteSignable(t *testing.T) { + var stamp, err = time.Parse(timeFormat, "2017-12-25T03:00:01.234Z") + if err != nil { + t.Fatal(err) + } + vote := &Vote{ ValidatorAddress: []byte("addr"), ValidatorIndex: 56789, Height: 12345, Round: 23456, + Timestamp: stamp, Type: byte(2), BlockID: BlockID{ Hash: []byte("hash"), @@ -22,7 +29,7 @@ func TestVoteSignable(t *testing.T) { signBytes := SignBytes("test_chain_id", vote) signStr := string(signBytes) - expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":23456,"type":2}}` + expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":23456,"timestamp":"2017-12-25T03:00:01.234Z","type":2}}` if signStr != expected { // NOTE: when this fails, you probably want to fix up consensus/replay_test too t.Errorf("Got unexpected sign string for Vote. Expected:\n%v\nGot:\n%v", expected, signStr)