gasprice.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. package gasprice
  17. import (
  18. "context"
  19. "math/big"
  20. "sort"
  21. "sync"
  22. "github.com/ethereum/go-ethereum/common"
  23. "github.com/ethereum/go-ethereum/core/types"
  24. "github.com/ethereum/go-ethereum/internal/ethapi"
  25. "github.com/ethereum/go-ethereum/params"
  26. "github.com/ethereum/go-ethereum/rpc"
  27. )
  28. var maxPrice = big.NewInt(500 * params.Shannon)
  29. type Config struct {
  30. Blocks int
  31. Percentile int
  32. Default *big.Int `toml:",omitempty"`
  33. }
  34. // Oracle recommends gas prices based on the content of recent
  35. // blocks. Suitable for both light and full clients.
  36. type Oracle struct {
  37. backend ethapi.Backend
  38. lastHead common.Hash
  39. lastPrice *big.Int
  40. cacheLock sync.RWMutex
  41. fetchLock sync.Mutex
  42. checkBlocks, maxEmpty, maxBlocks int
  43. percentile int
  44. }
  45. // NewOracle returns a new oracle.
  46. func NewOracle(backend ethapi.Backend, params Config) *Oracle {
  47. blocks := params.Blocks
  48. if blocks < 1 {
  49. blocks = 1
  50. }
  51. percent := params.Percentile
  52. if percent < 0 {
  53. percent = 0
  54. }
  55. if percent > 100 {
  56. percent = 100
  57. }
  58. return &Oracle{
  59. backend: backend,
  60. lastPrice: params.Default,
  61. checkBlocks: blocks,
  62. maxEmpty: blocks / 2,
  63. maxBlocks: blocks * 5,
  64. percentile: percent,
  65. }
  66. }
  67. // SuggestPrice returns the recommended gas price.
  68. func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
  69. gpo.cacheLock.RLock()
  70. lastHead := gpo.lastHead
  71. lastPrice := gpo.lastPrice
  72. gpo.cacheLock.RUnlock()
  73. head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
  74. headHash := head.Hash()
  75. if headHash == lastHead {
  76. return lastPrice, nil
  77. }
  78. gpo.fetchLock.Lock()
  79. defer gpo.fetchLock.Unlock()
  80. // try checking the cache again, maybe the last fetch fetched what we need
  81. gpo.cacheLock.RLock()
  82. lastHead = gpo.lastHead
  83. lastPrice = gpo.lastPrice
  84. gpo.cacheLock.RUnlock()
  85. if headHash == lastHead {
  86. return lastPrice, nil
  87. }
  88. blockNum := head.Number.Uint64()
  89. ch := make(chan getBlockPricesResult, gpo.checkBlocks)
  90. sent := 0
  91. exp := 0
  92. var blockPrices []*big.Int
  93. for sent < gpo.checkBlocks && blockNum > 0 {
  94. go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
  95. sent++
  96. exp++
  97. blockNum--
  98. }
  99. maxEmpty := gpo.maxEmpty
  100. for exp > 0 {
  101. res := <-ch
  102. if res.err != nil {
  103. return lastPrice, res.err
  104. }
  105. exp--
  106. if res.price != nil {
  107. blockPrices = append(blockPrices, res.price)
  108. continue
  109. }
  110. if maxEmpty > 0 {
  111. maxEmpty--
  112. continue
  113. }
  114. if blockNum > 0 && sent < gpo.maxBlocks {
  115. go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
  116. sent++
  117. exp++
  118. blockNum--
  119. }
  120. }
  121. price := lastPrice
  122. if len(blockPrices) > 0 {
  123. sort.Sort(bigIntArray(blockPrices))
  124. price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100]
  125. }
  126. if price.Cmp(maxPrice) > 0 {
  127. price = new(big.Int).Set(maxPrice)
  128. }
  129. gpo.cacheLock.Lock()
  130. gpo.lastHead = headHash
  131. gpo.lastPrice = price
  132. gpo.cacheLock.Unlock()
  133. return price, nil
  134. }
  135. type getBlockPricesResult struct {
  136. price *big.Int
  137. err error
  138. }
  139. type transactionsByGasPrice []*types.Transaction
  140. func (t transactionsByGasPrice) Len() int { return len(t) }
  141. func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
  142. func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 }
  143. // getBlockPrices calculates the lowest transaction gas price in a given block
  144. // and sends it to the result channel. If the block is empty, price is nil.
  145. func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, ch chan getBlockPricesResult) {
  146. block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
  147. if block == nil {
  148. ch <- getBlockPricesResult{nil, err}
  149. return
  150. }
  151. blockTxs := block.Transactions()
  152. txs := make([]*types.Transaction, len(blockTxs))
  153. copy(txs, blockTxs)
  154. sort.Sort(transactionsByGasPrice(txs))
  155. for _, tx := range txs {
  156. sender, err := types.Sender(signer, tx)
  157. if err == nil && sender != block.Coinbase() {
  158. ch <- getBlockPricesResult{tx.GasPrice(), nil}
  159. return
  160. }
  161. }
  162. ch <- getBlockPricesResult{nil, nil}
  163. }
  164. type bigIntArray []*big.Int
  165. func (s bigIntArray) Len() int { return len(s) }
  166. func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
  167. func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }