From 7bf5e2205a49a500672ff7415921aa6659c46611 Mon Sep 17 00:00:00 2001 From: Wei-Ning Huang Date: Sun, 13 Jan 2019 00:13:49 +0800 Subject: core: vm: add totalSupply and totalStaked in the governance contract (#144) Add totalSupply and totalStaked in the governance contract for the preperation of DEXON cryptoeconomics 4.0. Also removed the unstaked variable in node info and improve tests for delegate/undeleate. --- core/genesis.go | 6 ++++ core/vm/governance.go | 79 +++++++++++++++++++++++++++++++--------------- core/vm/governance_abi.go | 32 ++++++++++++++++--- core/vm/governance_test.go | 62 ++++++++++++++++++++++++++++++++---- 4 files changed, 144 insertions(+), 35 deletions(-) (limited to 'core') diff --git a/core/genesis.go b/core/genesis.go index 38216361f..0948569f1 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -265,6 +265,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { statedb, _ := state.New(common.Hash{}, state.NewDatabase(db)) govStateHelper := vm.GovernanceStateHelper{StateDB: statedb} + totalSupply := big.NewInt(0) totalStaked := big.NewInt(0) for addr, account := range g.Alloc { @@ -281,6 +282,8 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { for key, value := range account.Storage { statedb.SetState(addr, key, value) } + + totalSupply = new(big.Int).Add(totalSupply, account.Balance) } // For DEXON consensus genesis staking. @@ -316,6 +319,9 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { // Governance configuration. govStateHelper.UpdateConfiguration(g.Config.Dexcon) + + // Set totalSupply. + govStateHelper.IncTotalSupply(totalSupply) } root := statedb.IntermediateRoot(false) diff --git a/core/vm/governance.go b/core/vm/governance.go index b8228944a..bebc26e03 100644 --- a/core/vm/governance.go +++ b/core/vm/governance.go @@ -438,7 +438,7 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by info := g.state.Node(index) res, err := method.Outputs.Pack( info.Owner, info.PublicKey, info.Staked, info.Fined, - info.Name, info.Email, info.Location, info.Url, info.Unstaked) + info.Name, info.Email, info.Location, info.Url) if err != nil { return nil, errExecutionReverted } @@ -497,13 +497,27 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []by return nil, errExecutionReverted } return res, nil + case "totalStaked": + res, err := method.Outputs.Pack(g.state.TotalStaked()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "totalSupply": + res, err := method.Outputs.Pack(g.state.TotalSupply()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil } - return nil, nil + return nil, errExecutionReverted } // Storage position enums. const ( roundHeightLoc = iota + totalSupplyLoc + totalStakedLoc nodesLoc nodesOffsetByAddressLoc nodesOffsetByIDLoc @@ -702,6 +716,28 @@ func (s *GovernanceStateHelper) PushRoundHeight(height *big.Int) { s.setStateBigInt(loc, height) } +// uint256 public totalSupply; +func (s *GovernanceStateHelper) TotalSupply() *big.Int { + return s.getStateBigInt(big.NewInt(totalSupplyLoc)) +} +func (s *GovernanceStateHelper) IncTotalSupply(amount *big.Int) { + s.setStateBigInt(big.NewInt(totalSupplyLoc), new(big.Int).Add(s.TotalSupply(), amount)) +} +func (s *GovernanceStateHelper) DecTotalSupply(amount *big.Int) { + s.setStateBigInt(big.NewInt(totalSupplyLoc), new(big.Int).Sub(s.TotalSupply(), amount)) +} + +// uint256 public totalStaked; +func (s *GovernanceStateHelper) TotalStaked() *big.Int { + return s.getStateBigInt(big.NewInt(totalStakedLoc)) +} +func (s *GovernanceStateHelper) IncTotalStaked(amount *big.Int) { + s.setStateBigInt(big.NewInt(totalStakedLoc), new(big.Int).Add(s.TotalStaked(), amount)) +} +func (s *GovernanceStateHelper) DecTotalStaked(amount *big.Int) { + s.setStateBigInt(big.NewInt(totalStakedLoc), new(big.Int).Sub(s.TotalStaked(), amount)) +} + // struct Node { // address owner; // bytes publicKey; @@ -711,7 +747,6 @@ func (s *GovernanceStateHelper) PushRoundHeight(height *big.Int) { // string email; // string location; // string url; -// bool unstaked; // } // // Node[] nodes; @@ -725,10 +760,9 @@ type nodeInfo struct { Email string Location string Url string - Unstaked bool } -const nodeStructSize = 9 +const nodeStructSize = 8 func (s *GovernanceStateHelper) LenNodes() *big.Int { return s.getStateBigInt(big.NewInt(nodesLoc)) @@ -772,10 +806,6 @@ func (s *GovernanceStateHelper) Node(index *big.Int) *nodeInfo { loc = new(big.Int).Add(elementBaseLoc, big.NewInt(7)) node.Url = string(s.readBytes(loc)) - // Unstaked. - loc = new(big.Int).Add(elementBaseLoc, big.NewInt(8)) - node.Unstaked = s.getStateBigInt(loc).Cmp(big.NewInt(0)) > 0 - return node } func (s *GovernanceStateHelper) PushNode(n *nodeInfo) { @@ -787,7 +817,8 @@ func (s *GovernanceStateHelper) PushNode(n *nodeInfo) { } func (s *GovernanceStateHelper) UpdateNode(index *big.Int, n *nodeInfo) { arrayBaseLoc := s.getSlotLoc(big.NewInt(nodesLoc)) - elementBaseLoc := new(big.Int).Add(arrayBaseLoc, new(big.Int).Mul(index, big.NewInt(nodeStructSize))) + elementBaseLoc := new(big.Int).Add(arrayBaseLoc, + new(big.Int).Mul(index, big.NewInt(nodeStructSize))) // Owner. loc := elementBaseLoc @@ -820,14 +851,6 @@ func (s *GovernanceStateHelper) UpdateNode(index *big.Int, n *nodeInfo) { // Url. loc = new(big.Int).Add(elementBaseLoc, big.NewInt(7)) s.writeBytes(loc, []byte(n.Url)) - - // Unstaked. - loc = new(big.Int).Add(elementBaseLoc, big.NewInt(8)) - val := big.NewInt(0) - if n.Unstaked { - val = big.NewInt(1) - } - s.setStateBigInt(loc, val) } func (s *GovernanceStateHelper) PopLastNode() { // Decrease length by 1. @@ -851,9 +874,6 @@ 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) } @@ -1265,6 +1285,9 @@ func (s *GovernanceStateHelper) Stake( UndelegatedAt: big.NewInt(0), }) s.PutDelegatorOffset(addr, addr, offset) + + // Add to network total staked. + s.IncTotalStaked(staked) } const phiRatioMultiplier = 1000000.0 @@ -1725,6 +1748,9 @@ func (g *GovernanceContract) delegate(nodeAddr common.Address) ([]byte, error) { node.Staked = new(big.Int).Add(node.Staked, g.contract.Value()) g.state.UpdateNode(offset, node) + // Add to network total staked. + g.state.IncTotalStaked(g.contract.Value()) + // Push delegator record. offset = g.state.LenDelegators(nodeAddr) g.state.PushDelegator(nodeAddr, &delegatorInfo{ @@ -1810,6 +1836,10 @@ func (g *GovernanceContract) undelegateHelper(nodeAddr, caller common.Address) ( delegator := g.state.Delegator(nodeAddr, offset) + if delegator.UndelegatedAt.Cmp(big.NewInt(0)) != 0 { + return nil, errExecutionReverted + } + // Set undelegate time. delegator.UndelegatedAt = g.evm.Time g.state.UpdateDelegator(nodeAddr, offset, delegator) @@ -1818,6 +1848,9 @@ func (g *GovernanceContract) undelegateHelper(nodeAddr, caller common.Address) ( node.Staked = new(big.Int).Sub(node.Staked, delegator.Value) g.state.UpdateNode(nodeOffset, node) + // Subtract to network total staked. + g.state.DecTotalStaked(delegator.Value) + g.state.emitUndelegated(nodeAddr, caller) return g.useGas(100000) @@ -1914,10 +1947,6 @@ func (g *GovernanceContract) unstake() ([]byte, error) { i = i.Sub(i, big.NewInt(1)) } - // Mark node as unstaked. - node.Unstaked = true - g.state.UpdateNode(offset, node) - g.state.emitUnstaked(caller) return g.useGas(100000) diff --git a/core/vm/governance_abi.go b/core/vm/governance_abi.go index 74ccad0b7..1b700ac32 100644 --- a/core/vm/governance_abi.go +++ b/core/vm/governance_abi.go @@ -110,6 +110,20 @@ const GovernanceABIJSON = ` "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -151,10 +165,6 @@ const GovernanceABIJSON = ` { "name": "url", "type": "string" - }, - { - "name": "unstaked", - "type": "bool" } ], "payable": false, @@ -328,6 +338,20 @@ const GovernanceABIJSON = ` "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "totalStaked", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [], diff --git a/core/vm/governance_test.go b/core/vm/governance_test.go index 4c5236565..8566d6537 100644 --- a/core/vm/governance_test.go +++ b/core/vm/governance_test.go @@ -205,6 +205,7 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() { g.Require().Equal(1, int(g.s.LenNodes().Uint64())) g.Require().Equal(1, len(g.s.QualifiedNodes())) g.Require().Equal("Test1", g.s.Node(big.NewInt(0)).Name) + g.Require().Equal(amount.String(), g.s.TotalStaked().String()) // Check balance. g.Require().Equal(new(big.Int).Sub(balanceBeforeStake, amount), g.stateDB.GetBalance(addr)) @@ -219,9 +220,14 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() { g.Require().NoError(err) _, err = g.call(addr, input, big.NewInt(0)) g.Require().NoError(err) + g.Require().Equal(0, len(g.s.QualifiedNodes())) g.Require().Equal(1, int(g.s.LenDelegators(addr).Uint64())) g.Require().Equal(1, int(g.s.LenNodes().Uint64())) + node := g.s.Node(big.NewInt(0)) + g.Require().Equal(big.NewInt(0).String(), node.Staked.String()) + g.Require().Equal(big.NewInt(0).String(), g.s.TotalStaked().String()) + // Wait for lockup time than withdraw. time.Sleep(time.Second * 2) input, err = abiObject.Pack("withdraw", addr) @@ -229,6 +235,7 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() { _, err = g.call(addr, input, big.NewInt(0)) g.Require().NoError(err) + g.Require().Equal(0, len(g.s.QualifiedNodes())) g.Require().Equal(0, int(g.s.LenDelegators(addr).Uint64())) g.Require().Equal(0, int(g.s.LenNodes().Uint64())) @@ -251,16 +258,18 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() { g.Require().NoError(err) g.Require().Equal(2, len(g.s.QualifiedNodes())) + g.Require().Equal(new(big.Int).Mul(amount, big.NewInt(2)).String(), g.s.TotalStaked().String()) // 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)) + node = g.s.Node(big.NewInt(0)) g.Require().Equal("Test2", node.Name) - g.Require().Equal(true, node.Unstaked) + g.Require().Equal(big.NewInt(0).String(), node.Staked.String()) g.Require().Equal(1, len(g.s.QualifiedNodes())) + g.Require().Equal(amount.String(), g.s.TotalStaked().String()) time.Sleep(time.Second * 2) input, err = abiObject.Pack("withdraw", addr2) @@ -279,6 +288,8 @@ func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() { _, err = g.call(addr, input, big.NewInt(0)) g.Require().NoError(err) g.Require().Equal(0, len(g.s.QualifiedNodes())) + g.Require().Equal(big.NewInt(0).String(), g.s.TotalStaked().String()) + time.Sleep(time.Second * 2) input, err = abiObject.Pack("withdraw", addr) g.Require().NoError(err) @@ -322,6 +333,7 @@ func (g *GovernanceContractTestSuite) TestDelegateUndelegate() { g.Require().Equal(new(big.Int).Sub(balanceBeforeDelegate, amount), g.stateDB.GetBalance(addrDelegator)) g.Require().Equal(addrDelegator, g.s.Delegator(addr, big.NewInt(1)).Owner) g.Require().Equal(new(big.Int).Add(amount, ownerStaked), g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(g.s.Node(big.NewInt(0)).Staked.String(), g.s.TotalStaked().String()) g.Require().Equal(1, int(g.s.DelegatorsOffset(addr, addrDelegator).Int64())) // Same person delegate the 2nd time should fail. @@ -339,6 +351,7 @@ func (g *GovernanceContractTestSuite) TestDelegateUndelegate() { g.Require().Equal(addrDelegator2, g.s.Delegator(addr, big.NewInt(2)).Owner) g.Require().Equal(new(big.Int).Add(ownerStaked, new(big.Int).Mul(amount, big.NewInt(2))), g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(g.s.Node(big.NewInt(0)).Staked.String(), g.s.TotalStaked().String()) g.Require().Equal(2, int(g.s.DelegatorsOffset(addr, addrDelegator2).Int64())) // Qualified. @@ -349,8 +362,14 @@ func (g *GovernanceContractTestSuite) TestDelegateUndelegate() { input, err = abiObject.Pack("undelegate", addr) g.Require().NoError(err) _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().Equal(new(big.Int).Add(amount, ownerStaked), g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(g.s.Node(big.NewInt(0)).Staked.String(), g.s.TotalStaked().String()) g.Require().NoError(err) + // Undelegate the second time should fail. + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().Error(err) + // Withdraw within lockup time should fail. input, err = abiObject.Pack("withdraw", addr) g.Require().NoError(err) @@ -377,7 +396,7 @@ func (g *GovernanceContractTestSuite) TestDelegateUndelegate() { input, err = abiObject.Pack("withdraw", addr) g.Require().NoError(err) _, err = g.call(addrDelegator, input, big.NewInt(0)) - g.Require().NotNil(err) + g.Require().Error(err) // Undelegate addrDelegator2. balanceBeforeUnDelegate = g.stateDB.GetBalance(addrDelegator2) @@ -386,6 +405,9 @@ func (g *GovernanceContractTestSuite) TestDelegateUndelegate() { _, err = g.call(addrDelegator2, input, big.NewInt(0)) g.Require().NoError(err) + g.Require().Equal(ownerStaked, g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(g.s.Node(big.NewInt(0)).Staked.String(), g.s.TotalStaked().String()) + // Wait for lockup time than withdraw. time.Sleep(time.Second * 2) input, err = abiObject.Pack("withdraw", addr) @@ -399,6 +421,25 @@ func (g *GovernanceContractTestSuite) TestDelegateUndelegate() { // Unqualified g.Require().Equal(0, len(g.s.QualifiedNodes())) + + // Owner undelegate itself. + g.Require().Equal(1, int(g.s.LenNodes().Uint64())) + g.Require().Equal(1, int(g.s.LenDelegators(addr).Uint64())) + + input, err = abiObject.Pack("undelegate", addr) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + g.Require().Equal(big.NewInt(0).String(), g.s.Node(big.NewInt(0)).Staked.String()) + g.Require().Equal(big.NewInt(0).String(), g.s.TotalStaked().String()) + + time.Sleep(time.Second * 2) + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + + g.Require().Equal(0, int(g.s.LenNodes().Uint64())) + g.Require().Equal(0, int(g.s.LenDelegators(addr).Uint64())) } func (g *GovernanceContractTestSuite) TestFine() { @@ -485,7 +526,6 @@ func (g *GovernanceContractTestSuite) TestFine() { g.Require().NoError(err) _, err = g.call(addrDelegator, input, big.NewInt(0)) g.Require().NoError(err) - } func (g *GovernanceContractTestSuite) TestUnstakeWithExtraDelegators() { @@ -893,9 +933,19 @@ func (g *GovernanceContractTestSuite) TestMiscVariableReading() { privKey, addr := g.newPrefundAccount() pk := crypto.FromECDSAPub(&privKey.PublicKey) + input, err := abiObject.Pack("totalSupply") + g.Require().NoError(err) + res, err := g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + input, err = abiObject.Pack("totalStaked") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + // Stake. amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e4)) - input, err := abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + 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) @@ -917,7 +967,7 @@ func (g *GovernanceContractTestSuite) TestMiscVariableReading() { input, err = abiObject.Pack("nodes", big.NewInt(0)) g.Require().NoError(err) - res, err := g.call(addr, input, big.NewInt(0)) + res, err = g.call(addr, input, big.NewInt(0)) g.Require().NoError(err) input, err = abiObject.Pack("nodesLength") -- cgit v1.2.3