From 7deda53b7c104e4bcf78720c71b4c4ab67fc21b4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 11 Dec 2017 18:15:39 +0100 Subject: [PATCH 1/7] Add Timestamp to Proposal for issue #929 Store it as time.Timestamp locally, encode it as RFC3339 with milliseconds before signing the canonical form. --- types/block.go | 2 +- types/canonical_json.go | 2 ++ types/proposal.go | 16 ++++++++++++++-- types/proposal_test.go | 22 ++++++++++++++++------ 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/types/block.go b/types/block.go index 4c91c5fe1..461782fd8 100644 --- a/types/block.go +++ b/types/block.go @@ -420,7 +420,7 @@ func (data *Data) StringIndented(indent string) string { // BlockID defines the unique ID of a block as its Hash and its PartSetHeader type BlockID struct { - Hash data.Bytes `json:"hash"` + Hash data.Bytes `json:"hash,omitempty"` PartsHeader PartSetHeader `json:"parts"` } diff --git a/types/canonical_json.go b/types/canonical_json.go index a2e91164f..be371f242 100644 --- a/types/canonical_json.go +++ b/types/canonical_json.go @@ -22,6 +22,7 @@ type CanonicalJSONProposal struct { POLBlockID CanonicalJSONBlockID `json:"pol_block_id"` POLRound int `json:"pol_round"` Round int `json:"round"` + Timestamp string `json:"timestamp"` } type CanonicalJSONVote struct { @@ -78,6 +79,7 @@ func CanonicalProposal(proposal *Proposal) CanonicalJSONProposal { return CanonicalJSONProposal{ BlockPartsHeader: CanonicalPartSetHeader(proposal.BlockPartsHeader), Height: proposal.Height, + Timestamp: proposal.TimeString(), POLBlockID: CanonicalBlockID(proposal.POLBlockID), POLRound: proposal.POLRound, Round: proposal.Round, diff --git a/types/proposal.go b/types/proposal.go index 93e788961..58dfa0944 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "time" "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" @@ -14,6 +15,9 @@ 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 @@ -22,6 +26,7 @@ var ( type Proposal struct { Height int64 `json:"height"` Round int `json:"round"` + Timestamp time.Time `json:"timestamp"` BlockPartsHeader PartSetHeader `json:"block_parts_header"` POLRound int `json:"pol_round"` // -1 if null. POLBlockID BlockID `json:"pol_block_id"` // zero if null. @@ -34,16 +39,23 @@ func NewProposal(height int64, round int, blockPartsHeader PartSetHeader, polRou return &Proposal{ Height: height, Round: round, + Timestamp: time.Now().UTC(), BlockPartsHeader: blockPartsHeader, POLRound: polRound, POLBlockID: polBlockID, } } +// 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}", p.Height, p.Round, - p.BlockPartsHeader, p.POLRound, p.POLBlockID, p.Signature) + 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()) } // WriteSignBytes writes the Proposal bytes for signing diff --git a/types/proposal_test.go b/types/proposal_test.go index 352ba8de1..c2e62f258 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -2,20 +2,30 @@ package types import ( "testing" + "time" ) -var testProposal = &Proposal{ - Height: 12345, - Round: 23456, - BlockPartsHeader: PartSetHeader{111, []byte("blockparts")}, - POLRound: -1, +var testProposal *Proposal + +func init() { + var stamp, err = time.Parse(TimeFormat, "2018-02-11T07:09:22.765Z") + if err != nil { + panic(err) + } + testProposal = &Proposal{ + Height: 12345, + Round: 23456, + BlockPartsHeader: PartSetHeader{111, []byte("blockparts")}, + POLRound: -1, + Timestamp: stamp, + } } func TestProposalSignable(t *testing.T) { signBytes := SignBytes("test_chain_id", testProposal) signStr := string(signBytes) - expected := `{"chain_id":"test_chain_id","proposal":{"block_parts_header":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_block_id":{},"pol_round":-1,"round":23456}}` + expected := `{"chain_id":"test_chain_id","proposal":{"block_parts_header":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_block_id":{},"pol_round":-1,"round":23456,"timestamp":"2018-02-11T07:09:22.765Z"}}` if signStr != expected { t.Errorf("Got unexpected sign string for Proposal. Expected:\n%v\nGot:\n%v", expected, signStr) } From 599673690c8d99e43cf9e75589a1a14748d49b00 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 11 Dec 2017 18:34:46 +0100 Subject: [PATCH 2/7] 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) From a29c781295fc13fd58c77894e35a3aa3f43029ac Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 11 Dec 2017 18:42:58 +0100 Subject: [PATCH 3/7] Add default timestamp to all instances of *types.Vote --- consensus/common_test.go | 1 + consensus/state.go | 1 + consensus/types/height_vote_set_test.go | 2 ++ lite/helpers.go | 1 + types/priv_validator_test.go | 2 ++ types/vote_set_test.go | 7 +++++++ 6 files changed, 14 insertions(+) diff --git a/consensus/common_test.go b/consensus/common_test.go index 23e56e67e..377f1929f 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -74,6 +74,7 @@ func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartS ValidatorAddress: vs.PrivValidator.GetAddress(), Height: vs.Height, Round: vs.Round, + Timestamp: time.Now().UTC(), Type: voteType, BlockID: types.BlockID{hash, header}, } diff --git a/consensus/state.go b/consensus/state.go index eedc30bc0..68133f53a 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1466,6 +1466,7 @@ func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSet ValidatorIndex: valIndex, Height: cs.Height, Round: cs.Round, + Timestamp: time.Now().UTC(), Type: type_, BlockID: types.BlockID{hash, header}, } diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index e09d1419d..306592aa2 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -2,6 +2,7 @@ package types import ( "testing" + "time" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/types" @@ -54,6 +55,7 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []*types.PrivVal ValidatorIndex: valIndex, Height: height, Round: round, + Timestamp: time.Now().UTC(), Type: types.VoteTypePrecommit, BlockID: types.BlockID{[]byte("fakehash"), types.PartSetHeader{}}, } diff --git a/lite/helpers.go b/lite/helpers.go index 9319c4590..5702203ba 100644 --- a/lite/helpers.go +++ b/lite/helpers.go @@ -97,6 +97,7 @@ func makeVote(header *types.Header, vals *types.ValidatorSet, key crypto.PrivKey ValidatorIndex: idx, Height: header.Height, Round: 1, + Timestamp: time.Now().UTC(), Type: types.VoteTypePrecommit, BlockID: types.BlockID{Hash: header.Hash()}, } diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go index 3b13ed909..4e55fd49b 100644 --- a/types/priv_validator_test.go +++ b/types/priv_validator_test.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -165,6 +166,7 @@ func newVote(addr data.Bytes, idx int, height int64, round int, typ byte, blockI Height: height, Round: round, Type: typ, + Timestamp: time.Now().UTC(), BlockID: blockID, } } diff --git a/types/vote_set_test.go b/types/vote_set_test.go index b093c44fd..d125c5502 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -3,6 +3,7 @@ package types import ( "bytes" "testing" + "time" crypto "github.com/tendermint/go-crypto" cmn "github.com/tendermint/tmlibs/common" @@ -92,6 +93,7 @@ func TestAddVote(t *testing.T) { Height: height, Round: round, Type: VoteTypePrevote, + Timestamp: time.Now().UTC(), BlockID: BlockID{nil, PartSetHeader{}}, } _, err := signAddVote(val0, vote, voteSet) @@ -121,6 +123,7 @@ func Test2_3Majority(t *testing.T) { Height: height, Round: round, Type: VoteTypePrevote, + Timestamp: time.Now().UTC(), BlockID: BlockID{nil, PartSetHeader{}}, } // 6 out of 10 voted for nil. @@ -176,6 +179,7 @@ func Test2_3MajorityRedux(t *testing.T) { ValidatorIndex: -1, // NOTE: must fill in Height: height, Round: round, + Timestamp: time.Now().UTC(), Type: VoteTypePrevote, BlockID: BlockID{blockHash, blockPartsHeader}, } @@ -270,6 +274,7 @@ func TestBadVotes(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, + Timestamp: time.Now().UTC(), Type: VoteTypePrevote, BlockID: BlockID{nil, PartSetHeader{}}, } @@ -331,6 +336,7 @@ func TestConflicts(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, + Timestamp: time.Now().UTC(), Type: VoteTypePrevote, BlockID: BlockID{nil, PartSetHeader{}}, } @@ -459,6 +465,7 @@ func TestMakeCommit(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, + Timestamp: time.Now().UTC(), Type: VoteTypePrecommit, BlockID: BlockID{blockHash, blockPartsHeader}, } From 850310b03487603815706ea22e77281983f93a86 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 11 Dec 2017 19:42:29 +0100 Subject: [PATCH 4/7] Add test to isolate precommit failure types/vote_test.go now checks signature on a serialized and then deserialized vote. Turns out go-wire time encoding doesn't respect timezones, and the signatures don't check out. --- types/vote_test.go | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/types/vote_test.go b/types/vote_test.go index a8ea11bb2..bd8eda347 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -3,15 +3,18 @@ package types import ( "testing" "time" + + "github.com/stretchr/testify/require" + wire "github.com/tendermint/go-wire" ) -func TestVoteSignable(t *testing.T) { +func exampleVote() *Vote { var stamp, err = time.Parse(timeFormat, "2017-12-25T03:00:01.234Z") if err != nil { - t.Fatal(err) + panic(err) } - vote := &Vote{ + return &Vote{ ValidatorAddress: []byte("addr"), ValidatorIndex: 56789, Height: 12345, @@ -26,6 +29,10 @@ func TestVoteSignable(t *testing.T) { }, }, } +} + +func TestVoteSignable(t *testing.T) { + vote := exampleVote() signBytes := SignBytes("test_chain_id", vote) signStr := string(signBytes) @@ -35,3 +42,31 @@ func TestVoteSignable(t *testing.T) { t.Errorf("Got unexpected sign string for Vote. Expected:\n%v\nGot:\n%v", expected, signStr) } } + +func TestVoteVerifySignature(t *testing.T) { + privVal := GenPrivValidatorFS("") + pubKey := privVal.GetPubKey() + + vote := exampleVote() + signBytes := SignBytes("test_chain_id", vote) + + // sign it + signature, err := privVal.Signer.Sign(signBytes) + require.NoError(t, err) + + // verify the same vote + valid := pubKey.VerifyBytes(SignBytes("test_chain_id", vote), signature) + require.True(t, valid) + + // serialize, deserialize and verify again.... + precommit := new(Vote) + bs := wire.BinaryBytes(vote) + err = wire.ReadBinaryBytes(bs, &precommit) + require.NoError(t, err) + + // verify the transmitted vote + newSignBytes := SignBytes("test_chain_id", precommit) + require.Equal(t, string(signBytes), string(newSignBytes)) + valid = pubKey.VerifyBytes(newSignBytes, signature) + require.True(t, valid) +} From c4860f6c2953f93d6dd5d52fc8a3250748033508 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 11 Dec 2017 19:51:00 +0100 Subject: [PATCH 5/7] Force CanonicalTime to UTC fixes issue with vote serialization breaking the signatures --- types/canonical_json.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/types/canonical_json.go b/types/canonical_json.go index 22576247c..7d94d1e0c 100644 --- a/types/canonical_json.go +++ b/types/canonical_json.go @@ -113,5 +113,8 @@ func CanonicalHeartbeat(heartbeat *Heartbeat) CanonicalJSONHeartbeat { } func CanonicalTime(t time.Time) string { - return t.Format(timeFormat) + // note that sending time over go-wire resets it to + // local time, we need to force UTC here, so the + // signatures match + return t.UTC().Format(timeFormat) } From 8576ad58bd0972b86f1482db8063f3a08216fc57 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 11 Dec 2017 20:42:43 +0100 Subject: [PATCH 6/7] Cleanup canonical json --- types/block.go | 2 +- types/canonical_json.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/types/block.go b/types/block.go index 461782fd8..4c91c5fe1 100644 --- a/types/block.go +++ b/types/block.go @@ -420,7 +420,7 @@ func (data *Data) StringIndented(indent string) string { // BlockID defines the unique ID of a block as its Hash and its PartSetHeader type BlockID struct { - Hash data.Bytes `json:"hash,omitempty"` + Hash data.Bytes `json:"hash"` PartsHeader PartSetHeader `json:"parts"` } diff --git a/types/canonical_json.go b/types/canonical_json.go index 7d94d1e0c..41c67c24b 100644 --- a/types/canonical_json.go +++ b/types/canonical_json.go @@ -3,13 +3,14 @@ package types import ( "time" + wire "github.com/tendermint/go-wire" "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" +// timeFormat is used for generating the sigs +const timeFormat = wire.RFC3339Millis type CanonicalJSONBlockID struct { Hash data.Bytes `json:"hash,omitempty"` From 5ffb5f01cc5267be6d7212a9ad1e34fa45e039f4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 12 Dec 2017 12:58:29 +0100 Subject: [PATCH 7/7] Add more tests for Proposal/Vote serialization String() and Proposal valid after serializing. To be safe, but mainly to increase test coverage for the PR --- types/proposal_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ types/vote_test.go | 13 +++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/types/proposal_test.go b/types/proposal_test.go index a45ae873c..0d2af71e2 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -3,6 +3,10 @@ package types import ( "testing" "time" + + "github.com/stretchr/testify/require" + + wire "github.com/tendermint/go-wire" ) var testProposal *Proposal @@ -31,6 +35,42 @@ func TestProposalSignable(t *testing.T) { } } +func TestProposalString(t *testing.T) { + str := testProposal.String() + expected := `Proposal{12345/23456 111:626C6F636B70 (-1,:0:000000000000) {} @ 2018-02-11T07:09:22.765Z}` + if str != expected { + t.Errorf("Got unexpected string for Proposal. Expected:\n%v\nGot:\n%v", expected, str) + } +} + +func TestProposalVerifySignature(t *testing.T) { + privVal := GenPrivValidatorFS("") + pubKey := privVal.GetPubKey() + + prop := NewProposal(4, 2, PartSetHeader{777, []byte("proper")}, 2, BlockID{}) + signBytes := SignBytes("test_chain_id", prop) + + // sign it + signature, err := privVal.Signer.Sign(signBytes) + require.NoError(t, err) + + // verify the same proposal + valid := pubKey.VerifyBytes(SignBytes("test_chain_id", prop), signature) + require.True(t, valid) + + // serialize, deserialize and verify again.... + newProp := new(Proposal) + bs := wire.BinaryBytes(prop) + err = wire.ReadBinaryBytes(bs, &newProp) + require.NoError(t, err) + + // verify the transmitted proposal + newSignBytes := SignBytes("test_chain_id", newProp) + require.Equal(t, string(signBytes), string(newSignBytes)) + valid = pubKey.VerifyBytes(newSignBytes, signature) + require.True(t, valid) +} + func BenchmarkProposalWriteSignBytes(b *testing.B) { for i := 0; i < b.N; i++ { SignBytes("test_chain_id", testProposal) diff --git a/types/vote_test.go b/types/vote_test.go index bd8eda347..032cc9472 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/stretchr/testify/require" + wire "github.com/tendermint/go-wire" ) @@ -18,7 +19,7 @@ func exampleVote() *Vote { ValidatorAddress: []byte("addr"), ValidatorIndex: 56789, Height: 12345, - Round: 23456, + Round: 2, Timestamp: stamp, Type: byte(2), BlockID: BlockID{ @@ -36,13 +37,21 @@ 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,"timestamp":"2017-12-25T03:00:01.234Z","type":2}}` + expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":2,"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) } } +func TestVoteString(t *testing.T) { + str := exampleVote().String() + expected := `Vote{56789:616464720000 12345/02/2(Precommit) 686173680000 {} @ 2017-12-25T03:00:01.234Z}` + if str != expected { + t.Errorf("Got unexpected string for Proposal. Expected:\n%v\nGot:\n%v", expected, str) + } +} + func TestVoteVerifySignature(t *testing.T) { privVal := GenPrivValidatorFS("") pubKey := privVal.GetPubKey()