aboutsummaryrefslogtreecommitdiffstats
path: root/core/test
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2018-12-19 17:16:40 +0800
committerGitHub <noreply@github.com>2018-12-19 17:16:40 +0800
commitc7b4045802450df361216d9e7da3ec318e67cc34 (patch)
treec4060817a54e5cf455e830b21e6a91b9fc11004f /core/test
parent7bafefa5c70a26a28636123cb2b6598eea3ed380 (diff)
downloaddexon-consensus-c7b4045802450df361216d9e7da3ec318e67cc34.tar
dexon-consensus-c7b4045802450df361216d9e7da3ec318e67cc34.tar.gz
dexon-consensus-c7b4045802450df361216d9e7da3ec318e67cc34.tar.bz2
dexon-consensus-c7b4045802450df361216d9e7da3ec318e67cc34.tar.lz
dexon-consensus-c7b4045802450df361216d9e7da3ec318e67cc34.tar.xz
dexon-consensus-c7b4045802450df361216d9e7da3ec318e67cc34.tar.zst
dexon-consensus-c7b4045802450df361216d9e7da3ec318e67cc34.zip
core: Add a `MPKReady` so `MasterPublicKey` cannot be added afterwards (#375)
* Add type DKGReady * Add DKGReady to interface and state * DKG will wait for MPK to be ready before running * Modify test * Check if self's MPK is registered * Add test for delay add MPK * Rename Ready to MPKReady
Diffstat (limited to 'core/test')
-rw-r--r--core/test/governance.go29
-rw-r--r--core/test/state-change-request.go6
-rw-r--r--core/test/state.go73
-rw-r--r--core/test/state_test.go39
-rw-r--r--core/test/utils.go13
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)