From 9267d50de25ddf0f280eee797e2030ea989294e4 Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Tue, 9 Oct 2018 10:04:28 +0800 Subject: core: Add DKG final message. (#181) --- core/authenticator.go | 8 ++++++++ core/configuration-chain.go | 14 +++++++++++++- core/configuration-chain_test.go | 16 ++++++++++++++++ core/consensus.go | 9 +++++++++ core/crypto.go | 24 ++++++++++++++++++++++++ core/dkg-tsig-protocol.go | 10 ++++++++++ core/dkg-tsig-protocol_test.go | 19 +++++++++++++++++++ core/interfaces.go | 6 ++++++ core/nodeset-cache_test.go | 2 ++ core/test/governance.go | 25 +++++++++++++++++++++++++ core/types/dkg.go | 9 ++++++++- 11 files changed, 140 insertions(+), 2 deletions(-) (limited to 'core') 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 -- cgit v1.2.3