diff options
-rw-r--r-- | core/governance.go | 4 | ||||
-rw-r--r-- | core/tx_pool.go | 44 | ||||
-rw-r--r-- | core/tx_pool_test.go | 4 | ||||
-rw-r--r-- | core/vm/oracle_contract_abi.go | 18 | ||||
-rw-r--r-- | core/vm/oracle_contracts.go | 16 | ||||
-rw-r--r-- | core/vm/oracle_contracts_test.go | 12 | ||||
-rw-r--r-- | dex/api_backend.go | 2 | ||||
-rw-r--r-- | dex/app.go | 16 | ||||
-rw-r--r-- | dex/app_test.go | 37 | ||||
-rw-r--r-- | params/config.go | 12 | ||||
-rw-r--r-- | params/gen_dexcon_config.go | 6 | ||||
-rw-r--r-- | test/genesis.json | 3 |
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 957f4d886..60a874701 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 174d19af3..cad58c33f 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", |