Add Timestamp to Proposal for issue #929

Store it as time.Timestamp locally, encode it as RFC3339 with milliseconds
before signing the canonical form.
This commit is contained in:
Ethan Frey
2017-12-11 18:15:39 +01:00
parent 5ecae52bf1
commit 7deda53b7c
4 changed files with 33 additions and 9 deletions

View File

@ -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 // BlockID defines the unique ID of a block as its Hash and its PartSetHeader
type BlockID struct { type BlockID struct {
Hash data.Bytes `json:"hash"` Hash data.Bytes `json:"hash,omitempty"`
PartsHeader PartSetHeader `json:"parts"` PartsHeader PartSetHeader `json:"parts"`
} }

View File

@ -22,6 +22,7 @@ type CanonicalJSONProposal struct {
POLBlockID CanonicalJSONBlockID `json:"pol_block_id"` POLBlockID CanonicalJSONBlockID `json:"pol_block_id"`
POLRound int `json:"pol_round"` POLRound int `json:"pol_round"`
Round int `json:"round"` Round int `json:"round"`
Timestamp string `json:"timestamp"`
} }
type CanonicalJSONVote struct { type CanonicalJSONVote struct {
@ -78,6 +79,7 @@ func CanonicalProposal(proposal *Proposal) CanonicalJSONProposal {
return CanonicalJSONProposal{ return CanonicalJSONProposal{
BlockPartsHeader: CanonicalPartSetHeader(proposal.BlockPartsHeader), BlockPartsHeader: CanonicalPartSetHeader(proposal.BlockPartsHeader),
Height: proposal.Height, Height: proposal.Height,
Timestamp: proposal.TimeString(),
POLBlockID: CanonicalBlockID(proposal.POLBlockID), POLBlockID: CanonicalBlockID(proposal.POLBlockID),
POLRound: proposal.POLRound, POLRound: proposal.POLRound,
Round: proposal.Round, Round: proposal.Round,

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"time"
"github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire" "github.com/tendermint/go-wire"
@ -14,6 +15,9 @@ var (
ErrInvalidBlockPartHash = errors.New("Error invalid block part hash") 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. // Proposal defines a block proposal for the consensus.
// It refers to the block only by its PartSetHeader. // It refers to the block only by its PartSetHeader.
// It must be signed by the correct proposer for the given Height/Round // It must be signed by the correct proposer for the given Height/Round
@ -22,6 +26,7 @@ var (
type Proposal struct { type Proposal struct {
Height int64 `json:"height"` Height int64 `json:"height"`
Round int `json:"round"` Round int `json:"round"`
Timestamp time.Time `json:"timestamp"`
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.
@ -34,16 +39,23 @@ func NewProposal(height int64, round int, blockPartsHeader PartSetHeader, polRou
return &Proposal{ return &Proposal{
Height: height, Height: height,
Round: round, Round: round,
Timestamp: time.Now().UTC(),
BlockPartsHeader: blockPartsHeader, BlockPartsHeader: blockPartsHeader,
POLRound: polRound, POLRound: polRound,
POLBlockID: polBlockID, 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. // String returns a string representation of the Proposal.
func (p *Proposal) String() string { func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v %v (%v,%v) %v}", p.Height, p.Round, return fmt.Sprintf("Proposal{%v/%v %v (%v,%v) %v @ %s}",
p.BlockPartsHeader, p.POLRound, p.POLBlockID, p.Signature) p.Height, p.Round, p.BlockPartsHeader, p.POLRound,
p.POLBlockID, p.Signature, p.TimeString())
} }
// WriteSignBytes writes the Proposal bytes for signing // WriteSignBytes writes the Proposal bytes for signing

View File

@ -2,20 +2,30 @@ package types
import ( import (
"testing" "testing"
"time"
) )
var testProposal = &Proposal{ var testProposal *Proposal
Height: 12345,
Round: 23456, func init() {
BlockPartsHeader: PartSetHeader{111, []byte("blockparts")}, var stamp, err = time.Parse(TimeFormat, "2018-02-11T07:09:22.765Z")
POLRound: -1, 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) { func TestProposalSignable(t *testing.T) {
signBytes := SignBytes("test_chain_id", testProposal) signBytes := SignBytes("test_chain_id", testProposal)
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":{},"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 { 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)
} }