aboutsummaryrefslogtreecommitdiffstats
path: root/core/test/state_test.go
diff options
context:
space:
mode:
authorMission Liao <mission.liao@dexon.org>2018-10-24 17:50:09 +0800
committerGitHub <noreply@github.com>2018-10-24 17:50:09 +0800
commitdbee0586b0a565ae9a31a3c2d967f5c2af76f60d (patch)
treecdfaef7754aedfda8d02c7023364a645e391e59e /core/test/state_test.go
parentf90c15fcfa575e138355a449c49cd784ba54db17 (diff)
downloaddexon-consensus-dbee0586b0a565ae9a31a3c2d967f5c2af76f60d.tar
dexon-consensus-dbee0586b0a565ae9a31a3c2d967f5c2af76f60d.tar.gz
dexon-consensus-dbee0586b0a565ae9a31a3c2d967f5c2af76f60d.tar.bz2
dexon-consensus-dbee0586b0a565ae9a31a3c2d967f5c2af76f60d.tar.lz
dexon-consensus-dbee0586b0a565ae9a31a3c2d967f5c2af76f60d.tar.xz
dexon-consensus-dbee0586b0a565ae9a31a3c2d967f5c2af76f60d.tar.zst
dexon-consensus-dbee0586b0a565ae9a31a3c2d967f5c2af76f60d.zip
test: add test.State (#239)
* separate test utility and interface implementation for test.Governance. * add test.State. * integrate test.State to test.Governance. test.State is mainly used to emulate state propagation on fullnode.
Diffstat (limited to 'core/test/state_test.go')
-rw-r--r--core/test/state_test.go277
1 files changed, 277 insertions, 0 deletions
diff --git a/core/test/state_test.go b/core/test/state_test.go
new file mode 100644
index 0000000..b5ed383
--- /dev/null
+++ b/core/test/state_test.go
@@ -0,0 +1,277 @@
+// Copyright 2018 The dexon-consensus-core Authors
+// This file is part of the dexon-consensus-core library.
+//
+// The dexon-consensus-core 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-core 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-core library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package test
+
+import (
+ "sort"
+ "testing"
+ "time"
+
+ "github.com/dexon-foundation/dexon-consensus-core/common"
+ "github.com/dexon-foundation/dexon-consensus-core/core/crypto"
+ "github.com/dexon-foundation/dexon-consensus-core/core/crypto/dkg"
+ "github.com/dexon-foundation/dexon-consensus-core/core/crypto/ecdsa"
+ "github.com/dexon-foundation/dexon-consensus-core/core/types"
+ "github.com/stretchr/testify/suite"
+)
+
+type StateTestSuite struct {
+ suite.Suite
+}
+
+func (s *StateTestSuite) newDKGMasterPublicKey(
+ round uint64) *types.DKGMasterPublicKey {
+ prvKey, err := ecdsa.NewPrivateKey()
+ s.Require().NoError(err)
+ pubKey := prvKey.PublicKey()
+ nodeID := types.NewNodeID(pubKey)
+ _, pubShare := dkg.NewPrivateKeyShares(3)
+ dID, err := dkg.BytesID(nodeID.Hash[:])
+ s.Require().NoError(err)
+ return &types.DKGMasterPublicKey{
+ ProposerID: nodeID,
+ Round: round,
+ DKGID: dID,
+ PublicKeyShares: *pubShare,
+ }
+}
+
+func (s *StateTestSuite) newDKGComplaint(round uint64) *types.DKGComplaint {
+ prvKey, err := ecdsa.NewPrivateKey()
+ s.Require().NoError(err)
+ pubKey := prvKey.PublicKey()
+ nodeID := types.NewNodeID(pubKey)
+ // TODO(mission): sign it, and it doesn't make sense to complaint self.
+ return &types.DKGComplaint{
+ ProposerID: nodeID,
+ Round: round,
+ PrivateShare: types.DKGPrivateShare{
+ ProposerID: nodeID,
+ ReceiverID: nodeID,
+ Round: round,
+ PrivateShare: *dkg.NewPrivateKey(),
+ },
+ }
+}
+
+func (s *StateTestSuite) newDKGFinal(round uint64) *types.DKGFinalize {
+ prvKey, err := ecdsa.NewPrivateKey()
+ s.Require().NoError(err)
+ pubKey := prvKey.PublicKey()
+ nodeID := types.NewNodeID(pubKey)
+ // TODO(mission): sign it.
+ return &types.DKGFinalize{
+ ProposerID: nodeID,
+ Round: round,
+ }
+}
+
+func (s *StateTestSuite) genNodes(count int) (nodes []crypto.PublicKey) {
+ for i := 0; i < count; i++ {
+ prv, err := ecdsa.NewPrivateKey()
+ s.Require().NoError(err)
+ nodes = append(nodes, prv.PublicKey())
+ }
+ return
+}
+
+func (s *StateTestSuite) compareNodes(node1, node2 []crypto.PublicKey) bool {
+ id1 := common.Hashes{}
+ for _, n := range node1 {
+ id1 = append(id1, types.NewNodeID(n).Hash)
+ }
+ sort.Sort(id1)
+ id2 := common.Hashes{}
+ for _, n := range node2 {
+ id2 = append(id2, types.NewNodeID(n).Hash)
+ }
+ sort.Sort(id2)
+ if len(id1) != len(id2) {
+ return false
+ }
+ for idx, id := range id1 {
+ if id != id2[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+func (s *StateTestSuite) findNode(
+ nodes []crypto.PublicKey, node crypto.PublicKey) bool {
+ nodeID := types.NewNodeID(node)
+ for _, n := range nodes {
+ nID := types.NewNodeID(n)
+ if nID == nodeID {
+ return true
+ }
+ }
+ return false
+}
+
+func (s *StateTestSuite) makeDKGChanges(
+ st *State,
+ masterPubKey *types.DKGMasterPublicKey,
+ complaint *types.DKGComplaint,
+ final *types.DKGFinalize) {
+ st.RequestChange(StateAddDKGMasterPublicKey, masterPubKey)
+ st.RequestChange(StateAddDKGComplaint, complaint)
+ st.RequestChange(StateAddDKGFinal, final)
+}
+
+func (s *StateTestSuite) makeConfigChanges(st *State) {
+ st.RequestChange(StateChangeNumChains, uint32(7))
+ st.RequestChange(StateChangeLambdaBA, time.Nanosecond)
+ st.RequestChange(StateChangeLambdaDKG, time.Millisecond)
+ st.RequestChange(StateChangeRoundInterval, time.Hour)
+ st.RequestChange(StateChangeMinBlockInterval, time.Second)
+ st.RequestChange(StateChangeMaxBlockInterval, time.Minute)
+ st.RequestChange(StateChangeK, 1)
+ st.RequestChange(StateChangePhiRatio, float32(0.5))
+ st.RequestChange(StateChangeNotarySetSize, uint32(5))
+ st.RequestChange(StateChangeDKGSetSize, uint32(6))
+}
+
+func (s *StateTestSuite) checkConfigChanges(config *types.Config) {
+ req := s.Require()
+ req.Equal(config.NumChains, uint32(7))
+ req.Equal(config.LambdaBA, time.Nanosecond)
+ req.Equal(config.LambdaDKG, time.Millisecond)
+ req.Equal(config.RoundInterval, time.Hour)
+ req.Equal(config.MinBlockInterval, time.Second)
+ req.Equal(config.MaxBlockInterval, time.Minute)
+ req.Equal(config.K, 1)
+ req.Equal(config.PhiRatio, float32(0.5))
+ req.Equal(config.NotarySetSize, uint32(5))
+ req.Equal(config.DKGSetSize, uint32(6))
+}
+
+func (s *StateTestSuite) TestLocalMode() {
+ // Test State with local mode.
+ var (
+ req = s.Require()
+ lambda = 250 * time.Millisecond
+ )
+ genesisNodes := s.genNodes(20)
+ st := NewState(genesisNodes, lambda, true)
+ config1, nodes1 := st.Snapshot()
+ req.True(s.compareNodes(genesisNodes, nodes1))
+ // Check settings of config1 affected by genesisNodes and lambda.
+ req.Equal(config1.NumChains, uint32(len(genesisNodes)))
+ req.Equal(config1.LambdaBA, lambda)
+ req.Equal(config1.LambdaDKG, lambda*10)
+ req.Equal(config1.RoundInterval, lambda*10000)
+ req.Equal(config1.MaxBlockInterval, lambda*8)
+ req.Equal(config1.NotarySetSize, uint32(len(genesisNodes)))
+ req.Equal(config1.DKGSetSize, uint32(len(genesisNodes)))
+ req.Equal(config1.K, 0)
+ req.Equal(config1.PhiRatio, float32(0.667))
+ // Request some changes, every fields for config should be affected.
+ s.makeConfigChanges(st)
+ // Add new node.
+ prvKey, err := ecdsa.NewPrivateKey()
+ req.NoError(err)
+ pubKey := prvKey.PublicKey()
+ st.RequestChange(StateAddNode, pubKey)
+ config2, newNodes := st.Snapshot()
+ // Check if config changes are applied.
+ s.checkConfigChanges(config2)
+ // Check if new node is added.
+ req.True(s.findNode(newNodes, pubKey))
+ // Test adding CRS.
+ crs := common.NewRandomHash()
+ req.NoError(st.ProposeCRS(1, crs))
+ req.Equal(st.CRS(1), crs)
+ // Test adding node set, DKG complaints, final, master public key.
+ // Make sure everything is empty before changed.
+ req.Empty(st.DKGMasterPublicKeys(2))
+ req.Empty(st.DKGComplaints(2))
+ req.False(st.IsDKGFinal(2, 0))
+ // Add DKG stuffs.
+ masterPubKey := s.newDKGMasterPublicKey(2)
+ comp := s.newDKGComplaint(2)
+ final := s.newDKGFinal(2)
+ s.makeDKGChanges(st, masterPubKey, comp, final)
+ // Check DKGMasterPublicKeys.
+ masterKeyForRound := st.DKGMasterPublicKeys(2)
+ req.Len(masterKeyForRound, 1)
+ req.True(masterKeyForRound[0].Equal(masterPubKey))
+ // Check DKGComplaints.
+ compForRound := st.DKGComplaints(2)
+ req.Len(compForRound, 1)
+ req.True(compForRound[0].Equal(comp))
+ // Check IsDKGFinal.
+ req.True(st.IsDKGFinal(2, 0))
+}
+
+func (s *StateTestSuite) TestPacking() {
+ // Make sure everything works when requests are packing
+ // and unpacked to apply.
+ var (
+ req = s.Require()
+ lambda = 250 * time.Millisecond
+ )
+ // Make config changes.
+ genesisNodes := s.genNodes(20)
+ st := NewState(genesisNodes, lambda, false)
+ s.makeConfigChanges(st)
+ // Add new CRS.
+ crs := common.NewRandomHash()
+ req.NoError(st.ProposeCRS(1, crs))
+ // Add new node.
+ prvKey, err := ecdsa.NewPrivateKey()
+ req.NoError(err)
+ pubKey := prvKey.PublicKey()
+ st.RequestChange(StateAddNode, pubKey)
+ // Add DKG stuffs.
+ masterPubKey := s.newDKGMasterPublicKey(2)
+ comp := s.newDKGComplaint(2)
+ final := s.newDKGFinal(2)
+ s.makeDKGChanges(st, masterPubKey, comp, final)
+ // Make sure everything is empty before changed.
+ req.Empty(st.DKGMasterPublicKeys(2))
+ req.Empty(st.DKGComplaints(2))
+ req.False(st.IsDKGFinal(2, 0))
+ // Pack changes into bytes.
+ b, err := st.PackRequests()
+ req.NoError(err)
+ req.NotEmpty(b)
+ // Apply those bytes back.
+ req.NoError(st.Apply(b))
+ // Check if configs are changed.
+ config, nodes := st.Snapshot()
+ s.checkConfigChanges(config)
+ // Check if CRS is added.
+ req.Equal(st.CRS(1), crs)
+ // Check if new node is added.
+ req.True(s.findNode(nodes, pubKey))
+ // Check DKGMasterPublicKeys.
+ masterKeyForRound := st.DKGMasterPublicKeys(2)
+ req.Len(masterKeyForRound, 1)
+ req.True(masterKeyForRound[0].Equal(masterPubKey))
+ // Check DKGComplaints.
+ compForRound := st.DKGComplaints(2)
+ req.Len(compForRound, 1)
+ req.True(compForRound[0].Equal(comp))
+ // Check IsDKGFinal.
+ req.True(st.IsDKGFinal(2, 0))
+}
+
+func TestState(t *testing.T) {
+ suite.Run(t, new(StateTestSuite))
+}