Skip to content

Sparse Esplora fee estimates may fall back to 1 sat/vB for urgent targets #1028

Description

@ben-kaufman

TLDR

On mainnet, the Esplora fee estimator rejects a completely empty estimate map but accepts a non-empty map that lacks estimates for important confirmation targets.

When conversion fails for one of those missing targets, the code silently substitutes 1 sat/vB. This applies even to urgent targets such as UrgentOnChainSweep, whose normal fallback is substantially higher.

Affected code

src/chain/esplora.rs:368:

if estimates.is_empty() && self.config.network == Network::Bitcoin {
	return Err(Error::FeerateEstimationUpdateFailed);
}

for target in confirmation_targets {
	let num_blocks = get_num_block_defaults_for_target(target);

	let converted_estimate_sat_vb =
		esplora_client::convert_fee_rate(num_blocks, estimates.clone())
			.map_or(1.0, |converted| converted.max(1.0));

	let fee_rate =
		FeeRate::from_sat_per_kwu((converted_estimate_sat_vb * 250.0) as u64);

	let adjusted_fee_rate = apply_post_estimation_adjustments(target, fee_rate);
	new_fee_rate_cache.insert(target, adjusted_fee_rate);
}

Urgent targets include six-block confirmation estimates:

src/fee_estimator.rs:87:

match target {
	ConfirmationTarget::OnchainPayment => 6,
	ConfirmationTarget::ChannelFunding => 12,
	ConfirmationTarget::Lightning(ldk_target) => match ldk_target {
		LdkConfirmationTarget::MaximumFeeEstimate => 1,
		LdkConfirmationTarget::UrgentOnChainSweep => 6,
		// ...
	},
}

The existing target-specific fallback for UrgentOnChainSweep is 5,000 sat/kwu:

src/fee_estimator.rs:112:

LdkConfirmationTarget::UrgentOnChainSweep => 5000,

Impact

A partial or malformed Esplora response can replace urgent fee estimates with the relay-floor value instead of the target-specific fallback.

During a high-fee period, this can delay on-chain payments or time-sensitive channel sweeps. LDK fee bumping and the available confirmation windows reduce the likelihood of immediate loss, but the fallback moves in the unsafe direction for urgent transactions.

An arbitrary Lightning counterparty cannot directly supply this response. Exploitation would require control of, or interference with, the configured Esplora source. The issue can also occur non-maliciously if the source returns a sparse estimate map.

Issue #331 and PR #249 concern fee estimation generally but do not appear to track this sparse-map fallback behavior.

Proposed fix

On conversion failure, use the existing fallback for the requested target:

let fee_rate = esplora_client::convert_fee_rate(num_blocks, estimates.clone())
	.map(|estimate| {
		FeeRate::from_sat_per_kwu((estimate.max(1.0) * 250.0) as u64)
	})
	.unwrap_or_else(|| {
		FeeRate::from_sat_per_kwu(get_fallback_rate_for_target(target) as u64)
	});

For mainnet, also consider validating that the response provides sufficient coverage for all critical targets before replacing the current cache.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions