github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/eth/gasprice/gasprice.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package gasprice
    18  
    19  import (
    20  	"math/big"
    21  	"math/rand"
    22  	"sync"
    23  
    24  	"github.com/atheioschain/go-atheios/core"
    25  	"github.com/atheioschain/go-atheios/core/types"
    26  	"github.com/atheioschain/go-atheios/ethdb"
    27  	"github.com/atheioschain/go-atheios/event"
    28  	"github.com/atheioschain/go-atheios/logger"
    29  	"github.com/atheioschain/go-atheios/logger/glog"
    30  )
    31  
    32  const (
    33  	gpoProcessPastBlocks = 100
    34  
    35  	// for testing
    36  	gpoDefaultBaseCorrectionFactor = 110
    37  	gpoDefaultMinGasPrice          = 10000000000000
    38  )
    39  
    40  type blockPriceInfo struct {
    41  	baseGasPrice *big.Int
    42  }
    43  
    44  type GpoParams struct {
    45  	GpoMinGasPrice          *big.Int
    46  	GpoMaxGasPrice          *big.Int
    47  	GpoFullBlockRatio       int
    48  	GpobaseStepDown         int
    49  	GpobaseStepUp           int
    50  	GpobaseCorrectionFactor int
    51  }
    52  
    53  // GasPriceOracle recommends gas prices based on the content of recent
    54  // blocks.
    55  type GasPriceOracle struct {
    56  	chain         *core.BlockChain
    57  	db            ethdb.Database
    58  	evmux         *event.TypeMux
    59  	params        *GpoParams
    60  	initOnce      sync.Once
    61  	minPrice      *big.Int
    62  	lastBaseMutex sync.Mutex
    63  	lastBase      *big.Int
    64  
    65  	// state of listenLoop
    66  	blocks                        map[uint64]*blockPriceInfo
    67  	firstProcessed, lastProcessed uint64
    68  	minBase                       *big.Int
    69  }
    70  
    71  // NewGasPriceOracle returns a new oracle.
    72  func NewGasPriceOracle(chain *core.BlockChain, db ethdb.Database, evmux *event.TypeMux, params *GpoParams) *GasPriceOracle {
    73  	minprice := params.GpoMinGasPrice
    74  	if minprice == nil {
    75  		minprice = big.NewInt(gpoDefaultMinGasPrice)
    76  	}
    77  	minbase := new(big.Int).Mul(minprice, big.NewInt(100))
    78  	if params.GpobaseCorrectionFactor > 0 {
    79  		minbase = minbase.Div(minbase, big.NewInt(int64(params.GpobaseCorrectionFactor)))
    80  	}
    81  	return &GasPriceOracle{
    82  		chain:    chain,
    83  		db:       db,
    84  		evmux:    evmux,
    85  		params:   params,
    86  		blocks:   make(map[uint64]*blockPriceInfo),
    87  		minBase:  minbase,
    88  		minPrice: minprice,
    89  		lastBase: minprice,
    90  	}
    91  }
    92  
    93  func (gpo *GasPriceOracle) init() {
    94  	gpo.initOnce.Do(func() {
    95  		gpo.processPastBlocks()
    96  		go gpo.listenLoop()
    97  	})
    98  }
    99  
   100  func (self *GasPriceOracle) processPastBlocks() {
   101  	last := int64(-1)
   102  	cblock := self.chain.CurrentBlock()
   103  	if cblock != nil {
   104  		last = int64(cblock.NumberU64())
   105  	}
   106  	first := int64(0)
   107  	if last > gpoProcessPastBlocks {
   108  		first = last - gpoProcessPastBlocks
   109  	}
   110  	self.firstProcessed = uint64(first)
   111  	for i := first; i <= last; i++ {
   112  		block := self.chain.GetBlockByNumber(uint64(i))
   113  		if block != nil {
   114  			self.processBlock(block)
   115  		}
   116  	}
   117  
   118  }
   119  
   120  func (self *GasPriceOracle) listenLoop() {
   121  	events := self.evmux.Subscribe(core.ChainEvent{}, core.ChainSplitEvent{})
   122  	defer events.Unsubscribe()
   123  
   124  	for event := range events.Chan() {
   125  		switch event := event.Data.(type) {
   126  		case core.ChainEvent:
   127  			self.processBlock(event.Block)
   128  		case core.ChainSplitEvent:
   129  			self.processBlock(event.Block)
   130  		}
   131  	}
   132  }
   133  
   134  func (self *GasPriceOracle) processBlock(block *types.Block) {
   135  	i := block.NumberU64()
   136  	if i > self.lastProcessed {
   137  		self.lastProcessed = i
   138  	}
   139  
   140  	lastBase := self.minPrice
   141  	bpl := self.blocks[i-1]
   142  	if bpl != nil {
   143  		lastBase = bpl.baseGasPrice
   144  	}
   145  	if lastBase == nil {
   146  		return
   147  	}
   148  
   149  	var corr int
   150  	lp := self.lowestPrice(block)
   151  	if lp == nil {
   152  		return
   153  	}
   154  
   155  	if lastBase.Cmp(lp) < 0 {
   156  		corr = self.params.GpobaseStepUp
   157  	} else {
   158  		corr = -self.params.GpobaseStepDown
   159  	}
   160  
   161  	crand := int64(corr * (900 + rand.Intn(201)))
   162  	newBase := new(big.Int).Mul(lastBase, big.NewInt(1000000+crand))
   163  	newBase.Div(newBase, big.NewInt(1000000))
   164  
   165  	if newBase.Cmp(self.minBase) < 0 {
   166  		newBase = self.minBase
   167  	}
   168  
   169  	bpi := self.blocks[i]
   170  	if bpi == nil {
   171  		bpi = &blockPriceInfo{}
   172  		self.blocks[i] = bpi
   173  	}
   174  	bpi.baseGasPrice = newBase
   175  	self.lastBaseMutex.Lock()
   176  	self.lastBase = newBase
   177  	self.lastBaseMutex.Unlock()
   178  
   179  	glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", i, newBase.Int64())
   180  }
   181  
   182  // returns the lowers possible price with which a tx was or could have been included
   183  func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
   184  	gasUsed := big.NewInt(0)
   185  
   186  	receipts := core.GetBlockReceipts(self.db, block.Hash(), block.NumberU64())
   187  	if len(receipts) > 0 {
   188  		if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil {
   189  			gasUsed = receipts[len(receipts)-1].CumulativeGasUsed
   190  		}
   191  	}
   192  
   193  	if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(),
   194  		big.NewInt(int64(self.params.GpoFullBlockRatio)))) < 0 {
   195  		// block is not full, could have posted a tx with MinGasPrice
   196  		return big.NewInt(0)
   197  	}
   198  
   199  	txs := block.Transactions()
   200  	if len(txs) == 0 {
   201  		return big.NewInt(0)
   202  	}
   203  	// block is full, find smallest gasPrice
   204  	minPrice := txs[0].GasPrice()
   205  	for i := 1; i < len(txs); i++ {
   206  		price := txs[i].GasPrice()
   207  		if price.Cmp(minPrice) < 0 {
   208  			minPrice = price
   209  		}
   210  	}
   211  	return minPrice
   212  }
   213  
   214  // SuggestPrice returns the recommended gas price.
   215  func (self *GasPriceOracle) SuggestPrice() *big.Int {
   216  	self.init()
   217  	self.lastBaseMutex.Lock()
   218  	price := new(big.Int).Set(self.lastBase)
   219  	self.lastBaseMutex.Unlock()
   220  
   221  	price.Mul(price, big.NewInt(int64(self.params.GpobaseCorrectionFactor)))
   222  	price.Div(price, big.NewInt(100))
   223  	if price.Cmp(self.minPrice) < 0 {
   224  		price.Set(self.minPrice)
   225  	} else if self.params.GpoMaxGasPrice != nil && price.Cmp(self.params.GpoMaxGasPrice) > 0 {
   226  		price.Set(self.params.GpoMaxGasPrice)
   227  	}
   228  	return price
   229  }