diff options
author | Wei-Ning Huang <w@dexon.org> | 2019-03-29 14:28:04 +0800 |
---|---|---|
committer | Wei-Ning Huang <w@dexon.org> | 2019-04-09 21:32:59 +0800 |
commit | f0257e264b94b67137595e4a357589da7cfde82e (patch) | |
tree | 926b29bcd4e0639791fbe3da9a69b35effb75518 | |
parent | 3489e7ec917cd00d1995cd7a0f669bfc8aff0a8e (diff) | |
download | dexon-f0257e264b94b67137595e4a357589da7cfde82e.tar.gz 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.
-rw-r--r-- | core/vm/oracle_contract_abi.go | 2 | ||||
-rw-r--r-- | core/vm/oracle_contracts.go | 116 | ||||
-rw-r--r-- | core/vm/oracle_contracts_test.go | 29 | ||||
-rw-r--r-- | params/config.go | 40 | ||||
-rw-r--r-- | test/genesis.json | 8 |
5 files changed, 121 insertions, 74 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) diff --git a/params/config.go b/params/config.go index 3c68653db..6098024be 100644 --- a/params/config.go +++ b/params/config.go @@ -26,10 +26,10 @@ import ( // Genesis hashes to enforce below configs on. var ( - MainnetGenesisHash = common.HexToHash("0x8d5edbabff11387ccff24278e9251b56e18a2604b9913695b2bbd5072533dfb2") - TestnetGenesisHash = common.HexToHash("0xc70b04d189f2a1391e843552489fa6e19eb6b29021be2e7e6ceed6a2ccc855ff") - TaipeiGenesisHash = common.HexToHash("0xcc805f44f6917b04be770a70fb1cdff089f7197fb68bc6839ef46e62c8011e2c") - YilanGenesisHash = common.HexToHash("0x86fec7f128b5e6525f4177debddc2a375439593ebbd9d8c19a58e289d8621ce8") + MainnetGenesisHash = common.HexToHash("0x2cc76c5c8f969a84a7836780e15583f18b9687d5a27a86a17eb8faf05a18a567") + TestnetGenesisHash = common.HexToHash("0xe1df7b888109249932dad7f38435cfbbe4a525498b79c7c30f23069140fbca23") + TaipeiGenesisHash = common.HexToHash("0xd7c18ff0c38f727d94cffef4d880032399c7baafaf2eb8fd620865ac1dc5998a") + YilanGenesisHash = common.HexToHash("0xad24a02194aa8239fd1a14d77d0c905c306bbac8ec69125b442b8e8dbd1e486b") ) var ( @@ -64,9 +64,11 @@ var ( RoundLength: 600, MinBlockInterval: 1000, FineValues: []*big.Int{ - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(200)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), }, }, Recovery: &RecoveryConfig{ @@ -115,9 +117,11 @@ var ( RoundLength: 1200, MinBlockInterval: 1000, FineValues: []*big.Int{ - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(200)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), }, }, Recovery: &RecoveryConfig{ @@ -157,9 +161,11 @@ var ( RoundLength: 1200, MinBlockInterval: 500, FineValues: []*big.Int{ - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(200)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), }, }, Recovery: &RecoveryConfig{ @@ -207,9 +213,11 @@ var ( RoundLength: 1200, MinBlockInterval: 500, FineValues: []*big.Int{ - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)), - new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(200)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), }, }, Recovery: &RecoveryConfig{ diff --git a/test/genesis.json b/test/genesis.json index 02ad15409..05ffe2aab 100644 --- a/test/genesis.json +++ b/test/genesis.json @@ -28,9 +28,11 @@ "roundLength": 100, "minBlockInterval": 500, "fineValues": [ - "0x21e19e0c9bab2400000", - "0x21e19e0c9bab2400000", - "0x152d02c7e14af6800000" + "0xad78ebc5ac6200000", + "0xde0b6b3a7640000", + "0xd3c21bcecceda0000000", + "0xd3c21bcecceda0000000", + "0xd3c21bcecceda0000000" ] }, "recovery": { |