Skip to content

Commit a8005df

Browse files
committed
fix rate limiting when fetching headers, fix for fee history if there is only one block, fix for fee equalizer (+ tests)
1 parent aec6319 commit a8005df

File tree

5 files changed

+431
-33
lines changed

5 files changed

+431
-33
lines changed

.github/workflows/seth-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ jobs:
5858
network-type: Anvil
5959
url: "http://localhost:8545"
6060
extra_flags: "''"
61-
- regex: "'TestContractMap|TestGasEstimator|TestRPCHealthCheck|TestUtil|TestContract'"
61+
- regex: "'TestContractMap|TestGasEstimator|TestRPCHealthCheck|TestUtil|TestContract|TestGasAdjuster'"
6262
network-type: Geth
6363
url: "ws://localhost:8546"
6464
extra_flags: "-race"
6565
# TODO: still expects Geth WS URL for some reason
66-
- regex: "'TestContractMap|TestGasEstimator|TestRPCHealthCheck|TestUtil|TestContract'"
66+
- regex: "'TestContractMap|TestGasEstimator|TestRPCHealthCheck|TestUtil|TestContract|TestGasAdjuster'"
6767
network-type: Anvil
6868
url: "http://localhost:8545"
6969
extra_flags: "-race"

seth/client.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,11 @@ func NewClientRaw(
375375
// root key is element 0 in ephemeral
376376
for _, addr := range c.Addresses[1:] {
377377
eg.Go(func() error {
378-
return c.TransferETHFromKey(egCtx, 0, addr.Hex(), bd.AddrFunding, gasPrice)
378+
err := c.TransferETHFromKey(egCtx, 0, addr.Hex(), bd.AddrFunding, gasPrice)
379+
if err != nil {
380+
return fmt.Errorf("failed to fund ephemeral address %s: %w", addr.Hex(), err)
381+
}
382+
return nil
379383
})
380384
}
381385
if err := eg.Wait(); err != nil {
@@ -987,7 +991,7 @@ func (m *Client) CalculateGasEstimations(request GasEstimationRequest) GasEstima
987991
defer cancel()
988992

989993
var disableEstimationsIfNeeded = func(err error) {
990-
if errors.Is(err, GasEstimationErr) {
994+
if errors.Is(err, ErrGasEstimation) {
991995
L.Warn().Msg("Received incorrect gas estimations. Disabling them and reverting to hardcoded values. Remember to update your config!")
992996
m.Cfg.Network.GasPriceEstimationEnabled = false
993997
}

seth/gas.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func (m *GasEstimator) Stats(ctx context.Context, blockCount uint64, priorityPer
3838
"Alternatively, set 'gas_price_estimation_blocks = 0' to disable block-based estimation",
3939
err)
4040
}
41+
4142
if currentBlock == 0 {
4243
return GasSuggestions{}, fmt.Errorf("current block number is zero, which indicates either:\n" +
4344
" 1. The network hasn't produced any blocks yet (check if network is running)\n" +
@@ -47,7 +48,7 @@ func (m *GasEstimator) Stats(ctx context.Context, blockCount uint64, priorityPer
4748
"You can set 'gas_price_estimation_blocks = 0' to disable block-based estimation")
4849
}
4950
if blockCount >= currentBlock {
50-
blockCount = currentBlock - 1
51+
blockCount = max(currentBlock-1, 1) // avoid a case, when we ask for more blocks than exist and when currentBlock = 1
5152
}
5253

5354
hist, err := m.Client.Client.FeeHistory(ctx, blockCount, big.NewInt(mustSafeInt64(currentBlock)), []float64{priorityPerc})

seth/gas_adjuster.go

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ const (
3636
)
3737

3838
var (
39-
GasEstimationErr = errors.New("incorrect gas data received from node. Skipping gas estimation")
40-
BlockFetchingErr = errors.New("failed to fetch enough block headers for congestion calculation")
39+
ErrGasEstimation = errors.New("incorrect gas data received from node. Skipping gas estimation")
40+
ErrBlockFetching = errors.New("failed to fetch enough block headers for congestion calculation")
4141
)
4242

4343
// CalculateNetworkCongestionMetric calculates a simple congestion metric based on the last N blocks
@@ -47,10 +47,10 @@ func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy
4747
err := fmt.Errorf("header cache is not initialized. " +
4848
"This is an internal error that shouldn't happen. " +
4949
"If you see this, please open a GitHub issue at https://github.com/smartcontractkit/chainlink-testing-framework/issues with your configuration details")
50-
err = fmt.Errorf("%w: %v", BlockFetchingErr, err)
50+
err = fmt.Errorf("%w: %v", ErrBlockFetching, err)
5151
return 0, err
5252
}
53-
var getHeaderData = func(bn *big.Int) (*types.Header, error) {
53+
var getHeaderData = func(ctx context.Context, bn *big.Int) (*types.Header, error) {
5454
if bn == nil {
5555
return nil, fmt.Errorf("block number is nil")
5656
}
@@ -66,7 +66,7 @@ func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy
6666
timeout = 6
6767
}
6868

69-
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(mustSafeInt64(timeout))*time.Second)
69+
ctx, cancel := context.WithTimeout(ctx, time.Duration(mustSafeInt64(timeout))*time.Second)
7070
defer cancel()
7171
header, err := m.Client.HeaderByNumber(ctx, bn)
7272
if err != nil {
@@ -81,15 +81,15 @@ func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy
8181
defer cancel()
8282
lastBlockNumber, err := m.Client.BlockNumber(ctx)
8383
if err != nil {
84-
err = fmt.Errorf("%w: %v", BlockFetchingErr, err)
84+
err = fmt.Errorf("%w: %v", ErrBlockFetching, err)
8585
return 0, err
8686
}
8787

8888
L.Trace().Msgf("Block range for gas calculation: %d - %d", lastBlockNumber-blocksNumber, lastBlockNumber)
8989

90-
lastBlock, err := getHeaderData(big.NewInt(mustSafeInt64(lastBlockNumber)))
90+
lastBlock, err := getHeaderData(ctx, big.NewInt(mustSafeInt64(lastBlockNumber)))
9191
if err != nil {
92-
err = fmt.Errorf("%w: %v", BlockFetchingErr, err)
92+
err = fmt.Errorf("%w: %v", ErrBlockFetching, err)
9393
return 0, err
9494
}
9595

@@ -98,17 +98,17 @@ func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy
9898

9999
var wg sync.WaitGroup
100100
dataCh := make(chan *types.Header)
101+
defer close(dataCh)
101102

102-
limit := ratelimit.New(4) // 4 concurrent requests
103103
go func() {
104-
limit.Take()
105104
for header := range dataCh {
106105
headers = append(headers, header)
107106
// placed here, because we want to wait for all headers to be received and added to slice before continuing
108107
wg.Done()
109108
}
110109
}()
111110

111+
limit := ratelimit.New(4) // 4 concurrent requests
112112
startTime := time.Now()
113113
for i := lastBlockNumber; i > lastBlockNumber-blocksNumber; i-- {
114114
// better safe than sorry (might happen for brand-new chains)
@@ -118,7 +118,8 @@ func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy
118118

119119
wg.Add(1)
120120
go func(bn *big.Int) {
121-
header, err := getHeaderData(bn)
121+
limit.Take()
122+
header, err := getHeaderData(ctx, bn)
122123
if err != nil {
123124
L.Debug().Msgf("Failed to get block %d header due to: %s", bn.Int64(), err.Error())
124125
wg.Done()
@@ -129,7 +130,6 @@ func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy
129130
}
130131

131132
wg.Wait()
132-
close(dataCh)
133133

134134
endTime := time.Now()
135135
L.Debug().Msgf("Time to fetch %d block headers: %v", blocksNumber, endTime.Sub(startTime))
@@ -148,7 +148,7 @@ func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy
148148
" 3. Disable gas estimation: set gas_price_estimation_enabled = false\n"+
149149
" 4. Reduce gas_price_estimation_blocks to fetch fewer blocks",
150150
minBlockCount, len(headers), float64(len(headers))/float64(blocksNumber)*100)
151-
err = fmt.Errorf("%w: %v", BlockFetchingErr, err)
151+
err = fmt.Errorf("%w: %v", ErrBlockFetching, err)
152152

153153
return 0, err
154154
}
@@ -163,7 +163,7 @@ func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy
163163
"Valid strategies are: 'simple' (equal weight) or 'newest_first' (recent blocks weighted more).\n"+
164164
"This is likely a configuration error. Check your gas estimation settings",
165165
strategy)
166-
err = fmt.Errorf("%w: %v", BlockFetchingErr, err)
166+
err = fmt.Errorf("%w: %v", ErrBlockFetching, err)
167167
return 0, err
168168
}
169169
}
@@ -217,15 +217,15 @@ func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (
217217
// fallback to current fees if historical fetching fails
218218
baseFee, currentGasTip, err = m.currentIP1559Fees(ctx)
219219
if err != nil {
220-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
220+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
221221
return
222222
}
223223
L.Debug().Msg("Falling back to current EIP-1559 fees for gas estimation")
224224
}
225225
} else {
226226
baseFee, currentGasTip, err = m.currentIP1559Fees(ctx)
227227
if err != nil {
228-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
228+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
229229
return
230230
}
231231
}
@@ -237,7 +237,7 @@ func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (
237237
" 1. Use a different RPC endpoint\n" +
238238
" 2. Disable gas estimation: set gas_price_estimation_enabled = false in config\n" +
239239
" 3. Set explicit gas values: gas_price, gas_fee_cap, and gas_tip_cap (in your config (seth.toml or ClientBuilder)")
240-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
240+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
241241
return
242242
}
243243

@@ -263,7 +263,7 @@ func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (
263263
L.Debug().Msg("Suggested tip is 0.0. Will use historical base fee as tip.")
264264
currentGasTip = big.NewInt(int64(baseFee64))
265265
}
266-
} else if baseFeeTipMagnitudeDiff < 3 {
266+
} else if baseFeeTipMagnitudeDiff < -3 {
267267
L.Debug().Msg("Historical base fee is 3 orders of magnitude lower than suggested tip. Will use suggested tip as base fee.")
268268
baseFee64 = float64(currentGasTip.Int64())
269269
} else if baseFeeTipMagnitudeDiff > 3 {
@@ -278,7 +278,7 @@ func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (
278278
Int64("SuggestedTip", currentGasTip.Int64()).
279279
Msgf("Incorrect gas data received from node: base fee was 0. Skipping gas estimation")
280280
err = errors.New("incorrect gas data received from node: base fee was 0. Skipping gas estimation")
281-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
281+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
282282
return
283283
}
284284

@@ -295,7 +295,7 @@ func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (
295295
var adjustmentFactor float64
296296
adjustmentFactor, err = getAdjustmentFactor(priority)
297297
if err != nil {
298-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
298+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
299299
return
300300
}
301301

@@ -328,7 +328,7 @@ func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (
328328
var bufferAdjustment float64
329329
bufferAdjustment, err = getCongestionFactor(congestionClassification)
330330
if err != nil {
331-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
331+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
332332
return
333333
}
334334

@@ -339,8 +339,8 @@ func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (
339339
// Apply buffer also to the tip
340340
bufferedTipCapFloat := new(big.Float).Mul(new(big.Float).SetInt(adjustedTipCap), big.NewFloat(bufferAdjustment))
341341
adjustedTipCap, _ = bufferedTipCapFloat.Int(nil)
342-
} else if !errors.Is(err, BlockFetchingErr) {
343-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
342+
} else if !errors.Is(err, ErrBlockFetching) {
343+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
344344
return
345345
} else {
346346
L.Debug().
@@ -565,7 +565,7 @@ func (m *Client) GetSuggestedLegacyFees(ctx context.Context, priority string) (a
565565
}))
566566

567567
if retryErr != nil {
568-
err = fmt.Errorf("%w: %v", GasEstimationErr, retryErr)
568+
err = fmt.Errorf("%w: %v", ErrGasEstimation, retryErr)
569569
return
570570
}
571571

@@ -577,7 +577,7 @@ func (m *Client) GetSuggestedLegacyFees(ctx context.Context, priority string) (a
577577
var adjustmentFactor float64
578578
adjustmentFactor, err = getAdjustmentFactor(priority)
579579
if err != nil {
580-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
580+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
581581
return
582582
}
583583

@@ -605,15 +605,15 @@ func (m *Client) GetSuggestedLegacyFees(ctx context.Context, priority string) (a
605605
var bufferAdjustment float64
606606
bufferAdjustment, err = getCongestionFactor(congestionClassification)
607607
if err != nil {
608-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
608+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
609609
return
610610
}
611611

612612
// Calculate and apply the buffer.
613613
bufferedGasPriceFloat := new(big.Float).Mul(new(big.Float).SetInt(adjustedGasPrice), big.NewFloat(bufferAdjustment))
614614
adjustedGasPrice, _ = bufferedGasPriceFloat.Int(nil)
615-
} else if !errors.Is(err, BlockFetchingErr) {
616-
err = fmt.Errorf("%w: %v", GasEstimationErr, err)
615+
} else if !errors.Is(err, ErrBlockFetching) {
616+
err = fmt.Errorf("%w: %v", ErrGasEstimation, err)
617617
return
618618
} else {
619619
L.Debug().

0 commit comments

Comments
 (0)