aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMission Liao <mission.liao@dexon.org>2019-01-08 11:17:58 +0800
committerGitHub <noreply@github.com>2019-01-08 11:17:58 +0800
commit72e71281d966985d21fe2ec0298c540cf442d055 (patch)
treee87d99bfd47e59f44c3fd54e70d46ed4af3ee490
parentdfde793afad2e8668b1734737a6261f8818478c9 (diff)
downloaddexon-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.go3
-rw-r--r--core/agreement-state_test.go7
-rw-r--r--core/agreement.go19
-rw-r--r--core/agreement_test.go67
-rw-r--r--core/consensus.go8
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