diff --git a/types/block.go b/types/block.go index d971b90b6..a19afaac5 100644 --- a/types/block.go +++ b/types/block.go @@ -409,10 +409,9 @@ func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) { if blockID.IsZero() { wire.WriteTo([]byte("null"), w, n, err) } else { - wire.WriteTo([]byte(Fmt(`{"hash":"%X","parts":`, blockID.Hash)), w, n, err) - blockID.PartsHeader.WriteSignBytes(w, n, err) - wire.WriteTo([]byte("}"), w, n, err) + wire.WriteJSON(CanonicalBlockID(blockID), w, n, err) } + } func (blockID BlockID) String() string { diff --git a/types/canonical_json.go b/types/canonical_json.go new file mode 100644 index 000000000..68dd6924c --- /dev/null +++ b/types/canonical_json.go @@ -0,0 +1,77 @@ +package types + +// canonical json is go-wire's json for structs with fields in alphabetical order + +type CanonicalJSONBlockID struct { + Hash []byte `json:"hash,omitempty"` + PartsHeader CanonicalJSONPartSetHeader `json:"parts,omitempty"` +} + +type CanonicalJSONPartSetHeader struct { + Hash []byte `json:"hash"` + Total int `json:"total"` +} + +type CanonicalJSONProposal struct { + BlockPartsHeader CanonicalJSONPartSetHeader `json:"block_parts_header"` + Height int `json:"height"` + POLBlockID CanonicalJSONBlockID `json:"pol_block_id"` + POLRound int `json:"pol_round"` + Round int `json:"round"` +} + +type CanonicalJSONVote struct { + BlockID CanonicalJSONBlockID `json:"block_id"` + Height int `json:"height"` + Round int `json:"round"` + Type byte `json:"type"` +} + +//------------------------------------ +// Messages including a "chain id" can only be applied to one chain, hence "Once" + +type CanonicalJSONOnceProposal struct { + ChainID string `json:"chain_id"` + Proposal CanonicalJSONProposal `json:"proposal"` +} + +type CanonicalJSONOnceVote struct { + ChainID string `json:"chain_id"` + Vote CanonicalJSONVote `json:"vote"` +} + +//----------------------------------- +// Canonicalize the structs + +func CanonicalBlockID(blockID BlockID) CanonicalJSONBlockID { + return CanonicalJSONBlockID{ + Hash: blockID.Hash, + PartsHeader: CanonicalPartSetHeader(blockID.PartsHeader), + } +} + +func CanonicalPartSetHeader(psh PartSetHeader) CanonicalJSONPartSetHeader { + return CanonicalJSONPartSetHeader{ + psh.Hash, + psh.Total, + } +} + +func CanonicalProposal(proposal *Proposal) CanonicalJSONProposal { + return CanonicalJSONProposal{ + BlockPartsHeader: CanonicalPartSetHeader(proposal.BlockPartsHeader), + Height: proposal.Height, + POLBlockID: CanonicalBlockID(proposal.POLBlockID), + POLRound: proposal.POLRound, + Round: proposal.Round, + } +} + +func CanonicalVote(vote *Vote) CanonicalJSONVote { + return CanonicalJSONVote{ + CanonicalBlockID(vote.BlockID), + vote.Height, + vote.Round, + vote.Type, + } +} diff --git a/types/part_set.go b/types/part_set.go index f132c5fe6..c21524405 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -74,7 +74,7 @@ func (psh PartSetHeader) Equals(other PartSetHeader) bool { } func (psh PartSetHeader) WriteSignBytes(w io.Writer, n *int, err *error) { - wire.WriteTo([]byte(Fmt(`{"hash":"%X","total":%v}`, psh.Hash, psh.Total)), w, n, err) + wire.WriteJSON(CanonicalPartSetHeader(psh), w, n, err) } //------------------------------------- diff --git a/types/proposal.go b/types/proposal.go index 85ac5f061..9852011f3 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -5,7 +5,7 @@ import ( "fmt" "io" - . "github.com/tendermint/go-common" + //. "github.com/tendermint/go-common" "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) @@ -16,12 +16,12 @@ var ( ) type Proposal struct { - Height int `json:"height"` - Round int `json:"round"` - BlockPartsHeader PartSetHeader `json:"block_parts_header"` - POLRound int `json:"pol_round"` // -1 if null. - POLBlockID BlockID `json:"pol_block_id"` // zero if null. - Signature crypto.SignatureEd25519 `json:"signature"` + Height int `json:"height"` + Round int `json:"round"` + BlockPartsHeader PartSetHeader `json:"block_parts_header"` + POLRound int `json:"pol_round"` // -1 if null. + POLBlockID BlockID `json:"pol_block_id"` // zero if null. + Signature crypto.Signature `json:"signature"` } // polRound: -1 if no polRound. @@ -41,11 +41,8 @@ func (p *Proposal) String() string { } func (p *Proposal) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { - wire.WriteTo([]byte(Fmt(`{"chain_id":"%s"`, chainID)), w, n, err) - wire.WriteTo([]byte(`,"proposal":{"block_parts_header":`), w, n, err) - p.BlockPartsHeader.WriteSignBytes(w, n, err) - wire.WriteTo([]byte(Fmt(`,"height":%v,"pol_block_id":`, p.Height)), w, n, err) - p.POLBlockID.WriteSignBytes(w, n, err) - wire.WriteTo([]byte(Fmt(`,"pol_round":%v`, p.POLRound)), w, n, err) - wire.WriteTo([]byte(Fmt(`,"round":%v}}`, p.Round)), w, n, err) + wire.WriteJSON(CanonicalJSONOnceProposal{ + ChainID: chainID, + Proposal: CanonicalProposal(p), + }, w, n, err) } diff --git a/types/proposal_test.go b/types/proposal_test.go index 1d8c62051..622236b60 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -4,18 +4,43 @@ import ( "testing" ) +var testProposal = &Proposal{ + Height: 12345, + Round: 23456, + BlockPartsHeader: PartSetHeader{111, []byte("blockparts")}, + POLRound: -1, +} + func TestProposalSignable(t *testing.T) { - proposal := &Proposal{ - Height: 12345, - Round: 23456, - BlockPartsHeader: PartSetHeader{111, []byte("blockparts")}, - POLRound: -1, - } - signBytes := SignBytes("test_chain_id", proposal) + 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":null,"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}}` if signStr != expected { t.Errorf("Got unexpected sign string for Proposal. Expected:\n%v\nGot:\n%v", expected, signStr) } } + +func BenchmarkProposalWriteSignBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + SignBytes("test_chain_id", testProposal) + } +} + +func BenchmarkProposalSign(b *testing.B) { + privVal := GenPrivValidator() + for i := 0; i < b.N; i++ { + privVal.Sign(SignBytes("test_chain_id", testProposal)) + } +} + +func BenchmarkProposalVerifySignature(b *testing.B) { + signBytes := SignBytes("test_chain_id", testProposal) + privVal := GenPrivValidator() + signature := privVal.Sign(signBytes) + pubKey := privVal.PubKey + + for i := 0; i < b.N; i++ { + pubKey.VerifyBytes(SignBytes("test_chain_id", testProposal), signature) + } +} diff --git a/types/vote.go b/types/vote.go index 92d99f485..2a30da0ce 100644 --- a/types/vote.go +++ b/types/vote.go @@ -57,10 +57,10 @@ type Vote struct { } func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { - wire.WriteTo([]byte(Fmt(`{"chain_id":"%s"`, chainID)), w, n, err) - wire.WriteTo([]byte(`,"vote":{"block_id":`), w, n, err) - vote.BlockID.WriteSignBytes(w, n, err) - wire.WriteTo([]byte(Fmt(`,"height":%v,"round":%v,"type":%v}}`, vote.Height, vote.Round, vote.Type)), w, n, err) + wire.WriteJSON(CanonicalJSONOnceVote{ + chainID, + CanonicalVote(vote), + }, w, n, err) } func (vote *Vote) Copy() *Vote { diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 19523a825..48ccb0b1f 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -92,7 +92,10 @@ func TestAddVote(t *testing.T) { Type: VoteTypePrevote, BlockID: BlockID{nil, PartSetHeader{}}, } - signAddVote(val0, vote, voteSet) + _, err := signAddVote(val0, vote, voteSet) + if err != nil { + t.Error(err) + } if voteSet.GetByAddress(val0.Address) == nil { t.Errorf("Expected GetByAddress(val0.Address) to be present")