Browse Source

Fix proposal sign bytes. Start tracking blockID in POL

pull/314/head
Jae Kwon 8 years ago
committed by Ethan Buchman
parent
commit
655d829314
9 changed files with 67 additions and 23 deletions
  1. +2
    -1
      consensus/common_test.go
  2. +7
    -5
      consensus/height_vote_set.go
  3. +2
    -2
      consensus/reactor.go
  4. +5
    -3
      consensus/state.go
  5. +3
    -2
      consensus/state_test.go
  6. +7
    -3
      types/block.go
  7. +9
    -5
      types/proposal.go
  8. +2
    -2
      types/proposal_test.go
  9. +30
    -0
      types/vote_test.go

+ 2
- 1
consensus/common_test.go View File

@ -71,7 +71,8 @@ func decideProposal(cs1 *ConsensusState, vs *validatorStub, height, round int) (
} }
// Make proposal // Make proposal
proposal = types.NewProposal(height, round, blockParts.Header(), cs1.Votes.POLRound())
polRound, polBlockID := cs1.Votes.POLInfo()
proposal = types.NewProposal(height, round, blockParts.Header(), polRound, polBlockID)
if err := vs.SignProposal(config.GetString("chain_id"), proposal); err != nil { if err := vs.SignProposal(config.GetString("chain_id"), proposal); err != nil {
panic(err) panic(err)
} }


+ 7
- 5
consensus/height_vote_set.go View File

@ -133,17 +133,19 @@ func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet {
return hvs.getVoteSet(round, types.VoteTypePrecommit) return hvs.getVoteSet(round, types.VoteTypePrecommit)
} }
// Last round that has +2/3 prevotes for a particular block or nil.
// Last round and blockID that has +2/3 prevotes for a particular block or nil.
// Returns -1 if no such round exists. // Returns -1 if no such round exists.
func (hvs *HeightVoteSet) POLRound() int {
func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
hvs.mtx.Lock() hvs.mtx.Lock()
defer hvs.mtx.Unlock() defer hvs.mtx.Unlock()
for r := hvs.round; r >= 0; r-- { for r := hvs.round; r >= 0; r-- {
if hvs.getVoteSet(r, types.VoteTypePrevote).HasTwoThirdsMajority() {
return r
rvs := hvs.getVoteSet(r, types.VoteTypePrevote)
polBlockID, ok := rvs.TwoThirdsMajority()
if ok {
return r, polBlockID
} }
} }
return -1
return -1, types.BlockID{}
} }
func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet { func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {


+ 2
- 2
consensus/reactor.go View File

@ -391,13 +391,13 @@ OUTER_LOOP:
// Send Proposal && ProposalPOL BitArray? // Send Proposal && ProposalPOL BitArray?
if rs.Proposal != nil && !prs.Proposal { if rs.Proposal != nil && !prs.Proposal {
// Proposal
// Proposal: share the proposal metadata with peer.
{ {
msg := &ProposalMessage{Proposal: rs.Proposal} msg := &ProposalMessage{Proposal: rs.Proposal}
peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
ps.SetHasProposal(rs.Proposal) ps.SetHasProposal(rs.Proposal)
} }
// ProposalPOL.
// ProposalPOL: lets peer know which POL votes we have so far.
// Peer must receive ProposalMessage first. // Peer must receive ProposalMessage first.
// rs.Proposal was validated, so rs.Proposal.POLRound <= rs.Round, // rs.Proposal was validated, so rs.Proposal.POLRound <= rs.Round,
// so we definitely have rs.Votes.Prevotes(rs.Proposal.POLRound). // so we definitely have rs.Votes.Prevotes(rs.Proposal.POLRound).


+ 5
- 3
consensus/state.go View File

@ -853,7 +853,8 @@ func (cs *ConsensusState) decideProposal(height, round int) {
} }
// Make proposal // Make proposal
proposal := types.NewProposal(height, round, blockParts.Header(), cs.Votes.POLRound())
polRound, polBlockID := cs.Votes.POLInfo()
proposal := types.NewProposal(height, round, blockParts.Header(), polRound, polBlockID)
err := cs.privValidator.SignProposal(cs.state.ChainID, proposal) err := cs.privValidator.SignProposal(cs.state.ChainID, proposal)
if err == nil { if err == nil {
// Set fields // Set fields
@ -1064,8 +1065,9 @@ func (cs *ConsensusState) enterPrecommit(height int, round int) {
types.FireEventPolka(cs.evsw, cs.RoundStateEvent()) types.FireEventPolka(cs.evsw, cs.RoundStateEvent())
// the latest POLRound should be this round // the latest POLRound should be this round
if cs.Votes.POLRound() < round {
PanicSanity(Fmt("This POLRound should be %v but got %", round, cs.Votes.POLRound()))
polRound, _ := cs.Votes.POLInfo()
if polRound < round {
PanicSanity(Fmt("This POLRound should be %v but got %", round, polRound))
} }
// +2/3 prevoted nil. Unlock and precommit nil. // +2/3 prevoted nil. Unlock and precommit nil.


+ 3
- 2
consensus/state_test.go View File

@ -198,7 +198,7 @@ func TestBadProposal(t *testing.T) {
stateHash[0] = byte((stateHash[0] + 1) % 255) stateHash[0] = byte((stateHash[0] + 1) % 255)
propBlock.AppHash = stateHash propBlock.AppHash = stateHash
propBlockParts := propBlock.MakePartSet() propBlockParts := propBlock.MakePartSet()
proposal := types.NewProposal(vs2.Height, round, propBlockParts.Header(), -1)
proposal := types.NewProposal(vs2.Height, round, propBlockParts.Header(), -1, types.BlockID{})
if err := vs2.SignProposal(config.GetString("chain_id"), proposal); err != nil { if err := vs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
t.Fatal("failed to sign bad proposal", err) t.Fatal("failed to sign bad proposal", err)
} }
@ -832,6 +832,7 @@ func TestLockPOLSafety2(t *testing.T) {
prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1) prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
propBlockHash1 := propBlock1.Hash() propBlockHash1 := propBlock1.Hash()
propBlockParts1 := propBlock1.MakePartSet() propBlockParts1 := propBlock1.MakePartSet()
propBlockID1 := types.BlockID{propBlockHash1, propBlockParts1.Header()}
incrementRound(vs2, vs3, vs4) incrementRound(vs2, vs3, vs4)
@ -864,7 +865,7 @@ func TestLockPOLSafety2(t *testing.T) {
<-timeoutWaitCh <-timeoutWaitCh
// in round 2 we see the polkad block from round 0 // in round 2 we see the polkad block from round 0
newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0)
newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0, propBlockID1)
if err := vs3.SignProposal(config.GetString("chain_id"), newProp); err != nil { if err := vs3.SignProposal(config.GetString("chain_id"), newProp); err != nil {
t.Fatal(err) t.Fatal(err)
} }


+ 7
- 3
types/block.go View File

@ -376,9 +376,13 @@ func (blockID BlockID) Key() string {
} }
func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) { func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) {
wire.WriteTo([]byte(Fmt(`{"hash":"%X","parts":`, blockID.Hash)), w, n, err)
blockID.PartsHeader.WriteSignBytes(w, n, err)
wire.WriteTo([]byte("}"), w, n, err)
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)
}
} }
func (blockID BlockID) String() string { func (blockID BlockID) String() string {


+ 9
- 5
types/proposal.go View File

@ -19,29 +19,33 @@ 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.
Signature crypto.SignatureEd25519 `json:"signature"` Signature crypto.SignatureEd25519 `json:"signature"`
} }
// polRound: -1 if no polRound. // polRound: -1 if no polRound.
func NewProposal(height int, round int, blockPartsHeader PartSetHeader, polRound int) *Proposal {
func NewProposal(height int, round int, blockPartsHeader PartSetHeader, polRound int, polBlockID BlockID) *Proposal {
return &Proposal{ return &Proposal{
Height: height, Height: height,
Round: round, Round: round,
BlockPartsHeader: blockPartsHeader, BlockPartsHeader: blockPartsHeader,
POLRound: polRound, POLRound: polRound,
POLBlockID: polBlockID,
} }
} }
func (p *Proposal) String() string { func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v %v %v %v}", p.Height, p.Round,
p.BlockPartsHeader, p.POLRound, p.Signature)
return fmt.Sprintf("Proposal{%v/%v %v (%v,%v) %v}", p.Height, p.Round,
p.BlockPartsHeader, p.POLRound, p.POLBlockID, p.Signature)
} }
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.WriteTo([]byte(Fmt(`{"chain_id":"%s"`, chainID)), w, n, err)
wire.WriteTo([]byte(`,"proposal":{"block_parts_header":`), w, n, err) wire.WriteTo([]byte(`,"proposal":{"block_parts_header":`), w, n, err)
p.BlockPartsHeader.WriteSignBytes(w, n, err) p.BlockPartsHeader.WriteSignBytes(w, n, err)
wire.WriteTo([]byte(Fmt(`,"height":%v,"pol_round":%v`, p.Height, p.POLRound)), 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.WriteTo([]byte(Fmt(`,"round":%v}}`, p.Round)), w, n, err)
} }

+ 2
- 2
types/proposal_test.go View File

@ -14,8 +14,8 @@ func TestProposalSignable(t *testing.T) {
signBytes := SignBytes("test_chain_id", proposal) 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_round":-1,"round":23456}}`
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}}`
if signStr != expected { if signStr != expected {
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
t.Errorf("Got unexpected sign string for Proposal. Expected:\n%v\nGot:\n%v", expected, signStr)
} }
} }

+ 30
- 0
types/vote_test.go View File

@ -0,0 +1,30 @@
package types
import (
"testing"
)
func TestVoteSignable(t *testing.T) {
vote := &Vote{
ValidatorAddress: []byte("addr"),
ValidatorIndex: 56789,
Height: 12345,
Round: 23456,
Type: byte(2),
BlockID: BlockID{
Hash: []byte("hash"),
PartsHeader: PartSetHeader{
Total: 1000000,
Hash: []byte("parts_hash"),
},
},
}
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}}`
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)
}
}

Loading…
Cancel
Save