aboutsummaryrefslogblamecommitdiffstats
path: root/integration_test/round-event_test.go
blob: 19832e83d17e7f21ec7b2b9b7fcb89686d7846cd (plain) (tree)








































































                                                                                  


                                         





                                                                             
                                               



                                                                                 
                                              


         



                                              



                                                            
                                          

                                                                          
                                         













                                                                                  
                                                                                 

















                                                                                

                                       
                                                              








                                                                          



                                            
                                                                                  
                             


                                                  



                                                  


                                                                   

                                        


                                                                

                                        

                                                                

                                        

                                                                                 
                                            













                                                                    
                                                                




                                                                             
                                                                

                                                                             
                                                                












                                                                                  
                                                                                 










                                                   

                                       





                                              









                                                                                  
                                                                                 

















                                                                        


                                              
// 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, reset uint64,
    count int) {
    for idx, pubKey := range s.pubKeys[:count] {
        _, pubShare := dkg.NewPrivateKeyShares(utils.GetDKGThreshold(
            gov.Configuration(round)))
        mpk := &typesDKG.MasterPublicKey{
            Round:           round,
            Reset:           reset,
            DKGID:           typesDKG.NewID(types.NewNodeID(pubKey)),
            PublicKeyShares: *pubShare,
        }
        s.Require().NoError(s.signers[idx].SignDKGMasterPublicKey(mpk))
        gov.AddDKGMasterPublicKey(mpk)
    }
}

func (s *RoundEventTestSuite) proposeFinalize(
    gov *test.Governance,
    round, reset uint64,
    count int) {
    for idx, pubKey := range s.pubKeys[:count] {
        final := &typesDKG.Finalize{
            ProposerID: types.NewNodeID(pubKey),
            Round:      round,
            Reset:      reset,
        }
        s.Require().NoError(s.signers[idx].SignDKGFinalize(final))
        gov.AddDKGFinalize(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, 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, 2, 3)
    s.proposeFinalize(gov, 1, 2, 3)
    s.Require().Equal(rEvt.ValidateNextRound(80), uint(3))
    // 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.
    var (
        gov         = s.prepareGov()
        roundLength = uint64(100)
    )
    s.Require().NoError(gov.State().RequestChange(test.StateChangeRoundLength,
        roundLength))
    for r := uint64(2); r <= uint64(20); r++ {
        gov.ProposeCRS(r, getCRS(r, 0))
    }
    for r := uint64(0); r <= uint64(19); r++ {
        gov.NotifyRound(r, r*roundLength)
    }
    gov.NotifyRound(20, 2200)
    // Reset round#20 twice, then make it done DKG preparation.
    gov.ResetDKG(getCRS(20, 1))
    gov.ResetDKG(getCRS(20, 2))
    s.proposeMPK(gov, 20, 2, 3)
    s.proposeFinalize(gov, 20, 2, 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, 0, 3)
    s.proposeFinalize(gov, 21, 0, 3)
    // Propose CRS for round#22, and it works without reset.
    gov.ProposeCRS(22, getCRS(22, 0))
    s.proposeMPK(gov, 22, 0, 3)
    s.proposeFinalize(gov, 22, 0, 3)
    // Prepare utils.RoundEvent, starts from round#19, reset(for round#20)#1.
    rEvt, err := utils.NewRoundEvent(context.Background(), gov, s.logger, 19,
        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.
    s.Require().Equal(rEvt.ValidateNextRound(2080), uint(2))
    // 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.
    s.Require().Equal(rEvt.ValidateNextRound(2280), uint(1))
    s.Require().Len(evts, 3)
    s.Require().Equal(evts[2], evtParamToCheck{21, 0, 2300, gov.CRS(21)})
    s.Require().Equal(rEvt.ValidateNextRound(2380), uint(1))
    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, 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, 2, 3)
    s.proposeFinalize(gov, 1, 2, 3)
    rEvt.ValidateNextRound(80)
    begin, length = rEvt.LastPeriod()
    s.Require().Equal(begin, uint64(300))
    s.Require().Equal(length, uint64(200))
}

func (s *RoundEventTestSuite) TestTriggerInitEvent() {
    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, 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,
            })
        }
    })
    rEvt.TriggerInitEvent()
    s.Require().Len(evts, 1)
    s.Require().Equal(evts[0], evtParamToCheck{0, 0, 0, gov.CRS(0)})
}

func TestRoundEvent(t *testing.T) {
    suite.Run(t, new(RoundEventTestSuite))
}