diff options
author | Mission Liao <mission.liao@dexon.org> | 2019-03-13 11:41:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-13 11:41:03 +0800 |
commit | 92d64e7e743f7afce6ab811bce4d57fc67297567 (patch) | |
tree | 223ab615a0d49c3f0a046fc8b9c05923750f882e /integration_test/round-event_test.go | |
parent | 05b87ad32f30deebdbcb82e53362765a46bf8d0e (diff) | |
download | tangerine-consensus-92d64e7e743f7afce6ab811bce4d57fc67297567.tar.gz tangerine-consensus-92d64e7e743f7afce6ab811bce4d57fc67297567.tar.zst tangerine-consensus-92d64e7e743f7afce6ab811bce4d57fc67297567.zip |
utils: add RoundEvent (#482)
* Move core.roundBasedConfig to core/utils
* Refine utils.RoundBasedConfig
* Add utils.RoundEvent
Diffstat (limited to 'integration_test/round-event_test.go')
-rw-r--r-- | integration_test/round-event_test.go | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/integration_test/round-event_test.go b/integration_test/round-event_test.go new file mode 100644 index 0000000..f8cac26 --- /dev/null +++ b/integration_test/round-event_test.go @@ -0,0 +1,226 @@ +// Copyright 2019 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// <http://www.gnu.org/licenses/>. + +package integration + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core" + "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/crypto/dkg" + "github.com/dexon-foundation/dexon-consensus/core/test" + "github.com/dexon-foundation/dexon-consensus/core/types" + typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg" + "github.com/dexon-foundation/dexon-consensus/core/utils" + "github.com/stretchr/testify/suite" +) + +func getCRS(round, reset uint64) []byte { + return []byte(fmt.Sprintf("r#%d,reset#%d", round, reset)) +} + +type evtParamToCheck struct { + round uint64 + reset uint64 + height uint64 + crs common.Hash +} + +type RoundEventTestSuite struct { + suite.Suite + + pubKeys []crypto.PublicKey + signers []*utils.Signer + logger common.Logger +} + +func (s *RoundEventTestSuite) SetupSuite() { + prvKeys, pubKeys, err := test.NewKeys(4) + s.Require().NoError(err) + s.pubKeys = pubKeys + for _, k := range prvKeys { + s.signers = append(s.signers, utils.NewSigner(k)) + } + s.logger = &common.NullLogger{} +} + +func (s *RoundEventTestSuite) prepareGov() *test.Governance { + gov, err := test.NewGovernance( + test.NewState(1, s.pubKeys, 100*time.Millisecond, s.logger, true), + core.ConfigRoundShift) + s.Require().NoError(err) + return gov +} + +func (s *RoundEventTestSuite) proposeMPK(gov *test.Governance, round uint64, + count int) { + for idx, pubKey := range s.pubKeys[:count] { + _, pubShare := dkg.NewPrivateKeyShares(utils.GetDKGThreshold( + gov.Configuration(round))) + mpk := &typesDKG.MasterPublicKey{ + Round: round, + DKGID: typesDKG.NewID(types.NewNodeID(pubKey)), + PublicKeyShares: *pubShare, + } + s.Require().NoError(s.signers[idx].SignDKGMasterPublicKey(mpk)) + gov.AddDKGMasterPublicKey(round, mpk) + } +} + +func (s *RoundEventTestSuite) proposeFinalize(gov *test.Governance, + round uint64, count int) { + for idx, pubKey := range s.pubKeys[:count] { + final := &typesDKG.Finalize{ + ProposerID: types.NewNodeID(pubKey), + Round: round, + } + s.Require().NoError(s.signers[idx].SignDKGFinalize(final)) + gov.AddDKGFinalize(round, final) + } +} + +func (s *RoundEventTestSuite) TestFromRound0() { + // Prepare test.Governance. + gov := s.prepareGov() + s.Require().NoError(gov.State().RequestChange(test.StateChangeRoundLength, + uint64(100))) + gov.CatchUpWithRound(0) + s.Require().NoError(gov.State().RequestChange(test.StateChangeRoundLength, + uint64(200))) + gov.CatchUpWithRound(1) + // Prepare utils.RoundEvent, starts from genesis. + rEvt, err := utils.NewRoundEvent( + context.Background(), gov, s.logger, 0, 0, 0, core.ConfigRoundShift) + s.Require().NoError(err) + // Register a handler to collects triggered events. + var evts []evtParamToCheck + rEvt.Register(func(params []utils.RoundEventParam) { + for _, p := range params { + evts = append(evts, evtParamToCheck{ + round: p.Round, + reset: p.Reset, + height: p.BeginHeight, + crs: p.CRS, + }) + // Tricky part to make sure passed config is correct. + s.Require().Equal((p.Round+1)*100, p.Config.RoundLength) + } + }) + // Reset round#1 twice, then make it ready. + gov.ResetDKG([]byte("DKG round 1 reset 1")) + gov.ResetDKG([]byte("DKG round 1 reset 2")) + s.proposeMPK(gov, 1, 3) + s.proposeFinalize(gov, 1, 3) + rEvt.ValidateNextRound(80) + // Check collected events. + s.Require().Len(evts, 3) + s.Require().Equal(evts[0], evtParamToCheck{0, 1, 100, gov.CRS(0)}) + s.Require().Equal(evts[1], evtParamToCheck{0, 2, 200, gov.CRS(0)}) + s.Require().Equal(evts[2], evtParamToCheck{1, 0, 300, gov.CRS(1)}) +} + +func (s *RoundEventTestSuite) TestFromRoundN() { + // Prepare test.Governance. + gov := s.prepareGov() + s.Require().NoError(gov.State().RequestChange(test.StateChangeRoundLength, + uint64(100))) + gov.CatchUpWithRound(22) + for r := uint64(2); r <= uint64(20); r++ { + gov.ProposeCRS(r, getCRS(r, 0)) + } + // Reset round#20 twice, then make it done DKG preparation. + gov.ResetDKG(getCRS(20, 1)) + gov.ResetDKG(getCRS(20, 2)) + s.proposeMPK(gov, 20, 3) + s.proposeFinalize(gov, 20, 3) + s.Require().Equal(gov.DKGResetCount(20), uint64(2)) + // Propose CRS for round#21, and it works without reset. + gov.ProposeCRS(21, getCRS(21, 0)) + s.proposeMPK(gov, 21, 3) + s.proposeFinalize(gov, 21, 3) + // Propose CRS for round#22, and it works without reset. + gov.ProposeCRS(22, getCRS(22, 0)) + s.proposeMPK(gov, 22, 3) + s.proposeFinalize(gov, 22, 3) + // Prepare utils.RoundEvent, starts from round#19, reset(for round#20)#1. + rEvt, err := utils.NewRoundEvent(context.Background(), gov, s.logger, 19, + 1900, 2019, core.ConfigRoundShift) + s.Require().NoError(err) + // Register a handler to collects triggered events. + var evts []evtParamToCheck + rEvt.Register(func(params []utils.RoundEventParam) { + for _, p := range params { + evts = append(evts, evtParamToCheck{ + round: p.Round, + reset: p.Reset, + height: p.BeginHeight, + crs: p.CRS, + }) + } + }) + // Check for round#19, reset(for round#20)#2 at height=2080. + rEvt.ValidateNextRound(2080) + // Check collected events. + s.Require().Len(evts, 2) + s.Require().Equal(evts[0], evtParamToCheck{19, 2, 2100, gov.CRS(19)}) + s.Require().Equal(evts[1], evtParamToCheck{20, 0, 2200, gov.CRS(20)}) + // Round might exceed round-shift limitation would not be triggered. + rEvt.ValidateNextRound(2280) + s.Require().Len(evts, 3) + s.Require().Equal(evts[2], evtParamToCheck{21, 0, 2300, gov.CRS(21)}) + rEvt.ValidateNextRound(2380) + s.Require().Equal(evts[3], evtParamToCheck{22, 0, 2400, gov.CRS(22)}) +} + +func (s *RoundEventTestSuite) TestLastPeriod() { + gov := s.prepareGov() + s.Require().NoError(gov.State().RequestChange(test.StateChangeRoundLength, + uint64(100))) + gov.CatchUpWithRound(0) + s.Require().NoError(gov.State().RequestChange(test.StateChangeRoundLength, + uint64(200))) + gov.CatchUpWithRound(1) + // Prepare utils.RoundEvent, starts from genesis. + rEvt, err := utils.NewRoundEvent( + context.Background(), gov, s.logger, 0, 0, 0, core.ConfigRoundShift) + s.Require().NoError(err) + begin, length := rEvt.LastPeriod() + s.Require().Equal(begin, uint64(0)) + s.Require().Equal(length, uint64(100)) + // Reset round#1 twice, then make it ready. + gov.ResetDKG([]byte("DKG round 1 reset 1")) + gov.ResetDKG([]byte("DKG round 1 reset 2")) + rEvt.ValidateNextRound(80) + begin, length = rEvt.LastPeriod() + s.Require().Equal(begin, uint64(200)) + s.Require().Equal(length, uint64(100)) + s.proposeMPK(gov, 1, 3) + s.proposeFinalize(gov, 1, 3) + rEvt.ValidateNextRound(80) + begin, length = rEvt.LastPeriod() + s.Require().Equal(begin, uint64(300)) + s.Require().Equal(length, uint64(200)) +} + +func TestRoundEvent(t *testing.T) { + suite.Run(t, new(RoundEventTestSuite)) +} |