github.com/klaytn/klaytn@v1.12.1/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  // Tx gas price requirements has changed over the hardforks
   100  //
   101  // | Fork              | gasPrice                     | maxFeePerGas                 | maxPriorityFeePerGas         |
   102  // |------------------ |----------------------------- |----------------------------- |----------------------------- |
   103  // | Before EthTxType  | must be fixed UnitPrice (1)  | N/A (2)                      | N/A (2)                      |
   104  // | After EthTxType   | must be fixed UnitPrice (1)  | must be fixed UnitPrice (3)  | must be fixed UnitPrice (3)  |
   105  // | After Magma       | BaseFee or higher (4)        | BaseFee or higher (4)        | Ignored (4)                  |
   106  //
   107  // (1) If tx.type != 2 && !rules.IsMagma: https://github.com/klaytn/klaytn/blob/v1.11.1/blockchain/tx_pool.go#L729
   108  // (2) If tx.type == 2 && !rules.IsEthTxType: https://github.com/klaytn/klaytn/blob/v1.11.1/blockchain/tx_pool.go#L670
   109  // (3) If tx.type == 2 && !rules.IsMagma: https://github.com/klaytn/klaytn/blob/v1.11.1/blockchain/tx_pool.go#L710
   110  // (4) If tx.type == 2 && rules.IsMagma: https://github.com/klaytn/klaytn/blob/v1.11.1/blockchain/tx_pool.go#L703
   111  //
   112  // The suggested prices needs to match the requirements.
   113  //
   114  // | Fork              | SuggestPrice (for gasPrice and maxFeePerGas)                | SuggestTipCap (for maxPriorityFeePerGas) |
   115  // |------------------ |------------------------------------------------------------ |----------------------------- |
   116  // | Before Magma      | Fixed UnitPrice                                             | Fixed UnitPrice              |
   117  // | After Magma       | BaseFee * 2                                                 | Zero                         |
   118  
   119  // SuggestPrice returns the recommended gas price.
   120  // This value is intended to be used as gasPrice or maxFeePerGas.
   121  func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
   122  	if gpo.txPool == nil {
   123  		// If txpool is not set, just return 0. This is used for testing.
   124  		return common.Big0, nil
   125  	}
   126  
   127  	nextNum := new(big.Int).Add(gpo.backend.CurrentBlock().Number(), common.Big1)
   128  	if gpo.backend.ChainConfig().IsMagmaForkEnabled(nextNum) {
   129  		// After Magma, return the twice of BaseFee as a buffer.
   130  		baseFee := gpo.txPool.GasPrice()
   131  		return new(big.Int).Mul(baseFee, common.Big2), nil
   132  	} else {
   133  		// Before Magma, return the fixed UnitPrice.
   134  		unitPrice := gpo.txPool.GasPrice()
   135  		return unitPrice, nil
   136  	}
   137  }
   138  
   139  // SuggestTipCap returns the recommended gas tip cap.
   140  // This value is intended to be used as maxPriorityFeePerGas.
   141  // Though Klaytn does not recognize gas tip, this function returns some value for compatibility.
   142  func (gpo *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
   143  	if gpo.txPool == nil {
   144  		// If txpool is not set, just return 0. This is used for testing.
   145  		return common.Big0, nil
   146  	}
   147  
   148  	nextNum := new(big.Int).Add(gpo.backend.CurrentBlock().Number(), common.Big1)
   149  	if gpo.backend.ChainConfig().IsMagmaForkEnabled(nextNum) {
   150  		// After Magma, return zero
   151  		return common.Big0, nil
   152  	} else {
   153  		// Before Magma, return the fixed UnitPrice.
   154  		unitPrice := gpo.txPool.GasPrice()
   155  		return unitPrice, nil
   156  	}
   157  }