From 3a04c0d3405960e3a6eabb6adc700fa2fd5aac21 Mon Sep 17 00:00:00 2001 From: Mission Liao Date: Wed, 14 Nov 2018 15:25:54 +0800 Subject: utils: add utils package (#327) --- core/configuration-chain.go | 5 +- core/configuration-chain_test.go | 3 +- core/consensus.go | 7 +- core/lattice-data.go | 6 +- core/lattice-data_test.go | 3 +- core/nodeset-cache.go | 237 -------------------------------------- core/nodeset-cache_test.go | 125 -------------------- core/utils/nodeset-cache.go | 239 +++++++++++++++++++++++++++++++++++++++ core/utils/nodeset-cache_test.go | 112 ++++++++++++++++++ integration_test/node.go | 3 +- 10 files changed, 367 insertions(+), 373 deletions(-) delete mode 100644 core/nodeset-cache.go delete mode 100644 core/nodeset-cache_test.go create mode 100644 core/utils/nodeset-cache.go create mode 100644 core/utils/nodeset-cache_test.go diff --git a/core/configuration-chain.go b/core/configuration-chain.go index bda2fdf..03a4602 100644 --- a/core/configuration-chain.go +++ b/core/configuration-chain.go @@ -26,6 +26,7 @@ import ( "github.com/dexon-foundation/dexon-consensus/core/crypto" "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" ) // Errors for configuration chain.. @@ -51,7 +52,7 @@ type configurationChain struct { tsig map[common.Hash]*tsigProtocol tsigTouched map[common.Hash]struct{} tsigReady *sync.Cond - cache *NodeSetCache + cache *utils.NodeSetCache dkgSet map[types.NodeID]struct{} mpkReady bool pendingPrvShare map[types.NodeID]*typesDKG.PrivateShare @@ -64,7 +65,7 @@ func newConfigurationChain( ID types.NodeID, recv dkgReceiver, gov Governance, - cache *NodeSetCache, + cache *utils.NodeSetCache, logger common.Logger) *configurationChain { return &configurationChain{ ID: ID, diff --git a/core/configuration-chain_test.go b/core/configuration-chain_test.go index b3d8ff4..129d07c 100644 --- a/core/configuration-chain_test.go +++ b/core/configuration-chain_test.go @@ -32,6 +32,7 @@ import ( "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" ) type ConfigurationChainTestSuite struct { @@ -174,7 +175,7 @@ func (s *ConfigurationChainTestSuite) runDKG( for _, nID := range s.nIDs { gov, err := test.NewGovernance(pks, 50*time.Millisecond, ConfigRoundShift) s.Require().NoError(err) - cache := NewNodeSetCache(gov) + cache := utils.NewNodeSetCache(gov) cfgChains[nID] = newConfigurationChain( nID, recv, gov, cache, &common.NullLogger{}) recv.nodes[nID] = cfgChains[nID] diff --git a/core/consensus.go b/core/consensus.go index 69131e6..99cb7c1 100644 --- a/core/consensus.go +++ b/core/consensus.go @@ -29,6 +29,7 @@ import ( "github.com/dexon-foundation/dexon-consensus/core/crypto" "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" ) // Errors for consensus core. @@ -187,7 +188,7 @@ type consensusDKGReceiver struct { ID types.NodeID gov Governance authModule *Authenticator - nodeSetCache *NodeSetCache + nodeSetCache *utils.NodeSetCache cfgModule *configurationChain network Network logger common.Logger @@ -295,7 +296,7 @@ type Consensus struct { // Misc. dMoment time.Time - nodeSetCache *NodeSetCache + nodeSetCache *utils.NodeSetCache round uint64 roundToNotify uint64 lock sync.RWMutex @@ -319,7 +320,7 @@ func NewConsensus( var round uint64 logger.Debug("Calling Governance.Configuration", "round", round) config := gov.Configuration(round) - nodeSetCache := NewNodeSetCache(gov) + nodeSetCache := utils.NewNodeSetCache(gov) logger.Debug("Calling Governance.CRS", "round", round) // Setup auth module. authModule := NewAuthenticator(prv) diff --git a/core/lattice-data.go b/core/lattice-data.go index ce6d32b..6fe810a 100644 --- a/core/lattice-data.go +++ b/core/lattice-data.go @@ -26,12 +26,12 @@ import ( "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/dexon-foundation/dexon-consensus/core/utils" ) // Errors for sanity check error. var ( ErrDuplicatedAckOnOneChain = fmt.Errorf("duplicated ack on one chain") - ErrInvalidChainID = fmt.Errorf("invalid chain id") ErrInvalidProposerID = fmt.Errorf("invalid proposer id") ErrInvalidWitness = fmt.Errorf("invalid witness data") ErrInvalidBlock = fmt.Errorf("invalid block") @@ -181,7 +181,7 @@ func (data *latticeData) sanityCheck(b *types.Block) error { } // Check if the chain id is valid. if b.Position.ChainID >= config.numChains { - return ErrInvalidChainID + return utils.ErrInvalidChainID } // Make sure parent block is arrived. chain := data.chains[b.Position.ChainID] @@ -382,7 +382,7 @@ func (data *latticeData) prepareBlock(b *types.Block) error { } // If chainID is illegal in this round, reject it. if b.Position.ChainID >= config.numChains { - return ErrInvalidChainID + return utils.ErrInvalidChainID } // Reset fields to make sure we got these information from parent block. b.Position.Height = 0 diff --git a/core/lattice-data_test.go b/core/lattice-data_test.go index e4a894f..24f45e6 100644 --- a/core/lattice-data_test.go +++ b/core/lattice-data_test.go @@ -27,6 +27,7 @@ import ( "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/dexon-foundation/dexon-consensus/core/utils" "github.com/stretchr/testify/suite" ) @@ -217,7 +218,7 @@ func (s *LatticeDataTestSuite) TestSanityCheck() { Timestamp: time.Now().UTC(), }) // Invalid chain ID. - check(ErrInvalidChainID, &types.Block{ + check(utils.ErrInvalidChainID, &types.Block{ ParentHash: blocks[1][0].Hash, Position: types.Position{ ChainID: 100, diff --git a/core/nodeset-cache.go b/core/nodeset-cache.go deleted file mode 100644 index 26e3d55..0000000 --- a/core/nodeset-cache.go +++ /dev/null @@ -1,237 +0,0 @@ -// 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 core - -import ( - "errors" - "sync" - - "github.com/dexon-foundation/dexon-consensus/common" - "github.com/dexon-foundation/dexon-consensus/core/crypto" - "github.com/dexon-foundation/dexon-consensus/core/types" -) - -var ( - // ErrRoundNotReady means we got nil config. - ErrRoundNotReady = errors.New("round is not ready") -) - -type sets struct { - nodeSet *types.NodeSet - notarySet []map[types.NodeID]struct{} - dkgSet map[types.NodeID]struct{} -} - -// NodeSetCacheInterface interface specifies interface used by NodeSetCache. -type NodeSetCacheInterface interface { - // Configuration returns the configuration at a given round. - // Return the genesis configuration if round == 0. - Configuration(round uint64) *types.Config - - // CRS returns the CRS for a given round. - // Return the genesis CRS if round == 0. - CRS(round uint64) common.Hash - - // NodeSet returns the node set at a given round. - // Return the genesis node set if round == 0. - NodeSet(round uint64) []crypto.PublicKey -} - -// NodeSetCache caches node set information. -type NodeSetCache struct { - lock sync.RWMutex - nsIntf NodeSetCacheInterface - rounds map[uint64]*sets - keyPool map[types.NodeID]*struct { - pubKey crypto.PublicKey - refCnt int - } -} - -// NewNodeSetCache constructs an NodeSetCache instance. -func NewNodeSetCache(nsIntf NodeSetCacheInterface) *NodeSetCache { - return &NodeSetCache{ - nsIntf: nsIntf, - rounds: make(map[uint64]*sets), - keyPool: make(map[types.NodeID]*struct { - pubKey crypto.PublicKey - refCnt int - }), - } -} - -// Exists checks if a node is in node set of that round. -func (cache *NodeSetCache) Exists( - round uint64, nodeID types.NodeID) (exists bool, err error) { - - nIDs, exists := cache.get(round) - if !exists { - if nIDs, err = cache.update(round); err != nil { - return - } - } - _, exists = nIDs.nodeSet.IDs[nodeID] - return -} - -// GetPublicKey return public key for that node: -func (cache *NodeSetCache) GetPublicKey( - nodeID types.NodeID) (key crypto.PublicKey, exists bool) { - - cache.lock.RLock() - defer cache.lock.RUnlock() - - rec, exists := cache.keyPool[nodeID] - if exists { - key = rec.pubKey - } - return -} - -// GetNodeSet returns IDs of nodes set of this round as map. -func (cache *NodeSetCache) GetNodeSet( - round uint64) (nIDs *types.NodeSet, err error) { - - IDs, exists := cache.get(round) - if !exists { - if IDs, err = cache.update(round); err != nil { - return - } - } - nIDs = IDs.nodeSet.Clone() - return -} - -// GetNotarySet returns of notary set of this round. -func (cache *NodeSetCache) GetNotarySet( - round uint64, chainID uint32) (map[types.NodeID]struct{}, error) { - IDs, err := cache.getOrUpdate(round) - if err != nil { - return nil, err - } - if chainID >= uint32(len(IDs.notarySet)) { - return nil, ErrInvalidChainID - } - return cache.cloneMap(IDs.notarySet[chainID]), nil -} - -// GetDKGSet returns of DKG set of this round. -func (cache *NodeSetCache) GetDKGSet( - round uint64) (map[types.NodeID]struct{}, error) { - IDs, err := cache.getOrUpdate(round) - if err != nil { - return nil, err - } - return cache.cloneMap(IDs.dkgSet), nil -} - -func (cache *NodeSetCache) cloneMap( - nIDs map[types.NodeID]struct{}) map[types.NodeID]struct{} { - nIDsCopy := make(map[types.NodeID]struct{}, len(nIDs)) - for k := range nIDs { - nIDsCopy[k] = struct{}{} - } - return nIDsCopy -} - -func (cache *NodeSetCache) getOrUpdate(round uint64) (nIDs *sets, err error) { - s, exists := cache.get(round) - if !exists { - if s, err = cache.update(round); err != nil { - return - } - } - nIDs = s - return -} - -// update node set for that round. -// -// This cache would maintain 10 rounds before the updated round and purge -// rounds not in this range. -func (cache *NodeSetCache) update( - round uint64) (nIDs *sets, err error) { - - cache.lock.Lock() - defer cache.lock.Unlock() - - // Get the requested round. - keySet := cache.nsIntf.NodeSet(round) - if keySet == nil { - // That round is not ready yet. - err = ErrRoundNotReady - return - } - crs := cache.nsIntf.CRS(round) - if (crs == common.Hash{}) { - err = ErrRoundNotReady - return - } - // Cache new round. - nodeSet := types.NewNodeSet() - for _, key := range keySet { - nID := types.NewNodeID(key) - nodeSet.Add(nID) - if rec, exists := cache.keyPool[nID]; exists { - rec.refCnt++ - } else { - cache.keyPool[nID] = &struct { - pubKey crypto.PublicKey - refCnt int - }{key, 1} - } - } - cfg := cache.nsIntf.Configuration(round) - nIDs = &sets{ - nodeSet: nodeSet, - notarySet: make([]map[types.NodeID]struct{}, cfg.NumChains), - dkgSet: nodeSet.GetSubSet( - int(cfg.DKGSetSize), types.NewDKGSetTarget(crs)), - } - for i := range nIDs.notarySet { - nIDs.notarySet[i] = nodeSet.GetSubSet( - int(cfg.NotarySetSize), types.NewNotarySetTarget(crs, uint32(i))) - } - - cache.rounds[round] = nIDs - // Purge older rounds. - for rID, nIDs := range cache.rounds { - nodeSet := nIDs.nodeSet - if round-rID <= 5 { - continue - } - for nID := range nodeSet.IDs { - rec := cache.keyPool[nID] - if rec.refCnt--; rec.refCnt == 0 { - delete(cache.keyPool, nID) - } - } - delete(cache.rounds, rID) - } - return -} - -func (cache *NodeSetCache) get( - round uint64) (nIDs *sets, exists bool) { - - cache.lock.RLock() - defer cache.lock.RUnlock() - - nIDs, exists = cache.rounds[round] - return -} diff --git a/core/nodeset-cache_test.go b/core/nodeset-cache_test.go deleted file mode 100644 index 828ef49..0000000 --- a/core/nodeset-cache_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// 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 core - -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/ecdsa" - "github.com/dexon-foundation/dexon-consensus/core/test" - "github.com/dexon-foundation/dexon-consensus/core/types" - "github.com/stretchr/testify/suite" -) - -type nsIntf struct { - s *NodeSetCacheTestSuite - crs common.Hash - curKeys []crypto.PublicKey -} - -func (g *nsIntf) Configuration(round uint64) (cfg *types.Config) { - return &types.Config{ - NotarySetSize: 7, - DKGSetSize: 7, - NumChains: 4, - } -} -func (g *nsIntf) CRS(round uint64) (b common.Hash) { return g.crs } -func (g *nsIntf) NodeSet(round uint64) []crypto.PublicKey { - // Randomly generating keys, and check them for verification. - g.curKeys = []crypto.PublicKey{} - for i := 0; i < 10; i++ { - prvKey, err := ecdsa.NewPrivateKey() - g.s.Require().NoError(err) - g.curKeys = append(g.curKeys, prvKey.PublicKey()) - } - return g.curKeys -} - -type NodeSetCacheTestSuite struct { - suite.Suite -} - -func (s *NodeSetCacheTestSuite) TestGovernanceIntf() { - // NodeSetCacheInterface should let Governance implement it. - var gov Governance - _, pubKeys, err := test.NewKeys(7) - s.Require().NoError(err) - gov, err = test.NewGovernance(pubKeys, 250*time.Millisecond, ConfigRoundShift) - s.Require().NoError(err) - _, ok := gov.(NodeSetCacheInterface) - s.True(ok) -} - -func (s *NodeSetCacheTestSuite) TestBasicUsage() { - var ( - nsIntf = &nsIntf{ - s: s, - crs: common.NewRandomHash(), - } - cache = NewNodeSetCache(nsIntf) - req = s.Require() - ) - - chk := func( - cache *NodeSetCache, round uint64, nodeSet map[types.NodeID]struct{}) { - - for nID := range nodeSet { - // It should exists. - exists, err := cache.Exists(round, nID) - req.NoError(err) - req.True(exists) - // We could get keys. - key, exists := cache.GetPublicKey(nID) - req.NotNil(key) - req.True(exists) - } - } - - // Try to get round 0. - nodeSet0, err := cache.GetNodeSet(0) - req.NoError(err) - chk(cache, 0, nodeSet0.IDs) - notarySet, err := cache.GetNotarySet(0, 0) - req.NoError(err) - chk(cache, 0, notarySet) - dkgSet, err := cache.GetDKGSet(0) - req.NoError(err) - chk(cache, 0, dkgSet) - // Try to get round 1. - nodeSet1, err := cache.GetNodeSet(1) - req.NoError(err) - chk(cache, 0, nodeSet0.IDs) - chk(cache, 1, nodeSet1.IDs) - // Try to get round 6, round 0 should be purged. - nodeSet6, err := cache.GetNodeSet(6) - req.NoError(err) - chk(cache, 1, nodeSet1.IDs) - chk(cache, 6, nodeSet6.IDs) - for nID := range nodeSet0.IDs { - _, exists := cache.GetPublicKey(nID) - req.False(exists) - } -} - -func TestNodeSetCache(t *testing.T) { - suite.Run(t, new(NodeSetCacheTestSuite)) -} diff --git a/core/utils/nodeset-cache.go b/core/utils/nodeset-cache.go new file mode 100644 index 0000000..a8f8fe5 --- /dev/null +++ b/core/utils/nodeset-cache.go @@ -0,0 +1,239 @@ +// 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 utils + +import ( + "errors" + "sync" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/types" +) + +var ( + // ErrRoundNotReady means we got nil config. + ErrRoundNotReady = errors.New("round is not ready") + // ErrInvalidChainID means the chain ID is unexpected. + ErrInvalidChainID = errors.New("invalid chain id") +) + +type sets struct { + nodeSet *types.NodeSet + notarySet []map[types.NodeID]struct{} + dkgSet map[types.NodeID]struct{} +} + +// NodeSetCacheInterface interface specifies interface used by NodeSetCache. +type NodeSetCacheInterface interface { + // Configuration returns the configuration at a given round. + // Return the genesis configuration if round == 0. + Configuration(round uint64) *types.Config + + // CRS returns the CRS for a given round. + // Return the genesis CRS if round == 0. + CRS(round uint64) common.Hash + + // NodeSet returns the node set at a given round. + // Return the genesis node set if round == 0. + NodeSet(round uint64) []crypto.PublicKey +} + +// NodeSetCache caches node set information. +type NodeSetCache struct { + lock sync.RWMutex + nsIntf NodeSetCacheInterface + rounds map[uint64]*sets + keyPool map[types.NodeID]*struct { + pubKey crypto.PublicKey + refCnt int + } +} + +// NewNodeSetCache constructs an NodeSetCache instance. +func NewNodeSetCache(nsIntf NodeSetCacheInterface) *NodeSetCache { + return &NodeSetCache{ + nsIntf: nsIntf, + rounds: make(map[uint64]*sets), + keyPool: make(map[types.NodeID]*struct { + pubKey crypto.PublicKey + refCnt int + }), + } +} + +// Exists checks if a node is in node set of that round. +func (cache *NodeSetCache) Exists( + round uint64, nodeID types.NodeID) (exists bool, err error) { + + nIDs, exists := cache.get(round) + if !exists { + if nIDs, err = cache.update(round); err != nil { + return + } + } + _, exists = nIDs.nodeSet.IDs[nodeID] + return +} + +// GetPublicKey return public key for that node: +func (cache *NodeSetCache) GetPublicKey( + nodeID types.NodeID) (key crypto.PublicKey, exists bool) { + + cache.lock.RLock() + defer cache.lock.RUnlock() + + rec, exists := cache.keyPool[nodeID] + if exists { + key = rec.pubKey + } + return +} + +// GetNodeSet returns IDs of nodes set of this round as map. +func (cache *NodeSetCache) GetNodeSet( + round uint64) (nIDs *types.NodeSet, err error) { + + IDs, exists := cache.get(round) + if !exists { + if IDs, err = cache.update(round); err != nil { + return + } + } + nIDs = IDs.nodeSet.Clone() + return +} + +// GetNotarySet returns of notary set of this round. +func (cache *NodeSetCache) GetNotarySet( + round uint64, chainID uint32) (map[types.NodeID]struct{}, error) { + IDs, err := cache.getOrUpdate(round) + if err != nil { + return nil, err + } + if chainID >= uint32(len(IDs.notarySet)) { + return nil, ErrInvalidChainID + } + return cache.cloneMap(IDs.notarySet[chainID]), nil +} + +// GetDKGSet returns of DKG set of this round. +func (cache *NodeSetCache) GetDKGSet( + round uint64) (map[types.NodeID]struct{}, error) { + IDs, err := cache.getOrUpdate(round) + if err != nil { + return nil, err + } + return cache.cloneMap(IDs.dkgSet), nil +} + +func (cache *NodeSetCache) cloneMap( + nIDs map[types.NodeID]struct{}) map[types.NodeID]struct{} { + nIDsCopy := make(map[types.NodeID]struct{}, len(nIDs)) + for k := range nIDs { + nIDsCopy[k] = struct{}{} + } + return nIDsCopy +} + +func (cache *NodeSetCache) getOrUpdate(round uint64) (nIDs *sets, err error) { + s, exists := cache.get(round) + if !exists { + if s, err = cache.update(round); err != nil { + return + } + } + nIDs = s + return +} + +// update node set for that round. +// +// This cache would maintain 10 rounds before the updated round and purge +// rounds not in this range. +func (cache *NodeSetCache) update( + round uint64) (nIDs *sets, err error) { + + cache.lock.Lock() + defer cache.lock.Unlock() + + // Get the requested round. + keySet := cache.nsIntf.NodeSet(round) + if keySet == nil { + // That round is not ready yet. + err = ErrRoundNotReady + return + } + crs := cache.nsIntf.CRS(round) + if (crs == common.Hash{}) { + err = ErrRoundNotReady + return + } + // Cache new round. + nodeSet := types.NewNodeSet() + for _, key := range keySet { + nID := types.NewNodeID(key) + nodeSet.Add(nID) + if rec, exists := cache.keyPool[nID]; exists { + rec.refCnt++ + } else { + cache.keyPool[nID] = &struct { + pubKey crypto.PublicKey + refCnt int + }{key, 1} + } + } + cfg := cache.nsIntf.Configuration(round) + nIDs = &sets{ + nodeSet: nodeSet, + notarySet: make([]map[types.NodeID]struct{}, cfg.NumChains), + dkgSet: nodeSet.GetSubSet( + int(cfg.DKGSetSize), types.NewDKGSetTarget(crs)), + } + for i := range nIDs.notarySet { + nIDs.notarySet[i] = nodeSet.GetSubSet( + int(cfg.NotarySetSize), types.NewNotarySetTarget(crs, uint32(i))) + } + + cache.rounds[round] = nIDs + // Purge older rounds. + for rID, nIDs := range cache.rounds { + nodeSet := nIDs.nodeSet + if round-rID <= 5 { + continue + } + for nID := range nodeSet.IDs { + rec := cache.keyPool[nID] + if rec.refCnt--; rec.refCnt == 0 { + delete(cache.keyPool, nID) + } + } + delete(cache.rounds, rID) + } + return +} + +func (cache *NodeSetCache) get( + round uint64) (nIDs *sets, exists bool) { + + cache.lock.RLock() + defer cache.lock.RUnlock() + + nIDs, exists = cache.rounds[round] + return +} diff --git a/core/utils/nodeset-cache_test.go b/core/utils/nodeset-cache_test.go new file mode 100644 index 0000000..27c8c83 --- /dev/null +++ b/core/utils/nodeset-cache_test.go @@ -0,0 +1,112 @@ +// 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 utils + +import ( + "testing" + + "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core/crypto" + "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa" + "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/stretchr/testify/suite" +) + +type nsIntf struct { + s *NodeSetCacheTestSuite + crs common.Hash + curKeys []crypto.PublicKey +} + +func (g *nsIntf) Configuration(round uint64) (cfg *types.Config) { + return &types.Config{ + NotarySetSize: 7, + DKGSetSize: 7, + NumChains: 4, + } +} +func (g *nsIntf) CRS(round uint64) (b common.Hash) { return g.crs } +func (g *nsIntf) NodeSet(round uint64) []crypto.PublicKey { + // Randomly generating keys, and check them for verification. + g.curKeys = []crypto.PublicKey{} + for i := 0; i < 10; i++ { + prvKey, err := ecdsa.NewPrivateKey() + g.s.Require().NoError(err) + g.curKeys = append(g.curKeys, prvKey.PublicKey()) + } + return g.curKeys +} + +type NodeSetCacheTestSuite struct { + suite.Suite +} + +func (s *NodeSetCacheTestSuite) TestBasicUsage() { + var ( + nsIntf = &nsIntf{ + s: s, + crs: common.NewRandomHash(), + } + cache = NewNodeSetCache(nsIntf) + req = s.Require() + ) + + chk := func( + cache *NodeSetCache, round uint64, nodeSet map[types.NodeID]struct{}) { + + for nID := range nodeSet { + // It should exists. + exists, err := cache.Exists(round, nID) + req.NoError(err) + req.True(exists) + // We could get keys. + key, exists := cache.GetPublicKey(nID) + req.NotNil(key) + req.True(exists) + } + } + + // Try to get round 0. + nodeSet0, err := cache.GetNodeSet(0) + req.NoError(err) + chk(cache, 0, nodeSet0.IDs) + notarySet, err := cache.GetNotarySet(0, 0) + req.NoError(err) + chk(cache, 0, notarySet) + dkgSet, err := cache.GetDKGSet(0) + req.NoError(err) + chk(cache, 0, dkgSet) + // Try to get round 1. + nodeSet1, err := cache.GetNodeSet(1) + req.NoError(err) + chk(cache, 0, nodeSet0.IDs) + chk(cache, 1, nodeSet1.IDs) + // Try to get round 6, round 0 should be purged. + nodeSet6, err := cache.GetNodeSet(6) + req.NoError(err) + chk(cache, 1, nodeSet1.IDs) + chk(cache, 6, nodeSet6.IDs) + for nID := range nodeSet0.IDs { + _, exists := cache.GetPublicKey(nID) + req.False(exists) + } +} + +func TestNodeSetCache(t *testing.T) { + suite.Run(t, new(NodeSetCacheTestSuite)) +} diff --git a/integration_test/node.go b/integration_test/node.go index 4657d83..aa3a703 100644 --- a/integration_test/node.go +++ b/integration_test/node.go @@ -27,6 +27,7 @@ import ( "github.com/dexon-foundation/dexon-consensus/core/crypto" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/dexon-foundation/dexon-consensus/core/utils" ) type consensusEventType int @@ -162,7 +163,7 @@ func (n *Node) handleProposeBlock(when time.Time, payload interface{}) ( }) b, err := n.prepareBlock(pos.round, pos.chain, when) if err != nil { - if err == core.ErrInvalidChainID { + if err == utils.ErrInvalidChainID { // This chain is not included in this round, retry in next round. events = append(events, newProposeBlockEvent( n.ID, b.Position.Round+1, b.Position.ChainID, -- cgit v1.2.3