Merge pull request #325 from tendermint/sign_bytes

Sign bytes
This commit is contained in:
Ethan Buchman 2016-12-17 21:48:19 -05:00 committed by GitHub
commit f33cc3fb3b
7 changed files with 132 additions and 31 deletions

View File

@ -409,10 +409,9 @@ func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) {
if blockID.IsZero() { if blockID.IsZero() {
wire.WriteTo([]byte("null"), w, n, err) wire.WriteTo([]byte("null"), w, n, err)
} else { } else {
wire.WriteTo([]byte(Fmt(`{"hash":"%X","parts":`, blockID.Hash)), w, n, err) wire.WriteJSON(CanonicalBlockID(blockID), w, n, err)
blockID.PartsHeader.WriteSignBytes(w, n, err)
wire.WriteTo([]byte("}"), w, n, err)
} }
} }
func (blockID BlockID) String() string { func (blockID BlockID) String() string {

77
types/canonical_json.go Normal file
View File

@ -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,
}
}

View File

@ -74,7 +74,7 @@ func (psh PartSetHeader) Equals(other PartSetHeader) bool {
} }
func (psh PartSetHeader) WriteSignBytes(w io.Writer, n *int, err *error) { 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)
} }
//------------------------------------- //-------------------------------------

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
. "github.com/tendermint/go-common" //. "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire" "github.com/tendermint/go-wire"
) )
@ -16,12 +16,12 @@ var (
) )
type Proposal struct { type Proposal struct {
Height int `json:"height"` Height int `json:"height"`
Round int `json:"round"` Round int `json:"round"`
BlockPartsHeader PartSetHeader `json:"block_parts_header"` BlockPartsHeader PartSetHeader `json:"block_parts_header"`
POLRound int `json:"pol_round"` // -1 if null. POLRound int `json:"pol_round"` // -1 if null.
POLBlockID BlockID `json:"pol_block_id"` // zero if null. POLBlockID BlockID `json:"pol_block_id"` // zero if null.
Signature crypto.SignatureEd25519 `json:"signature"` Signature crypto.Signature `json:"signature"`
} }
// polRound: -1 if no polRound. // 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) { 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.WriteJSON(CanonicalJSONOnceProposal{
wire.WriteTo([]byte(`,"proposal":{"block_parts_header":`), w, n, err) ChainID: chainID,
p.BlockPartsHeader.WriteSignBytes(w, n, err) Proposal: CanonicalProposal(p),
wire.WriteTo([]byte(Fmt(`,"height":%v,"pol_block_id":`, p.Height)), w, n, err) }, 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)
} }

View File

@ -4,18 +4,43 @@ import (
"testing" "testing"
) )
var testProposal = &Proposal{
Height: 12345,
Round: 23456,
BlockPartsHeader: PartSetHeader{111, []byte("blockparts")},
POLRound: -1,
}
func TestProposalSignable(t *testing.T) { func TestProposalSignable(t *testing.T) {
proposal := &Proposal{ signBytes := SignBytes("test_chain_id", testProposal)
Height: 12345,
Round: 23456,
BlockPartsHeader: PartSetHeader{111, []byte("blockparts")},
POLRound: -1,
}
signBytes := SignBytes("test_chain_id", proposal)
signStr := string(signBytes) 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 { if signStr != expected {
t.Errorf("Got unexpected sign string for Proposal. Expected:\n%v\nGot:\n%v", expected, signStr) 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)
}
}

View File

@ -57,10 +57,10 @@ type Vote struct {
} }
func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) { 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.WriteJSON(CanonicalJSONOnceVote{
wire.WriteTo([]byte(`,"vote":{"block_id":`), w, n, err) chainID,
vote.BlockID.WriteSignBytes(w, n, err) CanonicalVote(vote),
wire.WriteTo([]byte(Fmt(`,"height":%v,"round":%v,"type":%v}}`, vote.Height, vote.Round, vote.Type)), w, n, err) }, w, n, err)
} }
func (vote *Vote) Copy() *Vote { func (vote *Vote) Copy() *Vote {

View File

@ -92,7 +92,10 @@ func TestAddVote(t *testing.T) {
Type: VoteTypePrevote, Type: VoteTypePrevote,
BlockID: BlockID{nil, PartSetHeader{}}, 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 { if voteSet.GetByAddress(val0.Address) == nil {
t.Errorf("Expected GetByAddress(val0.Address) to be present") t.Errorf("Expected GetByAddress(val0.Address) to be present")