aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go
diff options
context:
space:
mode:
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.go312
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
+}