aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2018-10-31 12:51:54 +0800
committerGitHub <noreply@github.com>2018-10-31 12:51:54 +0800
commiteccdddbff92c1588e628f874d73ae557351c76f7 (patch)
tree799e888f2223e53d88391b15f507226208ef349c
parent43a5264814bf318e6bd133fb0df51351f0811ddb (diff)
downloaddexon-consensus-eccdddbff92c1588e628f874d73ae557351c76f7.tar
dexon-consensus-eccdddbff92c1588e628f874d73ae557351c76f7.tar.gz
dexon-consensus-eccdddbff92c1588e628f874d73ae557351c76f7.tar.bz2
dexon-consensus-eccdddbff92c1588e628f874d73ae557351c76f7.tar.lz
dexon-consensus-eccdddbff92c1588e628f874d73ae557351c76f7.tar.xz
dexon-consensus-eccdddbff92c1588e628f874d73ae557351c76f7.tar.zst
dexon-consensus-eccdddbff92c1588e628f874d73ae557351c76f7.zip
core: Add a repeat vote state. (#280)
-rw-r--r--core/agreement-state.go44
-rw-r--r--core/agreement-state_test.go30
-rw-r--r--core/agreement_test.go43
3 files changed, 105 insertions, 12 deletions
diff --git a/core/agreement-state.go b/core/agreement-state.go
index 56c6c27..77195ac 100644
--- a/core/agreement-state.go
+++ b/core/agreement-state.go
@@ -19,7 +19,6 @@ package core
import (
"fmt"
- "math"
"github.com/dexon-foundation/dexon-consensus-core/common"
"github.com/dexon-foundation/dexon-consensus-core/core/types"
@@ -40,6 +39,7 @@ const (
statePreCommit
stateCommit
stateForward
+ stateRepeatVote
)
var nullBlockHash = common.Hash{}
@@ -127,26 +127,54 @@ func (s *commitState) nextState() (agreementState, error) {
} else {
hash = skipBlockHash
}
- s.a.recv.ProposeVote(&types.Vote{
+ vote := &types.Vote{
Type: types.VoteCom,
BlockHash: hash,
Period: s.a.period,
- })
- return newForwardState(s.a), nil
+ }
+ s.a.recv.ProposeVote(vote)
+ return newForwardState(s.a, vote), nil
}
// ----- ForwardState -----
type forwardState struct {
- a *agreementData
+ a *agreementData
+ vote *types.Vote
}
-func newForwardState(a *agreementData) *forwardState {
- return &forwardState{a: a}
+func newForwardState(a *agreementData, vote *types.Vote) *forwardState {
+ return &forwardState{
+ a: a,
+ vote: vote,
+ }
}
func (s *forwardState) state() agreementStateType { return stateForward }
-func (s *forwardState) clocks() int { return math.MaxInt32 }
+func (s *forwardState) clocks() int { return 4 }
func (s *forwardState) nextState() (agreementState, error) {
+ return newRepeatVoteState(s.a, s.vote), nil
+}
+
+// ----- RepeatVoteState -----
+// repeateVoteState is a special state to ensure the assumption in the consensus
+// algorithm that every vote will eventually arrive for all nodes.
+type repeatVoteState struct {
+ a *agreementData
+ vote *types.Vote
+}
+
+func newRepeatVoteState(a *agreementData, vote *types.Vote) *repeatVoteState {
+ return &repeatVoteState{
+ a: a,
+ vote: vote,
+ }
+}
+
+func (s *repeatVoteState) state() agreementStateType { return stateRepeatVote }
+func (s *repeatVoteState) clocks() int { return 4 }
+
+func (s *repeatVoteState) nextState() (agreementState, error) {
+ s.a.recv.ProposeVote(s.vote)
return s, nil
}
diff --git a/core/agreement-state_test.go b/core/agreement-state_test.go
index 429e124..862ac3e 100644
--- a/core/agreement-state_test.go
+++ b/core/agreement-state_test.go
@@ -233,14 +233,36 @@ func (s *AgreementStateTestSuite) TestCommitState() {
func (s *AgreementStateTestSuite) TestForwardState() {
a := s.newAgreement(4)
- state := newForwardState(a.data)
+ vote := &types.Vote{
+ BlockHash: common.NewRandomHash(),
+ }
+ state := newForwardState(a.data, vote)
s.Equal(stateForward, state.state())
- s.True(state.clocks() > 100000)
+ s.Equal(4, state.clocks())
- // nextState() should return instantly without doing anything.
- _, err := state.nextState()
+ newState, err := state.nextState()
s.Require().Nil(err)
s.Require().Len(s.voteChan, 0)
+ s.Equal(stateRepeatVote, newState.state())
+}
+
+func (s *AgreementStateTestSuite) TestRepeatVoteState() {
+ a := s.newAgreement(4)
+ vote := &types.Vote{
+ BlockHash: common.NewRandomHash(),
+ }
+ state := newRepeatVoteState(a.data, vote)
+ s.Equal(stateRepeatVote, state.state())
+ s.Equal(4, state.clocks())
+
+ for i := 0; i < 5; i++ {
+ newState, err := state.nextState()
+ s.Require().Nil(err)
+ s.Require().Len(s.voteChan, 1)
+ proposedVote := <-s.voteChan
+ s.Equal(vote, proposedVote)
+ s.Equal(stateRepeatVote, newState.state())
+ }
}
func TestAgreementState(t *testing.T) {
diff --git a/core/agreement_test.go b/core/agreement_test.go
index e4e05cd..0e3814e 100644
--- a/core/agreement_test.go
+++ b/core/agreement_test.go
@@ -183,6 +183,49 @@ func (s *AgreementTestSuite) TestSimpleConfirm() {
s.Equal(blockHash, confirmBlock)
}
+func (s *AgreementTestSuite) TestPartitionOnCommitVote() {
+ a := s.newAgreement(4)
+ // InitialState
+ a.nextState()
+ // PreCommitState
+ s.Require().Len(s.blockChan, 1)
+ blockHash := <-s.blockChan
+ block, exist := s.block[blockHash]
+ s.Require().True(exist)
+ s.Require().NoError(a.processBlock(block))
+ s.Require().Len(s.voteChan, 1)
+ vote := <-s.voteChan
+ s.Equal(types.VoteInit, vote.Type)
+ s.Equal(blockHash, vote.BlockHash)
+ a.nextState()
+ // CommitState
+ s.Require().Len(s.voteChan, 1)
+ vote = <-s.voteChan
+ s.Equal(types.VotePreCom, vote.Type)
+ s.Equal(blockHash, vote.BlockHash)
+ for nID := range s.auths {
+ v := s.copyVote(vote, nID)
+ s.Require().NoError(a.processVote(v))
+ }
+ a.nextState()
+ // ForwardState
+ s.Require().Len(s.voteChan, 1)
+ vote = <-s.voteChan
+ s.Equal(types.VoteCom, vote.Type)
+ s.Equal(blockHash, vote.BlockHash)
+ s.Equal(blockHash, a.data.lockValue)
+ s.Equal(uint64(1), a.data.lockRound)
+ // RepeateVoteState
+ a.nextState()
+ // The agreement does not receive others commit vote, it will keep re-sending.
+ for i := 0; i < 5; i++ {
+ a.nextState()
+ s.Require().Len(s.voteChan, 1)
+ proposedVote := <-s.voteChan
+ s.Equal(vote, proposedVote)
+ }
+}
+
func (s *AgreementTestSuite) TestFastForwardCond1() {
votes := 0
a := s.newAgreement(4)