diff options
Diffstat (limited to 'vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go')
-rw-r--r-- | vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go | 312 |
1 files changed, 312 insertions, 0 deletions
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 new file mode 100644 index 000000000..5e5a587cb --- /dev/null +++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go @@ -0,0 +1,312 @@ +// 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 +// <http://www.gnu.org/licenses/>. + +package core + +import ( + "fmt" + "sync" + "time" + + "github.com/dexon-foundation/dexon-consensus/common" + "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" +) + +// Errors for configuration chain.. +var ( + ErrDKGNotRegistered = fmt.Errorf( + "not yet registered in DKG protocol") + ErrTSigAlreadyRunning = fmt.Errorf( + "tsig is already running") + ErrDKGNotReady = fmt.Errorf( + "DKG is not ready") +) + +type configurationChain struct { + ID types.NodeID + recv dkgReceiver + gov Governance + dkg *dkgProtocol + logger common.Logger + dkgLock sync.RWMutex + dkgSigner map[uint64]*dkgShareSecret + gpk map[uint64]*DKGGroupPublicKey + dkgResult sync.RWMutex + tsig map[common.Hash]*tsigProtocol + tsigTouched map[common.Hash]struct{} + tsigReady *sync.Cond + // TODO(jimmy-dexon): add timeout to pending psig. + pendingPsig map[common.Hash][]*typesDKG.PartialSignature + prevHash common.Hash +} + +func newConfigurationChain( + ID types.NodeID, + recv dkgReceiver, + gov Governance, + logger common.Logger) *configurationChain { + return &configurationChain{ + ID: ID, + recv: recv, + gov: gov, + logger: logger, + dkgSigner: make(map[uint64]*dkgShareSecret), + gpk: make(map[uint64]*DKGGroupPublicKey), + tsig: make(map[common.Hash]*tsigProtocol), + tsigTouched: make(map[common.Hash]struct{}), + tsigReady: sync.NewCond(&sync.Mutex{}), + pendingPsig: make(map[common.Hash][]*typesDKG.PartialSignature), + } +} + +func (cc *configurationChain) registerDKG(round uint64, threshold int) { + cc.dkgLock.Lock() + defer cc.dkgLock.Unlock() + cc.dkg = newDKGProtocol( + cc.ID, + cc.recv, + round, + threshold) +} + +func (cc *configurationChain) runDKG(round uint64) error { + cc.dkgLock.Lock() + defer cc.dkgLock.Unlock() + if cc.dkg == nil || cc.dkg.round != round { + return ErrDKGNotRegistered + } + if func() bool { + cc.dkgResult.RLock() + defer cc.dkgResult.RUnlock() + _, exist := cc.gpk[round] + return exist + }() { + return nil + } + + ticker := newTicker(cc.gov, round, TickerDKG) + cc.dkgLock.Unlock() + <-ticker.Tick() + cc.dkgLock.Lock() + // Phase 2(T = 0): Exchange DKG secret key share. + cc.logger.Debug("Calling Governance.DKGMasterPublicKeys", "round", round) + cc.dkg.processMasterPublicKeys(cc.gov.DKGMasterPublicKeys(round)) + // Phase 3(T = 0~λ): Propose complaint. + // Propose complaint is done in `processMasterPublicKeys`. + cc.dkgLock.Unlock() + <-ticker.Tick() + cc.dkgLock.Lock() + // Phase 4(T = λ): Propose nack complaints. + cc.dkg.proposeNackComplaints() + cc.dkgLock.Unlock() + <-ticker.Tick() + cc.dkgLock.Lock() + // Phase 5(T = 2λ): Propose Anti nack complaint. + cc.logger.Debug("Calling Governance.DKGComplaints", "round", round) + cc.dkg.processNackComplaints(cc.gov.DKGComplaints(round)) + cc.dkgLock.Unlock() + <-ticker.Tick() + cc.dkgLock.Lock() + // Phase 6(T = 3λ): Rebroadcast anti nack complaint. + // Rebroadcast is done in `processPrivateShare`. + cc.dkgLock.Unlock() + <-ticker.Tick() + cc.dkgLock.Lock() + // Phase 7(T = 4λ): Enforce complaints and nack complaints. + cc.logger.Debug("Calling Governance.DKGComplaints", "round", round) + cc.dkg.enforceNackComplaints(cc.gov.DKGComplaints(round)) + // Enforce complaint is done in `processPrivateShare`. + // Phase 8(T = 5λ): DKG finalize. + cc.dkgLock.Unlock() + <-ticker.Tick() + cc.dkgLock.Lock() + cc.dkg.proposeFinalize() + // Phase 9(T = 6λ): DKG is ready. + cc.dkgLock.Unlock() + <-ticker.Tick() + cc.dkgLock.Lock() + // Normally, IsDKGFinal would return true here. Use this for in case of + // unexpected network fluctuation and ensure the robustness of DKG protocol. + cc.logger.Debug("Calling Governance.IsDKGFinal", "round", round) + for !cc.gov.IsDKGFinal(round) { + cc.logger.Info("DKG is not ready yet. Try again later...", + "nodeID", cc.ID) + time.Sleep(500 * time.Millisecond) + } + cc.logger.Debug("Calling Governance.DKGMasterPublicKeys", "round", round) + cc.logger.Debug("Calling Governance.DKGComplaints", "round", round) + gpk, err := NewDKGGroupPublicKey(round, + cc.gov.DKGMasterPublicKeys(round), + cc.gov.DKGComplaints(round), + cc.dkg.threshold) + 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]) + } + cc.logger.Info("Qualify Nodes", + "nodeID", cc.ID, + "round", round, + "count", len(gpk.qualifyIDs), + "qualifies", qualifies) + cc.dkgResult.Lock() + defer cc.dkgResult.Unlock() + cc.dkgSigner[round] = signer + cc.gpk[round] = gpk + return nil +} + +func (cc *configurationChain) preparePartialSignature( + round uint64, hash common.Hash) (*typesDKG.PartialSignature, error) { + signer, exist := func() (*dkgShareSecret, bool) { + cc.dkgResult.RLock() + defer cc.dkgResult.RUnlock() + signer, exist := cc.dkgSigner[round] + return signer, exist + }() + if !exist { + return nil, ErrDKGNotReady + } + return &typesDKG.PartialSignature{ + ProposerID: cc.ID, + Round: round, + Hash: hash, + PartialSignature: signer.sign(hash), + }, nil +} + +func (cc *configurationChain) touchTSigHash(hash common.Hash) (first bool) { + cc.tsigReady.L.Lock() + defer cc.tsigReady.L.Unlock() + _, exist := cc.tsigTouched[hash] + cc.tsigTouched[hash] = struct{}{} + return !exist +} + +func (cc *configurationChain) untouchTSigHash(hash common.Hash) { + cc.tsigReady.L.Lock() + defer cc.tsigReady.L.Unlock() + delete(cc.tsigTouched, hash) +} + +func (cc *configurationChain) runTSig( + round uint64, hash common.Hash) ( + crypto.Signature, error) { + gpk, exist := func() (*DKGGroupPublicKey, bool) { + cc.dkgResult.RLock() + defer cc.dkgResult.RUnlock() + gpk, exist := cc.gpk[round] + return gpk, exist + }() + if !exist { + return crypto.Signature{}, ErrDKGNotReady + } + cc.tsigReady.L.Lock() + defer cc.tsigReady.L.Unlock() + if _, exist := cc.tsig[hash]; exist { + return crypto.Signature{}, ErrTSigAlreadyRunning + } + cc.tsig[hash] = newTSigProtocol(gpk, hash) + pendingPsig := cc.pendingPsig[hash] + delete(cc.pendingPsig, hash) + go func() { + for _, psig := range pendingPsig { + if err := cc.processPartialSignature(psig); err != nil { + cc.logger.Error("failed to process partial signature", + "nodeID", cc.ID, + "error", err) + } + } + }() + var signature crypto.Signature + var err error + for func() bool { + signature, err = cc.tsig[hash].signature() + return err == ErrNotEnoughtPartialSignatures + }() { + // TODO(jimmy-dexon): add a timeout here. + cc.tsigReady.Wait() + } + delete(cc.tsig, hash) + if err != nil { + return crypto.Signature{}, err + } + return signature, nil +} + +func (cc *configurationChain) runBlockTSig( + round uint64, hash common.Hash) (crypto.Signature, error) { + sig, err := cc.runTSig(round, hash) + if err != nil { + return crypto.Signature{}, err + } + cc.logger.Info("Block TSIG", + "nodeID", cc.ID, + "round", round, + "signature", sig) + return sig, nil +} + +func (cc *configurationChain) runCRSTSig( + round uint64, crs common.Hash) ([]byte, error) { + sig, err := cc.runTSig(round, crs) + cc.logger.Info("CRS", + "nodeID", cc.ID, + "round", round+1, + "signature", sig) + return sig.Signature[:], err +} + +func (cc *configurationChain) processPrivateShare( + prvShare *typesDKG.PrivateShare) error { + cc.dkgLock.Lock() + defer cc.dkgLock.Unlock() + if cc.dkg == nil { + return nil + } + return cc.dkg.processPrivateShare(prvShare) +} + +func (cc *configurationChain) processPartialSignature( + psig *typesDKG.PartialSignature) error { + cc.tsigReady.L.Lock() + defer cc.tsigReady.L.Unlock() + if _, exist := cc.tsig[psig.Hash]; !exist { + ok, err := verifyDKGPartialSignatureSignature(psig) + if err != nil { + return err + } + if !ok { + return ErrIncorrectPartialSignatureSignature + } + cc.pendingPsig[psig.Hash] = append(cc.pendingPsig[psig.Hash], psig) + return nil + } + if err := cc.tsig[psig.Hash].processPartialSignature(psig); err != nil { + return err + } + cc.tsigReady.Broadcast() + return nil +} |