From 603f8d6d999c1bb5b16c2f5dfc88f8bc9da7fc33 Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Mon, 4 Mar 2019 14:07:47 +0800 Subject: core: resetDKG interface (#461) * core: resetDKG skeleton * Add Equal test * Add TestLocal * Add TestPacking --- core/interfaces.go | 6 ++++ core/test/governance.go | 21 +++++++++++ core/test/state-change-request.go | 1 + core/test/state.go | 44 +++++++++++++++++++++++ core/test/state_test.go | 75 +++++++++++++++++++++++++++++---------- 5 files changed, 129 insertions(+), 18 deletions(-) (limited to 'core') diff --git a/core/interfaces.go b/core/interfaces.go index 3879e36..b7e000a 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -143,6 +143,12 @@ type Governance interface { // ReportForkBlock reports a node for forking blocks. ReportForkBlock(block1, block2 *types.Block) + + // ResetDKG resets latest DKG data and propose new CRS. + ResetDKG(newSignedCRS []byte) + + // DKGResetCount returns the reset count for DKG of given round. + DKGResetCount(round uint64) uint64 } // Ticker define the capability to tick by interval. diff --git a/core/test/governance.go b/core/test/governance.go index d540280..937c953 100644 --- a/core/test/governance.go +++ b/core/test/governance.go @@ -240,6 +240,27 @@ func (g *Governance) ReportForkVote(vote1, vote2 *types.Vote) { func (g *Governance) ReportForkBlock(block1, block2 *types.Block) { } +// ResetDKG resets latest DKG data and propose new CRS. +func (g *Governance) ResetDKG(newSignedCRS []byte) { + g.lock.Lock() + defer g.lock.Unlock() + crs := crypto.Keccak256Hash(newSignedCRS) + if err := g.stateModule.RequestChange(StateResetDKG, crs); err != nil { + // ResetDKG can be proposed multiple times, other errors are not + // accepted. + if err != ErrDuplicatedChange { + panic(err) + } + return + } + g.broadcastPendingStateChanges() +} + +// DKGResetCount returns the reset count for DKG of given round. +func (g *Governance) DKGResetCount(round uint64) uint64 { + return g.stateModule.DKGResetCount(round) +} + // // Test Utilities // diff --git a/core/test/state-change-request.go b/core/test/state-change-request.go index 2aea614..b7c7bac 100644 --- a/core/test/state-change-request.go +++ b/core/test/state-change-request.go @@ -40,6 +40,7 @@ const ( StateAddDKGMasterPublicKey StateAddDKGMPKReady StateAddDKGFinal + StateResetDKG // Configuration related. StateChangeLambdaBA StateChangeLambdaDKG diff --git a/core/test/state.go b/core/test/state.go index 02ee412..f1cf365 100644 --- a/core/test/state.go +++ b/core/test/state.go @@ -68,6 +68,9 @@ var ( ErrStateDKGFinalsNotEqual = errors.New("dkg finalizations 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 + // equal. + ErrStateDKGResetCountNotEqual = errors.New("dkg reset count not equal") // ErrStatePendingChangesNotEqual means pending change requests of two // states are not equal. ErrStatePendingChangesNotEqual = errors.New("pending changes not equal") @@ -99,6 +102,7 @@ type State struct { dkgReadys map[uint64]map[types.NodeID]*typesDKG.MPKReady dkgFinals map[uint64]map[types.NodeID]*typesDKG.Finalize crs []common.Hash + dkgResetCount map[uint64]uint64 // Other stuffs local bool logger common.Logger @@ -143,6 +147,7 @@ func NewState( map[uint64]map[types.NodeID][]*typesDKG.Complaint), dkgMasterPublicKeys: make( map[uint64]map[types.NodeID]*typesDKG.MasterPublicKey), + dkgResetCount: make(map[uint64]uint64), appliedRequests: make(map[common.Hash]struct{}), } } @@ -200,6 +205,10 @@ func (s *State) unpackPayload( case StateAddDKGFinal: v = &typesDKG.Finalize{} err = rlp.DecodeBytes(raw.Payload, v) + case StateResetDKG: + var tmp common.Hash + err = rlp.DecodeBytes(raw.Payload, &tmp) + v = tmp case StateChangeLambdaBA: var tmp uint64 err = rlp.DecodeBytes(raw.Payload, &tmp) @@ -392,6 +401,15 @@ func (s *State) Equal(other *State) error { return ErrStateCRSsNotEqual } } + // Check dkgResetCount. + if len(s.dkgResetCount) != len(other.dkgResetCount) { + return ErrStateDKGResetCountNotEqual + } + for idx, count := range s.dkgResetCount { + if count != other.dkgResetCount[idx] { + return ErrStateDKGResetCountNotEqual + } + } // Check pending changes. checkPending := func( src, target map[common.Hash]*StateChangeRequest) error { @@ -478,6 +496,10 @@ func (s *State) Clone() (copied *State) { for _, crs := range s.crs { copied.crs = append(copied.crs, crs) } + copied.dkgResetCount = make(map[uint64]uint64, len(s.dkgResetCount)) + for round, count := range s.dkgResetCount { + copied.dkgResetCount[round] = count + } for hash := range s.appliedRequests { copied.appliedRequests[hash] = struct{}{} } @@ -654,6 +676,11 @@ func (s *State) isValidRequest(req *StateChangeRequest) (err error) { } else { return ErrMissingPreviousCRS } + case StateResetDKG: + newCRS := req.Payload.(common.Hash) + if s.crs[len(s.crs)-1].Equal(newCRS) { + return ErrDuplicatedChange + } } return nil } @@ -702,6 +729,14 @@ 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 StateResetDKG: + round := uint64(len(s.crs) - 1) + s.crs[round] = req.Payload.(common.Hash) + s.dkgResetCount[round]++ + delete(s.dkgMasterPublicKeys, round) + delete(s.dkgReadys, round) + delete(s.dkgComplaints, round) + delete(s.dkgFinals, round) case StateChangeLambdaBA: s.lambdaBA = time.Duration(req.Payload.(uint64)) case StateChangeLambdaDKG: @@ -752,6 +787,8 @@ func (s *State) RequestChange( payload = payload.(*typesDKG.MasterPublicKey) case StateAddDKGComplaint: payload = payload.(*typesDKG.Complaint) + case StateResetDKG: + payload = payload.(common.Hash) } req := NewStateChangeRequest(t, payload) s.lock.Lock() @@ -830,3 +867,10 @@ func (s *State) IsDKGFinal(round uint64, threshold int) bool { defer s.lock.RUnlock() return len(s.dkgFinals[round]) > threshold } + +// DKGResetCount returns the reset count for DKG of given round. +func (s *State) DKGResetCount(round uint64) uint64 { + s.lock.RLock() + defer s.lock.RUnlock() + return s.dkgResetCount[round] +} diff --git a/core/test/state_test.go b/core/test/state_test.go index 9a68930..ad3a1d6 100644 --- a/core/test/state_test.go +++ b/core/test/state_test.go @@ -174,8 +174,9 @@ func (s *StateTestSuite) TestEqual() { req.NoError(st.Equal(st2)) s.makeConfigChanges(st) req.Equal(st.Equal(st2), ErrStateConfigNotEqual) - crs := common.NewRandomHash() - req.NoError(st.ProposeCRS(1, crs)) + req.NoError(st.ProposeCRS(1, common.NewRandomHash())) + req.NoError(st.ProposeCRS(2, common.NewRandomHash())) + req.NoError(st.RequestChange(StateResetDKG, common.NewRandomHash())) masterPubKey := s.newDKGMasterPublicKey(2) ready := s.newDKGMPKReady(2) comp := s.newDKGComplaint(2) @@ -201,18 +202,24 @@ func (s *StateTestSuite) TestEqual() { req.NoError(st.Equal(st5)) delete(st5.dkgFinals, uint64(2)) req.Equal(st.Equal(st5), ErrStateDKGFinalsNotEqual) + // Remove dkgResetCount from cloned one to check if equal. + st6 := st.Clone() + req.NoError(st.Equal(st6)) + delete(st6.dkgResetCount, uint64(2)) + req.Equal(st.Equal(st6), ErrStateDKGResetCountNotEqual) + // Switch to remote mode. st.SwitchToRemoteMode() // Make some change. req.NoError(st.RequestChange(StateChangeNotarySetSize, uint32(100))) - st6 := st.Clone() - req.NoError(st.Equal(st6)) + str := st.Clone() + req.NoError(st.Equal(str)) // Remove the pending change, should not be equal. - req.Len(st6.ownRequests, 1) - for k := range st6.ownRequests { - delete(st6.ownRequests, k) + req.Len(str.ownRequests, 1) + for k := range str.ownRequests { + delete(str.ownRequests, k) } - req.Error(ErrStatePendingChangesNotEqual, st.Equal(st6)) + req.Error(ErrStatePendingChangesNotEqual, st.Equal(str)) } func (s *StateTestSuite) TestPendingChangesEqual() { @@ -269,6 +276,9 @@ func (s *StateTestSuite) TestLocalMode() { crs := common.NewRandomHash() req.NoError(st.ProposeCRS(1, crs)) req.Equal(st.CRS(1), crs) + crs = common.NewRandomHash() + req.NoError(st.ProposeCRS(2, crs)) + req.Equal(st.CRS(2), crs) // Test adding node set, DKG complaints, final, master public key. // Make sure everything is empty before changed. req.Empty(st.DKGMasterPublicKeys(2)) @@ -293,6 +303,15 @@ func (s *StateTestSuite) TestLocalMode() { req.True(compForRound[0].Equal(comp)) // Check IsDKGFinal. req.True(st.IsDKGFinal(2, 0)) + // 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.Empty(st.DKGComplaints(2)) + req.False(st.IsDKGFinal(2, 0)) } func (s *StateTestSuite) TestPacking() { @@ -302,6 +321,17 @@ func (s *StateTestSuite) TestPacking() { req = s.Require() lambda = 250 * time.Millisecond ) + packAndApply := func(st *State) { + // In remote mode, we need to manually convert own requests to global ones. + _, err := st.PackOwnRequests() + req.NoError(err) + // Pack changes into bytes. + b, err := st.PackRequests() + req.NoError(err) + req.NotEmpty(b) + // Apply those bytes back. + req.NoError(st.Apply(b)) + } // Make config changes. _, genesisNodes, err := NewKeys(20) req.NoError(err) @@ -310,6 +340,11 @@ func (s *StateTestSuite) TestPacking() { // Add new CRS. crs := common.NewRandomHash() req.NoError(st.ProposeCRS(1, crs)) + packAndApply(st) + // Check if CRS is added. + req.Equal(st.CRS(1), crs) + crs2 := common.NewRandomHash() + req.NoError(st.ProposeCRS(2, crs2)) // Add new node. prvKey, err := ecdsa.NewPrivateKey() req.NoError(err) @@ -326,20 +361,12 @@ func (s *StateTestSuite) TestPacking() { req.False(st.IsDKGMPKReady(2, 0)) req.Empty(st.DKGComplaints(2)) req.False(st.IsDKGFinal(2, 0)) - // In remote mode, we need to manually convert own requests to global ones. - _, err = st.PackOwnRequests() - req.NoError(err) - // Pack changes into bytes. - b, err := st.PackRequests() - req.NoError(err) - req.NotEmpty(b) - // Apply those bytes back. - req.NoError(st.Apply(b)) + packAndApply(st) // Check if configs are changed. config, nodes := st.Snapshot() s.checkConfigChanges(config) // Check if CRS is added. - req.Equal(st.CRS(1), crs) + req.Equal(st.CRS(2), crs2) // Check if new node is added. req.True(s.findNode(nodes, pubKey)) // Check DKGMasterPublicKeys. @@ -354,6 +381,18 @@ func (s *StateTestSuite) TestPacking() { req.True(st.IsDKGMPKReady(2, 0)) // Check IsDKGFinal. req.True(st.IsDKGFinal(2, 0)) + + // Test ResetDKG. + crs = common.NewRandomHash() + req.NoError(st.RequestChange(StateResetDKG, crs)) + packAndApply(st) + 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.Empty(st.DKGComplaints(2)) + req.False(st.IsDKGFinal(2, 0)) + } func (s *StateTestSuite) TestRequestBroadcastAndPack() { -- cgit v1.2.3