Skip to content

fix(electrum): gate low-fee estimate fallback to non-mainnet networks#4019

Open
lrsaturnino wants to merge 1 commit into
stack/testnet4-04-go-testnet4-runtimefrom
fix-electrum-mainnet-fee-fallback-gate
Open

fix(electrum): gate low-fee estimate fallback to non-mainnet networks#4019
lrsaturnino wants to merge 1 commit into
stack/testnet4-04-go-testnet4-runtimefrom
fix-electrum-mainnet-fee-fallback-gate

Conversation

@lrsaturnino
Copy link
Copy Markdown
Member

Problem

PR #4002 added an Electrum fee-estimate fallback: when blockchain.estimatefee returns no estimate for any confirmation target (common on quiet testnet4 mempools, which answer -32603 for every target), EstimateSatPerVByteFee returns a hardcoded 2 sat/vByte instead of an error.

That fallback is network-blind. Before, an oracle failure returned an error and no transaction was broadcast on a guessed feerate (fail-safe). After, the same failure yields a fixed 2 sat/vByte that flows into the production tBTC proposal fee paths (tbtcpg/deposit_sweep.go, redemptions.go, moving_funds.go, moved_funds_sweep.go) on mainnet as well, with no minimum-feerate floor in TransactionFeeEstimator.EstimateFee. On mainnet a 2 sat/vByte transaction can be left unconfirmable under congestion, or evicted if the dynamic mempool minimum feerate rises above it — stalling sweeps and redemptions (delayed mints / undelivered BTC). The funds are not lost, but operations can get stuck.

Solution

Gate the fallback to networks where an underpriced transaction is economically harmless — testnet, testnet4, regtest. On mainnet, and on any unset or unknown network, the oracle failure is surfaced as an error so callers do not broadcast at a guessed feerate (fail-closed default).

  • A new electrum.Config.Network field carries the resolved Bitcoin network into the connection. It is set from the client configuration in config.resolveElectrum, tagged mapstructure:"-", and assigned after config unmarshal — so a config-file key cannot re-enable the fallback on mainnet.
  • The fallback decision is factored into pure helpers (feeFallbackResult and lowFeeFallbackAllowed) so it can be tested deterministically without a live Electrum client.
  • testnet4's existing graceful degradation is preserved; only mainnet (and unknown-network) behavior changes — back to the pre-feat(go): add testnet4 mapping and harden runtime integration behavior #4002 fail-safe.

Tests

  • New unit test TestFeeFallbackResult covers the decision per network: mainnet and unknown fail safe with an error; testnet/testnet4/regtest use the fallback; transport-level failures always error.
  • config.TestResolveElectrum now asserts the resolved network is propagated into the Electrum config.
  • The Electrum integration test configs carry their network so the integration path exercises the gate rather than defaulting to Unknown.
  • Local CI is green: gofmt -l, go vet, go build ./..., go test (changed packages), go vet -tags=integration (compile), Staticcheck 2025.1.1, and gosec (0 issues).

Notes

This is a follow-up to the review of #4002 and is based on its head branch (stack/testnet4-04-go-testnet4-runtime), so the diff is scoped to this fix.

When the Electrum fee oracle returns no estimate for any confirmation
target, EstimateSatPerVByteFee returned a hardcoded 2 sat/vByte for every
network. On mainnet this turns a fail-safe (error, no broadcast) into a
fail-broadcast: the tBTC sweep and redemption fee paths build and broadcast
transactions at a feerate that can be left unconfirmable or evicted under
congestion, stalling sweeps and redemptions.

Gate the fallback to test networks (testnet, testnet4, regtest) via a new
Electrum Config.Network field resolved from the client configuration. On
mainnet, and on any unset or unknown network, the oracle failure is surfaced
as an error so callers do not broadcast at a guessed feerate. The field is
tagged mapstructure:"-" and set after config unmarshal, so a config key
cannot re-enable the fallback on mainnet.

Add a unit test covering the fallback decision per network and propagate the
resolved network into the Electrum integration test configs.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aef79111-4995-41fc-af99-50b3f50d2b10

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-electrum-mainnet-fee-fallback-gate

Comment @coderabbitai help to get the list of available commands and usage tips.

@lrsaturnino lrsaturnino marked this pull request as ready for review June 6, 2026 05:39
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