github.com/iotexproject/iotex-core@v1.14.1-rc1/gasstation/gasstattion.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package gasstation 7 8 import ( 9 "context" 10 "math/big" 11 "sort" 12 13 "github.com/iotexproject/go-pkgs/hash" 14 "github.com/iotexproject/iotex-address/address" 15 16 "github.com/iotexproject/iotex-core/action" 17 "github.com/iotexproject/iotex-core/action/protocol/execution/evm" 18 "github.com/iotexproject/iotex-core/blockchain" 19 "github.com/iotexproject/iotex-core/blockchain/block" 20 ) 21 22 // BlockDAO represents the block data access object 23 type BlockDAO interface { 24 GetBlockHash(uint64) (hash.Hash256, error) 25 GetBlockByHeight(uint64) (*block.Block, error) 26 } 27 28 // SimulateFunc is function that simulate execution 29 type SimulateFunc func(context.Context, address.Address, *action.Execution, evm.GetBlockHash) ([]byte, *action.Receipt, error) 30 31 // GasStation provide gas related api 32 type GasStation struct { 33 bc blockchain.Blockchain 34 dao BlockDAO 35 cfg Config 36 } 37 38 // NewGasStation creates a new gas station 39 func NewGasStation(bc blockchain.Blockchain, dao BlockDAO, cfg Config) *GasStation { 40 return &GasStation{ 41 bc: bc, 42 dao: dao, 43 cfg: cfg, 44 } 45 } 46 47 // SuggestGasPrice suggest gas price 48 func (gs *GasStation) SuggestGasPrice() (uint64, error) { 49 var ( 50 smallestPrices []*big.Int 51 endBlockHeight uint64 52 tip = gs.bc.TipHeight() 53 g = gs.bc.Genesis() 54 ) 55 if tip > uint64(gs.cfg.SuggestBlockWindow) { 56 endBlockHeight = tip - uint64(gs.cfg.SuggestBlockWindow) 57 } 58 maxGas := g.BlockGasLimitByHeight(tip) * (tip - endBlockHeight) 59 defaultGasPrice := gs.cfg.DefaultGas 60 gasConsumed := uint64(0) 61 for height := tip; height > endBlockHeight; height-- { 62 blk, err := gs.dao.GetBlockByHeight(height) 63 if err != nil { 64 return defaultGasPrice, err 65 } 66 if len(blk.Actions) == 0 { 67 continue 68 } 69 if len(blk.Actions) == 1 && action.IsSystemAction(blk.Actions[0]) { 70 continue 71 } 72 smallestPrice := blk.Actions[0].GasPrice() 73 for _, receipt := range blk.Receipts { 74 gasConsumed += receipt.GasConsumed 75 } 76 for _, act := range blk.Actions { 77 if action.IsSystemAction(act) { 78 continue 79 } 80 if smallestPrice.Cmp(act.GasPrice()) == 1 { 81 smallestPrice = act.GasPrice() 82 } 83 } 84 smallestPrices = append(smallestPrices, smallestPrice) 85 } 86 if len(smallestPrices) == 0 { 87 // return default price 88 return defaultGasPrice, nil 89 } 90 sort.Slice(smallestPrices, func(i, j int) bool { 91 return smallestPrices[i].Cmp(smallestPrices[j]) < 0 92 }) 93 gasPrice := smallestPrices[(len(smallestPrices)-1)*gs.cfg.Percentile/100].Uint64() 94 switch { 95 case gasConsumed > maxGas/2: 96 gasPrice += gasPrice / 10 97 case gasConsumed < maxGas/5: 98 gasPrice -= gasPrice / 10 99 } 100 if gasPrice < defaultGasPrice { 101 gasPrice = defaultGasPrice 102 } 103 return gasPrice, nil 104 }