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