aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2019-03-04 14:07:47 +0800
committerGitHub <noreply@github.com>2019-03-04 14:07:47 +0800
commit603f8d6d999c1bb5b16c2f5dfc88f8bc9da7fc33 (patch)
tree5268edc91774ae64717b5ee24cc6ecb9393bb43c
parent400c4f2fd9400c0fa01fc416fa1804ee7e87af88 (diff)
downloadtangerine-consensus-603f8d6d999c1bb5b16c2f5dfc88f8bc9da7fc33.tar.gz
tangerine-consensus-603f8d6d999c1bb5b16c2f5dfc88f8bc9da7fc33.tar.zst
tangerine-consensus-603f8d6d999c1bb5b16c2f5dfc88f8bc9da7fc33.zip
core: resetDKG interface (#461)
* core: resetDKG skeleton * Add Equal test * Add TestLocal * Add TestPacking
-rw-r--r--core/interfaces.go6
-rw-r--r--core/test/governance.go21
-rw-r--r--core/test/state-change-request.go1
-rw-r--r--core/test/state.go44
-rw-r--r--core/test/state_test.go75
5 files changed, 129 insertions, 18 deletions
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() {