diff options
author | Mission Liao <mission.liao@dexon.org> | 2019-01-08 11:17:58 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-08 11:17:58 +0800 |
commit | 72e71281d966985d21fe2ec0298c540cf442d055 (patch) | |
tree | e87d99bfd47e59f44c3fd54e70d46ed4af3ee490 | |
parent | dfde793afad2e8668b1734737a6261f8818478c9 (diff) | |
download | dexon-consensus-72e71281d966985d21fe2ec0298c540cf442d055.tar dexon-consensus-72e71281d966985d21fe2ec0298c540cf442d055.tar.gz dexon-consensus-72e71281d966985d21fe2ec0298c540cf442d055.tar.bz2 dexon-consensus-72e71281d966985d21fe2ec0298c540cf442d055.tar.lz dexon-consensus-72e71281d966985d21fe2ec0298c540cf442d055.tar.xz dexon-consensus-72e71281d966985d21fe2ec0298c540cf442d055.tar.zst dexon-consensus-72e71281d966985d21fe2ec0298c540cf442d055.zip |
core: report fork (#409)
-rw-r--r-- | core/agreement-mgr.go | 3 | ||||
-rw-r--r-- | core/agreement-state_test.go | 7 | ||||
-rw-r--r-- | core/agreement.go | 19 | ||||
-rw-r--r-- | core/agreement_test.go | 67 | ||||
-rw-r--r-- | core/consensus.go | 8 |
5 files changed, 89 insertions, 15 deletions
diff --git a/core/agreement-mgr.go b/core/agreement-mgr.go index 1bd077b..a995cca 100644 --- a/core/agreement-mgr.go +++ b/core/agreement-mgr.go @@ -185,7 +185,8 @@ func (mgr *agreementMgr) appendConfig( mgr.con.ID, recv, newLeaderSelector(genValidLeader(mgr), mgr.logger), - mgr.signer) + mgr.signer, + mgr.logger) // Hacky way to make agreement module self contained. recv.agreementModule = agrModule mgr.baModules = append(mgr.baModules, agrModule) diff --git a/core/agreement-state_test.go b/core/agreement-state_test.go index 43557f1..34088b7 100644 --- a/core/agreement-state_test.go +++ b/core/agreement-state_test.go @@ -60,6 +60,9 @@ func (r *agreementStateTestReceiver) ConfirmBlock(block common.Hash, func (r *agreementStateTestReceiver) PullBlocks(common.Hashes) {} +func (r *agreementStateTestReceiver) ReportForkVote(v1, v2 *types.Vote) {} +func (r *agreementStateTestReceiver) ReportForkBlock(b1, b2 *types.Block) {} + func (s *AgreementStateTestSuite) proposeBlock( leader *leaderSelector) *types.Block { block := &types.Block{ @@ -94,9 +97,10 @@ func (s *AgreementStateTestSuite) SetupTest() { } func (s *AgreementStateTestSuite) newAgreement(numNode int) *agreement { + logger := &common.NullLogger{} leader := newLeaderSelector(func(*types.Block) (bool, error) { return true, nil - }, &common.NullLogger{}) + }, logger) notarySet := make(map[types.NodeID]struct{}) for i := 0; i < numNode-1; i++ { prvKey, err := ecdsa.NewPrivateKey() @@ -114,6 +118,7 @@ func (s *AgreementStateTestSuite) newAgreement(numNode int) *agreement { }, leader, s.signers[s.ID], + logger, ) agreement.restart(notarySet, types.Position{}, types.NodeID{}, common.NewRandomHash()) return agreement diff --git a/core/agreement.go b/core/agreement.go index 91341e3..11ed89a 100644 --- a/core/agreement.go +++ b/core/agreement.go @@ -74,6 +74,8 @@ type agreementReceiver interface { // agreement module. ConfirmBlock(common.Hash, map[types.NodeID]*types.Vote) PullBlocks(common.Hashes) + ReportForkVote(v1, v2 *types.Vote) + ReportForkBlock(b1, b2 *types.Block) } type pendingBlock struct { @@ -116,6 +118,7 @@ type agreement struct { candidateBlock map[common.Hash]*types.Block fastForward chan uint64 signer *utils.Signer + logger common.Logger } // newAgreement creates a agreement instance. @@ -123,7 +126,8 @@ func newAgreement( ID types.NodeID, recv agreementReceiver, leader *leaderSelector, - signer *utils.Signer) *agreement { + signer *utils.Signer, + logger common.Logger) *agreement { agreement := &agreement{ data: &agreementData{ recv: recv, @@ -134,6 +138,7 @@ func newAgreement( candidateBlock: make(map[common.Hash]*types.Block), fastForward: make(chan uint64, 1), signer: signer, + logger: logger, } agreement.stop() return agreement @@ -219,11 +224,17 @@ func (a *agreement) restart( }() for _, block := range replayBlock { - a.processBlock(block) + if err := a.processBlock(block); err != nil { + a.logger.Error("failed to process block when restarting agreement", + "block", block) + } } for _, vote := range replayVote { - a.processVote(vote) + if err := a.processVote(vote); err != nil { + a.logger.Error("failed to process vote when restarting agreement", + "vote", vote) + } } } @@ -307,6 +318,7 @@ func (a *agreement) checkForkVote(vote *types.Vote) error { if votes, exist := a.data.votes[vote.Period]; exist { if oldVote, exist := votes[vote.Type][vote.ProposerID]; exist { if vote.BlockHash != oldVote.BlockHash { + a.data.recv.ReportForkVote(oldVote, vote) return &ErrForkVote{vote.ProposerID, oldVote, vote} } } @@ -461,6 +473,7 @@ func (a *agreement) processBlock(block *types.Block) error { } if b, exist := a.data.blocks[block.ProposerID]; exist { if b.Hash != block.Hash { + a.data.recv.ReportForkBlock(b, block) return &ErrFork{block.ProposerID, b.Hash, block.Hash} } return nil diff --git a/core/agreement_test.go b/core/agreement_test.go index a95e89a..7c15b83 100644 --- a/core/agreement_test.go +++ b/core/agreement_test.go @@ -28,7 +28,7 @@ import ( "github.com/stretchr/testify/suite" ) -// agreementTestReceiver implements core.agreementReceiveer +// agreementTestReceiver implements core.agreementReceiver. type agreementTestReceiver struct { s *AgreementTestSuite agreementIndex int @@ -58,6 +58,21 @@ func (r *agreementTestReceiver) PullBlocks(hashes common.Hashes) { } +// agreementTestForkReporter implement core.forkReporter. +type agreementTestForkReporter struct { + s *AgreementTestSuite +} + +func (r *agreementTestReceiver) ReportForkVote(v1, v2 *types.Vote) { + r.s.forkVoteChan <- v1.BlockHash + r.s.forkVoteChan <- v2.BlockHash +} + +func (r *agreementTestReceiver) ReportForkBlock(b1, b2 *types.Block) { + r.s.forkBlockChan <- b1.Hash + r.s.forkBlockChan <- b2.Hash +} + func (s *AgreementTestSuite) proposeBlock( nID types.NodeID, crs common.Hash) *types.Block { block := &types.Block{ @@ -74,14 +89,16 @@ func (s *AgreementTestSuite) proposeBlock( type AgreementTestSuite struct { suite.Suite - ID types.NodeID - signers map[types.NodeID]*utils.Signer - voteChan chan *types.Vote - blockChan chan common.Hash - confirmChan chan common.Hash - block map[common.Hash]*types.Block - pulledBlocks map[common.Hash]struct{} - agreement []*agreement + ID types.NodeID + signers map[types.NodeID]*utils.Signer + voteChan chan *types.Vote + blockChan chan common.Hash + confirmChan chan common.Hash + forkVoteChan chan common.Hash + forkBlockChan chan common.Hash + block map[common.Hash]*types.Block + pulledBlocks map[common.Hash]struct{} + agreement []*agreement } func (s *AgreementTestSuite) SetupTest() { @@ -94,6 +111,8 @@ func (s *AgreementTestSuite) SetupTest() { s.voteChan = make(chan *types.Vote, 100) s.blockChan = make(chan common.Hash, 100) s.confirmChan = make(chan common.Hash, 100) + s.forkVoteChan = make(chan common.Hash, 100) + s.forkBlockChan = make(chan common.Hash, 100) s.block = make(map[common.Hash]*types.Block) s.pulledBlocks = make(map[common.Hash]struct{}) } @@ -101,9 +120,10 @@ func (s *AgreementTestSuite) SetupTest() { func (s *AgreementTestSuite) newAgreement( numNotarySet, leaderIdx int) (*agreement, types.NodeID) { s.Require().True(leaderIdx < numNotarySet) + logger := &common.NullLogger{} leader := newLeaderSelector(func(*types.Block) (bool, error) { return true, nil - }, &common.NullLogger{}) + }, logger) agreementIdx := len(s.agreement) var leaderNode types.NodeID notarySet := make(map[types.NodeID]struct{}) @@ -129,6 +149,7 @@ func (s *AgreementTestSuite) newAgreement( }, leader, s.signers[s.ID], + logger, ) agreement.restart(notarySet, types.Position{}, leaderNode, common.NewRandomHash()) @@ -445,6 +466,32 @@ func (s *AgreementTestSuite) TestDecide() { s.Equal(hash, confirmBlock) } +func (s *AgreementTestSuite) TestForkVote() { + a, _ := s.newAgreement(4, -1) + a.data.period = 2 + for nID := range a.notarySet { + v01 := s.prepareVote(nID, types.VotePreCom, common.NewRandomHash(), 2) + v02 := s.prepareVote(nID, types.VotePreCom, common.NewRandomHash(), 2) + s.Require().NoError(a.processVote(v01)) + s.Require().IsType(&ErrForkVote{}, a.processVote(v02)) + s.Require().Equal(v01.BlockHash, <-s.forkVoteChan) + s.Require().Equal(v02.BlockHash, <-s.forkVoteChan) + break + } +} + +func (s AgreementTestSuite) TestForkBlock() { + a, _ := s.newAgreement(4, -1) + for nID := range a.notarySet { + b01 := s.proposeBlock(nID, a.data.leader.hashCRS) + b02 := s.proposeBlock(nID, a.data.leader.hashCRS) + s.Require().NoError(a.processBlock(b01)) + s.Require().IsType(&ErrFork{}, a.processBlock(b02)) + s.Require().Equal(b01.Hash, <-s.forkBlockChan) + s.Require().Equal(b02.Hash, <-s.forkBlockChan) + } +} + func TestAgreement(t *testing.T) { suite.Run(t, new(AgreementTestSuite)) } diff --git a/core/consensus.go b/core/consensus.go index ec7d9fd..c7cff3c 100644 --- a/core/consensus.go +++ b/core/consensus.go @@ -259,6 +259,14 @@ func (recv *consensusBAReceiver) PullBlocks(hashes common.Hashes) { recv.consensus.network.PullBlocks(hashes) } +func (recv *consensusBAReceiver) ReportForkVote(v1, v2 *types.Vote) { + recv.consensus.gov.ReportForkVote(v1, v2) +} + +func (recv *consensusBAReceiver) ReportForkBlock(b1, b2 *types.Block) { + recv.consensus.gov.ReportForkBlock(b1, b2) +} + // consensusDKGReceiver implements dkgReceiver. type consensusDKGReceiver struct { ID types.NodeID |