Skip to content

fix: retry estimatefee with more blocks when getting negative value - #1153

Open
grdddj wants to merge 1 commit into
masterfrom
grdddj/estimate_fee_negative_retry
Open

fix: retry estimatefee with more blocks when getting negative value#1153
grdddj wants to merge 1 commit into
masterfrom
grdddj/estimate_fee_negative_retry

Conversation

@grdddj

@grdddj grdddj commented Nov 5, 2024

Copy link
Copy Markdown
Contributor

Fix for dogecoin, when we receive a negative value from estimatefee, we increase the block query by one and try again ... with specified max retries (3 now)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds retry logic for fee estimation when a negative fee is returned, particularly addressing Dogecoin behavior. Introduces a helper with incremental block adjustments and updates the API handler to use it with a fixed retry limit.

  • New retry helper estimateFeeRetryNegative with block increment logic
  • Added max retry constant and replaced inline logic in apiEstimateFee
  • Modified error wrapping when retries fail

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread server/public.go
Comment on lines +1647 to +1664
// estimateFeeRetryNegative tries to estimate the fee X times if the fee is negative.
// It gradually increases the number of blocks to estimate the fee for.
func (s *PublicServer) estimateFeeRetryNegative(blocks int, conservative bool, maxRetries int) (big.Int, error) {
var fee big.Int
var err error
for i := 0; i < maxRetries; i++ {
fee, err = s.chain.EstimateSmartFee(blocks, conservative)
if err == nil && fee.Sign() != -1 {
return fee, nil
}
fee, err = s.chain.EstimateFee(blocks)
if err == nil && fee.Sign() != -1 {
return fee, nil
}
blocks++
}
return big.Int{}, fmt.Errorf("failed to estimate fee after %d retries", maxRetries)
}

Copilot AI Oct 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states it retries when the fee is negative, but the implementation also retries on RPC errors; additionally, 'X times' is ambiguous relative to two RPC calls per loop (EstimateSmartFee + EstimateFee). Update the comment to explicitly describe both retry conditions (negative result or error), note that up to 2 * maxRetries RPC calls are made, and clarify block increment behavior.

Copilot uses AI. Check for mistakes.
Comment thread server/public.go
Comment on lines +1654 to +1660
if err == nil && fee.Sign() != -1 {
return fee, nil
}
fee, err = s.chain.EstimateFee(blocks)
if err == nil && fee.Sign() != -1 {
return fee, nil
}

Copilot AI Oct 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each retry loop performs two RPC calls, doubling network load relative to the retry count. Consider either: (a) making EstimateSmartFee the first attempt and only falling back to EstimateFee if it errors (not when it returns a negative fee), or (b) counting each RPC invocation toward the retry budget to align cost with configured maxRetries.

Suggested change
if err == nil && fee.Sign() != -1 {
return fee, nil
}
fee, err = s.chain.EstimateFee(blocks)
if err == nil && fee.Sign() != -1 {
return fee, nil
}
if err == nil {
if fee.Sign() != -1 {
return fee, nil
}
// If EstimateSmartFee returns a negative fee, do not fallback, just retry with increased blocks
} else {
// Only fallback to EstimateFee if EstimateSmartFee errored
fee, err = s.chain.EstimateFee(blocks)
if err == nil && fee.Sign() != -1 {
return fee, nil
}
}

Copilot uses AI. Check for mistakes.
Comment thread server/public.go
}
blocks++
}
return big.Int{}, fmt.Errorf("failed to estimate fee after %d retries", maxRetries)

Copilot AI Oct 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The final error loses the underlying cause(s) (e.g., last RPC error or persistent negative fee) making debugging harder. Include the last non-nil error and optionally the last negative fee value, e.g.: return big.Int{}, fmt.Errorf("failed to estimate fee after %d retries: last error=%v last fee=%s", maxRetries, err, fee.String())

Suggested change
return big.Int{}, fmt.Errorf("failed to estimate fee after %d retries", maxRetries)
return big.Int{}, fmt.Errorf("failed to estimate fee after %d retries: last error=%v last fee=%s", maxRetries, err, fee.String())

Copilot uses AI. Check for mistakes.
Comment thread server/public.go
if err != nil {
return nil, err
}
return nil, api.NewAPIError(fmt.Sprintf("Failed to estimate fee: %v", err), true)

Copilot AI Oct 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrapped API error now surfaces only the generic retry failure message from estimateFeeRetryNegative (which currently omits root causes). After improving the underlying function's error detail, consider removing the generic 'Failed to estimate fee:' prefix or wrapping with additional context using %w to preserve stack (e.g., fmt.Errorf("estimating fee: %w", err)).

Suggested change
return nil, api.NewAPIError(fmt.Sprintf("Failed to estimate fee: %v", err), true)
return nil, api.NewAPIError(fmt.Errorf("estimating fee: %w", err).Error(), true)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants