aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2018-10-09 10:04:28 +0800
committerGitHub <noreply@github.com>2018-10-09 10:04:28 +0800
commit9267d50de25ddf0f280eee797e2030ea989294e4 (patch)
treee6345f8b2f04d360a12d7272bf17b137a13eec93 /core
parent8944f1ea16c531cbccc3f01d91854e942e040871 (diff)
downloaddexon-consensus-9267d50de25ddf0f280eee797e2030ea989294e4.tar
dexon-consensus-9267d50de25ddf0f280eee797e2030ea989294e4.tar.gz
dexon-consensus-9267d50de25ddf0f280eee797e2030ea989294e4.tar.bz2
dexon-consensus-9267d50de25ddf0f280eee797e2030ea989294e4.tar.lz
dexon-consensus-9267d50de25ddf0f280eee797e2030ea989294e4.tar.xz
dexon-consensus-9267d50de25ddf0f280eee797e2030ea989294e4.tar.zst
dexon-consensus-9267d50de25ddf0f280eee797e2030ea989294e4.zip
core: Add DKG final message. (#181)
Diffstat (limited to 'core')
-rw-r--r--core/authenticator.go8
-rw-r--r--core/configuration-chain.go14
-rw-r--r--core/configuration-chain_test.go16
-rw-r--r--core/consensus.go9
-rw-r--r--core/crypto.go24
-rw-r--r--core/dkg-tsig-protocol.go10
-rw-r--r--core/dkg-tsig-protocol_test.go19
-rw-r--r--core/interfaces.go6
-rw-r--r--core/nodeset-cache_test.go2
-rw-r--r--core/test/governance.go25
-rw-r--r--core/types/dkg.go9
11 files changed, 140 insertions, 2 deletions
diff --git a/core/authenticator.go b/core/authenticator.go
index de22c1f..97b62d6 100644
--- a/core/authenticator.go
+++ b/core/authenticator.go
@@ -101,6 +101,14 @@ func (au *Authenticator) SignDKGPartialSignature(
return
}
+// SignDKGFinalize signs a DKG finalize message.
+func (au *Authenticator) SignDKGFinalize(
+ final *types.DKGFinalize) (err error) {
+ final.ProposerID = au.proposerID
+ final.Signature, err = au.prvKey.Sign(hashDKGFinalize(final))
+ return
+}
+
// VerifyBlock verifies the signature of types.Block.
func (au *Authenticator) VerifyBlock(b *types.Block) (err error) {
hash, err := hashBlock(b)
diff --git a/core/configuration-chain.go b/core/configuration-chain.go
index c9dbab9..5cedcf4 100644
--- a/core/configuration-chain.go
+++ b/core/configuration-chain.go
@@ -21,6 +21,7 @@ import (
"fmt"
"log"
"sync"
+ "time"
"github.com/dexon-foundation/dexon-consensus-core/common"
"github.com/dexon-foundation/dexon-consensus-core/core/crypto"
@@ -123,10 +124,21 @@ func (cc *configurationChain) runDKG(round uint64) error {
// 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.
+ // 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.
+ for !cc.gov.IsDKGFinal(round) {
+ log.Printf("[%s] DKG is not ready yet. Try again later...\n", cc.ID)
+ time.Sleep(500 * time.Millisecond)
+ }
gpk, err := NewDKGGroupPublicKey(round,
cc.gov.DKGMasterPublicKeys(round),
cc.gov.DKGComplaints(round),
diff --git a/core/configuration-chain_test.go b/core/configuration-chain_test.go
index 45120b4..856d46e 100644
--- a/core/configuration-chain_test.go
+++ b/core/configuration-chain_test.go
@@ -125,6 +125,22 @@ func (r *testCCReceiver) ProposeDKGAntiNackComplaint(
}()
}
+func (r *testCCReceiver) ProposeDKGFinalize(final *types.DKGFinalize) {
+ prvKey, exist := r.s.prvKeys[final.ProposerID]
+ r.s.Require().True(exist)
+ var err error
+ final.Signature, err = prvKey.Sign(hashDKGFinalize(final))
+ r.s.Require().NoError(err)
+ for _, gov := range r.govs {
+ // Use Marshal/Unmarshal to do deep copy.
+ data, err := json.Marshal(final)
+ r.s.Require().NoError(err)
+ finalCopy := &types.DKGFinalize{}
+ r.s.Require().NoError(json.Unmarshal(data, finalCopy))
+ gov.AddDKGFinalize(finalCopy)
+ }
+}
+
func (s *ConfigurationChainTestSuite) setupNodes(n int) {
s.nIDs = make(types.NodeIDs, 0, n)
s.prvKeys = make(map[types.NodeID]crypto.PrivateKey, n)
diff --git a/core/consensus.go b/core/consensus.go
index dc1e3d4..47e075b 100644
--- a/core/consensus.go
+++ b/core/consensus.go
@@ -176,6 +176,15 @@ func (recv *consensusDKGReceiver) ProposeDKGAntiNackComplaint(
recv.network.BroadcastDKGPrivateShare(prv)
}
+// ProposeDKGFinalize propose a DKGFinalize message.
+func (recv *consensusDKGReceiver) ProposeDKGFinalize(final *types.DKGFinalize) {
+ if err := recv.authModule.SignDKGFinalize(final); err != nil {
+ log.Println(err)
+ return
+ }
+ recv.gov.AddDKGFinalize(final)
+}
+
// Consensus implements DEXON Consensus algorithm.
type Consensus struct {
// Node Info.
diff --git a/core/crypto.go b/core/crypto.go
index ab484a0..e286d2b 100644
--- a/core/crypto.go
+++ b/core/crypto.go
@@ -243,3 +243,27 @@ func verifyDKGPartialSignatureSignature(
}
return true, nil
}
+
+func hashDKGFinalize(final *types.DKGFinalize) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, final.Round)
+
+ return crypto.Keccak256Hash(
+ final.ProposerID.Hash[:],
+ binaryRound,
+ )
+}
+
+// VerifyDKGFinalizeSignature verifies DKGFinalize signature.
+func VerifyDKGFinalizeSignature(
+ final *types.DKGPartialSignature) (bool, error) {
+ hash := hashDKGPartialSignature(final)
+ pubKey, err := crypto.SigToPub(hash, final.Signature)
+ if err != nil {
+ return false, err
+ }
+ if final.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
diff --git a/core/dkg-tsig-protocol.go b/core/dkg-tsig-protocol.go
index 3853468..3be717f 100644
--- a/core/dkg-tsig-protocol.go
+++ b/core/dkg-tsig-protocol.go
@@ -60,6 +60,9 @@ type dkgReceiver interface {
// ProposeDKGAntiNackComplaint propose a DKGPrivateShare as an anti complaint.
ProposeDKGAntiNackComplaint(prv *types.DKGPrivateShare)
+
+ // ProposeDKGFinalize propose a DKGFinalize message.
+ ProposeDKGFinalize(final *types.DKGFinalize)
}
type dkgProtocol struct {
@@ -299,6 +302,13 @@ func (d *dkgProtocol) processPrivateShare(
return nil
}
+func (d *dkgProtocol) proposeFinalize() {
+ d.recv.ProposeDKGFinalize(&types.DKGFinalize{
+ ProposerID: d.ID,
+ Round: d.round,
+ })
+}
+
func (d *dkgProtocol) recoverShareSecret(qualifyIDs dkg.IDs) (
*dkgShareSecret, error) {
if len(qualifyIDs) <= d.threshold {
diff --git a/core/dkg-tsig-protocol_test.go b/core/dkg-tsig-protocol_test.go
index a3a0acc..9d66222 100644
--- a/core/dkg-tsig-protocol_test.go
+++ b/core/dkg-tsig-protocol_test.go
@@ -46,6 +46,7 @@ type testDKGReceiver struct {
mpk *types.DKGMasterPublicKey
prvShare map[types.NodeID]*types.DKGPrivateShare
antiComplaints map[types.NodeID]*types.DKGPrivateShare
+ final []*types.DKGFinalize
}
func newTestDKGReceiver(
@@ -90,6 +91,10 @@ func (r *testDKGReceiver) ProposeDKGAntiNackComplaint(
r.antiComplaints[prv.ReceiverID] = prv
}
+func (r *testDKGReceiver) ProposeDKGFinalize(final *types.DKGFinalize) {
+ r.final = append(r.final, final)
+}
+
func (s *DKGTSIGProtocolTestSuite) setupDKGParticipants(n int) {
s.nIDs = make(types.NodeIDs, 0, n)
s.prvKeys = make(map[types.NodeID]crypto.PrivateKey, n)
@@ -616,6 +621,20 @@ func (s *DKGTSIGProtocolTestSuite) TestPartialSignature() {
s.True(gpk.VerifySignature(msgHash, sig))
}
+func (s *DKGTSIGProtocolTestSuite) TestProposeFinalize() {
+ prvKey, err := ecdsa.NewPrivateKey()
+ s.Require().NoError(err)
+ recv := newTestDKGReceiver(s, prvKey)
+ nID := types.NewNodeID(prvKey.PublicKey())
+ protocol := newDKGProtocol(nID, recv, 1, 2)
+ protocol.proposeFinalize()
+ s.Require().Len(recv.final, 1)
+ final := recv.final[0]
+ s.Equal(&types.DKGFinalize{
+ ProposerID: nID,
+ Round: 1,
+ }, final)
+}
func TestDKGTSIGProtocol(t *testing.T) {
suite.Run(t, new(DKGTSIGProtocolTestSuite))
}
diff --git a/core/interfaces.go b/core/interfaces.go
index 3497535..9232251 100644
--- a/core/interfaces.go
+++ b/core/interfaces.go
@@ -116,6 +116,12 @@ type Governance interface {
// DKGMasterPublicKeys gets all the DKGMasterPublicKey of round.
DKGMasterPublicKeys(round uint64) []*types.DKGMasterPublicKey
+
+ // AddDKGFinalize adds a DKG finalize message.
+ AddDKGFinalize(final *types.DKGFinalize)
+
+ // IsDKGFinal checks if DKG is final.
+ IsDKGFinal(round uint64) bool
}
// Ticker define the capability to tick by interval.
diff --git a/core/nodeset-cache_test.go b/core/nodeset-cache_test.go
index a9a31c2..a1dded6 100644
--- a/core/nodeset-cache_test.go
+++ b/core/nodeset-cache_test.go
@@ -64,6 +64,8 @@ func (g *testGov) DKGMasterPublicKeys(
round uint64) (keys []*types.DKGMasterPublicKey) {
return
}
+func (g *testGov) AddDKGFinalize(final *types.DKGFinalize) {}
+func (g *testGov) IsDKGFinal(round uint64) bool { return true }
type NodeSetCacheTestSuite struct {
suite.Suite
diff --git a/core/test/governance.go b/core/test/governance.go
index 24fd2de..de3f34b 100644
--- a/core/test/governance.go
+++ b/core/test/governance.go
@@ -44,6 +44,7 @@ type Governance struct {
tsig map[uint64]crypto.Signature
DKGComplaint map[uint64][]*types.DKGComplaint
DKGMasterPublicKey map[uint64][]*types.DKGMasterPublicKey
+ DKGFinal map[uint64]map[types.NodeID]struct{}
RoundInterval time.Duration
MinBlockInterval time.Duration
MaxBlockInterval time.Duration
@@ -62,6 +63,7 @@ func NewGovernance(nodeCount int, lambda time.Duration) (
tsig: make(map[uint64]crypto.Signature),
DKGComplaint: make(map[uint64][]*types.DKGComplaint),
DKGMasterPublicKey: make(map[uint64][]*types.DKGMasterPublicKey),
+ DKGFinal: make(map[uint64]map[types.NodeID]struct{}),
RoundInterval: 365 * 86400 * time.Second,
MinBlockInterval: 1 * time.Millisecond,
MaxBlockInterval: lambda * 8,
@@ -135,8 +137,14 @@ func (g *Governance) PrivateKeys() (keys []crypto.PrivateKey) {
// AddDKGComplaint add a DKGComplaint.
func (g *Governance) AddDKGComplaint(complaint *types.DKGComplaint) {
+ if g.IsDKGFinal(complaint.Round) {
+ return
+ }
g.lock.Lock()
defer g.lock.Unlock()
+ if _, exist := g.DKGFinal[complaint.Round][complaint.ProposerID]; exist {
+ return
+ }
for _, comp := range g.DKGComplaint[complaint.Round] {
if comp == complaint {
return
@@ -184,3 +192,20 @@ func (g *Governance) DKGMasterPublicKeys(
}
return mpks
}
+
+// AddDKGFinalize adds a DKG finalize message.
+func (g *Governance) AddDKGFinalize(final *types.DKGFinalize) {
+ g.lock.Lock()
+ defer g.lock.Unlock()
+ if _, exist := g.DKGFinal[final.Round]; !exist {
+ g.DKGFinal[final.Round] = make(map[types.NodeID]struct{})
+ }
+ g.DKGFinal[final.Round][final.ProposerID] = struct{}{}
+}
+
+// IsDKGFinal checks if DKG is final.
+func (g *Governance) IsDKGFinal(round uint64) bool {
+ g.lock.RLock()
+ defer g.lock.RUnlock()
+ return len(g.DKGFinal[round]) > int(g.Configuration(round).DKGSetSize)/3*2
+}
diff --git a/core/types/dkg.go b/core/types/dkg.go
index a4c6bf1..c761504 100644
--- a/core/types/dkg.go
+++ b/core/types/dkg.go
@@ -69,13 +69,20 @@ type DKGComplaint struct {
// DKGPartialSignature describe a partial signature in DKG protocol.
type DKGPartialSignature struct {
- ProposerID NodeID `json:"proposerID"`
+ ProposerID NodeID `json:"proposer_id"`
Round uint64 `json:"round"`
Hash common.Hash `json:"hash"`
PartialSignature dkg.PartialSignature `json:"partial_signature"`
Signature crypto.Signature `json:"signature"`
}
+// DKGFinalize describe a dig finalize message in DKG protocol.
+type DKGFinalize struct {
+ ProposerID NodeID `json:"proposer_id"`
+ Round uint64 `json:"round"`
+ Signature crypto.Signature `json:"signature"`
+}
+
// IsNack returns true if it's a nack complaint in DKG protocol.
func (c *DKGComplaint) IsNack() bool {
return len(c.PrivateShare.Signature.Signature) == 0