// Copyright 2018 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 // . package test import ( "testing" "time" "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/crypto" "github.com/dexon-foundation/dexon-consensus/core/crypto/dkg" "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" ) type GovernanceTestSuite struct { suite.Suite } func (s *GovernanceTestSuite) TestEqual() { var req = s.Require() // Setup a base governance. _, genesisNodes, err := NewKeys(20) req.NoError(err) g1, err := NewGovernance(NewState( 1, genesisNodes, 100*time.Millisecond, &common.NullLogger{}, true), 2) req.NoError(err) // Create a governance with different lambda. g2, err := NewGovernance(NewState( 1, genesisNodes, 50*time.Millisecond, &common.NullLogger{}, true), 2) req.NoError(err) req.False(g1.Equal(g2, true)) // Create configs for 3 rounds for g1. g1.CatchUpWithRound(3) // Make a clone. g3 := g1.Clone() req.True(g1.Equal(g3, true)) // Create a new round for g1. g1.CatchUpWithRound(4) req.False(g1.Equal(g3, true)) // Make another clone. g4 := g1.Clone() req.True(g1.Equal(g4, true)) // Add a node to g4. _, newNodes, err := NewKeys(1) req.NoError(err) g4.State().RequestChange(StateAddNode, newNodes[0]) g1.CatchUpWithRound(5) g4.CatchUpWithRound(5) req.False(g1.Equal(g4, true)) // Make a clone. g5 := g1.Clone() // Change its roundShift g5.roundShift = 3 req.False(g1.Equal(g5, true)) // Prohibit some change. g1.Prohibit(StateAddDKGFinal) // Make a clone and should be equal. g6 := g1.Clone() req.True(g1.Equal(g6, true)) g6.Unprohibit(StateAddDKGFinal) req.False(g1.Equal(g6, true)) } func (s *GovernanceTestSuite) TestRegisterChange() { var ( req = s.Require() roundLength uint64 = 100 ) _, genesisNodes, err := NewKeys(20) req.NoError(err) g, err := NewGovernance(NewState( 1, genesisNodes, 100*time.Millisecond, &common.NullLogger{}, true), 2) req.NoError(err) req.NoError(g.State().RequestChange(StateChangeRoundLength, uint64(roundLength))) // Unable to register change for genesis round. req.Error(g.RegisterConfigChange(0, StateChangeNotarySetSize, uint32(32))) // Make some round prepared. g.CatchUpWithRound(4) req.Equal(g.Configuration(4).NotarySetSize, uint32(20)) // Unable to register change for prepared round. req.Error(g.RegisterConfigChange(4, StateChangeNotarySetSize, uint32(32))) // It's ok to make some change when condition is met. req.NoError(g.RegisterConfigChange(5, StateChangeNotarySetSize, uint32(32))) req.NoError(g.RegisterConfigChange(6, StateChangeNotarySetSize, uint32(32))) req.NoError(g.RegisterConfigChange(7, StateChangeNotarySetSize, uint32(40))) // In local mode, state for round 6 would be ready after notified with // round 2. g.NotifyRound(2, roundLength*2) g.NotifyRound(3, roundLength*3) // In local mode, state for round 7 would be ready after notified with // round 6. g.NotifyRound(4, roundLength*4) // Notify governance to take a snapshot for round 7's configuration. g.NotifyRound(5, roundLength*5) req.Equal(g.Configuration(6).NotarySetSize, uint32(32)) req.Equal(g.Configuration(7).NotarySetSize, uint32(40)) } func (s *GovernanceTestSuite) TestProhibit() { round := uint64(1) prvKeys, genesisNodes, err := NewKeys(4) s.Require().NoError(err) gov, err := NewGovernance(NewState( 1, genesisNodes, 100*time.Millisecond, &common.NullLogger{}, true), 2) s.Require().NoError(err) // Test MPK. proposeMPK := func(k crypto.PrivateKey) { signer := utils.NewSigner(k) _, pubShare := dkg.NewPrivateKeyShares(utils.GetDKGThreshold( gov.Configuration(round))) mpk := &typesDKG.MasterPublicKey{ Round: round, DKGID: typesDKG.NewID(types.NewNodeID(k.PublicKey())), PublicKeyShares: *pubShare, } s.Require().NoError(signer.SignDKGMasterPublicKey(mpk)) gov.AddDKGMasterPublicKey(mpk) } proposeMPK(prvKeys[0]) s.Require().Len(gov.DKGMasterPublicKeys(round), 1) gov.Prohibit(StateAddDKGMasterPublicKey) proposeMPK(prvKeys[1]) s.Require().Len(gov.DKGMasterPublicKeys(round), 1) gov.Unprohibit(StateAddDKGMasterPublicKey) proposeMPK(prvKeys[1]) s.Require().Len(gov.DKGMasterPublicKeys(round), 2) // Test Complaint. proposeComplaint := func(k crypto.PrivateKey) { signer := utils.NewSigner(k) comp := &typesDKG.Complaint{ ProposerID: types.NewNodeID(k.PublicKey()), Round: round, } s.Require().NoError(signer.SignDKGComplaint(comp)) gov.AddDKGComplaint(comp) } proposeComplaint(prvKeys[0]) s.Require().Len(gov.DKGComplaints(round), 1) gov.Prohibit(StateAddDKGComplaint) proposeComplaint(prvKeys[1]) s.Require().Len(gov.DKGComplaints(round), 1) gov.Unprohibit(StateAddDKGComplaint) proposeComplaint(prvKeys[1]) s.Require().Len(gov.DKGComplaints(round), 2) // Test DKG Final. proposeFinal := func(k crypto.PrivateKey) { signer := utils.NewSigner(k) final := &typesDKG.Finalize{ Round: round, ProposerID: types.NewNodeID(k.PublicKey()), } s.Require().NoError(signer.SignDKGFinalize(final)) gov.AddDKGFinalize(final) } gov.Prohibit(StateAddDKGFinal) for _, k := range prvKeys { proposeFinal(k) } s.Require().False(gov.IsDKGFinal(round)) gov.Unprohibit(StateAddDKGFinal) for _, k := range prvKeys { proposeFinal(k) } s.Require().True(gov.IsDKGFinal(round)) } func TestGovernance(t *testing.T) { suite.Run(t, new(GovernanceTestSuite)) }