github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/eth/gasprice/gasprice.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2015 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package gasprice
    26  
    27  import (
    28  	"context"
    29  	"math/big"
    30  	"sort"
    31  	"sync"
    32  
    33  	"github.com/ethereum/go-ethereum/common"
    34  	"github.com/ethereum/go-ethereum/core/types"
    35  	"github.com/ethereum/go-ethereum/internal/ethapi"
    36  	"github.com/ethereum/go-ethereum/params"
    37  	"github.com/ethereum/go-ethereum/rpc"
    38  )
    39  
    40  var maxPrice = big.NewInt(500 * params.Shannon)
    41  
    42  type Config struct {
    43  	Blocks     int
    44  	Percentile int
    45  	Default    *big.Int `toml:",omitempty"`
    46  }
    47  
    48  //Oracle根据近期的内容建议天然气价格
    49  //阻碍。适合轻客户和全客户。
    50  type Oracle struct {
    51  	backend   ethapi.Backend
    52  	lastHead  common.Hash
    53  	lastPrice *big.Int
    54  	cacheLock sync.RWMutex
    55  	fetchLock sync.Mutex
    56  
    57  	checkBlocks, maxEmpty, maxBlocks int
    58  	percentile                       int
    59  }
    60  
    61  //new oracle返回新的oracle。
    62  func NewOracle(backend ethapi.Backend, params Config) *Oracle {
    63  	blocks := params.Blocks
    64  	if blocks < 1 {
    65  		blocks = 1
    66  	}
    67  	percent := params.Percentile
    68  	if percent < 0 {
    69  		percent = 0
    70  	}
    71  	if percent > 100 {
    72  		percent = 100
    73  	}
    74  	return &Oracle{
    75  		backend:     backend,
    76  		lastPrice:   params.Default,
    77  		checkBlocks: blocks,
    78  		maxEmpty:    blocks / 2,
    79  		maxBlocks:   blocks * 5,
    80  		percentile:  percent,
    81  	}
    82  }
    83  
    84  //SuggestPrice返回建议的天然气价格。
    85  func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
    86  	gpo.cacheLock.RLock()
    87  	lastHead := gpo.lastHead
    88  	lastPrice := gpo.lastPrice
    89  	gpo.cacheLock.RUnlock()
    90  
    91  	head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
    92  	headHash := head.Hash()
    93  	if headHash == lastHead {
    94  		return lastPrice, nil
    95  	}
    96  
    97  	gpo.fetchLock.Lock()
    98  	defer gpo.fetchLock.Unlock()
    99  
   100  //尝试再次检查缓存,可能上次获取的是我们需要的
   101  	gpo.cacheLock.RLock()
   102  	lastHead = gpo.lastHead
   103  	lastPrice = gpo.lastPrice
   104  	gpo.cacheLock.RUnlock()
   105  	if headHash == lastHead {
   106  		return lastPrice, nil
   107  	}
   108  
   109  	blockNum := head.Number.Uint64()
   110  	ch := make(chan getBlockPricesResult, gpo.checkBlocks)
   111  	sent := 0
   112  	exp := 0
   113  	var blockPrices []*big.Int
   114  	for sent < gpo.checkBlocks && blockNum > 0 {
   115  		go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
   116  		sent++
   117  		exp++
   118  		blockNum--
   119  	}
   120  	maxEmpty := gpo.maxEmpty
   121  	for exp > 0 {
   122  		res := <-ch
   123  		if res.err != nil {
   124  			return lastPrice, res.err
   125  		}
   126  		exp--
   127  		if res.price != nil {
   128  			blockPrices = append(blockPrices, res.price)
   129  			continue
   130  		}
   131  		if maxEmpty > 0 {
   132  			maxEmpty--
   133  			continue
   134  		}
   135  		if blockNum > 0 && sent < gpo.maxBlocks {
   136  			go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
   137  			sent++
   138  			exp++
   139  			blockNum--
   140  		}
   141  	}
   142  	price := lastPrice
   143  	if len(blockPrices) > 0 {
   144  		sort.Sort(bigIntArray(blockPrices))
   145  		price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100]
   146  	}
   147  	if price.Cmp(maxPrice) > 0 {
   148  		price = new(big.Int).Set(maxPrice)
   149  	}
   150  
   151  	gpo.cacheLock.Lock()
   152  	gpo.lastHead = headHash
   153  	gpo.lastPrice = price
   154  	gpo.cacheLock.Unlock()
   155  	return price, nil
   156  }
   157  
   158  type getBlockPricesResult struct {
   159  	price *big.Int
   160  	err   error
   161  }
   162  
   163  type transactionsByGasPrice []*types.Transaction
   164  
   165  func (t transactionsByGasPrice) Len() int           { return len(t) }
   166  func (t transactionsByGasPrice) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
   167  func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 }
   168  
   169  //GetBlockPrices计算给定区块中的最低交易天然气价格
   170  //并发送到结果通道。如果块为空,则价格为零。
   171  func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, ch chan getBlockPricesResult) {
   172  	block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
   173  	if block == nil {
   174  		ch <- getBlockPricesResult{nil, err}
   175  		return
   176  	}
   177  
   178  	blockTxs := block.Transactions()
   179  	txs := make([]*types.Transaction, len(blockTxs))
   180  	copy(txs, blockTxs)
   181  	sort.Sort(transactionsByGasPrice(txs))
   182  
   183  	for _, tx := range txs {
   184  		sender, err := types.Sender(signer, tx)
   185  		if err == nil && sender != block.Coinbase() {
   186  			ch <- getBlockPricesResult{tx.GasPrice(), nil}
   187  			return
   188  		}
   189  	}
   190  	ch <- getBlockPricesResult{nil, nil}
   191  }
   192  
   193  type bigIntArray []*big.Int
   194  
   195  func (s bigIntArray) Len() int           { return len(s) }
   196  func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
   197  func (s bigIntArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }