diff options
Diffstat (limited to 'core/nodeset-cache.go')
-rw-r--r-- | core/nodeset-cache.go | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/core/nodeset-cache.go b/core/nodeset-cache.go new file mode 100644 index 0000000..d574817 --- /dev/null +++ b/core/nodeset-cache.go @@ -0,0 +1,158 @@ +// 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 core + +import ( + "errors" + "sync" + + "github.com/dexon-foundation/dexon-consensus-core/core/crypto" + "github.com/dexon-foundation/dexon-consensus-core/core/types" +) + +var ( + // ErrRoundNotReady means we got nil config from governance contract. + ErrRoundNotReady = errors.New("round is not ready") +) + +// NodeSetCache caches node set information from governance contract. +type NodeSetCache struct { + lock sync.RWMutex + gov Governance + rounds map[uint64]map[types.NodeID]struct{} + keyPool map[types.NodeID]*struct { + pubKey crypto.PublicKey + refCnt int + } +} + +// NewNodeSetCache constructs an NodeSetCache instance. +func NewNodeSetCache(gov Governance) *NodeSetCache { + return &NodeSetCache{ + gov: gov, + rounds: make(map[uint64]map[types.NodeID]struct{}), + 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[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 +} + +// GetNodeIDs returns IDs of nodes set of this round as map. +func (cache *NodeSetCache) GetNodeIDs( + round uint64) (nIDs map[types.NodeID]struct{}, err error) { + + IDs, exists := cache.get(round) + if !exists { + if IDs, err = cache.update(round); err != nil { + return + } + } + // Clone the map. + nIDs = make(map[types.NodeID]struct{}) + for ID := range IDs { + nIDs[ID] = struct{}{} + } + 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 map[types.NodeID]struct{}, err error) { + + cache.lock.Lock() + defer cache.lock.Unlock() + + // Get the requested round from governance contract. + keySet := cache.gov.GetNodeSet(round) + if keySet == nil { + // That round is not ready yet. + err = ErrRoundNotReady + return + } + // Cache new round. + nIDs = make(map[types.NodeID]struct{}) + for _, key := range keySet { + nID := types.NewNodeID(key) + nIDs[nID] = struct{}{} + if rec, exists := cache.keyPool[nID]; exists { + rec.refCnt++ + } else { + cache.keyPool[nID] = &struct { + pubKey crypto.PublicKey + refCnt int + }{key, 1} + } + } + cache.rounds[round] = nIDs + // Purge older rounds. + for rID, nIDs := range cache.rounds { + if round-rID <= 5 { + continue + } + for nID := range nIDs { + 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 map[types.NodeID]struct{}, exists bool) { + + cache.lock.RLock() + defer cache.lock.RUnlock() + + nIDs, exists = cache.rounds[round] + return +} |