From bbd2910687e9e5cd5c52e887f837f5c992ba2261 Mon Sep 17 00:00:00 2001 From: Wei-Ning Huang Date: Wed, 20 Mar 2019 11:55:01 +0800 Subject: consensus: dexcon: disqualify dead node (#280) Since a qualified node might fail stopped, we need to remove them from qualified nodes to maintain network integrity. We do this by inspect the previous round to see if there are dead nodes. A dead node is a notary set node that does not propose any block in the previous round. We disqualify them by fining them so their staked value is 1 wei below minStake. This make them unqualified for being notary set in the follow on rounds. --- core/vm/oracle_contracts.go | 49 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'core/vm/oracle_contracts.go') diff --git a/core/vm/oracle_contracts.go b/core/vm/oracle_contracts.go index f21700d86..cd11dc051 100644 --- a/core/vm/oracle_contracts.go +++ b/core/vm/oracle_contracts.go @@ -62,6 +62,7 @@ const ( nodesLoc nodesOffsetByAddressLoc nodesOffsetByNodeKeyAddressLoc + lastProposedHeightLoc crsRoundLoc crsLoc dkgRoundLoc @@ -332,7 +333,8 @@ func (s *GovernanceState) DecTotalStaked(amount *big.Int) { // string location; // string url; // uint256 unstaked; -// uint256 unstaked_at; +// uint256 unstakedAt; +// uint256 lastProposedHeight; // } // // Node[] nodes; @@ -543,6 +545,16 @@ func (s *GovernanceState) GetNodeByID(id coreTypes.NodeID) (*nodeInfo, error) { return node, nil } +// mapping(address => uint256) public lastProposedHeight; +func (s *GovernanceState) LastProposedHeight(addr common.Address) *big.Int { + loc := s.getMapLoc(big.NewInt(lastProposedHeightLoc), addr.Bytes()) + return s.getStateBigInt(loc) +} +func (s *GovernanceState) PutLastProposedHeight(addr common.Address, height *big.Int) { + loc := s.getMapLoc(big.NewInt(lastProposedHeightLoc), addr.Bytes()) + s.setStateBigInt(loc, height) +} + // uint256 public crsRound; func (s *GovernanceState) CRSRound() *big.Int { return s.getStateBigInt(big.NewInt(crsRoundLoc)) @@ -960,6 +972,31 @@ func (s *GovernanceState) Register( s.IncTotalStaked(staked) } +func (s *GovernanceState) Disqualify(n *nodeInfo) error { + nodeAddr, err := publicKeyToNodeKeyAddress(n.PublicKey) + if err != nil { + return err + } + + // Node might already been unstaked in the latest state. + offset := s.NodesOffsetByNodeKeyAddress(nodeAddr) + if offset.Cmp(big.NewInt(0)) < 0 { + return errors.New("node does not exist") + } + + // Fine the node so it's staked value is 1 wei under minStake. + 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) + } + + return nil +} + const decimalMultiplier = 100000000.0 // Configuration returns the current configuration. @@ -2252,6 +2289,16 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re return nil, errExecutionReverted } return res, nil + case "lastProposedHeight": + address := common.Address{} + if err := method.Inputs.Unpack(&address, arguments); err != nil { + return nil, errExecutionReverted + } + res, err := method.Outputs.Pack(g.state.LastProposedHeight(address)) + if err != nil { + return nil, errExecutionReverted + } + return res, nil case "lockupPeriod": res, err := method.Outputs.Pack(g.state.LockupPeriod()) if err != nil { -- cgit v1.2.3