aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWei-Ning Huang <w@dexon.org>2018-12-18 20:25:58 +0800
committerWei-Ning Huang <w@dexon.org>2019-04-09 13:49:59 +0800
commit92b358bb784d4d1e07f6a20e97b2ce6cb3054e7d (patch)
tree427d6500a6d6472123fa473e225d5dea4384aa2f
parent5218db859c54a2e00e7d9589c12b43f66429fe73 (diff)
downloaddexon-92b358bb784d4d1e07f6a20e97b2ce6cb3054e7d.tar.gz
dexon-92b358bb784d4d1e07f6a20e97b2ce6cb3054e7d.tar.zst
dexon-92b358bb784d4d1e07f6a20e97b2ce6cb3054e7d.zip
core: vm: add undelegate fund lockup mechanism (#94)
Only allow a user to withdraw funds after a certain lockup period. This way, the fund of a bad actor could be confiscated before he could escape.
-rw-r--r--cmd/gdex/dao_test.go2
-rw-r--r--core/genesis_alloc.go2
-rw-r--r--core/vm/governance.go198
-rw-r--r--core/vm/governance_test.go77
-rw-r--r--params/config.go11
-rw-r--r--params/gen_dexcon_config.go6
6 files changed, 251 insertions, 45 deletions
diff --git a/cmd/gdex/dao_test.go b/cmd/gdex/dao_test.go
index 26572762c..22f82d4f4 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("0xc8e4d0c33d92b7751fe3747f778aa27600508c8c922be1dbbc7db6ee967f4e6c")
+ genesisHash := common.HexToHash("0x5fc1fdb2eca492d256600c0d96a2ca7bdfd9412ac8557bcab54e05332260e26b")
if genesis != "" {
genesisHash = daoGenesisHash
}
diff --git a/core/genesis_alloc.go b/core/genesis_alloc.go
index 289c5b09f..ab8032f32 100644
--- a/core/genesis_alloc.go
+++ b/core/genesis_alloc.go
@@ -21,6 +21,6 @@ package core
// Use mkalloc.go to create/update them.
// nolint: misspell
-const mainnetAllocData = "\xf9\x03\x90\xea\x94\x12E\xa8g/\xa8\x81\u03c5\x8e\xf0\x1d4\xc4+U\u0672c\xff\u050bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\x80\x80\x80\u0100\x80\x80\x80\xea\x94*\x9df\x9eG\x91\x84^\xed\x01\xd4\xc0\xff\xf3\xb9'\u0314\xa8\x84\u050bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\x80\x80\x80\u0100\x80\x80\x80\xf8\xb7\x94U8QR\xef\xc1\x1bN\xb8\x11\xe9\xdf\x0ex\xf4\u00ff\xa9\x19F\xf8\xa0\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\x8ai\xe1\r\xe7fv\u0400\x00\x00\x80\xb8A\x04&:l4\x10\xe7\xab\nS\x13\xf6\xca\a[\xb4y\x9d<\x8cr8\x8b\xe9\x8d\xf6\x03\xa1\xe0\x9c%\x12\x96\x05\xbe0\x11x({\xa3\xea\xd7S\xc1\xea\x98&\xc2\xf4\x96\x83U\xa6\xb9{G^\x80\xbb%w\xbeR\x96\xf8D\x91DEXON Test Node 3\x90dexon3@dexon.org\x8eTaipei, Taiwan\x91https://dexon.org\xf8\xb7\x94^.\xa9\x87*J\xd9\x1co\rF\xd6\xed\xd4jL\x96\xdbuS\xf8\xa0\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\x8ai\xe1\r\xe7fv\u0400\x00\x00\x80\xb8A\x04B+T\"\u007f\x85\x98\x06</\xa4O\x8f_\xa1\x8cg\x10\xe2\xc3\xc0'S\xf7lSKH\x82\xb7\xf2\xf6\t\u070c\xb0:\u052eR\x8c~\x1e\x05\r\xb6L\x96.\x89W'\xf1\xf4B\u06dae\x10\x05\xfa\x97\f^\xf8D\x91DEXON Test Node 2\x90dexon2@dexon.org\x8eTaipei, Taiwan\x91https://dexon.org\xea\x94_\xb3\x95/4\xb5|e/\x9d\u06f4\rh\x8dWlT\x1b\xdc\u050bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\x80\x80\x80\u0100\x80\x80\x80\xf8\xb7\x94g\x8eyf\x17KT\x16\xa3\xf9\x18\x8a\x83\xe8\xec\xce%\xf9\x80s\xf8\xa0\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\x8ai\xe1\r\xe7fv\u0400\x00\x00\x80\xb8A\x04\x88\xfe\xba\u007f\vui|\xe3oeg\xb3\x946p\x9b\x91C1\x956\x9bP\xdc\u007fB\x8c\xa3\x19\x14\x9c\xb0)\xf5\xd5\x05\xbe\v\xb9\x8bD\x96|\xa1\aZ\xb1\xae\u03a2I_\xc5q\xb3-\xe5r9\x9c\x0e\xbd\x8a\xf8D\x91DEXON Test Node 0\x90dexon0@dexon.org\x8eTaipei, Taiwan\x91https://dexon.org\xf8\xb7\x94\xb6-\x1d\"\r\x18PrO\xaa\xe9%\xb6\x9f\u007fB(S\x1eJ\xf8\xa0\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\x8ai\xe1\r\xe7fv\u0400\x00\x00\x80\xb8A\x04J\x85\xe2\xf8\xdb\x1a\x011\xa3\t\xe7F\u057cR\v\x95\x9d\xe3\xac\xf5ZU\xc7\xd7\x17\xfa4\x91\x00c\x00\x06\xee\x85\xf7\x1a\x9c\xbc\xc08hJ\xfb\xbdNi\xf0\xc5\t\x149\xa98{\u007f\xdb\v\x8cN\xabE\xae\xfc\xf8D\x91DEXON Test Node 1\x90dexon1@dexon.org\x8eTaipei, Taiwan\x91https://dexon.org\xea\x94\xe0\xf8Y4\x03S\x85F\x93\xf57\xea3q\x06\xa3>\x9f\xea\xb0\u050bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\x80\x80\x80\u0100\x80\x80\x80"
+const mainnetAllocData = "\xf9\x03:\xf8\xb7\x94U8QR\xef\xc1\x1bN\xb8\x11\xe9\xdf\x0ex\xf4\u00ff\xa9\x19F\xf8\xa0\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\x8ai\xe1\r\xe7fv\u0400\x00\x00\x80\xb8A\x04&:l4\x10\xe7\xab\nS\x13\xf6\xca\a[\xb4y\x9d<\x8cr8\x8b\xe9\x8d\xf6\x03\xa1\xe0\x9c%\x12\x96\x05\xbe0\x11x({\xa3\xea\xd7S\xc1\xea\x98&\xc2\xf4\x96\x83U\xa6\xb9{G^\x80\xbb%w\xbeR\x96\xf8D\x91DEXON Test Node 3\x90dexon3@dexon.org\x8eTaipei, Taiwan\x91https://dexon.org\xf8\xb7\x94^.\xa9\x87*J\xd9\x1co\rF\xd6\xed\xd4jL\x96\xdbuS\xf8\xa0\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\x8ai\xe1\r\xe7fv\u0400\x00\x00\x80\xb8A\x04B+T\"\u007f\x85\x98\x06</\xa4O\x8f_\xa1\x8cg\x10\xe2\xc3\xc0'S\xf7lSKH\x82\xb7\xf2\xf6\t\u070c\xb0:\u052eR\x8c~\x1e\x05\r\xb6L\x96.\x89W'\xf1\xf4B\u06dae\x10\x05\xfa\x97\f^\xf8D\x91DEXON Test Node 2\x90dexon2@dexon.org\x8eTaipei, Taiwan\x91https://dexon.org\xf8\xb7\x94g\x8eyf\x17KT\x16\xa3\xf9\x18\x8a\x83\xe8\xec\xce%\xf9\x80s\xf8\xa0\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\x8ai\xe1\r\xe7fv\u0400\x00\x00\x80\xb8A\x04\x88\xfe\xba\u007f\vui|\xe3oeg\xb3\x946p\x9b\x91C1\x956\x9bP\xdc\u007fB\x8c\xa3\x19\x14\x9c\xb0)\xf5\xd5\x05\xbe\v\xb9\x8bD\x96|\xa1\aZ\xb1\xae\u03a2I_\xc5q\xb3-\xe5r9\x9c\x0e\xbd\x8a\xf8D\x91DEXON Test Node 0\x90dexon0@dexon.org\x8eTaipei, Taiwan\x91https://dexon.org\xf8\xb7\x94\xb6-\x1d\"\r\x18PrO\xaa\xe9%\xb6\x9f\u007fB(S\x1eJ\xf8\xa0\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\x8ai\xe1\r\xe7fv\u0400\x00\x00\x80\xb8A\x04J\x85\xe2\xf8\xdb\x1a\x011\xa3\t\xe7F\u057cR\v\x95\x9d\xe3\xac\xf5ZU\xc7\xd7\x17\xfa4\x91\x00c\x00\x06\xee\x85\xf7\x1a\x9c\xbc\xc08hJ\xfb\xbdNi\xf0\xc5\t\x149\xa98{\u007f\xdb\v\x8cN\xabE\xae\xfc\xf8D\x91DEXON Test Node 1\x90dexon1@dexon.org\x8eTaipei, Taiwan\x91https://dexon.org\ua53f\x8cH\xa6 \xba\xccF\x90\u007f\x9b\x89s-%\xe4z-|\xf7\u050bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\x80\x80\x80\u0100\x80\x80\x80\xea\x94\xe0\xf8Y4\x03S\x85F\x93\xf57\xea3q\x06\xa3>\x9f\xea\xb0\u050bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\x80\x80\x80\u0100\x80\x80\x80"
const testnetAllocData = mainnetAllocData
diff --git a/core/vm/governance.go b/core/vm/governance.go
index 8c6f70a04..f423c4ca3 100644
--- a/core/vm/governance.go
+++ b/core/vm/governance.go
@@ -165,6 +165,10 @@ const GovernanceABIJSON = `
{
"name": "url",
"type": "string"
+ },
+ {
+ "name": "unstaked",
+ "type": "bool"
}
],
"payable": false,
@@ -253,6 +257,10 @@ const GovernanceABIJSON = `
{
"name": "value",
"type": "uint256"
+ },
+ {
+ "name": "undelegated_at",
+ "type": "uint256"
}
],
"payable": false,
@@ -443,6 +451,20 @@ const GovernanceABIJSON = `
},
{
"constant": true,
+ "inputs": [],
+ "name": "lockupPeriod",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
"inputs": [
{
"name": "",
@@ -568,6 +590,10 @@ const GovernanceABIJSON = `
"type": "uint256"
},
{
+ "name": "LockupPeriod",
+ "type": "uint256"
+ },
+ {
"name": "BlockReward",
"type": "uint256"
},
@@ -807,6 +833,20 @@ const GovernanceABIJSON = `
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "NodeAddress",
+ "type": "address"
+ }
+ ],
+ "name": "withdraw",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
}
]
`
@@ -958,6 +998,12 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by
return nil, errExecutionReverted
}
return g.updateConfiguration(&cfg)
+ case "withdraw":
+ address := common.Address{}
+ if err := method.Inputs.Unpack(&address, arguments); err != nil {
+ return nil, errExecutionReverted
+ }
+ return g.withdraw(address)
// --------------------------------
// Solidity auto generated methods.
@@ -1087,6 +1133,12 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by
return nil, errExecutionReverted
}
return res, nil
+ case "lockupPeriod":
+ res, err := method.Outputs.Pack(g.state.LockupPeriod())
+ if err != nil {
+ return nil, errExecutionReverted
+ }
+ return res, nil
case "minBlockInterval":
res, err := method.Outputs.Pack(g.state.MinBlockInterval())
if err != nil {
@@ -1180,6 +1232,7 @@ const (
dkgFinalizedsCountLoc
ownerLoc
minStakeLoc
+ lockupPeriodLoc
blockRewardLoc
blockGasLimitLoc
numChainsLoc
@@ -1360,6 +1413,7 @@ func (s *GovernanceStateHelper) PushRoundHeight(height *big.Int) {
// string email;
// string location;
// string url;
+// bool unstaked;
// }
//
// Node[] nodes;
@@ -1372,9 +1426,10 @@ type nodeInfo struct {
Email string
Location string
Url string
+ Unstaked bool
}
-const nodeStructSize = 7
+const nodeStructSize = 8
func (s *GovernanceStateHelper) LenNodes() *big.Int {
return s.getStateBigInt(big.NewInt(nodesLoc))
@@ -1414,6 +1469,10 @@ func (s *GovernanceStateHelper) Node(index *big.Int) *nodeInfo {
loc = new(big.Int).Add(elementBaseLoc, big.NewInt(6))
node.Url = string(s.readBytes(loc))
+ // Unstaked.
+ loc = new(big.Int).Add(elementBaseLoc, big.NewInt(7))
+ node.Unstaked = s.getStateBigInt(loc).Cmp(big.NewInt(0)) > 0
+
return node
}
func (s *GovernanceStateHelper) PushNode(n *nodeInfo) {
@@ -1454,6 +1513,14 @@ func (s *GovernanceStateHelper) UpdateNode(index *big.Int, n *nodeInfo) {
// Url.
loc = new(big.Int).Add(elementBaseLoc, big.NewInt(6))
s.writeBytes(loc, []byte(n.Url))
+
+ // Unstaked.
+ loc = new(big.Int).Add(elementBaseLoc, big.NewInt(7))
+ val := big.NewInt(0)
+ if n.Unstaked {
+ val = big.NewInt(1)
+ }
+ s.setStateBigInt(loc, val)
}
func (s *GovernanceStateHelper) PopLastNode() {
// Decrease length by 1.
@@ -1499,14 +1566,16 @@ func (s *GovernanceStateHelper) DeleteNodesOffset(addr common.Address) {
// address node;
// address owner;
// uint256 value;
+// uint256 undelegated_at;
// }
type delegatorInfo struct {
- Owner common.Address
- Value *big.Int
+ Owner common.Address
+ Value *big.Int
+ UndelegatedAt *big.Int
}
-const delegatorStructSize = 2
+const delegatorStructSize = 3
// mapping(address => Delegator[]) public delegators;
func (s *GovernanceStateHelper) LenDelegators(nodeAddr common.Address) *big.Int {
@@ -1528,6 +1597,10 @@ func (s *GovernanceStateHelper) Delegator(nodeAddr common.Address, offset *big.I
loc = new(big.Int).Add(elementBaseLoc, big.NewInt(1))
delegator.Value = s.getStateBigInt(loc)
+ // UndelegatedAt.
+ loc = new(big.Int).Add(elementBaseLoc, big.NewInt(2))
+ delegator.UndelegatedAt = s.getStateBigInt(loc)
+
return delegator
}
func (s *GovernanceStateHelper) PushDelegator(nodeAddr common.Address, delegator *delegatorInfo) {
@@ -1550,6 +1623,10 @@ func (s *GovernanceStateHelper) UpdateDelegator(nodeAddr common.Address, offset
// Value.
loc = new(big.Int).Add(elementBaseLoc, big.NewInt(1))
s.setStateBigInt(loc, delegator.Value)
+
+ // UndelegatedAt.
+ loc = new(big.Int).Add(elementBaseLoc, big.NewInt(2))
+ s.setStateBigInt(loc, delegator.UndelegatedAt)
}
func (s *GovernanceStateHelper) PopLastDelegator(nodeAddr common.Address) {
// Decrease length by 1.
@@ -1558,7 +1635,10 @@ func (s *GovernanceStateHelper) PopLastDelegator(nodeAddr common.Address) {
loc := s.getMapLoc(big.NewInt(delegatorsLoc), nodeAddr.Bytes())
s.setStateBigInt(loc, newArrayLength)
- s.UpdateDelegator(nodeAddr, newArrayLength, &delegatorInfo{Value: big.NewInt(0)})
+ s.UpdateDelegator(nodeAddr, newArrayLength, &delegatorInfo{
+ Value: big.NewInt(0),
+ UndelegatedAt: big.NewInt(0),
+ })
}
// mapping(address => mapping(address => uint256)) delegatorsOffset;
@@ -1676,17 +1756,16 @@ func (s *GovernanceStateHelper) SetOwner(newOwner common.Address) {
func (s *GovernanceStateHelper) MinStake() *big.Int {
return s.getStateBigInt(big.NewInt(minStakeLoc))
}
-func (s *GovernanceStateHelper) SetMinStake(stake *big.Int) {
- s.setStateBigInt(big.NewInt(minStakeLoc), stake)
+
+// uint256 public lockupPeriod;
+func (s *GovernanceStateHelper) LockupPeriod() *big.Int {
+ return s.getStateBigInt(big.NewInt(lockupPeriodLoc))
}
// uint256 public blockReward;
func (s *GovernanceStateHelper) BlockReward() *big.Int {
return s.getStateBigInt(big.NewInt(blockRewardLoc))
}
-func (s *GovernanceStateHelper) SetBlockReward(reward *big.Int) {
- s.setStateBigInt(big.NewInt(blockRewardLoc), reward)
-}
// uint256 public blockGasLimit;
func (s *GovernanceStateHelper) BlockGasLimit() *big.Int {
@@ -1764,6 +1843,7 @@ const phiRatioMultiplier = 1000000.0
func (s *GovernanceStateHelper) Configuration() *params.DexconConfig {
return &params.DexconConfig{
MinStake: s.getStateBigInt(big.NewInt(minStakeLoc)),
+ LockupPeriod: s.getStateBigInt(big.NewInt(lockupPeriodLoc)).Uint64(),
BlockReward: s.getStateBigInt(big.NewInt(blockRewardLoc)),
BlockGasLimit: s.getStateBigInt(big.NewInt(blockGasLimitLoc)).Uint64(),
NumChains: uint32(s.getStateBigInt(big.NewInt(numChainsLoc)).Uint64()),
@@ -1781,6 +1861,7 @@ func (s *GovernanceStateHelper) Configuration() *params.DexconConfig {
// UpdateConfiguration updates system configuration.
func (s *GovernanceStateHelper) UpdateConfiguration(cfg *params.DexconConfig) {
s.setStateBigInt(big.NewInt(minStakeLoc), cfg.MinStake)
+ s.setStateBigInt(big.NewInt(lockupPeriodLoc), big.NewInt(int64(cfg.LockupPeriod)))
s.setStateBigInt(big.NewInt(blockRewardLoc), cfg.BlockReward)
s.setStateBigInt(big.NewInt(blockGasLimitLoc), big.NewInt(int64(cfg.BlockGasLimit)))
s.setStateBigInt(big.NewInt(numChainsLoc), big.NewInt(int64(cfg.NumChains)))
@@ -1796,6 +1877,7 @@ func (s *GovernanceStateHelper) UpdateConfiguration(cfg *params.DexconConfig) {
type rawConfigStruct struct {
MinStake *big.Int
+ LockupPeriod *big.Int
BlockReward *big.Int
BlockGasLimit *big.Int
NumChains *big.Int
@@ -1812,6 +1894,7 @@ type rawConfigStruct struct {
// UpdateConfigurationRaw updates system configuration.
func (s *GovernanceStateHelper) UpdateConfigurationRaw(cfg *rawConfigStruct) {
s.setStateBigInt(big.NewInt(minStakeLoc), cfg.MinStake)
+ s.setStateBigInt(big.NewInt(lockupPeriodLoc), cfg.LockupPeriod)
s.setStateBigInt(big.NewInt(blockRewardLoc), cfg.BlockReward)
s.setStateBigInt(big.NewInt(blockGasLimitLoc), cfg.BlockGasLimit)
s.setStateBigInt(big.NewInt(numChainsLoc), cfg.NumChains)
@@ -2082,8 +2165,9 @@ func (g *GovernanceContract) delegate(nodeAddr common.Address) ([]byte, error) {
// Push delegator record.
offset = g.state.LenDelegators(nodeAddr)
g.state.PushDelegator(nodeAddr, &delegatorInfo{
- Owner: caller,
- Value: value,
+ Owner: caller,
+ Value: value,
+ UndelegatedAt: big.NewInt(0),
})
g.state.PutDelegatorOffset(nodeAddr, caller, offset)
g.state.emitDelegated(nodeAddr, caller, value)
@@ -2146,18 +2230,62 @@ func (g *GovernanceContract) stake(
return g.useGas(100000)
}
-func (g *GovernanceContract) undelegateHelper(nodeAddr, owner common.Address) ([]byte, error) {
+func (g *GovernanceContract) undelegateHelper(nodeAddr, caller common.Address) ([]byte, error) {
nodeOffset := g.state.NodesOffset(nodeAddr)
if nodeOffset.Cmp(big.NewInt(0)) < 0 {
return nil, errExecutionReverted
}
- offset := g.state.DelegatorsOffset(nodeAddr, owner)
+ offset := g.state.DelegatorsOffset(nodeAddr, caller)
if offset.Cmp(big.NewInt(0)) < 0 {
return nil, errExecutionReverted
}
delegator := g.state.Delegator(nodeAddr, offset)
+
+ // Set undelegate time.
+ delegator.UndelegatedAt = g.evm.Time
+ g.state.UpdateDelegator(nodeAddr, offset, delegator)
+
+ // Subtract from the total staked of node.
+ node := g.state.Node(nodeOffset)
+ node.Staked = new(big.Int).Sub(node.Staked, delegator.Value)
+ g.state.UpdateNode(nodeOffset, node)
+
+ g.state.emitUndelegated(nodeAddr, caller)
+
+ return g.useGas(100000)
+}
+
+func (g *GovernanceContract) undelegate(nodeAddr common.Address) ([]byte, error) {
+ return g.undelegateHelper(nodeAddr, g.contract.Caller())
+}
+
+func (g *GovernanceContract) withdraw(nodeAddr common.Address) ([]byte, error) {
+ caller := g.contract.Caller()
+
+ nodeOffset := g.state.NodesOffset(nodeAddr)
+ if nodeOffset.Cmp(big.NewInt(0)) < 0 {
+ return nil, errExecutionReverted
+ }
+
+ offset := g.state.DelegatorsOffset(nodeAddr, caller)
+ if offset.Cmp(big.NewInt(0)) < 0 {
+ return nil, errExecutionReverted
+ }
+
+ delegator := g.state.Delegator(nodeAddr, offset)
+
+ // Not yet undelegated.
+ if delegator.UndelegatedAt.Cmp(big.NewInt(0)) == 0 {
+ return g.penalize()
+ }
+
+ unlockTime := new(big.Int).Add(delegator.UndelegatedAt, g.state.LockupPeriod())
+ if g.evm.Time.Cmp(unlockTime) <= 0 {
+ return g.penalize()
+ }
+
length := g.state.LenDelegators(nodeAddr)
lastIndex := new(big.Int).Sub(length, big.NewInt(1))
@@ -2167,25 +2295,30 @@ func (g *GovernanceContract) undelegateHelper(nodeAddr, owner common.Address) ([
g.state.UpdateDelegator(nodeAddr, offset, lastNode)
g.state.PutDelegatorOffset(nodeAddr, lastNode.Owner, offset)
}
- g.state.DeleteDelegatorsOffset(nodeAddr, owner)
+ g.state.DeleteDelegatorsOffset(nodeAddr, caller)
g.state.PopLastDelegator(nodeAddr)
- // Subtract from the total staked of node.
- node := g.state.Node(nodeOffset)
- node.Staked = new(big.Int).Sub(node.Staked, delegator.Value)
- g.state.UpdateNode(nodeOffset, node)
-
// Return the staked fund.
if !g.transfer(GovernanceContractAddress, delegator.Owner, delegator.Value) {
return nil, errExecutionReverted
}
- g.state.emitUndelegated(nodeAddr, owner)
- return g.useGas(100000)
-}
+ // We are the last delegator to withdraw the fund, remove the node info.
+ if g.state.LenDelegators(nodeAddr).Cmp(big.NewInt(0)) == 0 {
+ length := g.state.LenNodes()
+ lastIndex := new(big.Int).Sub(length, big.NewInt(1))
-func (g *GovernanceContract) undelegate(nodeAddr common.Address) ([]byte, error) {
- return g.undelegateHelper(nodeAddr, g.contract.Caller())
+ // Delete the node.
+ if offset.Cmp(lastIndex) != 0 {
+ lastNode := g.state.Node(lastIndex)
+ g.state.UpdateNode(offset, lastNode)
+ g.state.PutNodesOffset(lastNode.Owner, offset)
+ }
+ g.state.DeleteNodesOffset(nodeAddr)
+ g.state.PopLastNode()
+ }
+
+ return g.useGas(100000)
}
func (g *GovernanceContract) unstake() ([]byte, error) {
@@ -2206,17 +2339,10 @@ func (g *GovernanceContract) unstake() ([]byte, error) {
i = i.Sub(i, big.NewInt(1))
}
- length := g.state.LenNodes()
- lastIndex := new(big.Int).Sub(length, big.NewInt(1))
-
- // Delete the node.
- if offset.Cmp(lastIndex) != 0 {
- lastNode := g.state.Node(lastIndex)
- g.state.UpdateNode(offset, lastNode)
- g.state.PutNodesOffset(lastNode.Owner, offset)
- }
- g.state.DeleteNodesOffset(caller)
- g.state.PopLastNode()
+ // Mark node as unstaked.
+ node := g.state.Node(offset)
+ node.Unstaked = true
+ g.state.UpdateNode(offset, node)
g.state.emitUnstaked(caller)
diff --git a/core/vm/governance_test.go b/core/vm/governance_test.go
index 7718e99d2..018306992 100644
--- a/core/vm/governance_test.go
+++ b/core/vm/governance_test.go
@@ -103,6 +103,8 @@ func (g *GovernanceContractTestSuite) SetupTest() {
g.s = &GovernanceStateHelper{stateDB}
config := params.TestnetChainConfig.Dexcon
+ config.LockupPeriod = 1000
+
g.config = config
// Give governance contract balance so it will not be deleted because of being an empty state object.
@@ -155,7 +157,7 @@ func (g *GovernanceContractTestSuite) call(caller common.Address, input []byte,
}
return 0, false
},
- Time: big.NewInt(time.Now().UnixNano() / 1000000000),
+ Time: big.NewInt(time.Now().UnixNano() / 1000000),
BlockNumber: big.NewInt(0),
}
@@ -180,7 +182,7 @@ func (g *GovernanceContractTestSuite) TestTransferOwnership() {
g.Require().Equal(addr, g.s.Owner())
}
-func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutDelegators() {
+func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() {
privKey, addr := g.newPrefundAccount()
pk := crypto.FromECDSAPub(&privKey.PublicKey)
@@ -210,6 +212,17 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutDelegators() {
g.Require().Nil(err)
_, err = g.call(addr, input, big.NewInt(0))
g.Require().Nil(err)
+ g.Require().Equal(1, int(g.s.LenDelegators(addr).Uint64()))
+ g.Require().Equal(1, int(g.s.LenNodes().Uint64()))
+
+ // Wait for lockup time than withdraw.
+ time.Sleep(time.Second * 2)
+ input, err = abiObject.Pack("withdraw", addr)
+ g.Require().Nil(err)
+ _, err = g.call(addr, input, big.NewInt(0))
+ g.Require().Nil(err)
+
+ g.Require().Equal(0, int(g.s.LenDelegators(addr).Uint64()))
g.Require().Equal(0, int(g.s.LenNodes().Uint64()))
// Stake 2 nodes, and unstake the first then the second.
@@ -235,6 +248,12 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutDelegators() {
g.Require().Nil(err)
_, err = g.call(addr2, input, big.NewInt(0))
g.Require().Nil(err)
+ time.Sleep(time.Second * 2)
+ input, err = abiObject.Pack("withdraw", addr2)
+ g.Require().Nil(err)
+ _, err = g.call(addr2, input, big.NewInt(0))
+ g.Require().Nil(err)
+
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()))
@@ -244,6 +263,12 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutDelegators() {
g.Require().Nil(err)
_, err = g.call(addr, input, big.NewInt(0))
g.Require().Nil(err)
+ time.Sleep(time.Second * 2)
+ input, err = abiObject.Pack("withdraw", addr)
+ g.Require().Nil(err)
+ _, err = g.call(addr, input, big.NewInt(0))
+ g.Require().Nil(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.DelegatorsOffset(addr, addr).Int64()))
@@ -309,16 +334,49 @@ func (g *GovernanceContractTestSuite) TestDelegateUndelegate() {
g.Require().Nil(err)
_, err = g.call(addrDelegator, input, big.NewInt(0))
g.Require().Nil(err)
+
+ // Withdraw within lockup time should fail.
+ input, err = abiObject.Pack("withdraw", addr)
+ g.Require().Nil(err)
+ _, err = g.call(addrDelegator, input, big.NewInt(0))
+ g.Require().NotNil(err)
+
+ g.Require().Equal(3, int(g.s.LenDelegators(addr).Uint64()))
+ g.Require().Equal(balanceBeforeUnDelegate, g.stateDB.GetBalance(addrDelegator))
+ g.Require().NotEqual(-1, int(g.s.DelegatorsOffset(addr, addrDelegator).Int64()))
+
+ // Wait for lockup time than withdraw.
+ time.Sleep(time.Second * 2)
+ input, err = abiObject.Pack("withdraw", addr)
+ g.Require().Nil(err)
+ _, err = g.call(addrDelegator, input, big.NewInt(0))
+ g.Require().Nil(err)
+
g.Require().Equal(2, int(g.s.LenDelegators(addr).Uint64()))
g.Require().Equal(new(big.Int).Add(balanceBeforeUnDelegate, amount), g.stateDB.GetBalance(addrDelegator))
g.Require().Equal(-1, int(g.s.DelegatorsOffset(addr, addrDelegator).Int64()))
+ // Withdraw when their is no delegation should fail.
+ time.Sleep(time.Second)
+ input, err = abiObject.Pack("withdraw", addr)
+ g.Require().Nil(err)
+ _, err = g.call(addrDelegator, input, big.NewInt(0))
+ g.Require().NotNil(err)
+
// Undelegate addrDelegator2.
balanceBeforeUnDelegate = g.stateDB.GetBalance(addrDelegator2)
input, err = abiObject.Pack("undelegate", addr)
g.Require().Nil(err)
_, err = g.call(addrDelegator2, input, big.NewInt(0))
g.Require().Nil(err)
+
+ // Wait for lockup time than withdraw.
+ time.Sleep(time.Second * 2)
+ input, err = abiObject.Pack("withdraw", addr)
+ g.Require().Nil(err)
+ _, err = g.call(addrDelegator2, input, big.NewInt(0))
+ g.Require().Nil(err)
+
g.Require().Equal(1, int(g.s.LenDelegators(addr).Uint64()))
g.Require().Equal(new(big.Int).Add(balanceBeforeUnDelegate, amount), g.stateDB.GetBalance(addrDelegator2))
g.Require().Equal(-1, int(g.s.DelegatorsOffset(addr, addrDelegator2).Int64()))
@@ -327,7 +385,7 @@ func (g *GovernanceContractTestSuite) TestDelegateUndelegate() {
g.Require().Equal(0, len(g.s.QualifiedNodes()))
}
-func (g *GovernanceContractTestSuite) TestUnstakeWithDelegators() {
+func (g *GovernanceContractTestSuite) TestUnstakeWithExtraDelegators() {
privKey, addr := g.newPrefundAccount()
pk := crypto.FromECDSAPub(&privKey.PublicKey)
@@ -372,6 +430,17 @@ func (g *GovernanceContractTestSuite) TestUnstakeWithDelegators() {
g.Require().Nil(err)
_, err = g.call(addr, input, big.NewInt(0))
g.Require().Nil(err)
+
+ time.Sleep(time.Second * 2)
+ input, err = abiObject.Pack("withdraw", addr)
+ g.Require().Nil(err)
+ _, err = g.call(addr, input, big.NewInt(0))
+ g.Require().Nil(err)
+ _, err = g.call(addrDelegator, input, big.NewInt(0))
+ g.Require().Nil(err)
+ _, err = g.call(addrDelegator2, input, big.NewInt(0))
+ g.Require().Nil(err)
+
g.Require().Equal(0, int(g.s.LenDelegators(addr).Uint64()))
g.Require().Equal(0, int(g.s.LenNodes().Uint64()))
@@ -386,7 +455,7 @@ func (g *GovernanceContractTestSuite) TestUpdateConfiguration() {
_, addr := g.newPrefundAccount()
input, err := abiObject.Pack("updateConfiguration",
- new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)),
+ 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))
g.Require().Nil(err)
diff --git a/params/config.go b/params/config.go
index 09baec172..5bedf9772 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("0xc8e4d0c33d92b7751fe3747f778aa27600508c8c922be1dbbc7db6ee967f4e6c")
- TestnetGenesisHash = common.HexToHash("0x63b758fa30bf833430171514448288d4e67c1d6a989d1474fdd5c5888dfe77fd")
+ MainnetGenesisHash = common.HexToHash("0x5fc1fdb2eca492d256600c0d96a2ca7bdfd9412ac8557bcab54e05332260e26b")
+ TestnetGenesisHash = common.HexToHash("0x252c41c125e4a9137a39a20b810ddcd33a8023c407cac863ad2326a521375d0f")
)
var (
@@ -48,6 +48,7 @@ var (
GenesisCRSText: "In DEXON, we trust.",
Owner: common.HexToAddress("BF8C48A620bacc46907f9B89732D25E47A2D7Cf7"),
MinStake: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)),
+ LockupPeriod: 86400 * 3 * 1000,
BlockReward: big.NewInt(1e18),
BlockGasLimit: 40000000,
NumChains: 4,
@@ -87,6 +88,7 @@ var (
GenesisCRSText: "In DEXON, we trust.",
Owner: common.HexToAddress("BF8C48A620bacc46907f9B89732D25E47A2D7Cf7"),
MinStake: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)),
+ LockupPeriod: 86400 * 3 * 1000,
BlockReward: big.NewInt(1e18),
BlockGasLimit: 40000000,
NumChains: 6,
@@ -116,6 +118,7 @@ var (
GenesisCRSText: "In DEXON, we trust.",
Owner: common.HexToAddress("BF8C48A620bacc46907f9B89732D25E47A2D7Cf7"),
MinStake: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)),
+ LockupPeriod: 86400 * 3 * 1000,
BlockReward: big.NewInt(1e18),
BlockGasLimit: 40000000,
NumChains: 6,
@@ -258,6 +261,7 @@ type DexconConfig struct {
GenesisCRSText string `json:"genesisCRSText"`
Owner common.Address `json:"owner"`
MinStake *big.Int `json:"minStake"`
+ LockupPeriod uint64 `json:"lockupPeriod"`
BlockReward *big.Int `json:"blockReward"`
BlockGasLimit uint64 `json:"blockGasLimit"`
NumChains uint32 `json:"numChains"`
@@ -278,10 +282,11 @@ type dexconConfigSpecMarshaling struct {
// String implements the stringer interface, returning the consensus engine details.
func (d *DexconConfig) String() string {
- return fmt.Sprintf("{GenesisCRSText: %v Owner: %v MinStake: %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}",
d.GenesisCRSText,
d.Owner,
d.MinStake,
+ d.LockupPeriod,
d.BlockReward,
d.BlockGasLimit,
d.NumChains,
diff --git a/params/gen_dexcon_config.go b/params/gen_dexcon_config.go
index 9cd9395cc..55d98ba37 100644
--- a/params/gen_dexcon_config.go
+++ b/params/gen_dexcon_config.go
@@ -18,6 +18,7 @@ func (d DexconConfig) MarshalJSON() ([]byte, error) {
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"`
@@ -34,6 +35,7 @@ func (d DexconConfig) MarshalJSON() ([]byte, error) {
enc.GenesisCRSText = d.GenesisCRSText
enc.Owner = d.Owner
enc.MinStake = (*math.HexOrDecimal256)(d.MinStake)
+ enc.LockupPeriod = d.LockupPeriod
enc.BlockReward = (*math.HexOrDecimal256)(d.BlockReward)
enc.BlockGasLimit = d.BlockGasLimit
enc.NumChains = d.NumChains
@@ -54,6 +56,7 @@ func (d *DexconConfig) UnmarshalJSON(input []byte) error {
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"`
@@ -79,6 +82,9 @@ func (d *DexconConfig) UnmarshalJSON(input []byte) error {
if dec.MinStake != nil {
d.MinStake = (*big.Int)(dec.MinStake)
}
+ if dec.LockupPeriod != nil {
+ d.LockupPeriod = *dec.LockupPeriod
+ }
if dec.BlockReward != nil {
d.BlockReward = (*big.Int)(dec.BlockReward)
}