@@ -2,9 +2,12 @@ package backend
22
33import (
44 "fmt"
5+ gomath "math"
56 "math/big"
7+ "sync"
68
79 "github.com/ethereum/go-ethereum/common/hexutil"
10+ "github.com/ethereum/go-ethereum/common/math"
811 ethtypes "github.com/ethereum/go-ethereum/core/types"
912 "github.com/ethereum/go-ethereum/params"
1013 "github.com/ethereum/go-ethereum/rpc"
@@ -17,7 +20,8 @@ import (
1720 feemarkettypes "github.com/cosmos/evm/x/feemarket/types"
1821 evmtypes "github.com/cosmos/evm/x/vm/types"
1922
20- "cosmossdk.io/math"
23+ errorsmod "cosmossdk.io/errors"
24+ sdkmath "cosmossdk.io/math"
2125
2226 sdk "github.com/cosmos/cosmos-sdk/types"
2327)
@@ -69,7 +73,7 @@ func (b *Backend) BaseFee(blockRes *cmtrpctypes.ResultBlockResults) (*big.Int, e
6973 for i := len (blockRes .FinalizeBlockEvents ) - 1 ; i >= 0 ; i -- {
7074 evt := blockRes .FinalizeBlockEvents [i ]
7175 if evt .Type == evmtypes .EventTypeFeeMarket && len (evt .Attributes ) > 0 {
72- baseFee , ok := math .NewIntFromString (evt .Attributes [0 ].Value )
76+ baseFee , ok := sdkmath .NewIntFromString (evt .Attributes [0 ].Value )
7377 if ok {
7478 return baseFee .BigInt (), nil
7579 }
@@ -143,20 +147,45 @@ func (b *Backend) GetCoinbase() (sdk.AccAddress, error) {
143147 return address , nil
144148}
145149
150+ var (
151+ errInvalidPercentile = fmt .Errorf ("invalid reward percentile" )
152+ errRequestBeyondHead = fmt .Errorf ("request beyond head block" )
153+ )
154+
146155// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
147156func (b * Backend ) FeeHistory (
148- userBlockCount , // number blocks to fetch, maximum is 100
157+ userBlockCount math. HexOrDecimal64 , // number blocks to fetch, maximum is 100
149158 lastBlock rpc.BlockNumber , // the block to start search , to oldest
150159 rewardPercentiles []float64 , // percentiles to fetch reward
151160) (* rpctypes.FeeHistoryResult , error ) {
152- blockEnd := int64 (lastBlock ) //#nosec G115 -- checked for int overflow already
153-
154- if blockEnd < 0 {
155- blockNumber , err := b .BlockNumber ()
156- if err != nil {
157- return nil , err
161+ for i , p := range rewardPercentiles {
162+ if p < 0 || p > 100 {
163+ return nil , fmt .Errorf ("%w: %f" , errInvalidPercentile , p )
158164 }
159- blockEnd = int64 (blockNumber ) //#nosec G115 -- checked for int overflow already
165+ if i > 0 && p < rewardPercentiles [i - 1 ] {
166+ return nil , fmt .Errorf ("%w: #%d:%f > #%d:%f" , errInvalidPercentile , i - 1 , rewardPercentiles [i - 1 ], i , p )
167+ }
168+ }
169+ blkNumber , err := b .BlockNumber ()
170+ if err != nil {
171+ return nil , err
172+ }
173+ blockNumber := int64 (blkNumber ) //#nosec G115
174+ blockEnd := int64 (lastBlock ) //#nosec G115
175+
176+ switch lastBlock {
177+ case rpc .EarliestBlockNumber :
178+ blockEnd = 0
179+ case rpc .SafeBlockNumber , rpc .FinalizedBlockNumber , rpc .LatestBlockNumber , rpc .PendingBlockNumber :
180+ blockEnd = blockNumber
181+ default :
182+ if blockEnd < 0 {
183+ blockEnd = blockNumber
184+ }
185+ }
186+
187+ if blockNumber < blockEnd {
188+ return nil , fmt .Errorf ("%w: requested %d, head %d" , errRequestBeyondHead , blockEnd , blockNumber )
160189 }
161190
162191 blocks := int64 (userBlockCount ) // #nosec G115 -- checked for int overflow already
@@ -165,7 +194,7 @@ func (b *Backend) FeeHistory(
165194 return nil , fmt .Errorf ("FeeHistory user block count %d higher than %d" , blocks , maxBlockCount )
166195 }
167196
168- if blockEnd + 1 < blocks {
197+ if blockEnd < gomath . MaxInt64 && blockEnd + 1 < blocks {
169198 blocks = blockEnd + 1
170199 }
171200 // Ensure not trying to retrieve before genesis.
@@ -184,46 +213,85 @@ func (b *Backend) FeeHistory(
184213
185214 // rewards should only be calculated if reward percentiles were included
186215 calculateRewards := rewardCount != 0
216+ const maxBlockFetchers = 4
217+ for blockID := blockStart ; blockID <= blockEnd ; blockID += maxBlockFetchers {
218+ wg := sync.WaitGroup {}
219+ wgDone := make (chan bool )
220+ chanErr := make (chan error )
221+ for i := 0 ; i < maxBlockFetchers ; i ++ {
222+ if blockID + int64 (i ) >= blockEnd + 1 {
223+ break
224+ }
225+ value := blockID - blockStart + int64 (i )
226+ if value > gomath .MaxInt32 || value < gomath .MinInt32 {
227+ return nil , fmt .Errorf ("integer overflow: calculated value %d exceeds int32 limits" , value )
228+ }
229+ wg .Add (1 )
230+ go func (index int32 ) {
231+ defer func () {
232+ if r := recover (); r != nil {
233+ err = errorsmod .Wrapf (errorsmod .ErrPanic , "%v" , r )
234+ b .Logger .Error ("FeeHistory panicked" , "error" , err )
235+ chanErr <- err
236+ }
237+ wg .Done ()
238+ }()
239+ // fetch block
240+ // tendermint block
241+ blockNum := rpctypes .BlockNumber (blockStart + int64 (index ))
242+ tendermintblock , err := b .TendermintBlockByNumber (blockNum )
243+ if tendermintblock == nil {
244+ chanErr <- err
245+ return
246+ }
187247
188- // fetch block
189- for blockID := blockStart ; blockID <= blockEnd ; blockID ++ {
190- index := int32 (blockID - blockStart ) // #nosec G115
191- // tendermint block
192- tendermintblock , err := b .TendermintBlockByNumber (rpctypes .BlockNumber (blockID ))
193- if tendermintblock == nil {
194- return nil , err
195- }
196-
197- // eth block
198- ethBlock , err := b .GetBlockByNumber (rpctypes .BlockNumber (blockID ), true )
199- if ethBlock == nil {
200- return nil , err
201- }
248+ // eth block
249+ ethBlock , err := b .GetBlockByNumber (blockNum , true )
250+ if ethBlock == nil {
251+ chanErr <- err
252+ return
253+ }
202254
203- // tendermint block result
204- tendermintBlockResult , err := b .RPCClient .BlockResults (b .Ctx , & tendermintblock .Block .Height )
205- if tendermintBlockResult == nil {
206- b .Logger .Debug ("block result not found" , "height" , tendermintblock .Block .Height , "error" , err .Error ())
207- return nil , err
208- }
255+ // tendermint block result
256+ tendermintBlockResult , err := b .TendermintBlockResultByNumber (& tendermintblock .Block .Height )
257+ if tendermintBlockResult == nil {
258+ b .Logger .Debug ("block result not found" , "height" , tendermintblock .Block .Height , "error" , err .Error ())
259+ chanErr <- err
260+ return
261+ }
209262
210- oneFeeHistory := rpctypes.OneFeeHistory {}
211- err = b .processBlock (tendermintblock , & ethBlock , rewardPercentiles , tendermintBlockResult , & oneFeeHistory )
212- if err != nil {
213- return nil , err
214- }
263+ oneFeeHistory := rpctypes.OneFeeHistory {}
264+ err = b .ProcessBlocker (tendermintblock , & ethBlock , rewardPercentiles , tendermintBlockResult , & oneFeeHistory )
265+ if err != nil {
266+ chanErr <- err
267+ return
268+ }
215269
216- // copy
217- thisBaseFee [index ] = (* hexutil .Big )(oneFeeHistory .BaseFee )
218- thisBaseFee [index + 1 ] = (* hexutil .Big )(oneFeeHistory .NextBaseFee )
219- thisGasUsedRatio [index ] = oneFeeHistory .GasUsedRatio
220- if calculateRewards {
221- for j := 0 ; j < rewardCount ; j ++ {
222- reward [index ][j ] = (* hexutil .Big )(oneFeeHistory .Reward [j ])
223- if reward [index ][j ] == nil {
224- reward [index ][j ] = (* hexutil .Big )(big .NewInt (0 ))
270+ // copy
271+ thisBaseFee [index ] = (* hexutil .Big )(oneFeeHistory .BaseFee )
272+ // only use NextBaseFee as last item to avoid concurrent write
273+ if int (index ) == len (thisBaseFee )- 2 {
274+ thisBaseFee [index + 1 ] = (* hexutil .Big )(oneFeeHistory .NextBaseFee )
225275 }
226- }
276+ thisGasUsedRatio [index ] = oneFeeHistory .GasUsedRatio
277+ if calculateRewards {
278+ for j := 0 ; j < rewardCount ; j ++ {
279+ reward [index ][j ] = (* hexutil .Big )(oneFeeHistory .Reward [j ])
280+ if reward [index ][j ] == nil {
281+ reward [index ][j ] = (* hexutil .Big )(big .NewInt (0 ))
282+ }
283+ }
284+ }
285+ }(int32 (value ))
286+ }
287+ go func () {
288+ wg .Wait ()
289+ close (wgDone )
290+ }()
291+ select {
292+ case <- wgDone :
293+ case err := <- chanErr :
294+ return nil , err
227295 }
228296 }
229297
0 commit comments