github.com/klaytn/klaytn@v1.10.2/node/cn/gasprice/gasprice.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2016 The go-ethereum Authors
     3  // This file is part of go-ethereum.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from eth/gasprice/gasprice.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package gasprice
    22  
    23  import (
    24  	"context"
    25  	"math/big"
    26  	"sync"
    27  
    28  	"github.com/klaytn/klaytn/blockchain/types"
    29  	"github.com/klaytn/klaytn/networks/rpc"
    30  
    31  	"github.com/klaytn/klaytn/common"
    32  	"github.com/klaytn/klaytn/params"
    33  )
    34  
    35  var maxPrice = big.NewInt(500 * params.Ston)
    36  
    37  type Config struct {
    38  	Blocks           int
    39  	Percentile       int
    40  	MaxHeaderHistory int
    41  	MaxBlockHistory  int
    42  	Default          *big.Int `toml:",omitempty"`
    43  }
    44  
    45  // OracleBackend includes all necessary background APIs for oracle.
    46  type OracleBackend interface {
    47  	HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
    48  	BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
    49  	GetBlockReceipts(ctx context.Context, hash common.Hash) types.Receipts
    50  	ChainConfig() *params.ChainConfig
    51  	CurrentBlock() *types.Block
    52  }
    53  
    54  type TxPool interface {
    55  	GasPrice() *big.Int
    56  }
    57  
    58  // Oracle recommends gas prices based on the content of recent
    59  // blocks. Suitable for both light and full clients.
    60  type Oracle struct {
    61  	backend   OracleBackend
    62  	lastHead  common.Hash
    63  	lastPrice *big.Int
    64  	cacheLock sync.RWMutex
    65  	fetchLock sync.Mutex
    66  	txPool    TxPool
    67  
    68  	checkBlocks, maxEmpty, maxBlocks  int
    69  	percentile                        int
    70  	maxHeaderHistory, maxBlockHistory int
    71  }
    72  
    73  // NewOracle returns a new oracle.
    74  func NewOracle(backend OracleBackend, params Config, txPool TxPool) *Oracle {
    75  	blocks := params.Blocks
    76  	if blocks < 1 {
    77  		blocks = 1
    78  	}
    79  	percent := params.Percentile
    80  	if percent < 0 {
    81  		percent = 0
    82  	}
    83  	if percent > 100 {
    84  		percent = 100
    85  	}
    86  	return &Oracle{
    87  		backend:          backend,
    88  		lastPrice:        params.Default,
    89  		checkBlocks:      blocks,
    90  		maxEmpty:         blocks / 2,
    91  		maxBlocks:        blocks * 5,
    92  		percentile:       percent,
    93  		maxHeaderHistory: params.MaxHeaderHistory,
    94  		maxBlockHistory:  params.MaxBlockHistory,
    95  		txPool:           txPool,
    96  	}
    97  }
    98  
    99  // SuggestPrice returns the recommended gas price.
   100  func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
   101  	if gpo.txPool == nil {
   102  		// If txpool is not set, just return 0. This is used for testing.
   103  		return common.Big0, nil
   104  	}
   105  	// Since we have fixed gas price, we can directly get this value from TxPool.
   106  	suggestedPrice := gpo.txPool.GasPrice()
   107  	if gpo.backend.ChainConfig().IsMagmaForkEnabled(new(big.Int).Add(gpo.backend.CurrentBlock().Number(), common.Big1)) {
   108  		return new(big.Int).Mul(suggestedPrice, common.Big2), nil
   109  	}
   110  	return suggestedPrice, nil
   111  	/*
   112  		// TODO-Klaytn-RemoveLater Later remove below obsolete code if we don't need them anymore.
   113  		gpo.cacheLock.RLock()
   114  		lastHead := gpo.lastHead
   115  		lastPrice := gpo.lastPrice
   116  		gpo.cacheLock.RUnlock()
   117  
   118  		head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
   119  		headHash := head.Hash()
   120  		if headHash == lastHead {
   121  			return lastPrice, nil
   122  		}
   123  
   124  		gpo.fetchLock.Lock()
   125  		defer gpo.fetchLock.Unlock()
   126  
   127  		// try checking the cache again, maybe the last fetch fetched what we need
   128  		gpo.cacheLock.RLock()
   129  		lastHead = gpo.lastHead
   130  		lastPrice = gpo.lastPrice
   131  		gpo.cacheLock.RUnlock()
   132  		if headHash == lastHead {
   133  			return lastPrice, nil
   134  		}
   135  
   136  		blockNum := head.Number.Uint64()
   137  		ch := make(chan getBlockPricesResult, gpo.checkBlocks)
   138  		sent := 0
   139  		exp := 0
   140  		var blockPrices []*big.Int
   141  		for sent < gpo.checkBlocks && blockNum > 0 {
   142  			go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
   143  			sent++
   144  			exp++
   145  			blockNum--
   146  		}
   147  		maxEmpty := gpo.maxEmpty
   148  		for exp > 0 {
   149  			res := <-ch
   150  			if res.err != nil {
   151  				return lastPrice, res.err
   152  			}
   153  			exp--
   154  			if res.price != nil {
   155  				blockPrices = append(blockPrices, res.price)
   156  				continue
   157  			}
   158  			if maxEmpty > 0 {
   159  				maxEmpty--
   160  				continue
   161  			}
   162  			if blockNum > 0 && sent < gpo.maxBlocks {
   163  				go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
   164  				sent++
   165  				exp++
   166  				blockNum--
   167  			}
   168  		}
   169  		price := lastPrice
   170  		if len(blockPrices) > 0 {
   171  			sort.Sort(bigIntArray(blockPrices))
   172  			price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100]
   173  		}
   174  		if price.Cmp(maxPrice) > 0 {
   175  			price = new(big.Int).Set(maxPrice)
   176  		}
   177  
   178  		gpo.cacheLock.Lock()
   179  		gpo.lastHead = headHash
   180  		gpo.lastPrice = price
   181  		gpo.cacheLock.Unlock()
   182  		return price, nil
   183  	*/
   184  }
   185  
   186  // TODO-Klaytn-RemoveLater Later remove below obsolete code if we don't need them anymore.
   187  //type getBlockPricesResult struct {
   188  //	price *big.Int
   189  //	err   error
   190  //}
   191  //
   192  //type transactionsByGasPrice []*types.Transaction
   193  //
   194  //func (t transactionsByGasPrice) Len() int           { return len(t) }
   195  //func (t transactionsByGasPrice) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
   196  //func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 }
   197  //
   198  //type bigIntArray []*big.Int
   199  //
   200  //func (s bigIntArray) Len() int           { return len(s) }
   201  //func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
   202  //func (s bigIntArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }