From 194c8fdc922b4173cf433e8f92711459b97c3ab0 Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Tue, 20 Nov 2018 13:51:27 +0800 Subject: vendor: sync to latest core (#37) --- .../dexon-foundation/dexon-consensus/LICENSE | 165 ++++++++++++++ .../dexon-consensus/core/agreement-state.go | 18 ++ .../dexon-consensus/core/agreement.go | 10 + .../dexon-consensus/core/blockdb/memory.go | 4 + .../dexon-consensus/core/compaction-chain.go | 71 ++++-- .../dexon-consensus/core/configuration-chain.go | 39 +++- .../dexon-consensus/core/consensus.go | 140 ++++++------ .../dexon-consensus/core/lattice-data.go | 6 +- .../dexon-consensus/core/lattice.go | 49 +++-- .../dexon-consensus/core/nodeset-cache.go | 237 -------------------- .../dexon-consensus/core/total-ordering-syncer.go | 3 + .../dexon-consensus/core/types/block-randomness.go | 4 +- .../dexon-consensus/core/types/block.go | 2 +- .../dexon-consensus/core/types/dkg/dkg.go | 8 +- .../dexon-consensus/core/types/position.go | 3 +- .../dexon-consensus/core/types/vote.go | 3 +- .../dexon-foundation/dexon-consensus/core/utils.go | 9 + .../dexon-consensus/core/utils/nodeset-cache.go | 239 +++++++++++++++++++++ 18 files changed, 637 insertions(+), 373 deletions(-) create mode 100644 vendor/github.com/dexon-foundation/dexon-consensus/LICENSE delete mode 100644 vendor/github.com/dexon-foundation/dexon-consensus/core/nodeset-cache.go create mode 100644 vendor/github.com/dexon-foundation/dexon-consensus/core/utils/nodeset-cache.go (limited to 'vendor/github.com') diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/LICENSE b/vendor/github.com/dexon-foundation/dexon-consensus/LICENSE new file mode 100644 index 000000000..0a041280b --- /dev/null +++ b/vendor/github.com/dexon-foundation/dexon-consensus/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go index 9023799a3..77f293376 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go @@ -40,6 +40,7 @@ const ( stateCommit stateForward statePullVote + stateSleep ) var nullBlockHash = common.Hash{} @@ -156,3 +157,20 @@ func (s *pullVoteState) clocks() int { return 4 } func (s *pullVoteState) nextState() (agreementState, error) { return s, nil } + +// ----- SleepState ----- +// sleepState is a special state after BA has output and waits for restart. +type sleepState struct { + a *agreementData +} + +func newSleepState(a *agreementData) *sleepState { + return &sleepState{a: a} +} + +func (s *sleepState) state() agreementStateType { return stateSleep } +func (s *sleepState) clocks() int { return 65536 } + +func (s *sleepState) nextState() (agreementState, error) { + return s, nil +} diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go index f31b7efba..ff1c71a7c 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go @@ -232,6 +232,8 @@ func isStop(aID types.Position) bool { // clocks returns how many time this state is required. func (a *agreement) clocks() int { + a.data.lock.RLock() + defer a.data.lock.RUnlock() return a.state.clocks() } @@ -250,6 +252,14 @@ func (a *agreement) agreementID() types.Position { // nextState is called at the specific clock time. func (a *agreement) nextState() (err error) { + if func() bool { + a.lock.RLock() + defer a.lock.RUnlock() + return a.hasOutput + }() { + a.state = newSleepState(a.data) + return + } a.state, err = a.state.nextState() return } diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go index 760646e10..b45af229b 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go @@ -124,6 +124,10 @@ func (m *MemBackedBlockDB) Put(block types.Block) error { // Update updates a block in the database. func (m *MemBackedBlockDB) Update(block types.Block) error { + if !m.Has(block.Hash) { + return ErrBlockDoesNotExist + } + m.blocksMutex.Lock() defer m.blocksMutex.Unlock() diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go index 4a5ba3637..f6bc0149d 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go @@ -33,6 +33,10 @@ var ( "block not registered") ErrNotInitiazlied = fmt.Errorf( "not initialized") + ErrTSigNotReady = fmt.Errorf( + "tsig not ready") + ErrIncorrectBlockRandomnessResult = fmt.Errorf( + "incorrect block randomness result") ) type finalizedBlockHeap = types.ByFinalizationHeight @@ -154,6 +158,24 @@ func (cc *compactionChain) extractBlocks() []*types.Block { return deliveringBlocks } +func (cc *compactionChain) verifyRandomness( + blockHash common.Hash, round uint64, randomness []byte) (bool, error) { + if round == 0 { + return len(randomness) == 0, nil + } + // Randomness is not available at round 0. + v, ok, err := cc.tsigVerifier.UpdateAndGet(round) + if err != nil { + return false, err + } + if !ok { + return false, ErrTSigNotReady + } + return v.VerifySignature(blockHash, crypto.Signature{ + Type: "bls", + Signature: randomness}), nil +} + func (cc *compactionChain) processFinalizedBlock(block *types.Block) { if block.Finalization.Height <= cc.lastBlock().Finalization.Height { return @@ -166,6 +188,19 @@ func (cc *compactionChain) processFinalizedBlock(block *types.Block) { cc.lock.Lock() defer cc.lock.Unlock() + // The randomness result is missed previously. + if cc.blockRegisteredNoLock(block.Hash) { + ok, err := cc.verifyRandomness( + block.Hash, block.Position.Round, block.Finalization.Randomness) + if err != nil { + panic(err) + } + if ok { + cc.blockRandomness[block.Hash] = block.Finalization.Randomness + } + return + } + heap.Push(cc.pendingFinalizedBlocks, block) return @@ -206,22 +241,14 @@ func (cc *compactionChain) extractFinalizedBlocks() []*types.Block { b.Finalization.Height == prevBlock.Finalization.Height { continue } - round := b.Position.Round - if round != 0 { - // Randomness is not available at round 0. - v, ok, err := cc.tsigVerifier.UpdateAndGet(round) - if err != nil { - continue - } - if !ok { - toPending = append(toPending, b) - continue - } - if ok := v.VerifySignature(b.Hash, crypto.Signature{ - Type: "bls", - Signature: b.Finalization.Randomness}); !ok { - continue - } + ok, err := cc.verifyRandomness( + b.Hash, b.Position.Round, b.Finalization.Randomness) + if err != nil { + toPending = append(toPending, b) + continue + } + if !ok { + continue } // Fork resolution: choose block with smaller hash. if prevBlock.Finalization.Height == b.Finalization.Height { @@ -261,11 +288,19 @@ func (cc *compactionChain) processBlockRandomnessResult( rand *types.BlockRandomnessResult) error { cc.lock.Lock() defer cc.lock.Unlock() - // TODO(jimmy-dexon): the result should not be discarded here. Blocks may - // be registered later. if !cc.blockRegisteredNoLock(rand.BlockHash) { + // If the randomness result is discarded here, it'll later be processed by + //finalized block return ErrBlockNotRegistered } + ok, err := cc.verifyRandomness( + rand.BlockHash, rand.Position.Round, rand.Randomness) + if err != nil { + return err + } + if !ok { + return ErrIncorrectBlockRandomnessResult + } cc.blockRandomness[rand.BlockHash] = rand.Randomness return nil } diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go index bda2fdf62..fdfcd13d0 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/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, @@ -106,6 +107,10 @@ func (cc *configurationChain) runDKG(round uint64) error { cc.dkgLock.Lock() defer cc.dkgLock.Unlock() if cc.dkg == nil || cc.dkg.round != round { + if cc.dkg != nil && cc.dkg.round > round { + cc.logger.Warn("DKG canceled", "round", round) + return nil + } return ErrDKGNotRegistered } if func() bool { @@ -116,6 +121,11 @@ func (cc *configurationChain) runDKG(round uint64) error { }() { return nil } + cc.logger.Debug("Calling Governance.IsDKGFinal", "round", round) + if cc.gov.IsDKGFinal(round) { + cc.logger.Warn("DKG already final", "round", round) + return nil + } ticker := newTicker(cc.gov, round, TickerDKG) cc.dkgLock.Unlock() @@ -182,10 +192,6 @@ func (cc *configurationChain) runDKG(round uint64) error { if err != nil { return err } - signer, err := cc.dkg.recoverShareSecret(gpk.qualifyIDs) - if err != nil { - return err - } qualifies := "" for nID := range gpk.qualifyNodeIDs { qualifies += fmt.Sprintf("%s ", nID.String()[:6]) @@ -195,6 +201,14 @@ func (cc *configurationChain) runDKG(round uint64) error { "round", round, "count", len(gpk.qualifyIDs), "qualifies", qualifies) + if _, exist := gpk.qualifyNodeIDs[cc.ID]; !exist { + cc.logger.Warn("Self is not in Qualify Nodes") + return nil + } + signer, err := cc.dkg.recoverShareSecret(gpk.qualifyIDs) + if err != nil { + return err + } cc.dkgResult.Lock() defer cc.dkgResult.Unlock() cc.dkgSigner[round] = signer @@ -264,13 +278,24 @@ func (cc *configurationChain) runTSig( } } }() + timeout := make(chan struct{}, 1) + go func() { + // TODO(jimmy-dexon): make timeout configurable. + time.Sleep(5 * time.Second) + timeout <- struct{}{} + cc.tsigReady.Broadcast() + }() var signature crypto.Signature var err error for func() bool { signature, err = cc.tsig[hash].signature() + select { + case <-timeout: + return false + default: + } return err == ErrNotEnoughtPartialSignatures }() { - // TODO(jimmy-dexon): add a timeout here. cc.tsigReady.Wait() } delete(cc.tsig, hash) diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go index 3d46c5c8b..e09ee2579 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/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. @@ -55,8 +56,6 @@ var ( "incorrect vote position") ErrIncorrectVoteProposer = fmt.Errorf( "incorrect vote proposer") - ErrIncorrectBlockRandomnessResult = fmt.Errorf( - "incorrect block randomness result") ) // consensusBAReceiver implements agreementReceiver. @@ -135,6 +134,8 @@ func (recv *consensusBAReceiver) ConfirmBlock( "hash", hash, "chainID", recv.chainID) recv.agreementModule.addCandidateBlock(block) + recv.agreementModule.lock.Lock() + defer recv.agreementModule.lock.Unlock() recv.ConfirmBlock(block.Hash, votes) }() return @@ -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 @@ -284,6 +285,7 @@ type Consensus struct { // Dexon consensus v1's modules. lattice *Lattice ccModule *compactionChain + toSyncer *totalOrderingSyncer // Interfaces. db blockdb.BlockDatabase @@ -294,7 +296,7 @@ type Consensus struct { // Misc. dMoment time.Time - nodeSetCache *NodeSetCache + nodeSetCache *utils.NodeSetCache round uint64 roundToNotify uint64 lock sync.RWMutex @@ -318,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) @@ -441,7 +443,7 @@ func (con *Consensus) Run(initBlock *types.Block) { con.cfgModule.registerDKG(initRound, int(initConfig.DKGSetSize)/3+1) con.event.RegisterTime(con.dMoment.Add(initConfig.RoundInterval/4), func(time.Time) { - con.runDKGTSIG(initRound, initConfig) + con.runDKG(initRound, initConfig) }) } con.initialRound(con.dMoment, initRound, initConfig) @@ -492,6 +494,12 @@ BALoop: select { case newNotary := <-recv.restartNotary: if newNotary { + con.logger.Debug("Calling Governance.CRS", "round", recv.round) + crs = con.gov.CRS(recv.round) + if (crs == common.Hash{}) { + // Governance is out-of-sync. + continue BALoop + } configForNewRound := con.gov.Configuration(recv.round) recv.changeNotaryTime = recv.changeNotaryTime.Add(configForNewRound.RoundInterval) @@ -499,8 +507,6 @@ BALoop: if err != nil { panic(err) } - con.logger.Debug("Calling Governance.CRS", "round", recv.round) - crs = con.gov.CRS(recv.round) con.logger.Debug("Calling Governance.Configuration", "round", recv.round) nIDs = nodes.GetSubSet( @@ -541,8 +547,8 @@ BALoop: } } -// runDKGTSIG starts running DKG+TSIG protocol. -func (con *Consensus) runDKGTSIG(round uint64, config *types.Config) { +// runDKG starts running DKG protocol. +func (con *Consensus) runDKG(round uint64, config *types.Config) { con.dkgReady.L.Lock() defer con.dkgReady.L.Unlock() if con.dkgRunning != 0 { @@ -564,41 +570,18 @@ func (con *Consensus) runDKGTSIG(round uint64, config *types.Config) { } }() if err := con.cfgModule.runDKG(round); err != nil { - panic(err) - } - nodes, err := con.nodeSetCache.GetNodeSet(round) - if err != nil { - panic(err) - } - con.logger.Debug("Calling Governance.Configuration", "round", round) - hash := HashConfigurationBlock( - nodes.IDs, - con.gov.Configuration(round), - common.Hash{}, - con.cfgModule.prevHash) - psig, err := con.cfgModule.preparePartialSignature( - round, hash) - if err != nil { - panic(err) - } - if err = con.authModule.SignDKGPartialSignature(psig); err != nil { - panic(err) - } - if err = con.cfgModule.processPartialSignature(psig); err != nil { - panic(err) - } - con.logger.Debug("Calling Network.BroadcastDKGPartialSignature", - "proposer", psig.ProposerID, - "round", psig.Round, - "hash", psig.Hash) - con.network.BroadcastDKGPartialSignature(psig) - if _, err = con.cfgModule.runBlockTSig(round, hash); err != nil { - panic(err) + con.logger.Error("Failed to runDKG", "error", err) } }() } func (con *Consensus) runCRS(round uint64) { + con.logger.Debug("Calling Governance.CRS to check if already proposed", + "round", round+1) + if (con.gov.CRS(round+1) != common.Hash{}) { + con.logger.Info("CRS already proposed", "round", round+1) + return + } // Start running next round CRS. con.logger.Debug("Calling Governance.CRS", "round", round) psig, err := con.cfgModule.preparePartialSignature(round, con.gov.CRS(round)) @@ -683,7 +666,7 @@ func (con *Consensus) initialRound( con.logger.Debug("Calling Governance.Configuration", "round", nextRound) nextConfig := con.gov.Configuration(nextRound) - con.runDKGTSIG(nextRound, nextConfig) + con.runDKG(nextRound, nextConfig) }) }(round + 1) }) @@ -695,23 +678,6 @@ func (con *Consensus) initialRound( con.logger.Debug("Calling Governance.Configuration", "round", nextRound) nextConfig := con.gov.Configuration(nextRound) - // Get configuration for the round next to next round. Configuration - // for that round should be ready at this moment and is required for - // lattice module. This logic is related to: - // - roundShift - // - notifyGenesisRound - futureRound := nextRound + 1 - con.logger.Debug("Calling Governance.Configuration", - "round", futureRound) - futureConfig := con.gov.Configuration(futureRound) - con.logger.Debug("Append Config", "round", futureRound) - if err := con.lattice.AppendConfig( - futureRound, futureConfig); err != nil { - con.logger.Debug("Unable to append config", - "round", futureRound, - "error", err) - panic(err) - } con.initialRound( startTime.Add(config.RoundInterval), nextRound, nextConfig) }) @@ -833,6 +799,12 @@ func (con *Consensus) proposeEmptyBlock( // ProcessVote is the entry point to submit ont vote to a Consensus instance. func (con *Consensus) ProcessVote(vote *types.Vote) (err error) { + if vote.Position.ChainID >= uint32(len(con.baModules)) { + return nil + } + if isStop(con.baModules[vote.Position.ChainID].agreementID()) { + return nil + } v := vote.Clone() err = con.baModules[v.Position.ChainID].processVote(v) return err @@ -877,6 +849,9 @@ func (con *Consensus) ProcessAgreementResult( // Syncing BA Module. agreement := con.baModules[rand.Position.ChainID] aID := agreement.agreementID() + if isStop(aID) { + return nil + } if rand.Position.Newer(&aID) { con.logger.Info("Syncing BA", "position", &rand.Position) nodes, err := con.nodeSetCache.GetNodeSet(rand.Position.Round) @@ -959,31 +934,17 @@ func (con *Consensus) ProcessBlockRandomnessResult( if rand.Position.Round == 0 { return nil } - if !con.ccModule.blockRegistered(rand.BlockHash) { - return nil - } - round := rand.Position.Round - v, ok, err := con.ccModule.tsigVerifier.UpdateAndGet(round) - if err != nil { + if err := con.ccModule.processBlockRandomnessResult(rand); err != nil { + if err == ErrBlockNotRegistered { + err = nil + } return err } - if !ok { - return nil - } - if !v.VerifySignature( - rand.BlockHash, crypto.Signature{Signature: rand.Randomness}) { - return ErrIncorrectBlockRandomnessResult - } con.logger.Debug("Calling Network.BroadcastRandomnessResult", "hash", rand.BlockHash, "position", &rand.Position, "randomness", hex.EncodeToString(rand.Randomness)) con.network.BroadcastRandomnessResult(rand) - if err := con.ccModule.processBlockRandomnessResult(rand); err != nil { - if err != ErrBlockNotRegistered { - return err - } - } return nil } @@ -1000,6 +961,23 @@ func (con *Consensus) deliverBlock(b *types.Block) { con.logger.Debug("Calling Application.BlockDelivered", "block", b) con.app.BlockDelivered(b.Hash, b.Position, b.Finalization.Clone()) if b.Position.Round == con.roundToNotify { + // Get configuration for the round next to next round. Configuration + // for that round should be ready at this moment and is required for + // lattice module. This logic is related to: + // - roundShift + // - notifyGenesisRound + futureRound := con.roundToNotify + 1 + con.logger.Debug("Calling Governance.Configuration", + "round", con.roundToNotify) + futureConfig := con.gov.Configuration(futureRound) + con.logger.Debug("Append Config", "round", futureRound) + if err := con.lattice.AppendConfig( + futureRound, futureConfig); err != nil { + con.logger.Debug("Unable to append config", + "round", futureRound, + "error", err) + panic(err) + } // Only the first block delivered of that round would // trigger this noitification. con.logger.Debug("Calling Governance.NotifyRoundHeight", @@ -1048,7 +1026,10 @@ func (con *Consensus) processBlock(block *types.Block) (err error) { // processFinalizedBlock is the entry point for syncing blocks. func (con *Consensus) processFinalizedBlock(block *types.Block) (err error) { if err = con.lattice.SanityCheck(block); err != nil { - return + if err != ErrRetrySanityCheckLater { + return + } + err = nil } con.ccModule.processFinalizedBlock(block) for { @@ -1066,6 +1047,11 @@ func (con *Consensus) processFinalizedBlock(block *types.Block) (err error) { } err = nil } + con.lattice.ProcessFinalizedBlock(b) + // TODO(jimmy): BlockConfirmed and DeliverBlock may not be removed if + // application implements state snapshot. + con.logger.Debug("Calling Application.BlockConfirmed", "block", b) + con.app.BlockConfirmed(*b.Clone()) con.deliverBlock(b) } } diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice-data.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice-data.go index ce6d32ba3..6fe810ac0 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice-data.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/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/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go index 108f2887b..7b66bd557 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go @@ -41,6 +41,7 @@ type Lattice struct { pool blockPool retryAdd bool data *latticeData + toSyncer *totalOrderingSyncer toModule *totalOrdering ctModule *consensusTimestamp logger common.Logger @@ -64,6 +65,7 @@ func NewLattice( debug: debug, pool: newBlockPool(cfg.NumChains), data: newLatticeData(db, dMoment, round, cfg), + toSyncer: newTotalOrderingSyncer(cfg.NumChains), toModule: newTotalOrdering(dMoment, cfg), ctModule: newConsensusTimestamp(dMoment, round, cfg.NumChains), logger: logger, @@ -144,7 +146,6 @@ func (l *Lattice) SanityCheck(b *types.Block) (err error) { if _, ok := err.(*ErrAckingBlockNotExists); ok { err = ErrRetrySanityCheckLater } - l.logger.Error("Sanity Check failed", "error", err) return } return @@ -236,28 +237,31 @@ func (l *Lattice) ProcessBlock( return } - // Perform total ordering for each block added to lattice. - for _, b = range inLattice { - toDelivered, deliveredMode, err = l.toModule.processBlock(b) - if err != nil { - // All errors from total ordering is serious, should panic. - panic(err) - } - if len(toDelivered) == 0 { - continue - } - hashes := make(common.Hashes, len(toDelivered)) - for idx := range toDelivered { - hashes[idx] = toDelivered[idx].Hash - } - if l.debug != nil { - l.debug.TotalOrderingDelivered(hashes, deliveredMode) - } - // Perform consensus timestamp module. - if err = l.ctModule.processBlocks(toDelivered); err != nil { - return + for _, blockToSyncer := range inLattice { + toTotalOrdering := l.toSyncer.processBlock(blockToSyncer) + // Perform total ordering for each block added to lattice. + for _, b = range toTotalOrdering { + toDelivered, deliveredMode, err = l.toModule.processBlock(b) + if err != nil { + // All errors from total ordering is serious, should panic. + panic(err) + } + if len(toDelivered) == 0 { + continue + } + hashes := make(common.Hashes, len(toDelivered)) + for idx := range toDelivered { + hashes[idx] = toDelivered[idx].Hash + } + if l.debug != nil { + l.debug.TotalOrderingDelivered(hashes, deliveredMode) + } + // Perform consensus timestamp module. + if err = l.ctModule.processBlocks(toDelivered); err != nil { + return + } + delivered = append(delivered, toDelivered...) } - delivered = append(delivered, toDelivered...) } return } @@ -304,4 +308,5 @@ func (l *Lattice) ProcessFinalizedBlock(b *types.Block) { panic(err) } l.pool.purgeBlocks(b.Position.ChainID, b.Position.Height) + l.toSyncer.processFinalizedBlock(b) } diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/nodeset-cache.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/nodeset-cache.go deleted file mode 100644 index 26e3d55f9..000000000 --- a/vendor/github.com/dexon-foundation/dexon-consensus/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/vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go index aa90a1ded..1360611f7 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go @@ -156,6 +156,9 @@ func (tos *totalOrderingSyncer) processBlock( // The finalized block should be passed by the order of consensus height. func (tos *totalOrderingSyncer) processFinalizedBlock(block *types.Block) { + if tos.synced() { + return + } tos.lock.Lock() defer tos.lock.Unlock() if len(tos.pendingDeliveryBlocks) > 0 { diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go index 1eaa3e398..6df245b08 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go @@ -31,8 +31,8 @@ type AgreementResult struct { } func (r *AgreementResult) String() string { - return fmt.Sprintf( - "agreementResult[%s:%s]", r.BlockHash, &r.Position) + return fmt.Sprintf("agreementResult{Hash:%s %s}", + r.BlockHash.String()[:6], &r.Position) } // BlockRandomnessResult describes a block randomness result diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go index b24e1f715..f42b70267 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go @@ -221,7 +221,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { } func (b *Block) String() string { - return fmt.Sprintf("Block(%v:%s)", b.Hash.String()[:6], &b.Position) + return fmt.Sprintf("Block{Hash:%v %s}", b.Hash.String()[:6], &b.Position) } // Clone returns a deep copy of a block. diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go index 4053c5a28..cecc4f16c 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go @@ -61,7 +61,7 @@ type MasterPublicKey struct { } func (d *MasterPublicKey) String() string { - return fmt.Sprintf("MasterPublicKey[%s:%d]", + return fmt.Sprintf("MasterPublicKey{KP:%s Round:%d}", d.ProposerID.String()[:6], d.Round) } @@ -141,11 +141,11 @@ type Complaint struct { func (c *Complaint) String() string { if c.IsNack() { - return fmt.Sprintf("DKGNackComplaint[%s:%d]%s", + return fmt.Sprintf("DKGNackComplaint{CP:%s Round:%d PSP:%s}", c.ProposerID.String()[:6], c.Round, c.PrivateShare.ProposerID.String()[:6]) } - return fmt.Sprintf("Complaint[%s:%d]%v", + return fmt.Sprintf("DKGComplaint{CP:%s Round:%d PrivateShare:%v}", c.ProposerID.String()[:6], c.Round, c.PrivateShare) } @@ -175,7 +175,7 @@ type Finalize struct { } func (final *Finalize) String() string { - return fmt.Sprintf("DKGFinal[%s:%d]", + return fmt.Sprintf("DKGFinal{FP:%s Round:%d}", final.ProposerID.String()[:6], final.Round) } diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go index 1fc7e6bf4..8822f6ea9 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go @@ -29,7 +29,8 @@ type Position struct { } func (pos *Position) String() string { - return fmt.Sprintf("[%d:%d:%d]", pos.Round, pos.ChainID, pos.Height) + return fmt.Sprintf("Position{Round:%d Chain:%d Height:%d}", + pos.Round, pos.ChainID, pos.Height) } // Equal checks if two positions are equal, it panics when their chainIDs diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go index 12c3af892..7601542ae 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go @@ -52,7 +52,8 @@ type Vote struct { } func (v *Vote) String() string { - return fmt.Sprintf("Vote[%s:%s](%d:%d):%s", v.ProposerID.String()[:6], + return fmt.Sprintf("Vote{BP:%s %s Period:%d Type:%d Hash:%s}", + v.ProposerID.String()[:6], &v.Position, v.Period, v.Type, v.BlockHash.String()[:6]) } diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go index 6b9ce634f..f6be46130 100644 --- a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go @@ -27,8 +27,17 @@ import ( "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/crypto" "github.com/dexon-foundation/dexon-consensus/core/types" + "github.com/dexon-foundation/dexon-consensus/core/utils" ) +// NodeSetCache is type alias to avoid fullnode compile error when moving +// it to core/utils package. +type NodeSetCache = utils.NodeSetCache + +// NewNodeSetCache is function alias to avoid fullnode compile error when moving +// it to core/utils package. +var NewNodeSetCache = utils.NewNodeSetCache + var ( debug = false // ErrEmptyTimestamps would be reported if Block.timestamps is empty. diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/nodeset-cache.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/nodeset-cache.go new file mode 100644 index 000000000..a8f8fe58f --- /dev/null +++ b/vendor/github.com/dexon-foundation/dexon-consensus/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 +} -- cgit v1.2.3