github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/eth/gasprice/gasprice.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:37</date>
    10  //</624450089061978112>
    11  
    12  
    13  package gasprice
    14  
    15  import (
    16  	"context"
    17  	"math/big"
    18  	"sort"
    19  	"sync"
    20  
    21  	"github.com/ethereum/go-ethereum/common"
    22  	"github.com/ethereum/go-ethereum/core/types"
    23  	"github.com/ethereum/go-ethereum/internal/ethapi"
    24  	"github.com/ethereum/go-ethereum/params"
    25  	"github.com/ethereum/go-ethereum/rpc"
    26  )
    27  
    28  var maxPrice = big.NewInt(500 * params.GWei)
    29  
    30  type Config struct {
    31  	Blocks     int
    32  	Percentile int
    33  	Default    *big.Int `toml:",omitempty"`
    34  }
    35  
    36  //Oracle根据近期的内容建议天然气价格
    37  //阻碍。适合轻客户和全客户。
    38  type Oracle struct {
    39  	backend   ethapi.Backend
    40  	lastHead  common.Hash
    41  	lastPrice *big.Int
    42  	cacheLock sync.RWMutex
    43  	fetchLock sync.Mutex
    44  
    45  	checkBlocks, maxEmpty, maxBlocks int
    46  	percentile                       int
    47  }
    48  
    49  //new oracle返回新的oracle。
    50  func NewOracle(backend ethapi.Backend, params Config) *Oracle {
    51  	blocks := params.Blocks
    52  	if blocks < 1 {
    53  		blocks = 1
    54  	}
    55  	percent := params.Percentile
    56  	if percent < 0 {
    57  		percent = 0
    58  	}
    59  	if percent > 100 {
    60  		percent = 100
    61  	}
    62  	return &Oracle{
    63  		backend:     backend,
    64  		lastPrice:   params.Default,
    65  		checkBlocks: blocks,
    66  		maxEmpty:    blocks / 2,
    67  		maxBlocks:   blocks * 5,
    68  		percentile:  percent,
    69  	}
    70  }
    71  
    72  //SuggestPrice返回建议的天然气价格。
    73  func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
    74  	gpo.cacheLock.RLock()
    75  	lastHead := gpo.lastHead
    76  	lastPrice := gpo.lastPrice
    77  	gpo.cacheLock.RUnlock()
    78  
    79  	head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
    80  	headHash := head.Hash()
    81  	if headHash == lastHead {
    82  		return lastPrice, nil
    83  	}
    84  
    85  	gpo.fetchLock.Lock()
    86  	defer gpo.fetchLock.Unlock()
    87  
    88  //尝试再次检查缓存,可能上次获取的是我们需要的
    89  	gpo.cacheLock.RLock()
    90  	lastHead = gpo.lastHead
    91  	lastPrice = gpo.lastPrice
    92  	gpo.cacheLock.RUnlock()
    93  	if headHash == lastHead {
    94  		return lastPrice, nil
    95  	}
    96  
    97  	blockNum := head.Number.Uint64()
    98  	ch := make(chan getBlockPricesResult, gpo.checkBlocks)
    99  	sent := 0
   100  	exp := 0
   101  	var blockPrices []*big.Int
   102  	for sent < gpo.checkBlocks && blockNum > 0 {
   103  		go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
   104  		sent++
   105  		exp++
   106  		blockNum--
   107  	}
   108  	maxEmpty := gpo.maxEmpty
   109  	for exp > 0 {
   110  		res := <-ch
   111  		if res.err != nil {
   112  			return lastPrice, res.err
   113  		}
   114  		exp--
   115  		if res.price != nil {
   116  			blockPrices = append(blockPrices, res.price)
   117  			continue
   118  		}
   119  		if maxEmpty > 0 {
   120  			maxEmpty--
   121  			continue
   122  		}
   123  		if blockNum > 0 && sent < gpo.maxBlocks {
   124  			go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
   125  			sent++
   126  			exp++
   127  			blockNum--
   128  		}
   129  	}
   130  	price := lastPrice
   131  	if len(blockPrices) > 0 {
   132  		sort.Sort(bigIntArray(blockPrices))
   133  		price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100]
   134  	}
   135  	if price.Cmp(maxPrice) > 0 {
   136  		price = new(big.Int).Set(maxPrice)
   137  	}
   138  
   139  	gpo.cacheLock.Lock()
   140  	gpo.lastHead = headHash
   141  	gpo.lastPrice = price
   142  	gpo.cacheLock.Unlock()
   143  	return price, nil
   144  }
   145  
   146  type getBlockPricesResult struct {
   147  	price *big.Int
   148  	err   error
   149  }
   150  
   151  type transactionsByGasPrice []*types.Transaction
   152  
   153  func (t transactionsByGasPrice) Len() int           { return len(t) }
   154  func (t transactionsByGasPrice) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
   155  func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 }
   156  
   157  //GetBlockPrices计算给定区块中的最低交易天然气价格
   158  //并发送到结果通道。如果块为空,则价格为零。
   159  func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, ch chan getBlockPricesResult) {
   160  	block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
   161  	if block == nil {
   162  		ch <- getBlockPricesResult{nil, err}
   163  		return
   164  	}
   165  
   166  	blockTxs := block.Transactions()
   167  	txs := make([]*types.Transaction, len(blockTxs))
   168  	copy(txs, blockTxs)
   169  	sort.Sort(transactionsByGasPrice(txs))
   170  
   171  	for _, tx := range txs {
   172  		sender, err := types.Sender(signer, tx)
   173  		if err == nil && sender != block.Coinbase() {
   174  			ch <- getBlockPricesResult{tx.GasPrice(), nil}
   175  			return
   176  		}
   177  	}
   178  	ch <- getBlockPricesResult{nil, nil}
   179  }
   180  
   181  type bigIntArray []*big.Int
   182  
   183  func (s bigIntArray) Len() int           { return len(s) }
   184  func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
   185  func (s bigIntArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   186