From c4136340dc61adf17b276573e6706abba1a30649 Mon Sep 17 00:00:00 2001 From: CoderZhi Date: Fri, 19 Jan 2024 20:02:06 +0200 Subject: [PATCH] [gasstation] Update gas station logic (#4035) --- gasstation/gasstattion.go | 22 +++++-- gasstation/gasstattion_test.go | 112 ++++++++++++++++++++++++++++++--- 2 files changed, 121 insertions(+), 13 deletions(-) diff --git a/gasstation/gasstattion.go b/gasstation/gasstattion.go index 32c398e20a..ffc510e33f 100644 --- a/gasstation/gasstattion.go +++ b/gasstation/gasstattion.go @@ -53,11 +53,13 @@ func (gs *GasStation) SuggestGasPrice() (uint64, error) { if tip > uint64(gs.cfg.SuggestBlockWindow) { endBlockHeight = tip - uint64(gs.cfg.SuggestBlockWindow) } - + maxGas := gs.bc.Genesis().BlockGasLimit * (tip - endBlockHeight) + defaultGasPrice := gs.cfg.DefaultGas + gasConsumed := uint64(0) for height := tip; height > endBlockHeight; height-- { blk, err := gs.dao.GetBlockByHeight(height) if err != nil { - return gs.cfg.DefaultGas, err + return defaultGasPrice, err } if len(blk.Actions) == 0 { continue @@ -66,6 +68,9 @@ func (gs *GasStation) SuggestGasPrice() (uint64, error) { continue } smallestPrice := blk.Actions[0].GasPrice() + for _, receipt := range blk.Receipts { + gasConsumed += receipt.GasConsumed + } for _, act := range blk.Actions { if action.IsSystemAction(act) { continue @@ -76,17 +81,22 @@ func (gs *GasStation) SuggestGasPrice() (uint64, error) { } smallestPrices = append(smallestPrices, smallestPrice) } - if len(smallestPrices) == 0 { // return default price - return gs.cfg.DefaultGas, nil + return defaultGasPrice, nil } sort.Slice(smallestPrices, func(i, j int) bool { return smallestPrices[i].Cmp(smallestPrices[j]) < 0 }) gasPrice := smallestPrices[(len(smallestPrices)-1)*gs.cfg.Percentile/100].Uint64() - if gasPrice < gs.cfg.DefaultGas { - gasPrice = gs.cfg.DefaultGas + switch { + case gasConsumed > maxGas/2: + gasPrice += gasPrice / 10 + case gasConsumed < maxGas/5: + gasPrice -= gasPrice / 10 + } + if gasPrice < defaultGasPrice { + gasPrice = defaultGasPrice } return gasPrice, nil } diff --git a/gasstation/gasstattion_test.go b/gasstation/gasstattion_test.go index dd683e8e47..7b15936e49 100644 --- a/gasstation/gasstattion_test.go +++ b/gasstation/gasstattion_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "github.com/iotexproject/iotex-core/action" @@ -30,15 +31,28 @@ import ( "github.com/iotexproject/iotex-core/pkg/unit" "github.com/iotexproject/iotex-core/state/factory" "github.com/iotexproject/iotex-core/test/identityset" + "github.com/iotexproject/iotex-core/test/mock/mock_blockchain" + "github.com/iotexproject/iotex-core/test/mock/mock_blockdao" "github.com/iotexproject/iotex-core/testutil" ) -type testConfig struct { - Genesis genesis.Genesis - Chain blockchain.Config - ActPool actpool.Config - GasStation Config -} +type ( + testConfig struct { + Genesis genesis.Genesis + Chain blockchain.Config + ActPool actpool.Config + GasStation Config + } + testActionGas []struct { + gasPrice uint64 + gasConsumed uint64 + } + testCase struct { + name string + blocks []testActionGas + expectGasPrice uint64 + } +) func TestNewGasStation(t *testing.T) { require := require.New(t) @@ -131,7 +145,11 @@ func TestSuggestGasPriceForUserAction(t *testing.T) { gp, err := gs.SuggestGasPrice() require.NoError(t, err) // i from 10 to 29,gasprice for 20 to 39,60%*20+20=31 - require.Equal(t, big.NewInt(1).Mul(big.NewInt(int64(31)), big.NewInt(unit.Qev)).Uint64(), gp) + require.Equal( + t, + big.NewInt(1).Mul(big.NewInt(int64(31)), big.NewInt(unit.Qev)).Uint64()*9/10, + gp, + ) } func TestSuggestGasPriceForSystemAction(t *testing.T) { @@ -191,3 +209,83 @@ func TestSuggestGasPriceForSystemAction(t *testing.T) { // i from 10 to 29,gasprice for 20 to 39,60%*20+20=31 require.Equal(t, gs.cfg.DefaultGas, gp) } + +func TestSuggestGasPrice_GasConsumed(t *testing.T) { + cases := []testCase{ + { + name: "gas consumed > maxGas/2", + blocks: []testActionGas{ + {{uint64(unit.Qev) * 2, 100000000}}, + {{uint64(unit.Qev) * 2, 100000000}}, + {{uint64(unit.Qev) * 2, 100000000}}, + {{uint64(unit.Qev) * 2, 100000000}}, + {{uint64(unit.Qev) * 2, 100000000}}, + {{uint64(unit.Qev) * 2, 200000000}}, + }, + expectGasPrice: 2200000000000, + }, + { + name: "gas consumed < maxGas/5", + blocks: []testActionGas{ + {{uint64(unit.Qev) * 2, 1000000}}, + {{uint64(unit.Qev) * 2, 1000000}}, + {{uint64(unit.Qev) * 2, 1000000}}, + {{uint64(unit.Qev) * 2, 1000000}}, + {{uint64(unit.Qev) * 2, 1000000}}, + {{uint64(unit.Qev) * 2, 2000000}}, + }, + expectGasPrice: 1800000000000, + }, + { + name: "gas consumed between maxGas/5 - maxGas/2", + blocks: []testActionGas{ + {{uint64(unit.Qev) * 2, 10000000}}, + {{uint64(unit.Qev) * 2, 10000000}}, + {{uint64(unit.Qev) * 2, 10000000}}, + {{uint64(unit.Qev) * 2, 10000000}}, + {{uint64(unit.Qev) * 2, 10000000}}, + {{uint64(unit.Qev) * 2, 5000000}}, + }, + expectGasPrice: 2000000000000, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + r := require.New(t) + blocks := prepareBlocks(r, c.blocks) + ctrl := gomock.NewController(t) + bc := mock_blockchain.NewMockBlockchain(ctrl) + dao := mock_blockdao.NewMockBlockDAO(ctrl) + gs := NewGasStation(bc, dao, DefaultConfig) + bc.EXPECT().TipHeight().Return(uint64(len(blocks) - 1)).Times(1) + bc.EXPECT().Genesis().Return(genesis.Default).Times(1) + dao.EXPECT().GetBlockByHeight(gomock.Any()).DoAndReturn( + func(height uint64) (*block.Block, error) { + return blocks[height], nil + }, + ).AnyTimes() + gp, err := gs.SuggestGasPrice() + r.NoError(err) + r.Equal(c.expectGasPrice, gp) + }) + } +} + +func prepareBlocks(r *require.Assertions, cases []testActionGas) map[uint64]*block.Block { + blocks := map[uint64]*block.Block{} + for i := range cases { + actions := []action.SealedEnvelope{} + receipts := []*action.Receipt{} + for _, gas := range cases[i] { + seale, err := action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(1), 1, big.NewInt(0), []byte{}, 1000, big.NewInt(int64(gas.gasPrice))) + r.NoError(err) + actions = append(actions, seale) + receipts = append(receipts, &action.Receipt{GasConsumed: gas.gasConsumed}) + } + blocks[uint64(i)] = &block.Block{ + Body: block.Body{Actions: actions}, + Receipts: receipts, + } + } + return blocks +}