diff options
Diffstat (limited to 'core/test')
-rw-r--r-- | core/test/governance.go | 29 | ||||
-rw-r--r-- | core/test/state-change-request.go | 6 | ||||
-rw-r--r-- | core/test/state.go | 73 | ||||
-rw-r--r-- | core/test/state_test.go | 39 | ||||
-rw-r--r-- | core/test/utils.go | 13 |
5 files changed, 155 insertions, 5 deletions
diff --git a/core/test/governance.go b/core/test/governance.go index 9bb042e..769934b 100644 --- a/core/test/governance.go +++ b/core/test/governance.go @@ -163,6 +163,9 @@ func (g *Governance) AddDKGMasterPublicKey( if round != masterPublicKey.Round { return } + if g.IsDKGMPKReady(masterPublicKey.Round) { + return + } if err := g.stateModule.RequestChange( StateAddDKGMasterPublicKey, masterPublicKey); err != nil { panic(err) @@ -176,6 +179,32 @@ func (g *Governance) DKGMasterPublicKeys( return g.stateModule.DKGMasterPublicKeys(round) } +// AddDKGMPKReady adds a DKG ready message. +func (g *Governance) AddDKGMPKReady(round uint64, ready *typesDKG.MPKReady) { + if round != ready.Round { + return + } + if err := g.stateModule.RequestChange(StateAddDKGMPKReady, ready); err != nil { + panic(err) + } + g.broadcastPendingStateChanges() +} + +// IsDKGMPKReady checks if DKG is ready. +func (g *Governance) IsDKGMPKReady(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.IsDKGMPKReady(round, int(g.configs[round].DKGSetSize)/3*2) +} + // AddDKGFinalize adds a DKG finalize message. func (g *Governance) AddDKGFinalize(round uint64, final *typesDKG.Finalize) { if round != final.Round { diff --git a/core/test/state-change-request.go b/core/test/state-change-request.go index 1515fd2..83119b5 100644 --- a/core/test/state-change-request.go +++ b/core/test/state-change-request.go @@ -39,6 +39,7 @@ const ( StateAddCRS StateAddDKGComplaint StateAddDKGMasterPublicKey + StateAddDKGMPKReady StateAddDKGFinal // Configuration related. StateChangeNumChains @@ -115,6 +116,8 @@ func (req *StateChangeRequest) Clone() (copied *StateChangeRequest) { Round: crsReq.Round, CRS: crsReq.CRS, } + case StateAddDKGMPKReady: + copied.Payload = cloneDKGMPKReady(req.Payload.(*typesDKG.MPKReady)) case StateAddDKGFinal: copied.Payload = cloneDKGFinalize(req.Payload.(*typesDKG.Finalize)) case StateAddDKGMasterPublicKey: @@ -154,6 +157,9 @@ func (req *StateChangeRequest) String() (ret string) { ret += fmt.Sprintf( "{Type:AddDKGMasterPublicKey %s", req.Payload.(*typesDKG.MasterPublicKey)) + case StateAddDKGMPKReady: + ret += fmt.Sprintf( + "{Type:AddDKGMPKReady %s", req.Payload.(*typesDKG.MPKReady)) case StateAddDKGFinal: ret += fmt.Sprintf( "{Type:AddDKGFinal %s", req.Payload.(*typesDKG.Finalize)) diff --git a/core/test/state.go b/core/test/state.go index 30ed8af..3c24fb6 100644 --- a/core/test/state.go +++ b/core/test/state.go @@ -43,6 +43,8 @@ var ( ErrMissingPreviousCRS = errors.New("missing previous CRS") // ErrUnknownStateChangeType means a StateChangeType is not recognized. ErrUnknownStateChangeType = errors.New("unknown state change type") + // ErrProposerMPKIsReady means a proposer of one mpk is ready. + ErrProposerMPKIsReady = errors.New("proposer mpk is ready") // ErrProposerIsFinal means a proposer of one complaint is finalized. ErrProposerIsFinal = errors.New("proposer is final") // ErrStateConfigNotEqual means configuration part of two states is not @@ -59,6 +61,9 @@ var ( // states are not equal. ErrStateDKGMasterPublicKeysNotEqual = errors.New( "dkg master public keys not equal") + // ErrStateDKGMPKReadysNotEqual means DKG readys of two states are not + // equal. + ErrStateDKGMPKReadysNotEqual = errors.New("dkg readys not equal") // ErrStateDKGFinalsNotEqual means DKG finalizations of two states are not // equal. ErrStateDKGFinalsNotEqual = errors.New("dkg finalizations not equal") @@ -95,6 +100,7 @@ type State struct { // DKG & CRS dkgComplaints map[uint64]map[types.NodeID][]*typesDKG.Complaint dkgMasterPublicKeys map[uint64]map[types.NodeID]*typesDKG.MasterPublicKey + dkgReadys map[uint64]map[types.NodeID]*typesDKG.MPKReady dkgFinals map[uint64]map[types.NodeID]*typesDKG.Finalize crs []common.Hash // Other stuffs @@ -136,6 +142,8 @@ func NewState( dkgSetSize: uint32(len(nodes)), ownRequests: make(map[common.Hash]*StateChangeRequest), globalRequests: make(map[common.Hash]*StateChangeRequest), + dkgReadys: make( + map[uint64]map[types.NodeID]*typesDKG.MPKReady), dkgFinals: make( map[uint64]map[types.NodeID]*typesDKG.Finalize), dkgComplaints: make( @@ -194,6 +202,9 @@ func (s *State) unpackPayload( case StateAddDKGMasterPublicKey: v = &typesDKG.MasterPublicKey{} err = rlp.DecodeBytes(raw.Payload, v) + case StateAddDKGMPKReady: + v = &typesDKG.MPKReady{} + err = rlp.DecodeBytes(raw.Payload, v) case StateAddDKGFinal: v = &typesDKG.Finalize{} err = rlp.DecodeBytes(raw.Payload, v) @@ -351,6 +362,28 @@ func (s *State) Equal(other *State) error { } } } + // Check DKG readys. + if len(s.dkgReadys) != len(other.dkgReadys) { + return ErrStateDKGMPKReadysNotEqual + } + for round, readysForRound := range s.dkgReadys { + otherReadysForRound, exists := other.dkgReadys[round] + if !exists { + return ErrStateDKGMPKReadysNotEqual + } + if len(readysForRound) != len(otherReadysForRound) { + return ErrStateDKGMPKReadysNotEqual + } + for nID, ready := range readysForRound { + otherReady, exists := otherReadysForRound[nID] + if !exists { + return ErrStateDKGMPKReadysNotEqual + } + if !ready.Equal(otherReady) { + return ErrStateDKGMPKReadysNotEqual + } + } + } // Check DKG finals. if len(s.dkgFinals) != len(other.dkgFinals) { return ErrStateDKGFinalsNotEqual @@ -428,6 +461,7 @@ func (s *State) Clone() (copied *State) { map[uint64]map[types.NodeID][]*typesDKG.Complaint), dkgMasterPublicKeys: make( 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), appliedRequests: make(map[common.Hash]struct{}), } @@ -455,6 +489,12 @@ func (s *State) Clone() (copied *State) { cloneDKGMasterPublicKey(mKey) } } + for round, readysForRound := range s.dkgReadys { + copied.dkgReadys[round] = make(map[types.NodeID]*typesDKG.MPKReady) + for nID, ready := range readysForRound { + copied.dkgReadys[round][nID] = cloneDKGMPKReady(ready) + } + } for round, finalsForRound := range s.dkgFinals { copied.dkgFinals[round] = make(map[types.NodeID]*typesDKG.Finalize) for nID, final := range finalsForRound { @@ -587,6 +627,23 @@ func (s *State) isValidRequest(req *StateChangeRequest) (err error) { // NOTE: there would be no lock in this helper, callers should be // responsible for acquiring appropriate lock. switch req.Type { + case StateAddDKGMasterPublicKey: + mpk := req.Payload.(*typesDKG.MasterPublicKey) + // If we've received identical MPK, ignore it. + mpkForRound, exists := s.dkgMasterPublicKeys[mpk.Round] + if exists { + if oldMpk, exists := mpkForRound[mpk.ProposerID]; exists { + if !oldMpk.Equal(mpk) { + err = ErrDuplicatedChange + } + return + } + } + // If we've received MPK from that proposer, we would ignore + // its mpk. + if _, exists := s.dkgReadys[mpk.Round][mpk.ProposerID]; exists { + return ErrProposerMPKIsReady + } case StateAddDKGComplaint: comp := req.Payload.(*typesDKG.Complaint) // If we've received DKG final from that proposer, we would ignore @@ -656,6 +713,12 @@ func (s *State) applyRequest(req *StateChangeRequest) error { map[types.NodeID]*typesDKG.MasterPublicKey) } s.dkgMasterPublicKeys[mKey.Round][mKey.ProposerID] = mKey + case StateAddDKGMPKReady: + ready := req.Payload.(*typesDKG.MPKReady) + if _, exists := s.dkgReadys[ready.Round]; !exists { + s.dkgReadys[ready.Round] = make(map[types.NodeID]*typesDKG.MPKReady) + } + s.dkgReadys[ready.Round][ready.ProposerID] = ready case StateAddDKGFinal: final := req.Payload.(*typesDKG.Finalize) if _, exists := s.dkgFinals[final.Round]; !exists { @@ -714,6 +777,8 @@ func (s *State) RequestChange( // These cases for for type assertion, make sure callers pass expected types. case StateAddCRS: payload = payload.(*crsAdditionRequest) + case StateAddDKGMPKReady: + payload = payload.(*typesDKG.MPKReady) case StateAddDKGFinal: payload = payload.(*typesDKG.Finalize) case StateAddDKGMasterPublicKey: @@ -783,6 +848,14 @@ func (s *State) DKGMasterPublicKeys(round uint64) []*typesDKG.MasterPublicKey { return mpks } +// IsDKGMPKReady checks if current received dkg readys exceeds threshold. +// This information won't be snapshot, thus can't be cached in test.Governance. +func (s *State) IsDKGMPKReady(round uint64, threshold int) bool { + s.lock.RLock() + defer s.lock.RUnlock() + return len(s.dkgReadys[round]) > threshold +} + // IsDKGFinal checks if current received dkg finals exceeds threshold. // This information won't be snapshot, thus can't be cached in test.Governance. func (s *State) IsDKGFinal(round uint64, threshold int) bool { diff --git a/core/test/state_test.go b/core/test/state_test.go index 9daee3a..c05d41b 100644 --- a/core/test/state_test.go +++ b/core/test/state_test.go @@ -70,6 +70,17 @@ func (s *StateTestSuite) newDKGComplaint(round uint64) *typesDKG.Complaint { } } +func (s *StateTestSuite) newDKGMPKReady(round uint64) *typesDKG.MPKReady { + prvKey, err := ecdsa.NewPrivateKey() + s.Require().NoError(err) + pubKey := prvKey.PublicKey() + nodeID := types.NewNodeID(pubKey) + // TODO(mission): sign it. + return &typesDKG.MPKReady{ + ProposerID: nodeID, + Round: round, + } +} func (s *StateTestSuite) newDKGFinal(round uint64) *typesDKG.Finalize { prvKey, err := ecdsa.NewPrivateKey() s.Require().NoError(err) @@ -119,9 +130,11 @@ func (s *StateTestSuite) findNode( func (s *StateTestSuite) makeDKGChanges( st *State, masterPubKey *typesDKG.MasterPublicKey, + ready *typesDKG.MPKReady, complaint *typesDKG.Complaint, final *typesDKG.Finalize) { st.RequestChange(StateAddDKGMasterPublicKey, masterPubKey) + st.RequestChange(StateAddDKGMPKReady, ready) st.RequestChange(StateAddDKGComplaint, complaint) st.RequestChange(StateAddDKGFinal, final) } @@ -175,9 +188,10 @@ func (s *StateTestSuite) TestEqual() { crs := common.NewRandomHash() req.NoError(st.ProposeCRS(1, crs)) masterPubKey := s.newDKGMasterPublicKey(2) + ready := s.newDKGMPKReady(2) comp := s.newDKGComplaint(2) final := s.newDKGFinal(2) - s.makeDKGChanges(st, masterPubKey, comp, final) + s.makeDKGChanges(st, masterPubKey, ready, comp, final) // Remove dkg complaints from cloned one to check if equal. st3 := st.Clone() req.NoError(st.Equal(st3)) @@ -188,6 +202,11 @@ func (s *StateTestSuite) TestEqual() { req.NoError(st.Equal(st4)) delete(st4.dkgMasterPublicKeys, uint64(2)) req.Equal(st.Equal(st4), ErrStateDKGMasterPublicKeysNotEqual) + // Remove dkg ready from cloned one to check if equal. + st4a := st.Clone() + req.NoError(st.Equal(st4a)) + delete(st4a.dkgReadys, uint64(2)) + req.Equal(st.Equal(st4a), ErrStateDKGMPKReadysNotEqual) // Remove dkg finalize from cloned one to check if equal. st5 := st.Clone() req.NoError(st.Equal(st5)) @@ -222,9 +241,10 @@ func (s *StateTestSuite) TestPendingChangesEqual() { crs := common.NewRandomHash() req.NoError(st.ProposeCRS(1, crs)) masterPubKey := s.newDKGMasterPublicKey(2) + ready := s.newDKGMPKReady(2) comp := s.newDKGComplaint(2) final := s.newDKGFinal(2) - s.makeDKGChanges(st, masterPubKey, comp, final) + s.makeDKGChanges(st, masterPubKey, ready, comp, final) } func (s *StateTestSuite) TestLocalMode() { @@ -266,17 +286,21 @@ 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.Empty(st.DKGComplaints(2)) req.False(st.IsDKGFinal(2, 0)) // Add DKG stuffs. masterPubKey := s.newDKGMasterPublicKey(2) + ready := s.newDKGMPKReady(2) comp := s.newDKGComplaint(2) final := s.newDKGFinal(2) - s.makeDKGChanges(st, masterPubKey, comp, final) + s.makeDKGChanges(st, masterPubKey, ready, comp, final) // Check DKGMasterPublicKeys. masterKeyForRound := st.DKGMasterPublicKeys(2) req.Len(masterKeyForRound, 1) req.True(masterKeyForRound[0].Equal(masterPubKey)) + // Check IsDKGMPKReady. + req.True(st.IsDKGMPKReady(2, 0)) // Check DKGComplaints. compForRound := st.DKGComplaints(2) req.Len(compForRound, 1) @@ -307,11 +331,13 @@ func (s *StateTestSuite) TestPacking() { st.RequestChange(StateAddNode, pubKey) // Add DKG stuffs. masterPubKey := s.newDKGMasterPublicKey(2) + ready := s.newDKGMPKReady(2) comp := s.newDKGComplaint(2) final := s.newDKGFinal(2) - s.makeDKGChanges(st, masterPubKey, comp, final) + 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.Empty(st.DKGComplaints(2)) req.False(st.IsDKGFinal(2, 0)) // In remote mode, we need to manually convert own requests to global ones. @@ -338,6 +364,8 @@ func (s *StateTestSuite) TestPacking() { compForRound := st.DKGComplaints(2) req.Len(compForRound, 1) req.True(compForRound[0].Equal(comp)) + // Check IsDKGMPKReady. + req.True(st.IsDKGMPKReady(2, 0)) // Check IsDKGFinal. req.True(st.IsDKGFinal(2, 0)) } @@ -371,9 +399,10 @@ func (s *StateTestSuite) TestRequestBroadcastAndPack() { st.RequestChange(StateAddNode, pubKey) // Add DKG stuffs. masterPubKey := s.newDKGMasterPublicKey(2) + ready := s.newDKGMPKReady(2) comp := s.newDKGComplaint(2) final := s.newDKGFinal(2) - s.makeDKGChanges(st, masterPubKey, comp, final) + s.makeDKGChanges(st, masterPubKey, ready, comp, final) // Pack those changes into a byte stream, and pass it to other State // instance. packed, err := st.PackOwnRequests() diff --git a/core/test/utils.go b/core/test/utils.go index 6abd0b5..c6c08fc 100644 --- a/core/test/utils.go +++ b/core/test/utils.go @@ -147,6 +147,19 @@ func cloneDKGMasterPublicKey(mpk *typesDKG.MasterPublicKey) ( return } +func cloneDKGMPKReady(ready *typesDKG.MPKReady) ( + copied *typesDKG.MPKReady) { + b, err := rlp.EncodeToBytes(ready) + if err != nil { + panic(err) + } + copied = &typesDKG.MPKReady{} + if err = rlp.DecodeBytes(b, copied); err != nil { + panic(err) + } + return +} + func cloneDKGFinalize(final *typesDKG.Finalize) ( copied *typesDKG.Finalize) { b, err := rlp.EncodeToBytes(final) |