aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2019-02-25 18:27:34 +0800
committerWei-Ning Huang <w@dexon.org>2019-03-12 12:19:09 +0800
commitd779f5aead9e91b97c8d05136ddb7a6bd84f4276 (patch)
treea0f497e1109cc1ad89183f97aee193938bf5306e
parentd5e4399839e04f5d85dd3a06fb14d6ad117a79f3 (diff)
downloaddexon-d779f5aead9e91b97c8d05136ddb7a6bd84f4276.tar
dexon-d779f5aead9e91b97c8d05136ddb7a6bd84f4276.tar.gz
dexon-d779f5aead9e91b97c8d05136ddb7a6bd84f4276.tar.bz2
dexon-d779f5aead9e91b97c8d05136ddb7a6bd84f4276.tar.lz
dexon-d779f5aead9e91b97c8d05136ddb7a6bd84f4276.tar.xz
dexon-d779f5aead9e91b97c8d05136ddb7a6bd84f4276.tar.zst
dexon-d779f5aead9e91b97c8d05136ddb7a6bd84f4276.zip
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
-rw-r--r--core/governance.go4
-rw-r--r--core/tx_pool.go44
-rw-r--r--core/tx_pool_test.go4
-rw-r--r--core/vm/oracle_contract_abi.go18
-rw-r--r--core/vm/oracle_contracts.go16
-rw-r--r--core/vm/oracle_contracts_test.go12
-rw-r--r--dex/api_backend.go2
-rw-r--r--dex/app.go16
-rw-r--r--dex/app_test.go37
-rw-r--r--params/config.go12
-rw-r--r--params/gen_dexcon_config.go6
-rw-r--r--test/genesis.json3
12 files changed, 161 insertions, 13 deletions
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
@@ -566,6 +566,20 @@ const GovernanceABIJSON = `
{
"constant": true,
"inputs": [],
+ "name": "minGasPrice",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
"name": "lockupPeriod",
"outputs": [
{
@@ -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() {
diff --git a/dex/api_backend.go b/dex/api_backend.go
index 719625946..d423b41f7 100644
--- a/dex/api_backend.go
+++ b/dex/api_backend.go
@@ -195,7 +195,7 @@ func (b *DexAPIBackend) ProtocolVersion() int {
}
func (b *DexAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
- return b.gpo.SuggestPrice(ctx)
+ return b.dex.governance.MinGasPrice(b.dex.blockchain.CurrentBlock().Round()), nil
}
func (b *DexAPIBackend) ChainDb() ethdb.Database {
diff --git a/dex/app.go b/dex/app.go
index d2bd02f0c..ab4e80058 100644
--- a/dex/app.go
+++ b/dex/app.go
@@ -124,6 +124,18 @@ func (d *DexconApp) validateNonce(txs types.Transactions) (map[common.Address]ui
return addressFirstNonce, nil
}
+// validateGasPrice checks if no gas price is lower than minGasPrice defined in
+// governance contract.
+func (d *DexconApp) validateGasPrice(txs types.Transactions, round uint64) bool {
+ minGasPrice := d.gov.MinGasPrice(round)
+ for _, tx := range txs {
+ if minGasPrice.Cmp(tx.GasPrice()) > 0 {
+ return false
+ }
+ }
+ return true
+}
+
// PreparePayload is called when consensus core is preparing payload for block.
func (d *DexconApp) PreparePayload(position coreTypes.Position) (payload []byte, err error) {
// softLimit limits the runtime of inner call to preparePayload.
@@ -410,6 +422,10 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta
log.Error("Validate nonce failed", "error", err)
return coreTypes.VerifyInvalidBlock
}
+ if !d.validateGasPrice(transactions, block.Position.Round) {
+ log.Error("Validate gas price failed")
+ return coreTypes.VerifyInvalidBlock
+ }
// Check if nonce is strictly increasing for every address.
chainID := big.NewInt(int64(block.Position.ChainID))
diff --git a/dex/app_test.go b/dex/app_test.go
index 09c61c11c..73d952f31 100644
--- a/dex/app_test.go
+++ b/dex/app_test.go
@@ -234,7 +234,7 @@ func TestVerifyBlock(t *testing.T) {
common.BytesToAddress([]byte{9}),
big.NewInt(50000000000000001),
params.TxGas,
- big.NewInt(1),
+ big.NewInt(10),
nil)
tx, err = types.SignTx(tx, signer, key)
if err != nil {
@@ -269,7 +269,7 @@ func TestVerifyBlock(t *testing.T) {
common.BytesToAddress([]byte{9}),
big.NewInt(1),
params.TxGas,
- big.NewInt(1),
+ big.NewInt(10),
make([]byte, 1))
tx, err = types.SignTx(tx, signer, key)
if err != nil {
@@ -304,7 +304,7 @@ func TestVerifyBlock(t *testing.T) {
common.BytesToAddress([]byte{9}),
big.NewInt(1),
params.TxGas,
- big.NewInt(1),
+ big.NewInt(10),
make([]byte, 1))
tx1, err = types.SignTx(tx, signer, key)
if err != nil {
@@ -317,7 +317,7 @@ func TestVerifyBlock(t *testing.T) {
common.BytesToAddress([]byte{9}),
big.NewInt(1),
params.TxGas,
- big.NewInt(1),
+ big.NewInt(10),
make([]byte, 1))
tx2, err = types.SignTx(tx, signer, key)
if err != nil {
@@ -334,6 +334,30 @@ func TestVerifyBlock(t *testing.T) {
if status != coreTypes.VerifyInvalidBlock {
t.Fatalf("verify fail expect invalid block but get %v", status)
}
+
+ // Invalid gas price.
+ tx = types.NewTransaction(
+ 0,
+ common.BytesToAddress([]byte{9}),
+ big.NewInt(1),
+ params.TxGas,
+ big.NewInt(5),
+ make([]byte, 1))
+ tx, err = types.SignTx(tx, signer, key)
+ if err != nil {
+ return
+ }
+ block.Payload, err = rlp.EncodeToBytes(types.Transactions{tx})
+ if err != nil {
+ return
+ }
+
+ // Expect invalid block.
+ status = dex.app.VerifyBlock(block)
+ if status != coreTypes.VerifyInvalidBlock {
+ t.Fatalf("verify fail expect invalid block but get %v", status)
+ }
+
}
func TestBlockConfirmed(t *testing.T) {
@@ -489,6 +513,7 @@ func newTestDexonWithGenesis(allocKey *ecdsa.PrivateKey) (*Dexon, error) {
PublicKey: crypto.FromECDSAPub(&key.PublicKey),
},
}
+ genesis.Config.Dexcon.MinGasPrice = big.NewInt(10)
chainConfig, _, err := core.SetupGenesisBlock(db, genesis)
if err != nil {
return nil, err
@@ -530,7 +555,7 @@ func addTx(dex *Dexon, nonce int, signer types.Signer, key *ecdsa.PrivateKey) (
common.BytesToAddress([]byte{9}),
big.NewInt(int64(nonce*1)),
params.TxGas,
- big.NewInt(1),
+ big.NewInt(10),
nil)
tx, err := types.SignTx(tx, signer, key)
if err != nil {
@@ -638,7 +663,7 @@ func prepareDataWithoutTxPool(dex *Dexon, key *ecdsa.PrivateKey, startNonce, txN
common.BytesToAddress([]byte{9}),
big.NewInt(int64(n*1)),
params.TxGas,
- big.NewInt(1),
+ big.NewInt(10),
nil)
tx, err = types.SignTx(tx, signer, key)
if err != nil {
diff --git a/params/config.go b/params/config.go
index 839cdb57a..1bb7b0434 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("0x6c59132f64eae33054c0390e4d8f8ea5f0df1642b3a084c94388c44fe5eff70d")
- TestnetGenesisHash = common.HexToHash("0x31847855ec3c1ba9a03ac3311f283775a70d7b0422b525c335aa094e5b81c902")
+ MainnetGenesisHash = common.HexToHash("0xf80aae99a7c44bc54d7b0cc8a16645fa3ea65c01b180339f17b31a698b031271")
+ TestnetGenesisHash = common.HexToHash("0x7f704c8d0a773d0fcca231d40bf39495553886bf8800b4a06920786a802103e1")
)
var (
@@ -65,6 +65,7 @@ var (
new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)),
new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)),
},
+ MinGasPrice: new(big.Int).Mul(big.NewInt(1e9), big.NewInt(1)),
},
}
@@ -110,6 +111,7 @@ var (
new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)),
new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)),
},
+ MinGasPrice: new(big.Int).Mul(big.NewInt(1e9), big.NewInt(1)),
},
}
@@ -146,6 +148,7 @@ var (
new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e4)),
new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)),
},
+ MinGasPrice: new(big.Int).Mul(big.NewInt(1e9), big.NewInt(1)),
},
}
@@ -292,6 +295,7 @@ type DexconConfig struct {
RoundLength uint64 `json:"roundLength"`
MinBlockInterval uint64 `json:"minBlockInterval"`
FineValues []*big.Int `json:"fineValues"`
+ MinGasPrice *big.Int `json:"minGasPrice"`
}
type dexconConfigSpecMarshaling struct {
@@ -299,11 +303,12 @@ type dexconConfigSpecMarshaling struct {
NextHalvingSupply *math.HexOrDecimal256
LastHalvedAmount *math.HexOrDecimal256
FineValues []*math.HexOrDecimal256
+ MinGasPrice *math.HexOrDecimal256
}
// String implements the stringer interface, returning the consensus engine details.
func (d *DexconConfig) String() string {
- return fmt.Sprintf("{GenesisCRSText: %v Owner: %v MinStake: %v LockupPeriod: %v MiningVelocity: %v NextHalvingSupply: %v LastHalvedAmount: %v BlockGasLimit: %v LambdaBA: %v LambdaDKG: %v NotarySetSize: %v DKGSetSize: %v RoundLength: %v MinBlockInterval: %v FineValues: %v}",
+ return fmt.Sprintf("{GenesisCRSText: %v Owner: %v MinStake: %v LockupPeriod: %v MiningVelocity: %v NextHalvingSupply: %v LastHalvedAmount: %v BlockGasLimit: %v LambdaBA: %v LambdaDKG: %v NotarySetSize: %v DKGSetSize: %v RoundLength: %v MinBlockInterval: %v FineValues: %v MinGasPrice: %v}",
d.GenesisCRSText,
d.Owner,
d.MinStake,
@@ -319,6 +324,7 @@ func (d *DexconConfig) String() string {
d.RoundLength,
d.MinBlockInterval,
d.FineValues,
+ d.MinGasPrice,
)
}
diff --git a/params/gen_dexcon_config.go b/params/gen_dexcon_config.go
index 566ed9fcd..1afe11f56 100644
--- a/params/gen_dexcon_config.go
+++ b/params/gen_dexcon_config.go
@@ -30,6 +30,7 @@ func (d DexconConfig) MarshalJSON() ([]byte, error) {
RoundLength uint64 `json:"roundLength"`
MinBlockInterval uint64 `json:"minBlockInterval"`
FineValues []*math.HexOrDecimal256 `json:"fineValues"`
+ MinGasPrice *math.HexOrDecimal256 `json:"minGasPrice"`
}
var enc DexconConfig
enc.GenesisCRSText = d.GenesisCRSText
@@ -52,6 +53,7 @@ func (d DexconConfig) MarshalJSON() ([]byte, error) {
enc.FineValues[k] = (*math.HexOrDecimal256)(v)
}
}
+ enc.MinGasPrice = (*math.HexOrDecimal256)(d.MinGasPrice)
return json.Marshal(&enc)
}
@@ -73,6 +75,7 @@ func (d *DexconConfig) UnmarshalJSON(input []byte) error {
RoundLength *uint64 `json:"roundLength"`
MinBlockInterval *uint64 `json:"minBlockInterval"`
FineValues []*math.HexOrDecimal256 `json:"fineValues"`
+ MinGasPrice *math.HexOrDecimal256 `json:"minGasPrice"`
}
var dec DexconConfig
if err := json.Unmarshal(input, &dec); err != nil {
@@ -126,5 +129,8 @@ func (d *DexconConfig) UnmarshalJSON(input []byte) error {
d.FineValues[k] = (*big.Int)(v)
}
}
+ if dec.MinGasPrice != nil {
+ d.MinGasPrice = (*big.Int)(dec.MinGasPrice)
+ }
return nil
}
diff --git a/test/genesis.json b/test/genesis.json
index 96ad4924c..9bbb97de3 100644
--- a/test/genesis.json
+++ b/test/genesis.json
@@ -30,7 +30,8 @@
"0x21e19e0c9bab2400000",
"0x21e19e0c9bab2400000",
"0x152d02c7e14af6800000"
- ]
+ ],
+ "minGasPrice": "0x3b9aca00"
}
},
"nonce": "0x42",