aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWei-Ning Huang <w@dexon.org>2019-01-06 23:21:54 +0800
committerWei-Ning Huang <w@dexon.org>2019-03-12 12:19:09 +0800
commit7d2bde74ea108ccf2ea9facb5d6a74a77eee7ab9 (patch)
treedd8c2815881d8a72273742911e2514ba92c16711
parent01d282cc48e8c85eb18b7f5eccb555d09dbe2780 (diff)
downloaddexon-7d2bde74ea108ccf2ea9facb5d6a74a77eee7ab9.tar.gz
dexon-7d2bde74ea108ccf2ea9facb5d6a74a77eee7ab9.tar.zst
dexon-7d2bde74ea108ccf2ea9facb5d6a74a77eee7ab9.zip
core: vm: implement byzantine reporting mechanism (#128)
-rw-r--r--cmd/gdex/dao_test.go2
-rw-r--r--core/vm/governance.go431
-rw-r--r--core/vm/governance_test.go195
-rw-r--r--dex/app_test.go15
-rw-r--r--dex/governance.go66
-rw-r--r--params/config.go24
-rw-r--r--params/gen_dexcon_config.go74
-rw-r--r--test/genesis.json12
8 files changed, 719 insertions, 100 deletions
diff --git a/cmd/gdex/dao_test.go b/cmd/gdex/dao_test.go
index b972e9240..2d383ba10 100644
--- a/cmd/gdex/dao_test.go
+++ b/cmd/gdex/dao_test.go
@@ -127,7 +127,7 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc
}
defer db.Close()
- genesisHash := common.HexToHash("0xf0510808109583b9ef62f846886179f2fb40d1d4f1872d33529947bdd03e9bc1")
+ genesisHash := params.MainnetGenesisHash
if genesis != "" {
genesisHash = daoGenesisHash
}
diff --git a/core/vm/governance.go b/core/vm/governance.go
index 6dccc8c7d..1df82f84c 100644
--- a/core/vm/governance.go
+++ b/core/vm/governance.go
@@ -18,7 +18,10 @@
package vm
import (
+ "bytes"
+ "errors"
"math/big"
+ "sort"
"strings"
"github.com/dexon-foundation/dexon/accounts/abi"
@@ -331,6 +334,25 @@ const GovernanceABIJSON = `
},
{
"constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "name": "nodesOffsetByID",
+ "outputs": [
+ {
+ "name": "",
+ "type": "int256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
"inputs": [],
"name": "roundInterval",
"outputs": [
@@ -345,6 +367,25 @@ const GovernanceABIJSON = `
},
{
"constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "nodesOffsetByAddress",
+ "outputs": [
+ {
+ "name": "",
+ "type": "int256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
"inputs": [],
"name": "owner",
"outputs": [
@@ -362,14 +403,14 @@ const GovernanceABIJSON = `
"inputs": [
{
"name": "",
- "type": "address"
+ "type": "bytes32"
}
],
- "name": "nodesOffset",
+ "name": "finedRecords",
"outputs": [
{
"name": "",
- "type": "int256"
+ "type": "bool"
}
],
"payable": false,
@@ -398,6 +439,25 @@ const GovernanceABIJSON = `
"type": "uint256"
}
],
+ "name": "fineValues",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
"name": "roundHeight",
"outputs": [
{
@@ -684,6 +744,10 @@ const GovernanceABIJSON = `
{
"name": "MinBlockInterval",
"type": "uint256"
+ },
+ {
+ "name": "FineValues",
+ "type": "uint256[]"
}
],
"name": "updateConfiguration",
@@ -927,6 +991,28 @@ const GovernanceABIJSON = `
"payable": true,
"stateMutability": "payable",
"type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "Type",
+ "type": "uint256"
+ },
+ {
+ "name": "Arg1",
+ "type": "bytes"
+ },
+ {
+ "name": "Arg2",
+ "type": "bytes"
+ }
+ ],
+ "name": "report",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
}
]
`
@@ -936,6 +1022,16 @@ var GovernanceContractName2Method map[string]abi.Method
var sig2Method map[string]abi.Method
var events map[string]abi.Event
+type Bytes32 [32]byte
+
+type ReportType uint64
+
+const (
+ ReportTypeInvalidDKG = iota
+ ReportTypeForkVote
+ ReportTypeForkBlock
+)
+
func init() {
var err error
@@ -1037,6 +1133,12 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by
return nil, errExecutionReverted
}
return res, nil
+ case "payFine":
+ address := common.Address{}
+ if err := method.Inputs.Unpack(&address, arguments); err != nil {
+ return nil, errExecutionReverted
+ }
+ return g.payFine(address)
case "proposeCRS":
args := struct {
Round *big.Int
@@ -1046,6 +1148,16 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by
return nil, errExecutionReverted
}
return g.proposeCRS(args.Round, args.SignedCRS)
+ case "report":
+ args := struct {
+ Type *big.Int
+ Arg1 []byte
+ Arg2 []byte
+ }{}
+ if err := method.Inputs.Unpack(&args, arguments); err != nil {
+ return nil, errExecutionReverted
+ }
+ return g.report(args.Type, args.Arg1, args.Arg2)
case "stake":
args := struct {
PublicKey []byte
@@ -1093,12 +1205,6 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by
return nil, errExecutionReverted
}
return g.withdraw(address)
- case "payFine":
- address := common.Address{}
- if err := method.Inputs.Unpack(&address, arguments); err != nil {
- return nil, errExecutionReverted
- }
- return g.payFine(address)
// --------------------------------
// Solidity auto generated methods.
@@ -1234,6 +1340,28 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by
return nil, errExecutionReverted
}
return res, nil
+ case "finedRecords":
+ record := Bytes32{}
+ if err := method.Inputs.Unpack(&record, arguments); err != nil {
+ return nil, errExecutionReverted
+ }
+ value := g.state.FineRecords(record)
+ res, err := method.Outputs.Pack(value)
+ if err != nil {
+ return nil, errExecutionReverted
+ }
+ return res, nil
+ case "fineValues":
+ index := new(big.Int)
+ if err := method.Inputs.Unpack(&index, arguments); err != nil {
+ return nil, errExecutionReverted
+ }
+ value := g.state.FineValue(index)
+ res, err := method.Outputs.Pack(value)
+ if err != nil {
+ return nil, errExecutionReverted
+ }
+ return res, nil
case "k":
res, err := method.Outputs.Pack(g.state.K())
if err != nil {
@@ -1289,12 +1417,22 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by
return nil, errExecutionReverted
}
return res, nil
- case "nodesOffset":
+ case "nodesOffsetByAddress":
address := common.Address{}
if err := method.Inputs.Unpack(&address, arguments); err != nil {
return nil, errExecutionReverted
}
- res, err := method.Outputs.Pack(g.state.NodesOffset(address))
+ res, err := method.Outputs.Pack(g.state.NodesOffsetByAddress(address))
+ if err != nil {
+ return nil, errExecutionReverted
+ }
+ return res, nil
+ case "nodesOffsetByID":
+ var id Bytes32
+ if err := method.Inputs.Unpack(&id, arguments); err != nil {
+ return nil, errExecutionReverted
+ }
+ res, err := method.Outputs.Pack(g.state.NodesOffsetByID(id))
if err != nil {
return nil, errExecutionReverted
}
@@ -1341,7 +1479,8 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by
const (
roundHeightLoc = iota
nodesLoc
- nodesOffsetLoc
+ nodesOffsetByAddressLoc
+ nodesOffsetByIDLoc
delegatorsLoc
delegatorsOffsetLoc
crsLoc
@@ -1365,8 +1504,19 @@ const (
dkgSetSizeLoc
roundIntervalLoc
minBlockIntervalLoc
+ fineValuesLoc
+ finedRecordsLoc
)
+func publicKeyToNodeID(pkBytes []byte) (Bytes32, error) {
+ pk, err := crypto.UnmarshalPubkey(pkBytes)
+ if err != nil {
+ return Bytes32{}, err
+ }
+ id := Bytes32(coreTypes.NewNodeID(ecdsa.NewPublicKeyFromECDSA(pk)).Hash)
+ return id, nil
+}
+
// State manipulation helper fro the governance contract.
type GovernanceStateHelper struct {
StateDB StateDB
@@ -1675,6 +1825,9 @@ func (s *GovernanceStateHelper) QualifiedNodes() []*nodeInfo {
var nodes []*nodeInfo
for i := int64(0); i < int64(s.LenNodes().Uint64()); i++ {
node := s.Node(big.NewInt(i))
+ if node.Unstaked {
+ continue
+ }
if new(big.Int).Sub(node.Staked, node.Fined).Cmp(s.MinStake()) >= 0 {
nodes = append(nodes, node)
}
@@ -1682,20 +1835,44 @@ func (s *GovernanceStateHelper) QualifiedNodes() []*nodeInfo {
return nodes
}
-// mapping(address => uint256) public nodeOffset;
-func (s *GovernanceStateHelper) NodesOffset(addr common.Address) *big.Int {
- loc := s.getMapLoc(big.NewInt(nodesOffsetLoc), addr.Bytes())
+// mapping(address => uint256) public nodeOffsetByAddress;
+func (s *GovernanceStateHelper) NodesOffsetByAddress(addr common.Address) *big.Int {
+ loc := s.getMapLoc(big.NewInt(nodesOffsetByAddressLoc), addr.Bytes())
return new(big.Int).Sub(s.getStateBigInt(loc), big.NewInt(1))
}
-func (s *GovernanceStateHelper) PutNodesOffset(addr common.Address, offset *big.Int) {
- loc := s.getMapLoc(big.NewInt(nodesOffsetLoc), addr.Bytes())
+func (s *GovernanceStateHelper) PutNodesOffsetByAddress(addr common.Address, offset *big.Int) {
+ loc := s.getMapLoc(big.NewInt(nodesOffsetByAddressLoc), addr.Bytes())
s.setStateBigInt(loc, new(big.Int).Add(offset, big.NewInt(1)))
}
-func (s *GovernanceStateHelper) DeleteNodesOffset(addr common.Address) {
- loc := s.getMapLoc(big.NewInt(nodesOffsetLoc), addr.Bytes())
+func (s *GovernanceStateHelper) DeleteNodesOffsetByAddress(addr common.Address) {
+ loc := s.getMapLoc(big.NewInt(nodesOffsetByAddressLoc), addr.Bytes())
s.setStateBigInt(loc, big.NewInt(0))
}
+// mapping(address => uint256) public nodeOffsetByID;
+func (s *GovernanceStateHelper) NodesOffsetByID(id Bytes32) *big.Int {
+ loc := s.getMapLoc(big.NewInt(nodesOffsetByIDLoc), id[:])
+ return new(big.Int).Sub(s.getStateBigInt(loc), big.NewInt(1))
+}
+func (s *GovernanceStateHelper) PutNodesOffsetByID(id Bytes32, offset *big.Int) {
+ loc := s.getMapLoc(big.NewInt(nodesOffsetByIDLoc), id[:])
+ s.setStateBigInt(loc, new(big.Int).Add(offset, big.NewInt(1)))
+}
+func (s *GovernanceStateHelper) DeleteNodesOffsetByID(id Bytes32) {
+ loc := s.getMapLoc(big.NewInt(nodesOffsetByIDLoc), id[:])
+ s.setStateBigInt(loc, big.NewInt(0))
+}
+
+func (s *GovernanceStateHelper) PutNodeOffsets(n *nodeInfo, offset *big.Int) error {
+ id, err := publicKeyToNodeID(n.PublicKey)
+ if err != nil {
+ return err
+ }
+ s.PutNodesOffsetByID(id, offset)
+ s.PutNodesOffsetByAddress(n.Owner, offset)
+ return nil
+}
+
// struct Delegator {
// address node;
// address owner;
@@ -1841,6 +2018,20 @@ func (s *GovernanceStateHelper) UniqueDKGMasterPublicKeys(round *big.Int) []*dkg
}
return dkgMasterPKs
}
+func (s *GovernanceStateHelper) GetDKGMasterPublicKeyByProposerID(
+ round *big.Int, proposerID coreTypes.NodeID) (*dkgTypes.MasterPublicKey, error) {
+
+ for _, mpk := range s.DKGMasterPublicKeys(round) {
+ x := new(dkgTypes.MasterPublicKey)
+ if err := rlp.DecodeBytes(mpk, x); err != nil {
+ panic(err)
+ }
+ if x.ProposerID.Equal(proposerID) {
+ return x, nil
+ }
+ }
+ return nil, errors.New("not found")
+}
// bytes[][] public dkgComplaints;
func (s *GovernanceStateHelper) DKGComplaints(round *big.Int) [][]byte {
@@ -1981,12 +2172,48 @@ func (s *GovernanceStateHelper) MinBlockInterval() *big.Int {
return s.getStateBigInt(big.NewInt(minBlockIntervalLoc))
}
+// uint256[] public fineValues;
+func (s *GovernanceStateHelper) FineValue(index *big.Int) *big.Int {
+ arrayBaseLoc := s.getSlotLoc(big.NewInt(fineValuesLoc))
+ return s.getStateBigInt(new(big.Int).Add(arrayBaseLoc, index))
+}
+func (s *GovernanceStateHelper) FineValues() []*big.Int {
+ len := s.getStateBigInt(big.NewInt(fineValuesLoc))
+ result := make([]*big.Int, len.Uint64())
+ for i := 0; i < int(len.Uint64()); i++ {
+ result[i] = s.FineValue(big.NewInt(int64(i)))
+ }
+ return result
+}
+func (s *GovernanceStateHelper) SetFineValues(values []*big.Int) {
+ s.setStateBigInt(big.NewInt(fineValuesLoc), big.NewInt(int64(len(values))))
+
+ arrayBaseLoc := s.getSlotLoc(big.NewInt(fineValuesLoc))
+ for i, v := range values {
+ s.setStateBigInt(new(big.Int).Add(arrayBaseLoc, big.NewInt(int64(i))), v)
+ }
+}
+
+// uint256[] public fineRdecords;
+func (s *GovernanceStateHelper) FineRecords(recordHash Bytes32) bool {
+ loc := s.getMapLoc(big.NewInt(finedRecordsLoc), recordHash[:])
+ return s.getStateBigInt(loc).Cmp(big.NewInt(0)) > 0
+}
+func (s *GovernanceStateHelper) SetFineRecords(recordHash Bytes32, status bool) {
+ loc := s.getMapLoc(big.NewInt(finedRecordsLoc), recordHash[:])
+ value := int64(0)
+ if status {
+ value = int64(1)
+ }
+ s.setStateBigInt(loc, big.NewInt(value))
+}
+
// Stake is a helper function for creating genesis state.
func (s *GovernanceStateHelper) Stake(
addr common.Address, publicKey []byte, staked *big.Int,
name, email, location, url string) {
offset := s.LenNodes()
- s.PushNode(&nodeInfo{
+ node := &nodeInfo{
Owner: addr,
PublicKey: publicKey,
Staked: staked,
@@ -1995,8 +2222,23 @@ func (s *GovernanceStateHelper) Stake(
Email: email,
Location: location,
Url: url,
+ }
+ s.PushNode(node)
+ if err := s.PutNodeOffsets(node, offset); err != nil {
+ panic(err)
+ }
+
+ if staked.Cmp(big.NewInt(0)) == 0 {
+ return
+ }
+
+ offset = s.LenDelegators(addr)
+ s.PushDelegator(addr, &delegatorInfo{
+ Owner: addr,
+ Value: staked,
+ UndelegatedAt: big.NewInt(0),
})
- s.PutNodesOffset(addr, offset)
+ s.PutDelegatorOffset(addr, addr, offset)
}
const phiRatioMultiplier = 1000000.0
@@ -2017,6 +2259,7 @@ func (s *GovernanceStateHelper) Configuration() *params.DexconConfig {
DKGSetSize: uint32(s.getStateBigInt(big.NewInt(dkgSetSizeLoc)).Uint64()),
RoundInterval: s.getStateBigInt(big.NewInt(roundIntervalLoc)).Uint64(),
MinBlockInterval: s.getStateBigInt(big.NewInt(minBlockIntervalLoc)).Uint64(),
+ FineValues: s.FineValues(),
}
}
@@ -2035,6 +2278,7 @@ func (s *GovernanceStateHelper) UpdateConfiguration(cfg *params.DexconConfig) {
s.setStateBigInt(big.NewInt(dkgSetSizeLoc), big.NewInt(int64(cfg.DKGSetSize)))
s.setStateBigInt(big.NewInt(roundIntervalLoc), big.NewInt(int64(cfg.RoundInterval)))
s.setStateBigInt(big.NewInt(minBlockIntervalLoc), big.NewInt(int64(cfg.MinBlockInterval)))
+ s.SetFineValues(cfg.FineValues)
}
type rawConfigStruct struct {
@@ -2051,6 +2295,7 @@ type rawConfigStruct struct {
DKGSetSize *big.Int
RoundInterval *big.Int
MinBlockInterval *big.Int
+ FineValues []*big.Int
}
// UpdateConfigurationRaw updates system configuration.
@@ -2068,6 +2313,7 @@ func (s *GovernanceStateHelper) UpdateConfigurationRaw(cfg *rawConfigStruct) {
s.setStateBigInt(big.NewInt(dkgSetSizeLoc), cfg.DKGSetSize)
s.setStateBigInt(big.NewInt(roundIntervalLoc), cfg.RoundInterval)
s.setStateBigInt(big.NewInt(minBlockIntervalLoc), cfg.MinBlockInterval)
+ s.SetFineValues(cfg.FineValues)
}
// event ConfigurationChanged();
@@ -2229,6 +2475,32 @@ func (g *GovernanceContract) addDKGComplaint(round *big.Int, comp []byte) ([]byt
return g.penalize()
}
+ mpk, err := g.state.GetDKGMasterPublicKeyByProposerID(
+ round, dkgComplaint.PrivateShare.ProposerID)
+ if err != nil {
+ return g.penalize()
+ }
+
+ // Verify DKG complaint is correct.
+ ok, err := coreUtils.VerifyDKGComplaint(&dkgComplaint, mpk)
+ if !ok || err != nil {
+ return g.penalize()
+ }
+
+ // Fine the attacker.
+ need, err := coreUtils.NeedPenaltyDKGPrivateShare(&dkgComplaint, mpk)
+ if err != nil {
+ return g.penalize()
+ }
+ if need {
+ fineValue := g.state.FineValue(big.NewInt(ReportTypeInvalidDKG))
+ offset := g.state.NodesOffsetByID(Bytes32(dkgComplaint.PrivateShare.ProposerID.Hash))
+ node := g.state.Node(offset)
+ if err := g.fine(node.Owner, fineValue, comp, nil); err != nil {
+ return g.penalize()
+ }
+ }
+
g.state.PushDKGComplaint(round, comp)
// Set this to relatively high to prevent spamming
@@ -2242,7 +2514,7 @@ func (g *GovernanceContract) addDKGMasterPublicKey(round *big.Int, mpk []byte) (
}
caller := g.contract.Caller()
- offset := g.state.NodesOffset(caller)
+ offset := g.state.NodesOffsetByAddress(caller)
// Can not add dkg mpk if not staked.
if offset.Cmp(big.NewInt(0)) < 0 {
@@ -2344,7 +2616,7 @@ func (g *GovernanceContract) addDKGFinalize(round *big.Int, finalize []byte) ([]
}
func (g *GovernanceContract) delegate(nodeAddr common.Address) ([]byte, error) {
- offset := g.state.NodesOffset(nodeAddr)
+ offset := g.state.NodesOffsetByAddress(nodeAddr)
if offset.Cmp(big.NewInt(0)) < 0 {
return nil, errExecutionReverted
}
@@ -2401,12 +2673,7 @@ func (g *GovernanceContract) stake(
}
caller := g.contract.Caller()
- offset := g.state.NodesOffset(caller)
-
- // Check if public key is valid.
- if _, err := crypto.UnmarshalPubkey(publicKey); err != nil {
- return g.penalize()
- }
+ offset := g.state.NodesOffsetByAddress(caller)
// Can not stake if already staked.
if offset.Cmp(big.NewInt(0)) >= 0 {
@@ -2414,7 +2681,7 @@ func (g *GovernanceContract) stake(
}
offset = g.state.LenNodes()
- g.state.PushNode(&nodeInfo{
+ node := &nodeInfo{
Owner: caller,
PublicKey: publicKey,
Staked: big.NewInt(0),
@@ -2423,8 +2690,11 @@ func (g *GovernanceContract) stake(
Email: email,
Location: location,
Url: url,
- })
- g.state.PutNodesOffset(caller, offset)
+ }
+ g.state.PushNode(node)
+ if err := g.state.PutNodeOffsets(node, offset); err != nil {
+ return g.penalize()
+ }
// Delegate fund to itself.
if g.contract.Value().Cmp(big.NewInt(0)) > 0 {
@@ -2438,7 +2708,7 @@ func (g *GovernanceContract) stake(
}
func (g *GovernanceContract) undelegateHelper(nodeAddr, caller common.Address) ([]byte, error) {
- nodeOffset := g.state.NodesOffset(nodeAddr)
+ nodeOffset := g.state.NodesOffsetByAddress(nodeAddr)
if nodeOffset.Cmp(big.NewInt(0)) < 0 {
return nil, errExecutionReverted
}
@@ -2475,7 +2745,7 @@ func (g *GovernanceContract) undelegate(nodeAddr common.Address) ([]byte, error)
func (g *GovernanceContract) withdraw(nodeAddr common.Address) ([]byte, error) {
caller := g.contract.Caller()
- nodeOffset := g.state.NodesOffset(nodeAddr)
+ nodeOffset := g.state.NodesOffsetByAddress(nodeAddr)
if nodeOffset.Cmp(big.NewInt(0)) < 0 {
return nil, errExecutionReverted
}
@@ -2523,9 +2793,11 @@ func (g *GovernanceContract) withdraw(nodeAddr common.Address) ([]byte, error) {
if offset.Cmp(lastIndex) != 0 {
lastNode := g.state.Node(lastIndex)
g.state.UpdateNode(offset, lastNode)
- g.state.PutNodesOffset(lastNode.Owner, offset)
+ if err := g.state.PutNodeOffsets(lastNode, offset); err != nil {
+ panic(err)
+ }
}
- g.state.DeleteNodesOffset(nodeAddr)
+ g.state.DeleteNodesOffsetByAddress(nodeAddr)
g.state.PopLastNode()
}
@@ -2534,7 +2806,7 @@ func (g *GovernanceContract) withdraw(nodeAddr common.Address) ([]byte, error) {
func (g *GovernanceContract) unstake() ([]byte, error) {
caller := g.contract.Caller()
- offset := g.state.NodesOffset(caller)
+ offset := g.state.NodesOffsetByAddress(caller)
if offset.Cmp(big.NewInt(0)) < 0 {
return nil, errExecutionReverted
}
@@ -2567,7 +2839,7 @@ func (g *GovernanceContract) unstake() ([]byte, error) {
func (g *GovernanceContract) payFine(nodeAddr common.Address) ([]byte, error) {
caller := g.contract.Caller()
- nodeOffset := g.state.NodesOffset(nodeAddr)
+ nodeOffset := g.state.NodesOffsetByAddress(nodeAddr)
if nodeOffset.Cmp(big.NewInt(0)) < 0 {
return nil, errExecutionReverted
}
@@ -2639,6 +2911,89 @@ func (g *GovernanceContract) proposeCRS(nextRound *big.Int, signedCRS []byte) ([
return g.useGas(0)
}
+type sortBytes [][]byte
+
+func (s sortBytes) Less(i, j int) bool {
+ return bytes.Compare(s[i], s[j]) < 0
+}
+
+func (s sortBytes) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func (s sortBytes) Len() int {
+ return len(s)
+}
+
+func (g *GovernanceContract) fine(nodeAddr common.Address, amount *big.Int, payloads ...[]byte) error {
+ sort.Sort(sortBytes(payloads))
+
+ hash := Bytes32(crypto.Keccak256Hash(payloads...))
+ if g.state.FineRecords(hash) {
+ return errors.New("already fined")
+ }
+ g.state.SetFineRecords(hash, true)
+
+ nodeOffset := g.state.NodesOffsetByAddress(nodeAddr)
+ if nodeOffset.Cmp(big.NewInt(0)) < 0 {
+ return errExecutionReverted
+ }
+
+ // Set fined value.
+ node := g.state.Node(nodeOffset)
+ node.Fined = new(big.Int).Add(node.Fined, amount)
+ g.state.UpdateNode(nodeOffset, node)
+
+ return nil
+}
+
+func (g *GovernanceContract) report(reportType *big.Int, arg1, arg2 []byte) ([]byte, error) {
+ typeEnum := ReportType(reportType.Uint64())
+ var reportedNodeID coreTypes.NodeID
+
+ switch typeEnum {
+ case ReportTypeForkVote:
+ vote1 := new(coreTypes.Vote)
+ if err := rlp.DecodeBytes(arg1, vote1); err != nil {
+ return g.penalize()
+ }
+ vote2 := new(coreTypes.Vote)
+ if err := rlp.DecodeBytes(arg2, vote2); err != nil {
+ return g.penalize()
+ }
+ need, err := coreUtils.NeedPenaltyForkVote(vote1, vote2)
+ if !need || err != nil {
+ return g.penalize()
+ }
+ reportedNodeID = vote1.ProposerID
+ case ReportTypeForkBlock:
+ block1 := new(coreTypes.Block)
+ if err := rlp.DecodeBytes(arg1, block1); err != nil {
+ return g.penalize()
+ }
+ block2 := new(coreTypes.Block)
+ if err := rlp.DecodeBytes(arg2, block2); err != nil {
+ return g.penalize()
+ }
+ need, err := coreUtils.NeedPenaltyForkBlock(block1, block2)
+ if !need || err != nil {
+ return g.penalize()
+ }
+ reportedNodeID = block1.ProposerID
+ default:
+ return g.penalize()
+ }
+
+ offset := g.state.NodesOffsetByID(Bytes32(reportedNodeID.Hash))
+ node := g.state.Node(offset)
+
+ fineValue := g.state.FineValue(reportType)
+ if err := g.fine(node.Owner, fineValue, arg1, arg2); err != nil {
+ return nil, errExecutionReverted
+ }
+ return nil, nil
+}
+
func (g *GovernanceContract) transferOwnership(newOwner common.Address) ([]byte, error) {
// Only owner can update configuration.
if g.contract.Caller() != g.state.Owner() {
diff --git a/core/vm/governance_test.go b/core/vm/governance_test.go
index a91aba7b6..4c5236565 100644
--- a/core/vm/governance_test.go
+++ b/core/vm/governance_test.go
@@ -22,14 +22,22 @@ import (
"crypto/ecdsa"
"math/big"
"math/rand"
+ "sort"
"testing"
"time"
+ coreCommon "github.com/dexon-foundation/dexon-consensus/common"
+ coreCrypto "github.com/dexon-foundation/dexon-consensus/core/crypto"
+ coreEcdsa "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa"
+ coreTypes "github.com/dexon-foundation/dexon-consensus/core/types"
+ coreUtils "github.com/dexon-foundation/dexon-consensus/core/utils"
+
"github.com/dexon-foundation/dexon/common"
"github.com/dexon-foundation/dexon/core/state"
"github.com/dexon-foundation/dexon/crypto"
"github.com/dexon-foundation/dexon/ethdb"
"github.com/dexon-foundation/dexon/params"
+ "github.com/dexon-foundation/dexon/rlp"
"github.com/stretchr/testify/suite"
)
@@ -187,16 +195,15 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() {
pk := crypto.FromECDSAPub(&privKey.PublicKey)
// Stake.
- amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e4))
+ amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5))
balanceBeforeStake := g.stateDB.GetBalance(addr)
input, err := abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org")
g.Require().NoError(err)
_, err = g.call(addr, input, amount)
g.Require().NoError(err)
- // Node staked but staked fund < MinStake so is still unqualified.
g.Require().Equal(1, int(g.s.LenNodes().Uint64()))
- g.Require().Equal(0, len(g.s.QualifiedNodes()))
+ g.Require().Equal(1, len(g.s.QualifiedNodes()))
g.Require().Equal("Test1", g.s.Node(big.NewInt(0)).Name)
// Check balance.
@@ -235,7 +242,7 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() {
_, err = g.call(addr2, input, amount)
g.Require().NoError(err)
g.Require().Equal("Test2", g.s.Node(big.NewInt(0)).Name)
- g.Require().Equal(0, int(g.s.NodesOffset(addr2).Int64()))
+ g.Require().Equal(0, int(g.s.NodesOffsetByAddress(addr2).Int64()))
// 1st node Stake.
input, err = abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org")
@@ -243,26 +250,35 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() {
_, err = g.call(addr, input, amount)
g.Require().NoError(err)
+ g.Require().Equal(2, len(g.s.QualifiedNodes()))
+
// 2nd node Unstake.
input, err = abiObject.Pack("unstake")
g.Require().NoError(err)
_, err = g.call(addr2, input, big.NewInt(0))
g.Require().NoError(err)
+ node := g.s.Node(big.NewInt(0))
+ g.Require().Equal("Test2", node.Name)
+ g.Require().Equal(true, node.Unstaked)
+ g.Require().Equal(1, len(g.s.QualifiedNodes()))
+
time.Sleep(time.Second * 2)
input, err = abiObject.Pack("withdraw", addr2)
g.Require().NoError(err)
_, err = g.call(addr2, input, big.NewInt(0))
g.Require().NoError(err)
+ g.Require().Equal(1, len(g.s.QualifiedNodes()))
g.Require().Equal(1, int(g.s.LenNodes().Uint64()))
g.Require().Equal("Test1", g.s.Node(big.NewInt(0)).Name)
- g.Require().Equal(-1, int(g.s.NodesOffset(addr2).Int64()))
+ g.Require().Equal(-1, int(g.s.NodesOffsetByAddress(addr2).Int64()))
// 1st node Unstake.
input, err = abiObject.Pack("unstake")
g.Require().NoError(err)
_, err = g.call(addr, input, big.NewInt(0))
g.Require().NoError(err)
+ g.Require().Equal(0, len(g.s.QualifiedNodes()))
time.Sleep(time.Second * 2)
input, err = abiObject.Pack("withdraw", addr)
g.Require().NoError(err)
@@ -270,7 +286,7 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() {
g.Require().NoError(err)
g.Require().Equal(0, int(g.s.LenNodes().Uint64()))
- g.Require().Equal(-1, int(g.s.NodesOffset(addr).Int64()))
+ g.Require().Equal(-1, int(g.s.NodesOffsetByAddress(addr).Int64()))
g.Require().Equal(-1, int(g.s.DelegatorsOffset(addr, addr).Int64()))
// Check balance.
@@ -424,7 +440,7 @@ func (g *GovernanceContractTestSuite) TestFine() {
g.Require().NotNil(err)
// Fined.
- offset := g.s.NodesOffset(addr)
+ offset := g.s.NodesOffsetByAddress(addr)
g.Require().True(offset.Cmp(big.NewInt(0)) >= 0)
node := g.s.Node(offset)
node.Fined = new(big.Int).Set(amount)
@@ -544,7 +560,8 @@ func (g *GovernanceContractTestSuite) TestUpdateConfiguration() {
input, err := abiObject.Pack("updateConfiguration",
new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)), big.NewInt(1000),
big.NewInt(1e18), big.NewInt(8000000), big.NewInt(6), big.NewInt(250), big.NewInt(2500),
- big.NewInt(0), big.NewInt(667000), big.NewInt(4), big.NewInt(4), big.NewInt(600000), big.NewInt(900))
+ big.NewInt(0), big.NewInt(667000), big.NewInt(4), big.NewInt(4), big.NewInt(600000), big.NewInt(900),
+ []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)})
g.Require().NoError(err)
// Call with non-owner.
@@ -729,6 +746,149 @@ func (g *GovernanceContractTestSuite) TestConfigurationReading() {
g.Require().Equal(g.config.MinBlockInterval, value.Uint64())
}
+func (g *GovernanceContractTestSuite) TestReportForkVote() {
+ key, addr := g.newPrefundAccount()
+ pkBytes := crypto.FromECDSAPub(&key.PublicKey)
+
+ // Stake.
+ amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e4))
+ input, err := abiObject.Pack("stake", pkBytes, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org")
+ g.Require().NoError(err)
+ _, err = g.call(addr, input, amount)
+ g.Require().NoError(err)
+
+ pubKey := coreEcdsa.NewPublicKeyFromECDSA(&key.PublicKey)
+ privKey := coreEcdsa.NewPrivateKeyFromECDSA(key)
+ vote1 := coreTypes.NewVote(coreTypes.VoteCom, coreCommon.NewRandomHash(), uint64(0))
+ vote1.ProposerID = coreTypes.NewNodeID(pubKey)
+
+ vote2 := vote1.Clone()
+ for vote2.BlockHash == vote1.BlockHash {
+ vote2.BlockHash = coreCommon.NewRandomHash()
+ }
+ vote1.Signature, err = privKey.Sign(coreUtils.HashVote(vote1))
+ g.Require().NoError(err)
+ vote2.Signature, err = privKey.Sign(coreUtils.HashVote(vote2))
+ g.Require().NoError(err)
+
+ vote1Bytes, err := rlp.EncodeToBytes(vote1)
+ g.Require().NoError(err)
+ vote2Bytes, err := rlp.EncodeToBytes(vote2)
+ g.Require().NoError(err)
+
+ // Report wrong type (fork block)
+ input, err = abiObject.Pack("report", big.NewInt(2), vote1Bytes, vote2Bytes)
+ g.Require().NoError(err)
+ _, err = g.call(addr, input, big.NewInt(0))
+ g.Require().Error(err)
+
+ input, err = abiObject.Pack("report", big.NewInt(1), vote1Bytes, vote2Bytes)
+ g.Require().NoError(err)
+ _, err = g.call(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)))
+
+ // Duplicate report should fail.
+ input, err = abiObject.Pack("report", big.NewInt(1), vote1Bytes, vote2Bytes)
+ g.Require().NoError(err)
+ _, err = g.call(addr, input, big.NewInt(0))
+ g.Require().Error(err)
+
+ // Check if finedRecords is set.
+ payloads := [][]byte{vote1Bytes, vote2Bytes}
+ sort.Sort(sortBytes(payloads))
+
+ hash := Bytes32(crypto.Keccak256Hash(payloads...))
+ input, err = abiObject.Pack("finedRecords", hash)
+ g.Require().NoError(err)
+ res, err := g.call(addr, input, big.NewInt(0))
+ g.Require().NoError(err)
+
+ var value bool
+ err = abiObject.Unpack(&value, "finedRecords", res)
+ g.Require().NoError(err)
+ g.Require().True(value)
+}
+
+func (g *GovernanceContractTestSuite) TestReportForkBlock() {
+ key, addr := g.newPrefundAccount()
+ pkBytes := crypto.FromECDSAPub(&key.PublicKey)
+
+ // Stake.
+ amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e4))
+ input, err := abiObject.Pack("stake", pkBytes, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org")
+ g.Require().NoError(err)
+ _, err = g.call(addr, input, amount)
+ g.Require().NoError(err)
+
+ privKey := coreEcdsa.NewPrivateKeyFromECDSA(key)
+ block1 := &coreTypes.Block{
+ ProposerID: coreTypes.NewNodeID(privKey.PublicKey()),
+ ParentHash: coreCommon.NewRandomHash(),
+ Timestamp: time.Now(),
+ }
+
+ block2 := block1.Clone()
+ for block2.ParentHash == block1.ParentHash {
+ block2.ParentHash = coreCommon.NewRandomHash()
+ }
+
+ hashBlock := func(block *coreTypes.Block) coreCommon.Hash {
+ block.PayloadHash = coreCrypto.Keccak256Hash(block.Payload)
+ var err error
+ block.Hash, err = coreUtils.HashBlock(block)
+ g.Require().NoError(err)
+ return block.Hash
+ }
+
+ block1.Signature, err = privKey.Sign(hashBlock(block1))
+ g.Require().NoError(err)
+ block2.Signature, err = privKey.Sign(hashBlock(block2))
+ g.Require().NoError(err)
+
+ block1Bytes, err := rlp.EncodeToBytes(block1)
+ g.Require().NoError(err)
+ block2Bytes, err := rlp.EncodeToBytes(block2)
+ g.Require().NoError(err)
+
+ // Report wrong type (fork vote)
+ input, err = abiObject.Pack("report", big.NewInt(1), block1Bytes, block2Bytes)
+ g.Require().NoError(err)
+ _, err = g.call(addr, input, big.NewInt(0))
+ g.Require().Error(err)
+
+ input, err = abiObject.Pack("report", big.NewInt(2), block1Bytes, block2Bytes)
+ g.Require().NoError(err)
+ _, err = g.call(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)))
+
+ // Duplicate report should fail.
+ input, err = abiObject.Pack("report", big.NewInt(2), block1Bytes, block2Bytes)
+ g.Require().NoError(err)
+ _, err = g.call(addr, input, big.NewInt(0))
+ g.Require().Error(err)
+
+ // Check if finedRecords is set.
+ payloads := [][]byte{block1Bytes, block2Bytes}
+ sort.Sort(sortBytes(payloads))
+
+ hash := Bytes32(crypto.Keccak256Hash(payloads...))
+ input, err = abiObject.Pack("finedRecords", hash)
+ g.Require().NoError(err)
+ res, err := g.call(addr, input, big.NewInt(0))
+ g.Require().NoError(err)
+
+ var value bool
+ err = abiObject.Unpack(&value, "finedRecords", res)
+ g.Require().NoError(err)
+ g.Require().True(value)
+}
+
func (g *GovernanceContractTestSuite) TestMiscVariableReading() {
privKey, addr := g.newPrefundAccount()
pk := crypto.FromECDSAPub(&privKey.PublicKey)
@@ -769,11 +929,21 @@ func (g *GovernanceContractTestSuite) TestMiscVariableReading() {
g.Require().NoError(err)
g.Require().Equal(1, int(value.Uint64()))
- input, err = abiObject.Pack("nodesOffset", addr)
+ input, err = abiObject.Pack("nodesOffsetByAddress", addr)
g.Require().NoError(err)
res, err = g.call(addr, input, big.NewInt(0))
g.Require().NoError(err)
- err = abiObject.Unpack(&value, "nodesOffset", res)
+ err = abiObject.Unpack(&value, "nodesOffsetByAddress", res)
+ g.Require().NoError(err)
+ g.Require().Equal(0, int(value.Uint64()))
+
+ id, err := publicKeyToNodeID(pk)
+ g.Require().NoError(err)
+ input, err = abiObject.Pack("nodesOffsetByID", id)
+ g.Require().NoError(err)
+ res, err = g.call(addr, input, big.NewInt(0))
+ g.Require().NoError(err)
+ err = abiObject.Unpack(&value, "nodesOffsetByID", res)
g.Require().NoError(err)
g.Require().Equal(0, int(value.Uint64()))
@@ -797,6 +967,11 @@ func (g *GovernanceContractTestSuite) TestMiscVariableReading() {
err = abiObject.Unpack(&value, "delegatorsOffset", res)
g.Require().NoError(err)
g.Require().Equal(2, int(value.Uint64()))
+
+ input, err = abiObject.Pack("fineValues", big.NewInt(0))
+ g.Require().NoError(err)
+ res, err = g.call(addr, input, big.NewInt(0))
+ g.Require().NoError(err)
}
func TestGovernanceContract(t *testing.T) {
diff --git a/dex/app_test.go b/dex/app_test.go
index 9278160fb..7fc4933de 100644
--- a/dex/app_test.go
+++ b/dex/app_test.go
@@ -494,12 +494,18 @@ func BenchmarkBlockDeliveredFlow(b *testing.B) {
func newTestDexonWithGenesis(allocKey *ecdsa.PrivateKey) (*Dexon, error) {
db := ethdb.NewMemDatabase()
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ panic(err)
+ }
+
testBankAddress := crypto.PubkeyToAddress(allocKey.PublicKey)
genesis := core.DefaultTestnetGenesisBlock()
genesis.Alloc = core.GenesisAlloc{
testBankAddress: {
- Balance: big.NewInt(100000000000000000),
- Staked: big.NewInt(50000000000000000),
+ Balance: big.NewInt(100000000000000000),
+ Staked: big.NewInt(50000000000000000),
+ PublicKey: crypto.FromECDSAPub(&key.PublicKey),
},
}
chainConfig, _, err := core.SetupGenesisBlock(db, genesis)
@@ -507,11 +513,6 @@ func newTestDexonWithGenesis(allocKey *ecdsa.PrivateKey) (*Dexon, error) {
return nil, err
}
- key, err := crypto.GenerateKey()
- if err != nil {
- return nil, err
- }
-
config := Config{PrivateKey: key}
vmConfig := vm.Config{IsBlockProposer: true}
diff --git a/dex/governance.go b/dex/governance.go
index 199bcdc87..ec029f2f1 100644
--- a/dex/governance.go
+++ b/dex/governance.go
@@ -259,6 +259,64 @@ func (d *DexconGovernance) AddDKGFinalize(round uint64, final *dkgTypes.Finalize
}
}
+// ReportForkVote reports a node for forking votes.
+func (d *DexconGovernance) ReportForkVote(vote1, vote2 *coreTypes.Vote) {
+ method := vm.GovernanceContractName2Method["report"]
+
+ vote1Bytes, err := rlp.EncodeToBytes(vote1)
+ if err != nil {
+ log.Error("failed to RLP encode vote1 to bytes", "err", err)
+ return
+ }
+
+ vote2Bytes, err := rlp.EncodeToBytes(vote2)
+ if err != nil {
+ log.Error("failed to RLP encode vote2 to bytes", "err", err)
+ return
+ }
+
+ res, err := method.Inputs.Pack(big.NewInt(vm.ReportTypeForkVote), vote1Bytes, vote2Bytes)
+ if err != nil {
+ log.Error("failed to pack report input", "err", err)
+ return
+ }
+
+ data := append(method.Id(), res...)
+ err = d.sendGovTx(context.Background(), data)
+ if err != nil {
+ log.Error("failed to send report fork vote tx", "err", err)
+ }
+}
+
+// ReportForkBlock reports a node for forking blocks.
+func (d *DexconGovernance) ReportForkBlock(block1, block2 *coreTypes.Block) {
+ method := vm.GovernanceContractName2Method["report"]
+
+ block1Bytes, err := rlp.EncodeToBytes(block1)
+ if err != nil {
+ log.Error("failed to RLP encode block1 to bytes", "err", err)
+ return
+ }
+
+ block2Bytes, err := rlp.EncodeToBytes(block2)
+ if err != nil {
+ log.Error("failed to RLP encode block2 to bytes", "err", err)
+ return
+ }
+
+ res, err := method.Inputs.Pack(big.NewInt(vm.ReportTypeForkBlock), block1Bytes, block2Bytes)
+ if err != nil {
+ log.Error("failed to pack report input", "err", err)
+ return
+ }
+
+ data := append(method.Id(), res...)
+ err = d.sendGovTx(context.Background(), data)
+ if err != nil {
+ log.Error("failed to send report fork block tx", "err", err)
+ }
+}
+
func (d *DexconGovernance) GetNumChains(round uint64) uint32 {
return d.Configuration(round).NumChains
}
@@ -292,11 +350,3 @@ func (d *DexconGovernance) DKGSet(round uint64) (map[string]struct{}, error) {
}
return r, nil
}
-
-func (d *DexconGovernance) ReportForkVote(vote1, vote2 *coreTypes.Vote) {
- // TODO: finish this.
-}
-
-func (d *DexconGovernance) ReportForkBlock(block1, block2 *coreTypes.Block) {
- // TODO: finish this.
-}
diff --git a/params/config.go b/params/config.go
index 6cdd95f5f..6cdec3c28 100644
--- a/params/config.go
+++ b/params/config.go
@@ -26,8 +26,8 @@ import (
// Genesis hashes to enforce below configs on.
var (
- MainnetGenesisHash = common.HexToHash("0xf0510808109583b9ef62f846886179f2fb40d1d4f1872d33529947bdd03e9bc1")
- TestnetGenesisHash = common.HexToHash("0x3e14e72125f46254853814b6e9f0b79d296e16b1d60d312516002bf8cfad62f8")
+ MainnetGenesisHash = common.HexToHash("0x81abe3e66f63afe3806b64e7529d2a3f2ae9be7ba3e6fca2a561808bf7875a03")
+ TestnetGenesisHash = common.HexToHash("0x3fe7a48aca63addc5e502fe60a37a865b454c649e106d07d3cd810e086e1fc6d")
)
var (
@@ -60,6 +60,11 @@ var (
DKGSetSize: 4,
RoundInterval: 600000,
MinBlockInterval: 900,
+ 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)),
+ },
},
}
@@ -100,6 +105,11 @@ var (
DKGSetSize: 4,
RoundInterval: 600000,
MinBlockInterval: 900,
+ 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)),
+ },
},
}
@@ -131,6 +141,11 @@ var (
DKGSetSize: 13,
RoundInterval: 3600000,
MinBlockInterval: 900,
+ 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)),
+ },
},
}
@@ -274,16 +289,18 @@ type DexconConfig struct {
DKGSetSize uint32 `json:"dkgSetSize"`
RoundInterval uint64 `json:"roundInterval"`
MinBlockInterval uint64 `json:"minBlockInterval"`
+ FineValues []*big.Int `json:"fineValues"`
}
type dexconConfigSpecMarshaling struct {
MinStake *math.HexOrDecimal256
BlockReward *math.HexOrDecimal256
+ FineValues []*math.HexOrDecimal256
}
// String implements the stringer interface, returning the consensus engine details.
func (d *DexconConfig) String() string {
- return fmt.Sprintf("{GenesisCRSText: %v Owner: %v MinStake: %v LockupPeriod: %v BlockReward: %v BlockGasLimit: %v NumChains: %v LambdaBA: %v LambdaDKG: %v K: %v PhiRatio: %v NotarySetSize: %v DKGSetSize: %v RoundInterval: %v MinBlockInterval: %v}",
+ return fmt.Sprintf("{GenesisCRSText: %v Owner: %v MinStake: %v LockupPeriod: %v BlockReward: %v BlockGasLimit: %v NumChains: %v LambdaBA: %v LambdaDKG: %v K: %v PhiRatio: %v NotarySetSize: %v DKGSetSize: %v RoundInterval: %v MinBlockInterval: %v FineValues: %v}",
d.GenesisCRSText,
d.Owner,
d.MinStake,
@@ -299,6 +316,7 @@ func (d *DexconConfig) String() string {
d.DKGSetSize,
d.RoundInterval,
d.MinBlockInterval,
+ d.FineValues,
)
}
diff --git a/params/gen_dexcon_config.go b/params/gen_dexcon_config.go
index 55d98ba37..4ec55c0c9 100644
--- a/params/gen_dexcon_config.go
+++ b/params/gen_dexcon_config.go
@@ -15,21 +15,22 @@ var _ = (*dexconConfigSpecMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (d DexconConfig) MarshalJSON() ([]byte, error) {
type DexconConfig struct {
- GenesisCRSText string `json:"genesisCRSText"`
- Owner common.Address `json:"owner"`
- MinStake *math.HexOrDecimal256 `json:"minStake"`
- LockupPeriod uint64 `json:"lockupPeriod"`
- BlockReward *math.HexOrDecimal256 `json:"blockReward"`
- BlockGasLimit uint64 `json:"blockGasLimit"`
- NumChains uint32 `json:"numChains"`
- LambdaBA uint64 `json:"lambdaBA"`
- LambdaDKG uint64 `json:"lambdaDKG"`
- K uint32 `json:"k"`
- PhiRatio float32 `json:"phiRatio"`
- NotarySetSize uint32 `json:"notarySetSize"`
- DKGSetSize uint32 `json:"dkgSetSize"`
- RoundInterval uint64 `json:"roundInterval"`
- MinBlockInterval uint64 `json:"minBlockInterval"`
+ GenesisCRSText string `json:"genesisCRSText"`
+ Owner common.Address `json:"owner"`
+ MinStake *math.HexOrDecimal256 `json:"minStake"`
+ LockupPeriod uint64 `json:"lockupPeriod"`
+ BlockReward *math.HexOrDecimal256 `json:"blockReward"`
+ BlockGasLimit uint64 `json:"blockGasLimit"`
+ NumChains uint32 `json:"numChains"`
+ LambdaBA uint64 `json:"lambdaBA"`
+ LambdaDKG uint64 `json:"lambdaDKG"`
+ K uint32 `json:"k"`
+ PhiRatio float32 `json:"phiRatio"`
+ NotarySetSize uint32 `json:"notarySetSize"`
+ DKGSetSize uint32 `json:"dkgSetSize"`
+ RoundInterval uint64 `json:"roundInterval"`
+ MinBlockInterval uint64 `json:"minBlockInterval"`
+ FineValues []*math.HexOrDecimal256 `json:"fineValues"`
}
var enc DexconConfig
enc.GenesisCRSText = d.GenesisCRSText
@@ -47,27 +48,34 @@ func (d DexconConfig) MarshalJSON() ([]byte, error) {
enc.DKGSetSize = d.DKGSetSize
enc.RoundInterval = d.RoundInterval
enc.MinBlockInterval = d.MinBlockInterval
+ if d.FineValues != nil {
+ enc.FineValues = make([]*math.HexOrDecimal256, len(d.FineValues))
+ for k, v := range d.FineValues {
+ enc.FineValues[k] = (*math.HexOrDecimal256)(v)
+ }
+ }
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (d *DexconConfig) UnmarshalJSON(input []byte) error {
type DexconConfig struct {
- GenesisCRSText *string `json:"genesisCRSText"`
- Owner *common.Address `json:"owner"`
- MinStake *math.HexOrDecimal256 `json:"minStake"`
- LockupPeriod *uint64 `json:"lockupPeriod"`
- BlockReward *math.HexOrDecimal256 `json:"blockReward"`
- BlockGasLimit *uint64 `json:"blockGasLimit"`
- NumChains *uint32 `json:"numChains"`
- LambdaBA *uint64 `json:"lambdaBA"`
- LambdaDKG *uint64 `json:"lambdaDKG"`
- K *uint32 `json:"k"`
- PhiRatio *float32 `json:"phiRatio"`
- NotarySetSize *uint32 `json:"notarySetSize"`
- DKGSetSize *uint32 `json:"dkgSetSize"`
- RoundInterval *uint64 `json:"roundInterval"`
- MinBlockInterval *uint64 `json:"minBlockInterval"`
+ GenesisCRSText *string `json:"genesisCRSText"`
+ Owner *common.Address `json:"owner"`
+ MinStake *math.HexOrDecimal256 `json:"minStake"`
+ LockupPeriod *uint64 `json:"lockupPeriod"`
+ BlockReward *math.HexOrDecimal256 `json:"blockReward"`
+ BlockGasLimit *uint64 `json:"blockGasLimit"`
+ NumChains *uint32 `json:"numChains"`
+ LambdaBA *uint64 `json:"lambdaBA"`
+ LambdaDKG *uint64 `json:"lambdaDKG"`
+ K *uint32 `json:"k"`
+ PhiRatio *float32 `json:"phiRatio"`
+ NotarySetSize *uint32 `json:"notarySetSize"`
+ DKGSetSize *uint32 `json:"dkgSetSize"`
+ RoundInterval *uint64 `json:"roundInterval"`
+ MinBlockInterval *uint64 `json:"minBlockInterval"`
+ FineValues []*math.HexOrDecimal256 `json:"fineValues"`
}
var dec DexconConfig
if err := json.Unmarshal(input, &dec); err != nil {
@@ -118,5 +126,11 @@ func (d *DexconConfig) UnmarshalJSON(input []byte) error {
if dec.MinBlockInterval != nil {
d.MinBlockInterval = *dec.MinBlockInterval
}
+ if dec.FineValues != nil {
+ d.FineValues = make([]*big.Int, len(dec.FineValues))
+ for k, v := range dec.FineValues {
+ d.FineValues[k] = (*big.Int)(v)
+ }
+ }
return nil
}
diff --git a/test/genesis.json b/test/genesis.json
index 6a48eee29..c053120f5 100644
--- a/test/genesis.json
+++ b/test/genesis.json
@@ -2,17 +2,18 @@
"config": {
"chainId": 238,
"homesteadBlock": 0,
- "daoForkBlock": 0,
"daoForkSupport": true,
"eip150Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
+ "constantinopleBlock": 0,
"dexcon": {
"genesisCRSText": "In DEXON, we trust.",
- "owner": "0xBF8C48A620bacc46907f9B89732D25E47A2D7Cf7",
+ "owner": "0xbf8c48a620bacc46907f9b89732d25e47a2d7cf7",
"minStake": "0x152d02c7e14af6800000",
+ "lockupPeriod": 259200000,
"blockReward": "0xde0b6b3a7640000",
"blockGasLimit": 40000000,
"numChains": 6,
@@ -23,7 +24,12 @@
"notarySetSize": 4,
"dkgSetSize": 4,
"roundInterval": 600000,
- "minBlockInterval": 900
+ "minBlockInterval": 900,
+ "fineValues": [
+ "0x21e19e0c9bab2400000",
+ "0x21e19e0c9bab2400000",
+ "0x152d02c7e14af6800000"
+ ]
}
},
"nonce": "0x42",