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  }