feehistory.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright 2021 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. "encoding/binary"
  20. "errors"
  21. "fmt"
  22. "math"
  23. "math/big"
  24. "sort"
  25. "sync/atomic"
  26. "github.com/ethereum/go-ethereum/common"
  27. "github.com/ethereum/go-ethereum/consensus/misc"
  28. "github.com/ethereum/go-ethereum/core/types"
  29. "github.com/ethereum/go-ethereum/log"
  30. "github.com/ethereum/go-ethereum/rpc"
  31. )
  32. var (
  33. errInvalidPercentile = errors.New("invalid reward percentile")
  34. errRequestBeyondHead = errors.New("request beyond head block")
  35. )
  36. const (
  37. // maxBlockFetchers is the max number of goroutines to spin up to pull blocks
  38. // for the fee history calculation (mostly relevant for LES).
  39. maxBlockFetchers = 4
  40. )
  41. // blockFees represents a single block for processing
  42. type blockFees struct {
  43. // set by the caller
  44. blockNumber uint64
  45. header *types.Header
  46. block *types.Block // only set if reward percentiles are requested
  47. receipts types.Receipts
  48. // filled by processBlock
  49. results processedFees
  50. err error
  51. }
  52. // processedFees contains the results of a processed block and is also used for caching
  53. type processedFees struct {
  54. reward []*big.Int
  55. baseFee, nextBaseFee *big.Int
  56. gasUsedRatio float64
  57. }
  58. // txGasAndReward is sorted in ascending order based on reward
  59. type (
  60. txGasAndReward struct {
  61. gasUsed uint64
  62. reward *big.Int
  63. }
  64. sortGasAndReward []txGasAndReward
  65. )
  66. func (s sortGasAndReward) Len() int { return len(s) }
  67. func (s sortGasAndReward) Swap(i, j int) {
  68. s[i], s[j] = s[j], s[i]
  69. }
  70. func (s sortGasAndReward) Less(i, j int) bool {
  71. return s[i].reward.Cmp(s[j].reward) < 0
  72. }
  73. // processBlock takes a blockFees structure with the blockNumber, the header and optionally
  74. // the block field filled in, retrieves the block from the backend if not present yet and
  75. // fills in the rest of the fields.
  76. func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
  77. chainconfig := oracle.backend.ChainConfig()
  78. if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil {
  79. bf.results.baseFee = new(big.Int)
  80. }
  81. if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) {
  82. bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header)
  83. } else {
  84. bf.results.nextBaseFee = new(big.Int)
  85. }
  86. bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
  87. if len(percentiles) == 0 {
  88. // rewards were not requested, return null
  89. return
  90. }
  91. if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) {
  92. log.Error("Block or receipts are missing while reward percentiles are requested")
  93. return
  94. }
  95. bf.results.reward = make([]*big.Int, len(percentiles))
  96. if len(bf.block.Transactions()) == 0 {
  97. // return an all zero row if there are no transactions to gather data from
  98. for i := range bf.results.reward {
  99. bf.results.reward[i] = new(big.Int)
  100. }
  101. return
  102. }
  103. sorter := make(sortGasAndReward, len(bf.block.Transactions()))
  104. for i, tx := range bf.block.Transactions() {
  105. reward, _ := tx.EffectiveGasTip(bf.block.BaseFee())
  106. sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward}
  107. }
  108. sort.Stable(sorter)
  109. var txIndex int
  110. sumGasUsed := sorter[0].gasUsed
  111. for i, p := range percentiles {
  112. thresholdGasUsed := uint64(float64(bf.block.GasUsed()) * p / 100)
  113. for sumGasUsed < thresholdGasUsed && txIndex < len(bf.block.Transactions())-1 {
  114. txIndex++
  115. sumGasUsed += sorter[txIndex].gasUsed
  116. }
  117. bf.results.reward[i] = sorter[txIndex].reward
  118. }
  119. }
  120. // resolveBlockRange resolves the specified block range to absolute block numbers while also
  121. // enforcing backend specific limitations. The pending block and corresponding receipts are
  122. // also returned if requested and available.
  123. // Note: an error is only returned if retrieving the head header has failed. If there are no
  124. // retrievable blocks in the specified range then zero block count is returned with no error.
  125. func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) {
  126. var (
  127. headBlock *types.Header
  128. pendingBlock *types.Block
  129. pendingReceipts types.Receipts
  130. err error
  131. )
  132. // Get the chain's current head.
  133. if headBlock, err = oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err != nil {
  134. return nil, nil, 0, 0, err
  135. }
  136. head := rpc.BlockNumber(headBlock.Number.Uint64())
  137. // Fail if request block is beyond the chain's current head.
  138. if head < reqEnd {
  139. return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, reqEnd, head)
  140. }
  141. // Resolve block tag.
  142. if reqEnd < 0 {
  143. var (
  144. resolved *types.Header
  145. err error
  146. )
  147. switch reqEnd {
  148. case rpc.PendingBlockNumber:
  149. if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil {
  150. resolved = pendingBlock.Header()
  151. } else {
  152. // Pending block not supported by backend, process only until latest block.
  153. resolved = headBlock
  154. // Update total blocks to return to account for this.
  155. blocks--
  156. }
  157. case rpc.LatestBlockNumber:
  158. // Retrieved above.
  159. resolved = headBlock
  160. case rpc.SafeBlockNumber:
  161. resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber)
  162. case rpc.FinalizedBlockNumber:
  163. resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber)
  164. case rpc.EarliestBlockNumber:
  165. resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.EarliestBlockNumber)
  166. }
  167. if resolved == nil || err != nil {
  168. return nil, nil, 0, 0, err
  169. }
  170. // Absolute number resolved.
  171. reqEnd = rpc.BlockNumber(resolved.Number.Uint64())
  172. }
  173. // If there are no blocks to return, short circuit.
  174. if blocks == 0 {
  175. return nil, nil, 0, 0, nil
  176. }
  177. // Ensure not trying to retrieve before genesis.
  178. if int(reqEnd+1) < blocks {
  179. blocks = int(reqEnd + 1)
  180. }
  181. return pendingBlock, pendingReceipts, uint64(reqEnd), blocks, nil
  182. }
  183. // FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
  184. // The range can be specified either with absolute block numbers or ending with the latest
  185. // or pending block. Backends may or may not support gathering data from the pending block
  186. // or blocks older than a certain age (specified in maxHistory). The first block of the
  187. // actually processed range is returned to avoid ambiguity when parts of the requested range
  188. // are not available or when the head has changed during processing this request.
  189. // Three arrays are returned based on the processed blocks:
  190. // - reward: the requested percentiles of effective priority fees per gas of transactions in each
  191. // block, sorted in ascending order and weighted by gas used.
  192. // - baseFee: base fee per gas in the given block
  193. // - gasUsedRatio: gasUsed/gasLimit in the given block
  194. // Note: baseFee includes the next block after the newest of the returned range, because this
  195. // value can be derived from the newest block.
  196. func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
  197. if blocks < 1 {
  198. return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
  199. }
  200. maxFeeHistory := oracle.maxHeaderHistory
  201. if len(rewardPercentiles) != 0 {
  202. maxFeeHistory = oracle.maxBlockHistory
  203. }
  204. if blocks > maxFeeHistory {
  205. log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
  206. blocks = maxFeeHistory
  207. }
  208. for i, p := range rewardPercentiles {
  209. if p < 0 || p > 100 {
  210. return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
  211. }
  212. if i > 0 && p < rewardPercentiles[i-1] {
  213. return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
  214. }
  215. }
  216. var (
  217. pendingBlock *types.Block
  218. pendingReceipts []*types.Receipt
  219. err error
  220. )
  221. pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks)
  222. if err != nil || blocks == 0 {
  223. return common.Big0, nil, nil, nil, err
  224. }
  225. oldestBlock := lastBlock + 1 - uint64(blocks)
  226. var (
  227. next = oldestBlock
  228. results = make(chan *blockFees, blocks)
  229. )
  230. percentileKey := make([]byte, 8*len(rewardPercentiles))
  231. for i, p := range rewardPercentiles {
  232. binary.LittleEndian.PutUint64(percentileKey[i*8:(i+1)*8], math.Float64bits(p))
  233. }
  234. for i := 0; i < maxBlockFetchers && i < blocks; i++ {
  235. go func() {
  236. for {
  237. // Retrieve the next block number to fetch with this goroutine
  238. blockNumber := atomic.AddUint64(&next, 1) - 1
  239. if blockNumber > lastBlock {
  240. return
  241. }
  242. fees := &blockFees{blockNumber: blockNumber}
  243. if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() {
  244. fees.block, fees.receipts = pendingBlock, pendingReceipts
  245. fees.header = fees.block.Header()
  246. oracle.processBlock(fees, rewardPercentiles)
  247. results <- fees
  248. } else {
  249. cacheKey := struct {
  250. number uint64
  251. percentiles string
  252. }{blockNumber, string(percentileKey)}
  253. if p, ok := oracle.historyCache.Get(cacheKey); ok {
  254. fees.results = p.(processedFees)
  255. results <- fees
  256. } else {
  257. if len(rewardPercentiles) != 0 {
  258. fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber))
  259. if fees.block != nil && fees.err == nil {
  260. fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash())
  261. fees.header = fees.block.Header()
  262. }
  263. } else {
  264. fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber))
  265. }
  266. if fees.header != nil && fees.err == nil {
  267. oracle.processBlock(fees, rewardPercentiles)
  268. if fees.err == nil {
  269. oracle.historyCache.Add(cacheKey, fees.results)
  270. }
  271. }
  272. // send to results even if empty to guarantee that blocks items are sent in total
  273. results <- fees
  274. }
  275. }
  276. }
  277. }()
  278. }
  279. var (
  280. reward = make([][]*big.Int, blocks)
  281. baseFee = make([]*big.Int, blocks+1)
  282. gasUsedRatio = make([]float64, blocks)
  283. firstMissing = blocks
  284. )
  285. for ; blocks > 0; blocks-- {
  286. fees := <-results
  287. if fees.err != nil {
  288. return common.Big0, nil, nil, nil, fees.err
  289. }
  290. i := int(fees.blockNumber - oldestBlock)
  291. if fees.results.baseFee != nil {
  292. reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.results.reward, fees.results.baseFee, fees.results.nextBaseFee, fees.results.gasUsedRatio
  293. } else {
  294. // getting no block and no error means we are requesting into the future (might happen because of a reorg)
  295. if i < firstMissing {
  296. firstMissing = i
  297. }
  298. }
  299. }
  300. if firstMissing == 0 {
  301. return common.Big0, nil, nil, nil, nil
  302. }
  303. if len(rewardPercentiles) != 0 {
  304. reward = reward[:firstMissing]
  305. } else {
  306. reward = nil
  307. }
  308. baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing]
  309. return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, nil
  310. }