From a1be735b1cd6d02bc69b9bb011c56e62cec290f5 Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Thu, 18 Apr 2019 14:14:54 +0800 Subject: core: vm: add withdrawable (#373) --- core/vm/oracle_contract_abi.go | 14 ++++++++++++ core/vm/oracle_contracts.go | 48 +++++++++++++++++++++++++++------------- core/vm/oracle_contracts_test.go | 19 ++++++++++++++++ 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/core/vm/oracle_contract_abi.go b/core/vm/oracle_contract_abi.go index dc44552a2..35d0f16d1 100644 --- a/core/vm/oracle_contract_abi.go +++ b/core/vm/oracle_contract_abi.go @@ -1135,6 +1135,20 @@ const GovernanceABIJSON = ` "stateMutability": "nonpayable", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "withdrawable", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": false, "inputs": [ diff --git a/core/vm/oracle_contracts.go b/core/vm/oracle_contracts.go index b47f99ee0..899128493 100644 --- a/core/vm/oracle_contracts.go +++ b/core/vm/oracle_contracts.go @@ -1827,6 +1827,9 @@ func (g *GovernanceContract) unstake(amount *big.Int) ([]byte, error) { } func (g *GovernanceContract) withdraw() ([]byte, error) { + if !g.withdrawable() { + return nil, errExecutionReverted + } caller := g.contract.Caller() offset := g.state.NodesOffsetByAddress(caller) @@ -1836,21 +1839,6 @@ func (g *GovernanceContract) withdraw() ([]byte, error) { node := g.state.Node(offset) - // Can not withdraw if there are unpaied fine. - if node.Fined.Cmp(big.NewInt(0)) > 0 { - return nil, errExecutionReverted - } - - // Can not withdraw if there are no pending withdrawal. - if node.Unstaked.Cmp(big.NewInt(0)) == 0 { - return nil, errExecutionReverted - } - - unlockTime := new(big.Int).Add(node.UnstakedAt, g.state.LockupPeriod()) - if g.evm.Time.Cmp(unlockTime) <= 0 { - return nil, errExecutionReverted - } - amount := node.Unstaked node.Unstaked = big.NewInt(0) node.UnstakedAt = big.NewInt(0) @@ -1880,6 +1868,30 @@ func (g *GovernanceContract) withdraw() ([]byte, error) { return g.useGas(GovernanceActionGasCost) } +func (g *GovernanceContract) withdrawable() bool { + caller := g.contract.Caller() + + offset := g.state.NodesOffsetByAddress(caller) + if offset.Cmp(big.NewInt(0)) < 0 { + return false + } + + node := g.state.Node(offset) + + // Can not withdraw if there are unpaied fine. + if node.Fined.Cmp(big.NewInt(0)) > 0 { + return false + } + + // Can not withdraw if there are no pending withdrawal. + if node.Unstaked.Cmp(big.NewInt(0)) == 0 { + return false + } + + unlockTime := new(big.Int).Add(node.UnstakedAt, g.state.LockupPeriod()) + return g.evm.Time.Cmp(unlockTime) > 0 +} + func (g *GovernanceContract) payFine(nodeAddr common.Address) ([]byte, error) { nodeOffset := g.state.NodesOffsetByAddress(nodeAddr) if nodeOffset.Cmp(big.NewInt(0)) < 0 { @@ -2290,6 +2302,12 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re return g.updateConfiguration(&cfg) case "withdraw": return g.withdraw() + case "withdrawable": + res, err := method.Outputs.Pack(g.withdrawable()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil // -------------------------------- // Solidity auto generated methods. diff --git a/core/vm/oracle_contracts_test.go b/core/vm/oracle_contracts_test.go index 7c935913a..a7680b69e 100644 --- a/core/vm/oracle_contracts_test.go +++ b/core/vm/oracle_contracts_test.go @@ -411,13 +411,26 @@ func (g *OracleContractsTestSuite) TestStakingMechanism() { g.Require().Equal(0, len(g.s.QualifiedNodes())) g.Require().Equal(amount.String(), g.s.TotalStaked().String()) + var ok bool // Withdraw immediately should fail. + input, err = GovernanceABI.ABI.Pack("withdrawable") + g.Require().NoError(err) + output, err := g.call(GovernanceContractAddress, addr, input, big.NewInt(0)) + g.Require().NoError(err) + GovernanceABI.ABI.Unpack(&ok, "withdrawable", output) + g.Require().False(ok) input, err = GovernanceABI.ABI.Pack("withdraw") g.Require().NoError(err) _, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0)) g.Require().Error(err) // Wait for lockup time than withdraw. + input, err = GovernanceABI.ABI.Pack("withdrawable") + g.Require().NoError(err) + output, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0)) + g.Require().NoError(err) + GovernanceABI.ABI.Unpack(&ok, "withdrawable", output) + g.Require().False(ok) time.Sleep(time.Second * 2) input, err = GovernanceABI.ABI.Pack("withdraw") g.Require().NoError(err) @@ -434,6 +447,12 @@ func (g *OracleContractsTestSuite) TestStakingMechanism() { g.Require().NoError(err) time.Sleep(time.Second * 2) + input, err = GovernanceABI.ABI.Pack("withdrawable") + g.Require().NoError(err) + output, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0)) + g.Require().NoError(err) + GovernanceABI.ABI.Unpack(&ok, "withdrawable", output) + g.Require().True(ok) input, err = GovernanceABI.ABI.Pack("withdraw") g.Require().NoError(err) _, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0)) -- cgit v1.2.3