From 5832a21db625bfa98a3b7909bb6996dd1fd2e4d3 Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Mon, 25 Feb 2019 18:27:34 +0800 Subject: core: Fixed gas price (#205) * core/vm: update abi * core/vm: add MinGasPrice to gov * params: Add MinGasPrice to Config * dex: SuggestPrice from Governance * test: add minGasPrice to genesis.json * core: check underpriced tx * dex: verify with gas price --- core/governance.go | 4 ++++ core/tx_pool.go | 44 +++++++++++++++++++++++++++++++++++++++- core/tx_pool_test.go | 4 ++++ core/vm/oracle_contract_abi.go | 18 ++++++++++++++++ core/vm/oracle_contracts.go | 16 +++++++++++++++ core/vm/oracle_contracts_test.go | 12 ++++++++++- 6 files changed, 96 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/governance.go b/core/governance.go index 12124760e..9929867c9 100644 --- a/core/governance.go +++ b/core/governance.go @@ -134,3 +134,7 @@ func (g *Governance) IsDKGFinal(round uint64) bool { count := headHelper.DKGFinalizedsCount(big.NewInt(int64(round))).Uint64() return count >= threshold } + +func (g *Governance) MinGasPrice(round uint64) *big.Int { + return g.GetGovStateHelperAtRound(round).MinGasPrice() +} diff --git a/core/tx_pool.go b/core/tx_pool.go index 871b50be1..0a1b3f132 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -24,10 +24,13 @@ import ( "sync" "time" + dexCore "github.com/dexon-foundation/dexon-consensus/core" + "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/common/prque" "github.com/dexon-foundation/dexon/core/state" "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm" "github.com/dexon-foundation/dexon/event" "github.com/dexon-foundation/dexon/log" "github.com/dexon-foundation/dexon/metrics" @@ -115,6 +118,7 @@ const ( type blockChain interface { CurrentBlock() *types.Block GetBlock(hash common.Hash, number uint64) *types.Block + GetBlockByNumber(number uint64) *types.Block StateAt(root common.Hash) (*state.StateDB, error) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription @@ -206,6 +210,7 @@ type TxPool struct { chainconfig *params.ChainConfig chain blockChain gasPrice *big.Int + govGasPrice *big.Int txFeed event.Feed scope event.SubscriptionScope chainHeadCh chan ChainHeadEvent @@ -385,6 +390,28 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { pool.currentState = statedb pool.pendingState = state.ManageState(statedb) pool.currentMaxGas = newHead.GasLimit + if oldHead == nil || oldHead.Round != newHead.Round { + round := newHead.Round + if round < dexCore.ConfigRoundShift { + round = 0 + } else { + round -= dexCore.ConfigRoundShift + } + state := &vm.GovernanceStateHelper{StateDB: statedb} + height := state.RoundHeight(new(big.Int).SetUint64((round))).Uint64() + block := pool.chain.GetBlockByNumber(height) + if block == nil { + log.Error("Failed to get block", "round", round, "height", height) + panic("cannot get config for new round's min gas price") + } + configState, err := pool.chain.StateAt(block.Header().Root) + if err != nil { + log.Error("Failed to get txpool state for min gas price", "err", err) + panic("cannot get state for new round's min gas price") + } + govState := &vm.GovernanceStateHelper{StateDB: configState} + pool.setGovPrice(govState.MinGasPrice()) + } // validate the pool of pending transactions, this will remove // any transactions that have been included in the block or @@ -438,10 +465,21 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { defer pool.mu.Unlock() pool.gasPrice = price + pool.removeUnderpricedTx(price) + log.Info("Transaction pool price threshold updated", "price", price) +} + +// setGovPrice updates the minimum price required by the transaction pool for a +// new transaction, and drops all transactions below this threshold. +func (pool *TxPool) setGovPrice(price *big.Int) { + pool.govGasPrice = price + pool.removeUnderpricedTx(price) +} + +func (pool *TxPool) removeUnderpricedTx(price *big.Int) { for _, tx := range pool.priced.Cap(price, pool.locals) { pool.removeTx(tx.Hash(), false) } - log.Info("Transaction pool price threshold updated", "price", price) } // State returns the virtual managed state of the transaction pool. @@ -551,6 +589,10 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if err != nil { return ErrInvalidSender } + // Drop all transactions under governance minimum gas price. + if pool.govGasPrice.Cmp(tx.GasPrice()) > 0 { + return ErrUnderpriced + } // Drop non-local transactions under our own minimal accepted gas price local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 { diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 96151850d..70d4a351a 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -61,6 +61,10 @@ func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block return bc.CurrentBlock() } +func (bc *testBlockChain) GetBlockByNumber(uint64) *types.Block { + return bc.CurrentBlock() +} + func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { return bc.statedb, nil } diff --git a/core/vm/oracle_contract_abi.go b/core/vm/oracle_contract_abi.go index fde973203..ff4a94b29 100644 --- a/core/vm/oracle_contract_abi.go +++ b/core/vm/oracle_contract_abi.go @@ -563,6 +563,20 @@ const GovernanceABIJSON = ` "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "minGasPrice", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [], @@ -855,6 +869,10 @@ const GovernanceABIJSON = ` { "name": "FineValues", "type": "uint256[]" + }, + { + "name": "MinGasPrice", + "type": "uint256" } ], "name": "updateConfiguration", diff --git a/core/vm/oracle_contracts.go b/core/vm/oracle_contracts.go index 1eeb57a5a..826e0396c 100644 --- a/core/vm/oracle_contracts.go +++ b/core/vm/oracle_contracts.go @@ -83,6 +83,7 @@ const ( fineValuesLoc finedRecordsLoc dkgResetCountLoc + minGasPriceLoc ) func publicKeyToNodeID(pkBytes []byte) (Bytes32, error) { @@ -894,6 +895,11 @@ func (s *GovernanceStateHelper) IncDKGResetCount(round *big.Int) { s.setStateBigInt(loc, new(big.Int).Add(count, big.NewInt(1))) } +// uint256 public minGasPrice; +func (s *GovernanceStateHelper) MinGasPrice() *big.Int { + return s.getStateBigInt(big.NewInt(minGasPriceLoc)) +} + // Stake is a helper function for creating genesis state. func (s *GovernanceStateHelper) Stake( addr common.Address, publicKey []byte, staked *big.Int, @@ -948,6 +954,7 @@ func (s *GovernanceStateHelper) Configuration() *params.DexconConfig { RoundLength: s.getStateBigInt(big.NewInt(roundLengthLoc)).Uint64(), MinBlockInterval: s.getStateBigInt(big.NewInt(minBlockIntervalLoc)).Uint64(), FineValues: s.FineValues(), + MinGasPrice: s.getStateBigInt(big.NewInt(minGasPriceLoc)), } } @@ -966,6 +973,7 @@ func (s *GovernanceStateHelper) UpdateConfiguration(cfg *params.DexconConfig) { s.setStateBigInt(big.NewInt(roundLengthLoc), big.NewInt(int64(cfg.RoundLength))) s.setStateBigInt(big.NewInt(minBlockIntervalLoc), big.NewInt(int64(cfg.MinBlockInterval))) s.SetFineValues(cfg.FineValues) + s.setStateBigInt(big.NewInt(minGasPriceLoc), cfg.MinGasPrice) } type rawConfigStruct struct { @@ -979,6 +987,7 @@ type rawConfigStruct struct { RoundLength *big.Int MinBlockInterval *big.Int FineValues []*big.Int + MinGasPrice *big.Int } // UpdateConfigurationRaw updates system configuration. @@ -993,6 +1002,7 @@ func (s *GovernanceStateHelper) UpdateConfigurationRaw(cfg *rawConfigStruct) { s.setStateBigInt(big.NewInt(roundLengthLoc), cfg.RoundLength) s.setStateBigInt(big.NewInt(minBlockIntervalLoc), cfg.MinBlockInterval) s.SetFineValues(cfg.FineValues) + s.setStateBigInt(big.NewInt(minGasPriceLoc), cfg.MinGasPrice) } // event ConfigurationChanged(); @@ -2227,6 +2237,12 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re return nil, errExecutionReverted } return res, nil + case "minGasPrice": + res, err := method.Outputs.Pack(g.state.MinGasPrice()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil case "miningVelocity": res, err := method.Outputs.Pack(g.state.MiningVelocity()) if err != nil { diff --git a/core/vm/oracle_contracts_test.go b/core/vm/oracle_contracts_test.go index dd17dddea..4980d4b77 100644 --- a/core/vm/oracle_contracts_test.go +++ b/core/vm/oracle_contracts_test.go @@ -654,7 +654,8 @@ func (g *OracleContractsTestSuite) TestUpdateConfiguration() { big.NewInt(4), big.NewInt(600), big.NewInt(900), - []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)}) + []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)}, + big.NewInt(2e9)) g.Require().NoError(err) // Call with non-owner. @@ -770,6 +771,15 @@ func (g *OracleContractsTestSuite) TestConfigurationReading() { err = GovernanceABI.ABI.Unpack(&value, "minBlockInterval", res) g.Require().NoError(err) g.Require().Equal(g.config.MinBlockInterval, value.Uint64()) + + // MinGasPrice. + input, err = GovernanceABI.ABI.Pack("minGasPrice") + g.Require().NoError(err) + res, err = g.call(GovernanceContractAddress, addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = GovernanceABI.ABI.Unpack(&value, "minGasPrice", res) + g.Require().NoError(err) + g.Require().Equal(g.config.MinGasPrice, value) } func (g *OracleContractsTestSuite) TestReportForkVote() { -- cgit v1.2.3