aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorWei-Ning Huang <w@dexon.org>2019-03-29 14:28:04 +0800
committerWei-Ning Huang <w@dexon.org>2019-04-09 21:32:59 +0800
commitf0257e264b94b67137595e4a357589da7cfde82e (patch)
tree926b29bcd4e0639791fbe3da9a69b35effb75518 /core
parent3489e7ec917cd00d1995cd7a0f669bfc8aff0a8e (diff)
downloaddexon-f0257e264b94b67137595e4a357589da7cfde82e.tar
dexon-f0257e264b94b67137595e4a357589da7cfde82e.tar.gz
dexon-f0257e264b94b67137595e4a357589da7cfde82e.tar.bz2
dexon-f0257e264b94b67137595e4a357589da7cfde82e.tar.lz
dexon-f0257e264b94b67137595e4a357589da7cfde82e.tar.xz
dexon-f0257e264b94b67137595e4a357589da7cfde82e.tar.zst
dexon-f0257e264b94b67137595e4a357589da7cfde82e.zip
core: vm: make fail stop fine value configurable (#312)
A node is now quailified only if it has no pending fine and staked >= minstake.
Diffstat (limited to 'core')
-rw-r--r--core/vm/oracle_contract_abi.go2
-rw-r--r--core/vm/oracle_contracts.go116
-rw-r--r--core/vm/oracle_contracts_test.go29
3 files changed, 92 insertions, 55 deletions
diff --git a/core/vm/oracle_contract_abi.go b/core/vm/oracle_contract_abi.go
index 3345a8f4c..4572a74ae 100644
--- a/core/vm/oracle_contract_abi.go
+++ b/core/vm/oracle_contract_abi.go
@@ -771,7 +771,7 @@ const GovernanceABIJSON = `
"type": "bytes"
}
],
- "name": "ForkReported",
+ "name": "Reported",
"type": "event"
},
{
diff --git a/core/vm/oracle_contracts.go b/core/vm/oracle_contracts.go
index 3da5e3335..4459b95b9 100644
--- a/core/vm/oracle_contracts.go
+++ b/core/vm/oracle_contracts.go
@@ -44,12 +44,14 @@ import (
type Bytes32 [32]byte
-type ReportType uint64
+type FineType uint64
const (
- ReportTypeInvalidDKG = iota
- ReportTypeForkVote
- ReportTypeForkBlock
+ FineTypeFailStop = iota
+ FineTypeFailStopDKG
+ FineTypeInvalidDKG
+ FineTypeForkVote
+ FineTypeForkBlock
)
const GovernanceActionGasCost = 200000
@@ -484,7 +486,11 @@ func (s *GovernanceState) QualifiedNodes() []*nodeInfo {
var nodes []*nodeInfo
for i := int64(0); i < int64(s.LenNodes().Uint64()); i++ {
node := s.Node(big.NewInt(i))
- if new(big.Int).Sub(node.Staked, node.Fined).Cmp(s.MinStake()) >= 0 {
+ // Node with unpaid fine is consider unqualified.
+ if node.Fined.Cmp(big.NewInt(0)) > 0 {
+ continue
+ }
+ if node.Staked.Cmp(s.MinStake()) >= 0 {
nodes = append(nodes, node)
}
}
@@ -970,15 +976,11 @@ func (s *GovernanceState) Disqualify(n *nodeInfo) error {
return errors.New("node does not exist")
}
- // Fine the node so it's staked value is 1 wei under minStake.
+ // Set fined value.
node := s.Node(offset)
- extra := new(big.Int).Sub(new(big.Int).Sub(node.Staked, node.Fined), s.MinStake())
- amount := new(big.Int).Add(extra, big.NewInt(1))
-
- if amount.Cmp(big.NewInt(0)) > 0 {
- node.Fined = new(big.Int).Add(node.Fined, amount)
- s.UpdateNode(offset, node)
- }
+ amount := s.FineValue(big.NewInt(FineTypeFailStop))
+ node.Fined = new(big.Int).Add(node.Fined, amount)
+ s.UpdateNode(offset, node)
return nil
}
@@ -1134,7 +1136,7 @@ func (s *GovernanceState) emitNodeRemoved(nodeAddr common.Address) {
}
// event ForkReported(address indexed NodeAddress, address indexed Type, bytes Arg1, bytes Arg2);
-func (s *GovernanceState) emitForkReported(nodeAddr common.Address, reportType *big.Int, arg1, arg2 []byte) {
+func (s *GovernanceState) emitReported(nodeAddr common.Address, reportType *big.Int, arg1, arg2 []byte) {
t, err := abi.NewType("bytes", nil)
if err != nil {
@@ -1160,7 +1162,7 @@ func (s *GovernanceState) emitForkReported(nodeAddr common.Address, reportType *
}
s.StateDB.AddLog(&types.Log{
Address: GovernanceContractAddress,
- Topics: []common.Hash{GovernanceABI.Events["ForkReported"].Id(), nodeAddr.Hash()},
+ Topics: []common.Hash{GovernanceABI.Events["Reported"].Id(), nodeAddr.Hash()},
Data: data,
})
}
@@ -1338,6 +1340,39 @@ func (g *GovernanceContract) clearDKG() {
g.state.ResetDKGFinalizedsCount()
}
+func (g *GovernanceContract) fineFailStopDKG(threshold int) {
+ complaintsByID := map[coreTypes.NodeID]map[coreTypes.NodeID]struct{}{}
+ for _, complaint := range g.state.DKGComplaints() {
+ comp := new(dkgTypes.Complaint)
+ if err := rlp.DecodeBytes(complaint, comp); err != nil {
+ panic(err)
+ }
+
+ if comp.IsNack() {
+ if _, exist := complaintsByID[comp.PrivateShare.ProposerID]; !exist {
+ complaintsByID[comp.PrivateShare.ProposerID] =
+ make(map[coreTypes.NodeID]struct{})
+ }
+ complaintsByID[comp.PrivateShare.ProposerID][comp.ProposerID] = struct{}{}
+ }
+ }
+ for id, complaints := range complaintsByID {
+ if len(complaints) > threshold {
+ offset := g.state.NodesOffsetByNodeKeyAddress(IdToAddress(id))
+ // Node might have been unstaked.
+ if offset.Cmp(big.NewInt(0)) < 0 {
+ continue
+ }
+
+ node := g.state.Node(offset)
+ amount := g.state.FineValue(big.NewInt(FineTypeFailStopDKG))
+ node.Fined = new(big.Int).Add(node.Fined, amount)
+ g.state.UpdateNode(offset, node)
+ g.state.emitFined(node.Owner, amount)
+ }
+ }
+}
+
func (g *GovernanceContract) addDKGComplaint(comp []byte) ([]byte, error) {
caller := g.contract.Caller()
offset := g.state.NodesOffsetByNodeKeyAddress(caller)
@@ -1352,13 +1387,11 @@ func (g *GovernanceContract) addDKGComplaint(comp []byte) ([]byte, error) {
return nil, errExecutionReverted
}
- // Calculate 2f
- threshold := new(big.Int).Mul(
- big.NewInt(2),
- new(big.Int).Div(g.state.NotarySetSize(), big.NewInt(3)))
+ // Calculate 2f + 1
+ threshold := 2*g.configNotarySetSize(g.evm.Round).Uint64()/3 + 1
// If 2f + 1 of DKG set is finalized, one can not propose complaint anymore.
- if g.state.DKGFinalizedsCount().Cmp(threshold) > 0 {
+ if g.state.DKGFinalizedsCount().Uint64() >= threshold {
return nil, errExecutionReverted
}
@@ -1410,7 +1443,7 @@ func (g *GovernanceContract) addDKGComplaint(comp []byte) ([]byte, error) {
if err != nil {
return nil, errExecutionReverted
}
- fineValue := g.state.FineValue(big.NewInt(ReportTypeInvalidDKG))
+ fineValue := g.state.FineValue(big.NewInt(FineTypeInvalidDKG))
if err := g.fine(node.Owner, fineValue, comp, nil); err != nil {
return nil, errExecutionReverted
}
@@ -1455,13 +1488,11 @@ func (g *GovernanceContract) addDKGMasterPublicKey(mpk []byte) ([]byte, error) {
return nil, errExecutionReverted
}
- // Calculate 2f
- threshold := new(big.Int).Mul(
- big.NewInt(2),
- new(big.Int).Div(g.state.NotarySetSize(), big.NewInt(3)))
+ // Calculate 2f + 1
+ threshold := 2*g.configNotarySetSize(g.evm.Round).Uint64()/3 + 1
// If 2f + 1 of DKG set is mpk ready, one can not propose mpk anymore.
- if g.state.DKGMPKReadysCount().Cmp(threshold) > 0 {
+ if g.state.DKGMPKReadysCount().Uint64() >= threshold {
return nil, errExecutionReverted
}
@@ -1549,6 +1580,14 @@ func (g *GovernanceContract) addDKGFinalize(finalize []byte) ([]byte, error) {
g.state.IncDKGFinalizedsCount()
}
+ threshold := 2*g.configNotarySetSize(g.evm.Round).Uint64()/3 + 1
+
+ if g.state.DKGFinalizedsCount().Uint64() >= threshold {
+ tsigThreshold := coreUtils.GetDKGThreshold(&coreTypes.Config{
+ NotarySetSize: uint32(g.configNotarySetSize(g.evm.Round).Uint64())})
+ g.fineFailStopDKG(tsigThreshold)
+ }
+
return g.useGas(GovernanceActionGasCost)
}
@@ -1836,11 +1875,11 @@ func (g *GovernanceContract) fine(nodeAddr common.Address, amount *big.Int, payl
}
func (g *GovernanceContract) report(reportType *big.Int, arg1, arg2 []byte) ([]byte, error) {
- typeEnum := ReportType(reportType.Uint64())
+ typeEnum := FineType(reportType.Uint64())
var reportedNodeID coreTypes.NodeID
switch typeEnum {
- case ReportTypeForkVote:
+ case FineTypeForkVote:
vote1 := new(coreTypes.Vote)
if err := rlp.DecodeBytes(arg1, vote1); err != nil {
return nil, errExecutionReverted
@@ -1854,7 +1893,7 @@ func (g *GovernanceContract) report(reportType *big.Int, arg1, arg2 []byte) ([]b
return nil, errExecutionReverted
}
reportedNodeID = vote1.ProposerID
- case ReportTypeForkBlock:
+ case FineTypeForkBlock:
block1 := new(coreTypes.Block)
if err := rlp.DecodeBytes(arg1, block1); err != nil {
return nil, errExecutionReverted
@@ -1877,7 +1916,7 @@ func (g *GovernanceContract) report(reportType *big.Int, arg1, arg2 []byte) ([]b
return nil, errExecutionReverted
}
- g.state.emitForkReported(node.Owner, reportType, arg1, arg2)
+ g.state.emitReported(node.Owner, reportType, arg1, arg2)
fineValue := g.state.FineValue(reportType)
if err := g.fine(node.Owner, fineValue, arg1, arg2); err != nil {
@@ -1922,15 +1961,13 @@ func (g *GovernanceContract) resetDKG(newSignedCRS []byte) ([]byte, error) {
}
// Check if next DKG did not success.
- // Calculate 2f
- threshold := new(big.Int).Mul(
- big.NewInt(2),
- new(big.Int).Div(g.state.NotarySetSize(), big.NewInt(3)))
+ // Calculate 2f + 1
+ threshold := 2*g.configNotarySetSize(g.evm.Round).Uint64()/3 + 1
tsigThreshold := coreUtils.GetDKGThreshold(&coreTypes.Config{
- NotarySetSize: uint32(g.state.NotarySetSize().Uint64())})
+ NotarySetSize: uint32(g.configNotarySetSize(g.evm.Round).Uint64())})
// If 2f + 1 of DKG set is finalized, check if DKG succeeded.
- if g.state.DKGFinalizedsCount().Cmp(threshold) > 0 {
+ if g.state.DKGFinalizedsCount().Uint64() >= threshold {
_, err := g.coreDKGUtils.NewGroupPublicKey(&g.state, nextRound, tsigThreshold)
// DKG success.
if err == nil {
@@ -1943,6 +1980,9 @@ func (g *GovernanceContract) resetDKG(newSignedCRS []byte) ([]byte, error) {
}
}
+ // Fine fail stop DKGs.
+ g.fineFailStopDKG(tsigThreshold)
+
// Update CRS.
state, err := getRoundState(g.evm, round)
if err != nil {
@@ -2514,7 +2554,7 @@ func PackReportForkVote(vote1, vote2 *coreTypes.Vote) ([]byte, error) {
return nil, err
}
- res, err := method.Inputs.Pack(big.NewInt(ReportTypeForkVote), vote1Bytes, vote2Bytes)
+ res, err := method.Inputs.Pack(big.NewInt(FineTypeForkVote), vote1Bytes, vote2Bytes)
if err != nil {
return nil, err
}
@@ -2535,7 +2575,7 @@ func PackReportForkBlock(block1, block2 *coreTypes.Block) ([]byte, error) {
return nil, err
}
- res, err := method.Inputs.Pack(big.NewInt(ReportTypeForkBlock), block1Bytes, block2Bytes)
+ res, err := method.Inputs.Pack(big.NewInt(FineTypeForkBlock), block1Bytes, block2Bytes)
if err != nil {
return nil, err
}
diff --git a/core/vm/oracle_contracts_test.go b/core/vm/oracle_contracts_test.go
index ad6ffd9e3..af80a7132 100644
--- a/core/vm/oracle_contracts_test.go
+++ b/core/vm/oracle_contracts_test.go
@@ -89,6 +89,7 @@ func (g *GovernanceStateTestSuite) SetupTest() {
config := params.TestnetChainConfig.Dexcon
g.s.Initialize(config, new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e7)))
+ statedb.AddBalance(GovernanceContractAddress, big.NewInt(1))
statedb.Commit(true)
}
@@ -117,8 +118,9 @@ func (g *GovernanceStateTestSuite) TestReadWriteEraseBytes() {
}
func (g *GovernanceStateTestSuite) TestReadWriteErase1DArray() {
+ emptyOffset := 100
for j := 0; j < 50; j++ {
- idx := big.NewInt(int64(j))
+ idx := big.NewInt(int64(j + emptyOffset))
data := make([][]byte, 30)
for key := range data {
data[key] = randomBytes(3, 32)
@@ -147,12 +149,7 @@ func (g *GovernanceStateTestSuite) TestDisqualify() {
// Disqualify
g.s.Disqualify(node)
node = g.s.Node(big.NewInt(0))
- g.Require().Equal(uint64(1), node.Fined.Uint64())
-
- // Disqualify again should change nothing.
- g.s.Disqualify(node)
- node = g.s.Node(big.NewInt(0))
- g.Require().Equal(uint64(1), node.Fined.Uint64())
+ g.Require().Equal(uint64(0xd78ebc5ac6200000), node.Fined.Uint64())
// Disqualify none exist node should return error.
privKey2, _ := newPrefundAccount(g.stateDB)
@@ -549,7 +546,7 @@ func (g *OracleContractsTestSuite) TestUpdateConfiguration() {
big.NewInt(264*decimalMultiplier),
big.NewInt(600),
big.NewInt(900),
- []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
+ []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1), big.NewInt(1)})
g.Require().NoError(err)
// Call with non-owner.
@@ -717,21 +714,21 @@ func (g *OracleContractsTestSuite) TestReportForkVote() {
g.Require().NoError(err)
// Report wrong type (fork block)
- input, err = GovernanceABI.ABI.Pack("report", big.NewInt(2), vote1Bytes, vote2Bytes)
+ input, err = GovernanceABI.ABI.Pack("report", big.NewInt(FineTypeForkBlock), vote1Bytes, vote2Bytes)
g.Require().NoError(err)
_, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0))
g.Require().Error(err)
- input, err = GovernanceABI.ABI.Pack("report", big.NewInt(1), vote1Bytes, vote2Bytes)
+ input, err = GovernanceABI.ABI.Pack("report", big.NewInt(FineTypeForkVote), vote1Bytes, vote2Bytes)
g.Require().NoError(err)
_, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0))
g.Require().NoError(err)
node := g.s.Node(big.NewInt(0))
- g.Require().Equal(node.Fined, g.s.FineValue(big.NewInt(1)))
+ g.Require().Equal(node.Fined, g.s.FineValue(big.NewInt(FineTypeForkVote)))
// Duplicate report should fail.
- input, err = GovernanceABI.ABI.Pack("report", big.NewInt(1), vote1Bytes, vote2Bytes)
+ input, err = GovernanceABI.ABI.Pack("report", big.NewInt(FineTypeForkVote), vote1Bytes, vote2Bytes)
g.Require().NoError(err)
_, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0))
g.Require().Error(err)
@@ -794,21 +791,21 @@ func (g *OracleContractsTestSuite) TestReportForkBlock() {
g.Require().NoError(err)
// Report wrong type (fork vote)
- input, err = GovernanceABI.ABI.Pack("report", big.NewInt(1), block1Bytes, block2Bytes)
+ input, err = GovernanceABI.ABI.Pack("report", big.NewInt(FineTypeForkVote), block1Bytes, block2Bytes)
g.Require().NoError(err)
_, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0))
g.Require().Error(err)
- input, err = GovernanceABI.ABI.Pack("report", big.NewInt(2), block1Bytes, block2Bytes)
+ input, err = GovernanceABI.ABI.Pack("report", big.NewInt(FineTypeForkBlock), block1Bytes, block2Bytes)
g.Require().NoError(err)
_, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0))
g.Require().NoError(err)
node := g.s.Node(big.NewInt(0))
- g.Require().Equal(node.Fined, g.s.FineValue(big.NewInt(2)))
+ g.Require().Equal(node.Fined, g.s.FineValue(big.NewInt(FineTypeForkBlock)))
// Duplicate report should fail.
- input, err = GovernanceABI.ABI.Pack("report", big.NewInt(2), block1Bytes, block2Bytes)
+ input, err = GovernanceABI.ABI.Pack("report", big.NewInt(FineTypeForkBlock), block1Bytes, block2Bytes)
g.Require().NoError(err)
_, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0))
g.Require().Error(err)