diff options
author | Wei-Ning Huang <w@dexon.org> | 2019-01-06 23:21:54 +0800 |
---|---|---|
committer | Wei-Ning Huang <w@dexon.org> | 2019-03-12 12:19:09 +0800 |
commit | 7d2bde74ea108ccf2ea9facb5d6a74a77eee7ab9 (patch) | |
tree | dd8c2815881d8a72273742911e2514ba92c16711 | |
parent | 01d282cc48e8c85eb18b7f5eccb555d09dbe2780 (diff) | |
download | dexon-7d2bde74ea108ccf2ea9facb5d6a74a77eee7ab9.tar.gz dexon-7d2bde74ea108ccf2ea9facb5d6a74a77eee7ab9.tar.zst dexon-7d2bde74ea108ccf2ea9facb5d6a74a77eee7ab9.zip |
core: vm: implement byzantine reporting mechanism (#128)
-rw-r--r-- | cmd/gdex/dao_test.go | 2 | ||||
-rw-r--r-- | core/vm/governance.go | 431 | ||||
-rw-r--r-- | core/vm/governance_test.go | 195 | ||||
-rw-r--r-- | dex/app_test.go | 15 | ||||
-rw-r--r-- | dex/governance.go | 66 | ||||
-rw-r--r-- | params/config.go | 24 | ||||
-rw-r--r-- | params/gen_dexcon_config.go | 74 | ||||
-rw-r--r-- | test/genesis.json | 12 |
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", |