aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2019-04-15 11:44:04 +0800
committerGitHub <noreply@github.com>2019-04-15 11:44:04 +0800
commit79be89a6b0b1d24b889e7c9fe0244026af4d49d0 (patch)
treebacd2dd8a224d2cffbe9965439707df7fa2888de
parent7abe09214fbf04f9f1f7f4f6ec57cd1f924953d6 (diff)
downloaddexon-consensus-79be89a6b0b1d24b889e7c9fe0244026af4d49d0.tar
dexon-consensus-79be89a6b0b1d24b889e7c9fe0244026af4d49d0.tar.gz
dexon-consensus-79be89a6b0b1d24b889e7c9fe0244026af4d49d0.tar.bz2
dexon-consensus-79be89a6b0b1d24b889e7c9fe0244026af4d49d0.tar.lz
dexon-consensus-79be89a6b0b1d24b889e7c9fe0244026af4d49d0.tar.xz
dexon-consensus-79be89a6b0b1d24b889e7c9fe0244026af4d49d0.tar.zst
dexon-consensus-79be89a6b0b1d24b889e7c9fe0244026af4d49d0.zip
core: Add DKGSuccess (#569)
* core: Add DKGSuccess * core: reset if not enough of dkg success
-rw-r--r--core/configuration-chain.go1
-rw-r--r--core/configuration-chain_test.go13
-rw-r--r--core/consensus.go16
-rw-r--r--core/dkg-tsig-protocol.go11
-rw-r--r--core/dkg-tsig-protocol_test.go21
-rw-r--r--core/interfaces.go6
-rw-r--r--core/test/governance.go32
-rw-r--r--core/test/state-change-request.go1
-rw-r--r--core/test/state.go65
-rw-r--r--core/test/state_test.go24
-rw-r--r--core/test/utils.go14
-rw-r--r--core/types/dkg/dkg.go34
-rw-r--r--core/types/dkg/dkg_test.go36
-rw-r--r--core/utils/crypto.go27
-rw-r--r--core/utils/crypto_test.go23
-rw-r--r--core/utils/round-event.go2
-rw-r--r--core/utils/signer.go7
17 files changed, 311 insertions, 22 deletions
diff --git a/core/configuration-chain.go b/core/configuration-chain.go
index e9e04a2..4e70ff0 100644
--- a/core/configuration-chain.go
+++ b/core/configuration-chain.go
@@ -400,6 +400,7 @@ func (cc *configurationChain) runDKGPhaseNine(round uint64, reset uint64) error
cc.db.PutDKGPrivateKey(round, reset, *signer.privateKey); err != nil {
return err
}
+ cc.dkg.proposeSuccess()
cc.dkgResult.Lock()
defer cc.dkgResult.Unlock()
cc.dkgSigner[round] = signer
diff --git a/core/configuration-chain_test.go b/core/configuration-chain_test.go
index 61f0906..edaacf6 100644
--- a/core/configuration-chain_test.go
+++ b/core/configuration-chain_test.go
@@ -114,6 +114,12 @@ func (r *testCCGlobalReceiver) ProposeDKGFinalize(final *typesDKG.Finalize) {
}
}
+func (r *testCCGlobalReceiver) ProposeDKGSuccess(success *typesDKG.Success) {
+ for _, gov := range r.govs {
+ gov.AddDKGSuccess(test.CloneDKGSuccess(success))
+ }
+}
+
type testCCReceiver struct {
signer *utils.Signer
recv *testCCGlobalReceiver
@@ -176,6 +182,13 @@ func (r *testCCReceiver) ProposeDKGFinalize(final *typesDKG.Finalize) {
r.recv.ProposeDKGFinalize(final)
}
+func (r *testCCReceiver) ProposeDKGSuccess(success *typesDKG.Success) {
+ if err := r.signer.SignDKGSuccess(success); err != nil {
+ panic(err)
+ }
+ r.recv.ProposeDKGSuccess(success)
+}
+
func (s *ConfigurationChainTestSuite) setupNodes(n int) {
s.nIDs = make(types.NodeIDs, 0, n)
s.signers = make(map[types.NodeID]*utils.Signer, n)
diff --git a/core/consensus.go b/core/consensus.go
index 968b90e..7c44bdf 100644
--- a/core/consensus.go
+++ b/core/consensus.go
@@ -496,6 +496,16 @@ func (recv *consensusDKGReceiver) ProposeDKGFinalize(final *typesDKG.Finalize) {
recv.gov.AddDKGFinalize(final)
}
+// ProposeDKGSuccess propose a DKGSuccess message.
+func (recv *consensusDKGReceiver) ProposeDKGSuccess(success *typesDKG.Success) {
+ if err := recv.signer.SignDKGSuccess(success); err != nil {
+ recv.logger.Error("Failed to sign DKG successize", "error", err)
+ return
+ }
+ recv.logger.Debug("Calling Governance.AddDKGSuccess", "success", success)
+ recv.gov.AddDKGSuccess(success)
+}
+
// Consensus implements DEXON Consensus algorithm.
type Consensus struct {
// Node Info.
@@ -818,6 +828,12 @@ func (con *Consensus) prepare(initBlock *types.Block) (err error) {
"reset", e.Reset)
return false
}
+ if !con.gov.IsDKGSuccess(nextRound) {
+ con.logger.Error("Next DKG is not success, reset it",
+ "round", e.Round,
+ "reset", e.Reset)
+ return false
+ }
gpk, err := typesDKG.NewGroupPublicKey(
nextRound,
con.gov.DKGMasterPublicKeys(nextRound),
diff --git a/core/dkg-tsig-protocol.go b/core/dkg-tsig-protocol.go
index d81d485..8383ad1 100644
--- a/core/dkg-tsig-protocol.go
+++ b/core/dkg-tsig-protocol.go
@@ -102,6 +102,9 @@ type dkgReceiver interface {
// ProposeDKGFinalize propose a DKGFinalize message.
ProposeDKGFinalize(final *typesDKG.Finalize)
+
+ // ProposeDKGSuccess propose a DKGSuccess message.
+ ProposeDKGSuccess(final *typesDKG.Success)
}
type dkgProtocol struct {
@@ -514,6 +517,14 @@ func (d *dkgProtocol) proposeFinalize() {
})
}
+func (d *dkgProtocol) proposeSuccess() {
+ d.recv.ProposeDKGSuccess(&typesDKG.Success{
+ ProposerID: d.ID,
+ Round: d.round,
+ Reset: d.reset,
+ })
+}
+
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 89fd105..184460b 100644
--- a/core/dkg-tsig-protocol_test.go
+++ b/core/dkg-tsig-protocol_test.go
@@ -50,6 +50,7 @@ type testDKGReceiver struct {
antiComplaints map[types.NodeID]*typesDKG.PrivateShare
ready []*typesDKG.MPKReady
final []*typesDKG.Finalize
+ success []*typesDKG.Success
}
func newTestDKGReceiver(s *DKGTSIGProtocolTestSuite,
@@ -102,6 +103,10 @@ func (r *testDKGReceiver) ProposeDKGFinalize(final *typesDKG.Finalize) {
r.final = append(r.final, final)
}
+func (r *testDKGReceiver) ProposeDKGSuccess(success *typesDKG.Success) {
+ r.success = append(r.success, success)
+}
+
func (s *DKGTSIGProtocolTestSuite) setupDKGParticipants(n int) {
s.nIDs = make(types.NodeIDs, 0, n)
s.signers = make(map[types.NodeID]*utils.Signer, n)
@@ -878,6 +883,22 @@ func (s *DKGTSIGProtocolTestSuite) TestProposeFinalize() {
}, final)
}
+func (s *DKGTSIGProtocolTestSuite) TestProposeSuccess() {
+ prvKey, err := ecdsa.NewPrivateKey()
+ s.Require().NoError(err)
+ recv := newTestDKGReceiver(s, utils.NewSigner(prvKey))
+ nID := types.NewNodeID(prvKey.PublicKey())
+ protocol := newDKGProtocol(nID, recv, 1, 3, 2)
+ protocol.proposeSuccess()
+ s.Require().Len(recv.success, 1)
+ success := recv.success[0]
+ s.Equal(&typesDKG.Success{
+ ProposerID: nID,
+ Round: 1,
+ Reset: 3,
+ }, success)
+}
+
func (s *DKGTSIGProtocolTestSuite) TestTSigVerifierCache() {
k := 3
n := 10
diff --git a/core/interfaces.go b/core/interfaces.go
index c16c624..c88b3dc 100644
--- a/core/interfaces.go
+++ b/core/interfaces.go
@@ -141,6 +141,12 @@ type Governance interface {
// IsDKGFinal checks if DKG is final.
IsDKGFinal(round uint64) bool
+ // AddDKGSuccess adds a DKG success message.
+ AddDKGSuccess(success *typesDKG.Success)
+
+ // IsDKGSuccess checks if DKG is success.
+ IsDKGSuccess(round uint64) bool
+
// ReportForkVote reports a node for forking votes.
ReportForkVote(vote1, vote2 *types.Vote)
diff --git a/core/test/governance.go b/core/test/governance.go
index 14b7838..204e68b 100644
--- a/core/test/governance.go
+++ b/core/test/governance.go
@@ -236,7 +236,7 @@ func (g *Governance) IsDKGMPKReady(round uint64) bool {
if round >= uint64(len(g.configs)) {
return false
}
- return g.stateModule.IsDKGMPKReady(round, int(g.configs[round].NotarySetSize)/3*2)
+ return g.stateModule.IsDKGMPKReady(round, int(g.configs[round].NotarySetSize)*2/3+1)
}
// AddDKGFinalize adds a DKG finalize message.
@@ -264,7 +264,35 @@ func (g *Governance) IsDKGFinal(round uint64) bool {
if round >= uint64(len(g.configs)) {
return false
}
- return g.stateModule.IsDKGFinal(round, int(g.configs[round].NotarySetSize)/3*2)
+ return g.stateModule.IsDKGFinal(round, int(g.configs[round].NotarySetSize)*2/3+1)
+}
+
+// AddDKGSuccess adds a DKG success message.
+func (g *Governance) AddDKGSuccess(success *typesDKG.Success) {
+ if g.isProhibited(StateAddDKGSuccess) {
+ return
+ }
+ if err := g.stateModule.RequestChange(StateAddDKGSuccess, success); err != nil {
+ if err != ErrChangeWontApply {
+ panic(err)
+ }
+ }
+ g.broadcastPendingStateChanges()
+}
+
+// IsDKGSuccess checks if DKG is success.
+func (g *Governance) IsDKGSuccess(round uint64) bool {
+ if round == 0 || round == 1 {
+ // Round 0, 1 are genesis round, their configs should be created
+ // by default.
+ g.CatchUpWithRound(round)
+ }
+ g.lock.RLock()
+ defer g.lock.RUnlock()
+ if round >= uint64(len(g.configs)) {
+ return false
+ }
+ return g.stateModule.IsDKGFinal(round, int(g.configs[round].NotarySetSize)*5/6)
}
// ReportForkVote reports a node for forking votes.
diff --git a/core/test/state-change-request.go b/core/test/state-change-request.go
index fe55d6e..4ddd40f 100644
--- a/core/test/state-change-request.go
+++ b/core/test/state-change-request.go
@@ -40,6 +40,7 @@ const (
StateAddDKGMasterPublicKey
StateAddDKGMPKReady
StateAddDKGFinal
+ StateAddDKGSuccess
StateResetDKG
// Configuration related.
StateChangeLambdaBA
diff --git a/core/test/state.go b/core/test/state.go
index 41c6b38..7597377 100644
--- a/core/test/state.go
+++ b/core/test/state.go
@@ -66,6 +66,9 @@ var (
// ErrStateDKGFinalsNotEqual means DKG finalizations of two states are not
// equal.
ErrStateDKGFinalsNotEqual = errors.New("dkg finalizations not equal")
+ // ErrStateDKGSuccessesNotEqual means DKG successes of two states are not
+ // equal.
+ ErrStateDKGSuccessesNotEqual = errors.New("dkg successes not equal")
// ErrStateCRSsNotEqual means CRSs of two states are not equal.
ErrStateCRSsNotEqual = errors.New("crs not equal")
// ErrStateDKGResetCountNotEqual means dkgResetCount of two states are not
@@ -103,6 +106,7 @@ type State struct {
dkgMasterPublicKeys map[uint64]map[types.NodeID]*typesDKG.MasterPublicKey
dkgReadys map[uint64]map[types.NodeID]*typesDKG.MPKReady
dkgFinals map[uint64]map[types.NodeID]*typesDKG.Finalize
+ dkgSuccesses map[uint64]map[types.NodeID]*typesDKG.Success
crs []common.Hash
dkgResetCount map[uint64]uint64
// Other stuffs
@@ -150,6 +154,8 @@ func NewState(
map[uint64]map[types.NodeID]*typesDKG.MPKReady),
dkgFinals: make(
map[uint64]map[types.NodeID]*typesDKG.Finalize),
+ dkgSuccesses: make(
+ map[uint64]map[types.NodeID]*typesDKG.Success),
dkgComplaints: make(
map[uint64]map[types.NodeID][]*typesDKG.Complaint),
dkgMasterPublicKeys: make(
@@ -211,6 +217,9 @@ func (s *State) unpackPayload(
case StateAddDKGFinal:
v = &typesDKG.Finalize{}
err = rlp.DecodeBytes(raw.Payload, v)
+ case StateAddDKGSuccess:
+ v = &typesDKG.Success{}
+ err = rlp.DecodeBytes(raw.Payload, v)
case StateResetDKG:
var tmp common.Hash
err = rlp.DecodeBytes(raw.Payload, &tmp)
@@ -393,6 +402,28 @@ func (s *State) Equal(other *State) error {
}
}
}
+ // Check DKG successes.
+ if len(s.dkgSuccesses) != len(other.dkgSuccesses) {
+ return ErrStateDKGSuccessesNotEqual
+ }
+ for round, successesForRound := range s.dkgSuccesses {
+ otherSuccessesForRound, exists := other.dkgSuccesses[round]
+ if !exists {
+ return ErrStateDKGSuccessesNotEqual
+ }
+ if len(successesForRound) != len(otherSuccessesForRound) {
+ return ErrStateDKGSuccessesNotEqual
+ }
+ for nID, success := range successesForRound {
+ otherSuccesse, exists := otherSuccessesForRound[nID]
+ if !exists {
+ return ErrStateDKGSuccessesNotEqual
+ }
+ if !success.Equal(otherSuccesse) {
+ return ErrStateDKGSuccessesNotEqual
+ }
+ }
+ }
// Check CRS part.
if len(s.crs) != len(other.crs) {
return ErrStateCRSsNotEqual
@@ -455,6 +486,7 @@ func (s *State) Clone() (copied *State) {
map[uint64]map[types.NodeID]*typesDKG.MasterPublicKey),
dkgReadys: make(map[uint64]map[types.NodeID]*typesDKG.MPKReady),
dkgFinals: make(map[uint64]map[types.NodeID]*typesDKG.Finalize),
+ dkgSuccesses: make(map[uint64]map[types.NodeID]*typesDKG.Success),
appliedRequests: make(map[common.Hash]struct{}),
}
// Nodes
@@ -493,6 +525,12 @@ func (s *State) Clone() (copied *State) {
copied.dkgFinals[round][nID] = CloneDKGFinalize(final)
}
}
+ for round, successesForRound := range s.dkgSuccesses {
+ copied.dkgSuccesses[round] = make(map[types.NodeID]*typesDKG.Success)
+ for nID, success := range successesForRound {
+ copied.dkgSuccesses[round][nID] = CloneDKGSuccess(success)
+ }
+ }
for _, crs := range s.crs {
copied.crs = append(copied.crs, crs)
}
@@ -636,6 +674,11 @@ func (s *State) isValidRequest(req *StateChangeRequest) error {
if final.Reset != s.dkgResetCount[final.Round] {
return ErrChangeWontApply
}
+ case StateAddDKGSuccess:
+ success := req.Payload.(*typesDKG.Success)
+ if success.Reset != s.dkgResetCount[success.Round] {
+ return ErrChangeWontApply
+ }
case StateAddDKGMasterPublicKey:
mpk := req.Payload.(*typesDKG.MasterPublicKey)
if mpk.Reset != s.dkgResetCount[mpk.Round] {
@@ -747,6 +790,13 @@ func (s *State) applyRequest(req *StateChangeRequest) error {
s.dkgFinals[final.Round] = make(map[types.NodeID]*typesDKG.Finalize)
}
s.dkgFinals[final.Round][final.ProposerID] = final
+ case StateAddDKGSuccess:
+ success := req.Payload.(*typesDKG.Success)
+ if _, exists := s.dkgSuccesses[success.Round]; !exists {
+ s.dkgSuccesses[success.Round] =
+ make(map[types.NodeID]*typesDKG.Success)
+ }
+ s.dkgSuccesses[success.Round][success.ProposerID] = success
case StateResetDKG:
round := uint64(len(s.crs) - 1)
s.crs[round] = req.Payload.(common.Hash)
@@ -755,6 +805,7 @@ func (s *State) applyRequest(req *StateChangeRequest) error {
delete(s.dkgReadys, round)
delete(s.dkgComplaints, round)
delete(s.dkgFinals, round)
+ delete(s.dkgSuccesses, round)
case StateChangeLambdaBA:
s.lambdaBA = time.Duration(req.Payload.(uint64))
case StateChangeLambdaDKG:
@@ -799,6 +850,8 @@ func (s *State) RequestChange(
payload = payload.(*typesDKG.MPKReady)
case StateAddDKGFinal:
payload = payload.(*typesDKG.Finalize)
+ case StateAddDKGSuccess:
+ payload = payload.(*typesDKG.Success)
case StateAddDKGMasterPublicKey:
payload = payload.(*typesDKG.MasterPublicKey)
case StateAddDKGComplaint:
@@ -873,7 +926,7 @@ func (s *State) DKGMasterPublicKeys(round uint64) []*typesDKG.MasterPublicKey {
func (s *State) IsDKGMPKReady(round uint64, threshold int) bool {
s.lock.RLock()
defer s.lock.RUnlock()
- return len(s.dkgReadys[round]) > threshold
+ return len(s.dkgReadys[round]) >= threshold
}
// IsDKGFinal checks if current received dkg finals exceeds threshold.
@@ -881,7 +934,15 @@ func (s *State) IsDKGMPKReady(round uint64, threshold int) bool {
func (s *State) IsDKGFinal(round uint64, threshold int) bool {
s.lock.RLock()
defer s.lock.RUnlock()
- return len(s.dkgFinals[round]) > threshold
+ return len(s.dkgFinals[round]) >= threshold
+}
+
+// IsDKGSuccess checks if current received dkg successes exceeds threshold.
+// This information won't be snapshot, thus can't be cached in test.Governance.
+func (s *State) IsDKGSuccess(round uint64, threshold int) bool {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ return len(s.dkgSuccesses[round]) >= threshold
}
// DKGResetCount returns the reset count for DKG of given round.
diff --git a/core/test/state_test.go b/core/test/state_test.go
index e3ed9bb..63f9d27 100644
--- a/core/test/state_test.go
+++ b/core/test/state_test.go
@@ -282,9 +282,9 @@ func (s *StateTestSuite) TestLocalMode() {
// Test adding node set, DKG complaints, final, master public key.
// Make sure everything is empty before changed.
req.Empty(st.DKGMasterPublicKeys(2))
- req.False(st.IsDKGMPKReady(2, 0))
+ req.False(st.IsDKGMPKReady(2, 1))
req.Empty(st.DKGComplaints(2))
- req.False(st.IsDKGFinal(2, 0))
+ req.False(st.IsDKGFinal(2, 1))
// Add DKG stuffs.
masterPubKey := s.newDKGMasterPublicKey(2, 0)
ready := s.newDKGMPKReady(2, 0)
@@ -296,22 +296,22 @@ func (s *StateTestSuite) TestLocalMode() {
req.Len(masterKeyForRound, 1)
req.True(masterKeyForRound[0].Equal(masterPubKey))
// Check IsDKGMPKReady.
- req.True(st.IsDKGMPKReady(2, 0))
+ req.True(st.IsDKGMPKReady(2, 1))
// Check DKGComplaints.
compForRound := st.DKGComplaints(2)
req.Len(compForRound, 1)
req.True(compForRound[0].Equal(comp))
// Check IsDKGFinal.
- req.True(st.IsDKGFinal(2, 0))
+ req.True(st.IsDKGFinal(2, 1))
// Test ResetDKG.
crs = common.NewRandomHash()
req.NoError(st.RequestChange(StateResetDKG, crs))
req.Equal(st.CRS(2), crs)
// Make sure all DKG fields are cleared.
req.Empty(st.DKGMasterPublicKeys(2))
- req.False(st.IsDKGMPKReady(2, 0))
+ req.False(st.IsDKGMPKReady(2, 1))
req.Empty(st.DKGComplaints(2))
- req.False(st.IsDKGFinal(2, 0))
+ req.False(st.IsDKGFinal(2, 1))
}
func (s *StateTestSuite) TestPacking() {
@@ -353,9 +353,9 @@ func (s *StateTestSuite) TestPacking() {
s.makeDKGChanges(st, masterPubKey, ready, comp, final)
// Make sure everything is empty before changed.
req.Empty(st.DKGMasterPublicKeys(2))
- req.False(st.IsDKGMPKReady(2, 0))
+ req.False(st.IsDKGMPKReady(2, 1))
req.Empty(st.DKGComplaints(2))
- req.False(st.IsDKGFinal(2, 0))
+ req.False(st.IsDKGFinal(2, 1))
packAndApply(st)
// Check if configs are changed.
config, nodes := st.Snapshot()
@@ -373,9 +373,9 @@ func (s *StateTestSuite) TestPacking() {
req.Len(compForRound, 1)
req.True(compForRound[0].Equal(comp))
// Check IsDKGMPKReady.
- req.True(st.IsDKGMPKReady(2, 0))
+ req.True(st.IsDKGMPKReady(2, 1))
// Check IsDKGFinal.
- req.True(st.IsDKGFinal(2, 0))
+ req.True(st.IsDKGFinal(2, 1))
// Test ResetDKG.
crs = common.NewRandomHash()
@@ -384,9 +384,9 @@ func (s *StateTestSuite) TestPacking() {
req.Equal(st.CRS(2), crs)
// Make sure all DKG fields are cleared.
req.Empty(st.DKGMasterPublicKeys(2))
- req.False(st.IsDKGMPKReady(2, 0))
+ req.False(st.IsDKGMPKReady(2, 1))
req.Empty(st.DKGComplaints(2))
- req.False(st.IsDKGFinal(2, 0))
+ req.False(st.IsDKGFinal(2, 1))
}
func (s *StateTestSuite) TestRequestBroadcastAndPack() {
diff --git a/core/test/utils.go b/core/test/utils.go
index 74cde45..e02e194 100644
--- a/core/test/utils.go
+++ b/core/test/utils.go
@@ -170,6 +170,20 @@ func CloneDKGFinalize(final *typesDKG.Finalize) (
return
}
+// CloneDKGSuccess clones a typesDKG.Success instance.
+func CloneDKGSuccess(success *typesDKG.Success) (
+ copied *typesDKG.Success) {
+ b, err := rlp.EncodeToBytes(success)
+ if err != nil {
+ panic(err)
+ }
+ copied = &typesDKG.Success{}
+ if err = rlp.DecodeBytes(b, copied); err != nil {
+ panic(err)
+ }
+ return
+}
+
// CloneDKGPrivateShare clones a typesDKG.PrivateShare instance.
func CloneDKGPrivateShare(prvShare *typesDKG.PrivateShare) (
copied *typesDKG.PrivateShare) {
diff --git a/core/types/dkg/dkg.go b/core/types/dkg/dkg.go
index 868f0da..cb921e5 100644
--- a/core/types/dkg/dkg.go
+++ b/core/types/dkg/dkg.go
@@ -242,6 +242,11 @@ func (c *Complaint) DecodeRLP(s *rlp.Stream) error {
return nil
}
+// IsNack returns true if it's a nack complaint in DKG protocol.
+func (c *Complaint) IsNack() bool {
+ return len(c.PrivateShare.Signature.Signature) == 0
+}
+
// PartialSignature describe a partial signature in DKG protocol.
type PartialSignature struct {
ProposerID types.NodeID `json:"proposer_id"`
@@ -251,7 +256,7 @@ type PartialSignature struct {
Signature crypto.Signature `json:"signature"`
}
-// MPKReady describe a dig ready message in DKG protocol.
+// MPKReady describe a dkg ready message in DKG protocol.
type MPKReady struct {
ProposerID types.NodeID `json:"proposer_id"`
Round uint64 `json:"round"`
@@ -275,7 +280,7 @@ func (ready *MPKReady) Equal(other *MPKReady) bool {
bytes.Compare(ready.Signature.Signature, other.Signature.Signature) == 0
}
-// Finalize describe a dig finalize message in DKG protocol.
+// Finalize describe a dkg finalize message in DKG protocol.
type Finalize struct {
ProposerID types.NodeID `json:"proposer_id"`
Round uint64 `json:"round"`
@@ -299,9 +304,28 @@ func (final *Finalize) Equal(other *Finalize) bool {
bytes.Compare(final.Signature.Signature, other.Signature.Signature) == 0
}
-// IsNack returns true if it's a nack complaint in DKG protocol.
-func (c *Complaint) IsNack() bool {
- return len(c.PrivateShare.Signature.Signature) == 0
+// Success describe a dkg success message in DKG protocol.
+type Success struct {
+ ProposerID types.NodeID `json:"proposer_id"`
+ Round uint64 `json:"round"`
+ Reset uint64 `json:"reset"`
+ Signature crypto.Signature `json:"signature"`
+}
+
+func (s *Success) String() string {
+ return fmt.Sprintf("DKGSuccess{SP:%s Round:%d Reset:%d}",
+ s.ProposerID.String()[:6],
+ s.Round,
+ s.Reset)
+}
+
+// Equal check equality of two Success instances.
+func (s *Success) Equal(other *Success) bool {
+ return s.ProposerID.Equal(other.ProposerID) &&
+ s.Round == other.Round &&
+ s.Reset == other.Reset &&
+ s.Signature.Type == other.Signature.Type &&
+ bytes.Compare(s.Signature.Signature, other.Signature.Signature) == 0
}
// GroupPublicKey is the result of DKG protocol.
diff --git a/core/types/dkg/dkg_test.go b/core/types/dkg/dkg_test.go
index 9f50feb..ea6a565 100644
--- a/core/types/dkg/dkg_test.go
+++ b/core/types/dkg/dkg_test.go
@@ -394,6 +394,42 @@ func (s *DKGTestSuite) TestFinalizeEquality() {
req.True(final1.Equal(final2))
}
+func (s *DKGTestSuite) TestSuccessEquality() {
+ var req = s.Require()
+ success1 := &Success{
+ ProposerID: types.NodeID{Hash: common.NewRandomHash()},
+ Round: 1,
+ Reset: 2,
+ Signature: crypto.Signature{
+ Signature: s.genRandomBytes(),
+ },
+ }
+ // Make a copy
+ success2 := &Success{}
+ s.clone(success1, success2)
+ req.True(success1.Equal(success2))
+ // Change proposer ID.
+ success2.ProposerID = types.NodeID{Hash: common.NewRandomHash()}
+ req.False(success1.Equal(success2))
+ success2.ProposerID = success1.ProposerID
+ // Change round.
+ success2.Round = success1.Round + 1
+ req.False(success1.Equal(success2))
+ success2.Round = success1.Round
+ // Change reset.
+ success2.Reset = success1.Reset + 1
+ req.False(success1.Equal(success2))
+ success2.Reset = success1.Reset
+ // Change signature.
+ success2.Signature = crypto.Signature{
+ Signature: s.genRandomBytes(),
+ }
+ req.False(success1.Equal(success2))
+ success2.Signature = success1.Signature
+ // After changing every field back, they should be equal.
+ req.True(success1.Equal(success2))
+}
+
func TestDKG(t *testing.T) {
suite.Run(t, new(DKGTestSuite))
}
diff --git a/core/utils/crypto.go b/core/utils/crypto.go
index 496944d..42ee612 100644
--- a/core/utils/crypto.go
+++ b/core/utils/crypto.go
@@ -325,6 +325,19 @@ func hashDKGFinalize(final *typesDKG.Finalize) common.Hash {
)
}
+func hashDKGSuccess(success *typesDKG.Success) common.Hash {
+ binaryRound := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryRound, success.Round)
+ binaryReset := make([]byte, 8)
+ binary.LittleEndian.PutUint64(binaryReset, success.Reset)
+
+ return crypto.Keccak256Hash(
+ success.ProposerID.Hash[:],
+ binaryRound,
+ binaryReset,
+ )
+}
+
// VerifyDKGFinalizeSignature verifies DKGFinalize signature.
func VerifyDKGFinalizeSignature(
final *typesDKG.Finalize) (bool, error) {
@@ -339,6 +352,20 @@ func VerifyDKGFinalizeSignature(
return true, nil
}
+// VerifyDKGSuccessSignature verifies DKGSuccess signature.
+func VerifyDKGSuccessSignature(
+ success *typesDKG.Success) (bool, error) {
+ hash := hashDKGSuccess(success)
+ pubKey, err := crypto.SigToPub(hash, success.Signature)
+ if err != nil {
+ return false, err
+ }
+ if success.ProposerID != types.NewNodeID(pubKey) {
+ return false, nil
+ }
+ return true, nil
+}
+
// Rehash hashes the hash again and again and again...
func Rehash(hash common.Hash, count uint) common.Hash {
result := hash
diff --git a/core/utils/crypto_test.go b/core/utils/crypto_test.go
index 24ea68e..29396c5 100644
--- a/core/utils/crypto_test.go
+++ b/core/utils/crypto_test.go
@@ -302,6 +302,29 @@ func (s *CryptoTestSuite) TestDKGSignature() {
s.Require().NoError(err)
s.False(ok)
final.Reset--
+
+ success := &typesDKG.Success{
+ ProposerID: nID,
+ Round: 5,
+ Reset: 6,
+ }
+ success.Signature, err = prv.Sign(hashDKGSuccess(success))
+ s.Require().NoError(err)
+ ok, err = VerifyDKGSuccessSignature(success)
+ s.Require().NoError(err)
+ s.True(ok)
+ // Test incorrect round.
+ success.Round++
+ ok, err = VerifyDKGSuccessSignature(success)
+ s.Require().NoError(err)
+ s.False(ok)
+ success.Round--
+ // Test incorrect reset.
+ success.Reset++
+ ok, err = VerifyDKGSuccessSignature(success)
+ s.Require().NoError(err)
+ s.False(ok)
+ success.Reset--
}
func TestCrypto(t *testing.T) {
diff --git a/core/utils/round-event.go b/core/utils/round-event.go
index 602d2da..b1d4d23 100644
--- a/core/utils/round-event.go
+++ b/core/utils/round-event.go
@@ -84,7 +84,7 @@ func (e RoundEventParam) NextTouchNodeSetCacheHeight() uint64 {
// NextDKGResetHeight returns the height to reset DKG for next period.
func (e RoundEventParam) NextDKGResetHeight() uint64 {
- return e.BeginHeight + e.Config.RoundLength*8/10
+ return e.BeginHeight + e.Config.RoundLength*85/100
}
// NextDKGRegisterHeight returns the height to register DKG.
diff --git a/core/utils/signer.go b/core/utils/signer.go
index 9904410..ff76743 100644
--- a/core/utils/signer.go
+++ b/core/utils/signer.go
@@ -145,3 +145,10 @@ func (s *Signer) SignDKGFinalize(final *typesDKG.Finalize) (err error) {
final.Signature, err = s.prvKey.Sign(hashDKGFinalize(final))
return
}
+
+// SignDKGSuccess signs a DKG success message.
+func (s *Signer) SignDKGSuccess(success *typesDKG.Success) (err error) {
+ success.ProposerID = s.proposerID
+ success.Signature, err = s.prvKey.Sign(hashDKGSuccess(success))
+ return
+}