aboutsummaryrefslogtreecommitdiffstats
path: root/core/configuration-chain.go
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2018-09-26 09:55:08 +0800
committerGitHub <noreply@github.com>2018-09-26 09:55:08 +0800
commita79c10ff1f4717e7e26096f81a57df10b8c9a592 (patch)
tree69081c65669f0a68e7f27a81efffbacfb1983f10 /core/configuration-chain.go
parent0ffea9dadcfc0d8a740942a2d666eccc00613cd4 (diff)
downloadtangerine-consensus-a79c10ff1f4717e7e26096f81a57df10b8c9a592.tar
tangerine-consensus-a79c10ff1f4717e7e26096f81a57df10b8c9a592.tar.gz
tangerine-consensus-a79c10ff1f4717e7e26096f81a57df10b8c9a592.tar.bz2
tangerine-consensus-a79c10ff1f4717e7e26096f81a57df10b8c9a592.tar.lz
tangerine-consensus-a79c10ff1f4717e7e26096f81a57df10b8c9a592.tar.xz
tangerine-consensus-a79c10ff1f4717e7e26096f81a57df10b8c9a592.tar.zst
tangerine-consensus-a79c10ff1f4717e7e26096f81a57df10b8c9a592.zip
core: run TSIG for first configuration block at startup (#135)
Diffstat (limited to 'core/configuration-chain.go')
-rw-r--r--core/configuration-chain.go239
1 files changed, 239 insertions, 0 deletions
diff --git a/core/configuration-chain.go b/core/configuration-chain.go
new file mode 100644
index 0000000..88232c6
--- /dev/null
+++ b/core/configuration-chain.go
@@ -0,0 +1,239 @@
+// 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 (
+ "fmt"
+ "log"
+ "sync"
+
+ "github.com/dexon-foundation/dexon-consensus-core/common"
+ "github.com/dexon-foundation/dexon-consensus-core/core/types"
+ "github.com/dexon-foundation/dexon-consensus-core/crypto"
+)
+
+// Errors for configuration chain..
+var (
+ ErrDKGNotRegistered = fmt.Errorf(
+ "not yet registered in DKG protocol")
+ ErrDKGNotReady = fmt.Errorf(
+ "DKG is not ready")
+)
+
+type configurationChain struct {
+ ID types.NodeID
+ recv dkgReceiver
+ gov Governance
+ sigToPub SigToPubFn
+ dkg *dkgProtocol
+ dkgLock sync.RWMutex
+ dkgSigner map[uint64]*dkgShareSecret
+ gpk map[uint64]*dkgGroupPublicKey
+ dkgResult sync.RWMutex
+ tsig *tsigProtocol
+ tsigReady *sync.Cond
+ // TODO(jimmy-dexon): add timeout to pending psig.
+ pendingPsig []*types.DKGPartialSignature
+ prevHash common.Hash
+}
+
+func newConfigurationChain(
+ ID types.NodeID,
+ recv dkgReceiver,
+ gov Governance,
+ sigToPub SigToPubFn) *configurationChain {
+ return &configurationChain{
+ ID: ID,
+ recv: recv,
+ gov: gov,
+ sigToPub: sigToPub,
+ dkgSigner: make(map[uint64]*dkgShareSecret),
+ gpk: make(map[uint64]*dkgGroupPublicKey),
+ tsigReady: sync.NewCond(&sync.Mutex{}),
+ }
+}
+
+func (cc *configurationChain) registerDKG(round uint64, threshold int) {
+ cc.dkg = newDKGProtocol(
+ cc.ID,
+ cc.recv,
+ round,
+ threshold,
+ cc.sigToPub)
+}
+
+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, TickerDKG)
+ cc.dkgLock.Unlock()
+ <-ticker.Tick()
+ cc.dkgLock.Lock()
+ // Phase 2(T = 0): Exchange DKG secret key share.
+ 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.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.dkg.enforceNackComplaints(cc.gov.DKGComplaints(round))
+ // Enforce complaint is done in `processPrivateShare`.
+ // Phase 8(T = 5λ): DKG is ready.
+ cc.dkgLock.Unlock()
+ <-ticker.Tick()
+ cc.dkgLock.Lock()
+ gpk, err := newDKGGroupPublicKey(round,
+ cc.gov.DKGMasterPublicKeys(round),
+ cc.gov.DKGComplaints(round),
+ cc.dkg.threshold, cc.sigToPub)
+ 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])
+ }
+ log.Printf("[%s] Qualify Nodes(%d): %s\n",
+ cc.ID, len(gpk.qualifyIDs), 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) (*types.DKGPartialSignature, 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 &types.DKGPartialSignature{
+ ProposerID: cc.ID,
+ Round: round,
+ PartialSignature: signer.sign(hash),
+ }, nil
+}
+
+func (cc *configurationChain) runBlockTSig(
+ round uint64, hash common.Hash) error {
+ gpk, exist := func() (*dkgGroupPublicKey, bool) {
+ cc.dkgResult.RLock()
+ defer cc.dkgResult.RUnlock()
+ gpk, exist := cc.gpk[round]
+ return gpk, exist
+ }()
+ if !exist {
+ return ErrDKGNotReady
+ }
+ cc.tsigReady.L.Lock()
+ defer cc.tsigReady.L.Unlock()
+ cc.tsig = newTSigProtocol(gpk, hash, types.TSigConfigurationBlock)
+ pendingPsig := cc.pendingPsig
+ cc.pendingPsig = []*types.DKGPartialSignature{}
+ go func() {
+ for _, psig := range pendingPsig {
+ if err := cc.processPartialSignature(psig); err != nil {
+ log.Printf("[%s] %s", cc.ID, err)
+ }
+ }
+ }()
+ var signature crypto.Signature
+ var err error
+ for func() bool {
+ signature, err = cc.tsig.signature()
+ return err == ErrNotEnoughtPartialSignatures
+ }() {
+ cc.tsigReady.Wait()
+ }
+ cc.tsig = nil
+ if err != nil {
+ return err
+ }
+ log.Printf("[%s] TSIG: %s\n", cc.ID, signature)
+ return nil
+}
+
+func (cc *configurationChain) processPrivateShare(
+ prvShare *types.DKGPrivateShare) error {
+ cc.dkgLock.Lock()
+ defer cc.dkgLock.Unlock()
+ if cc.dkg == nil {
+ return nil
+ }
+ return cc.dkg.processPrivateShare(prvShare)
+}
+
+func (cc *configurationChain) processPartialSignature(
+ psig *types.DKGPartialSignature) error {
+ cc.tsigReady.L.Lock()
+ defer cc.tsigReady.L.Unlock()
+ if cc.tsig == nil {
+ ok, err := verifyDKGPartialSignatureSignature(psig, cc.sigToPub)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return ErrIncorrectPartialSignatureSignature
+ }
+ cc.pendingPsig = append(cc.pendingPsig, psig)
+ return nil
+ }
+ if err := cc.tsig.processPartialSignature(psig); err != nil {
+ return err
+ }
+ cc.tsigReady.Broadcast()
+ return nil
+}