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

     1  // Copyright 2016 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  	"sort"
    22  	"sync"
    23  
    24  	"github.com/atheioschain/go-atheios/common"
    25  	"github.com/atheioschain/go-atheios/internal/ethapi"
    26  	"github.com/atheioschain/go-atheios/rpc"
    27  	"golang.org/x/net/context"
    28  )
    29  
    30  const (
    31  	LpoAvgCount     = 5
    32  	LpoMinCount     = 3
    33  	LpoMaxBlocks    = 20
    34  	LpoSelect       = 50
    35  	LpoDefaultPrice = 20000000000
    36  )
    37  
    38  // LightPriceOracle recommends gas prices based on the content of recent
    39  // blocks. Suitable for both light and full clients.
    40  type LightPriceOracle struct {
    41  	backend   ethapi.Backend
    42  	lastHead  common.Hash
    43  	lastPrice *big.Int
    44  	cacheLock sync.RWMutex
    45  	fetchLock sync.Mutex
    46  }
    47  
    48  // NewLightPriceOracle returns a new oracle.
    49  func NewLightPriceOracle(backend ethapi.Backend) *LightPriceOracle {
    50  	return &LightPriceOracle{
    51  		backend:   backend,
    52  		lastPrice: big.NewInt(LpoDefaultPrice),
    53  	}
    54  }
    55  
    56  // SuggestPrice returns the recommended gas price.
    57  func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
    58  	self.cacheLock.RLock()
    59  	lastHead := self.lastHead
    60  	lastPrice := self.lastPrice
    61  	self.cacheLock.RUnlock()
    62  
    63  	head, _ := self.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
    64  	headHash := head.Hash()
    65  	if headHash == lastHead {
    66  		return lastPrice, nil
    67  	}
    68  
    69  	self.fetchLock.Lock()
    70  	defer self.fetchLock.Unlock()
    71  
    72  	// try checking the cache again, maybe the last fetch fetched what we need
    73  	self.cacheLock.RLock()
    74  	lastHead = self.lastHead
    75  	lastPrice = self.lastPrice
    76  	self.cacheLock.RUnlock()
    77  	if headHash == lastHead {
    78  		return lastPrice, nil
    79  	}
    80  
    81  	blockNum := head.Number.Uint64()
    82  	chn := make(chan lpResult, LpoMaxBlocks)
    83  	sent := 0
    84  	exp := 0
    85  	var lps bigIntArray
    86  	for sent < LpoAvgCount && blockNum > 0 {
    87  		go self.getLowestPrice(ctx, blockNum, chn)
    88  		sent++
    89  		exp++
    90  		blockNum--
    91  	}
    92  	maxEmpty := LpoAvgCount - LpoMinCount
    93  	for exp > 0 {
    94  		res := <-chn
    95  		if res.err != nil {
    96  			return nil, res.err
    97  		}
    98  		exp--
    99  		if res.price != nil {
   100  			lps = append(lps, res.price)
   101  		} else {
   102  			if maxEmpty > 0 {
   103  				maxEmpty--
   104  			} else {
   105  				if blockNum > 0 && sent < LpoMaxBlocks {
   106  					go self.getLowestPrice(ctx, blockNum, chn)
   107  					sent++
   108  					exp++
   109  					blockNum--
   110  				}
   111  			}
   112  		}
   113  	}
   114  	price := lastPrice
   115  	if len(lps) > 0 {
   116  		sort.Sort(lps)
   117  		price = lps[(len(lps)-1)*LpoSelect/100]
   118  	}
   119  
   120  	self.cacheLock.Lock()
   121  	self.lastHead = headHash
   122  	self.lastPrice = price
   123  	self.cacheLock.Unlock()
   124  	return price, nil
   125  }
   126  
   127  type lpResult struct {
   128  	price *big.Int
   129  	err   error
   130  }
   131  
   132  // getLowestPrice calculates the lowest transaction gas price in a given block
   133  // and sends it to the result channel. If the block is empty, price is nil.
   134  func (self *LightPriceOracle) getLowestPrice(ctx context.Context, blockNum uint64, chn chan lpResult) {
   135  	block, err := self.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
   136  	if block == nil {
   137  		chn <- lpResult{nil, err}
   138  		return
   139  	}
   140  	txs := block.Transactions()
   141  	if len(txs) == 0 {
   142  		chn <- lpResult{nil, nil}
   143  		return
   144  	}
   145  	// find smallest gasPrice
   146  	minPrice := txs[0].GasPrice()
   147  	for i := 1; i < len(txs); i++ {
   148  		price := txs[i].GasPrice()
   149  		if price.Cmp(minPrice) < 0 {
   150  			minPrice = price
   151  		}
   152  	}
   153  	chn <- lpResult{minPrice, nil}
   154  }
   155  
   156  type bigIntArray []*big.Int
   157  
   158  func (s bigIntArray) Len() int           { return len(s) }
   159  func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
   160  func (s bigIntArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }