Skip to content

Commit 043ab75

Browse files
committed
Introduce FeerateStrategy enum for onchain claims
This refactors the existing `force_feerate_bump` flag into an enum as we plan to introduce a new flag/enum variant in a future commit.
1 parent 71b4bd0 commit 043ab75

File tree

3 files changed

+68
-55
lines changed

3 files changed

+68
-55
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use crate::chain::{BestBlock, WatchedOutput};
4444
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
4545
use crate::chain::transaction::{OutPoint, TransactionData};
4646
use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::WriteableEcdsaChannelSigner, SignerProvider, EntropySource};
47-
use crate::chain::onchaintx::{ClaimEvent, OnchainTxHandler};
47+
use crate::chain::onchaintx::{ClaimEvent, FeerateStrategy, OnchainTxHandler};
4848
use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
4949
use crate::chain::Filter;
5050
use crate::util::logger::{Logger, Record};
@@ -1765,7 +1765,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
17651765
let logger = WithChannelMonitor::from_impl(logger, &*inner);
17661766
let current_height = inner.best_block.height;
17671767
inner.onchain_tx_handler.rebroadcast_pending_claims(
1768-
current_height, &broadcaster, &fee_estimator, &logger,
1768+
current_height, FeerateStrategy::HighestOfPreviousOrNew, &broadcaster, &fee_estimator, &logger,
17691769
);
17701770
}
17711771

lightning/src/chain/onchaintx.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,15 @@ pub(crate) enum OnchainClaim {
210210
Event(ClaimEvent),
211211
}
212212

213+
/// Represents the different feerates a pending request can use when generating a claim.
214+
pub(crate) enum FeerateStrategy {
215+
/// We must pick the highest between the most recently used and the current feerate estimate.
216+
HighestOfPreviousOrNew,
217+
/// We must force a bump of the most recently used feerate, either by using the current feerate
218+
/// estimate if it's higher, or manually bumping.
219+
ForceBump,
220+
}
221+
213222
/// OnchainTxHandler receives claiming requests, aggregates them if it's sound, broadcast and
214223
/// do RBF bumping if possible.
215224
#[derive(Clone)]
@@ -474,8 +483,8 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
474483
/// invoking this every 30 seconds, or lower if running in an environment with spotty
475484
/// connections, like on mobile.
476485
pub(super) fn rebroadcast_pending_claims<B: Deref, F: Deref, L: Logger>(
477-
&mut self, current_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>,
478-
logger: &L,
486+
&mut self, current_height: u32, feerate_strategy: FeerateStrategy, broadcaster: &B,
487+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
479488
)
480489
where
481490
B::Target: BroadcasterInterface,
@@ -488,7 +497,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
488497
bump_requests.push((*claim_id, request.clone()));
489498
}
490499
for (claim_id, request) in bump_requests {
491-
self.generate_claim(current_height, &request, false /* force_feerate_bump */, fee_estimator, logger)
500+
self.generate_claim(current_height, &request, &feerate_strategy, fee_estimator, logger)
492501
.map(|(_, new_feerate, claim)| {
493502
let mut bumped_feerate = false;
494503
if let Some(mut_request) = self.pending_claim_requests.get_mut(&claim_id) {
@@ -528,7 +537,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
528537
/// Panics if there are signing errors, because signing operations in reaction to on-chain
529538
/// events are not expected to fail, and if they do, we may lose funds.
530539
fn generate_claim<F: Deref, L: Logger>(
531-
&mut self, cur_height: u32, cached_request: &PackageTemplate, force_feerate_bump: bool,
540+
&mut self, cur_height: u32, cached_request: &PackageTemplate, feerate_strategy: &FeerateStrategy,
532541
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
533542
) -> Option<(u32, u64, OnchainClaim)>
534543
where F::Target: FeeEstimator,
@@ -577,7 +586,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
577586
if cached_request.is_malleable() {
578587
if cached_request.requires_external_funding() {
579588
let target_feerate_sat_per_1000_weight = cached_request.compute_package_feerate(
580-
fee_estimator, ConfirmationTarget::OnChainSweep, force_feerate_bump
589+
fee_estimator, ConfirmationTarget::OnChainSweep, feerate_strategy,
581590
);
582591
if let Some(htlcs) = cached_request.construct_malleable_package_with_external_funding(self) {
583592
return Some((
@@ -597,7 +606,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
597606
let predicted_weight = cached_request.package_weight(&self.destination_script);
598607
if let Some((output_value, new_feerate)) = cached_request.compute_package_output(
599608
predicted_weight, self.destination_script.dust_value().to_sat(),
600-
force_feerate_bump, fee_estimator, logger,
609+
feerate_strategy, fee_estimator, logger,
601610
) {
602611
assert!(new_feerate != 0);
603612

@@ -630,7 +639,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
630639

631640
let conf_target = ConfirmationTarget::OnChainSweep;
632641
let package_target_feerate_sat_per_1000_weight = cached_request
633-
.compute_package_feerate(fee_estimator, conf_target, force_feerate_bump);
642+
.compute_package_feerate(fee_estimator, conf_target, feerate_strategy);
634643
if let Some(input_amount_sat) = output.funding_amount {
635644
let fee_sat = input_amount_sat - tx.output.iter().map(|output| output.value).sum::<u64>();
636645
let commitment_tx_feerate_sat_per_1000_weight =
@@ -767,7 +776,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
767776
// height timer expiration (i.e in how many blocks we're going to take action).
768777
for mut req in preprocessed_requests {
769778
if let Some((new_timer, new_feerate, claim)) = self.generate_claim(
770-
cur_height, &req, true /* force_feerate_bump */, &*fee_estimator, &*logger,
779+
cur_height, &req, &FeerateStrategy::ForceBump, &*fee_estimator, &*logger,
771780
) {
772781
req.set_timer(new_timer);
773782
req.set_feerate(new_feerate);
@@ -967,7 +976,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
967976
log_trace!(logger, "Bumping {} candidates", bump_candidates.len());
968977
for (claim_id, request) in bump_candidates.iter() {
969978
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(
970-
cur_height, &request, true /* force_feerate_bump */, &*fee_estimator, &*logger,
979+
cur_height, &request, &FeerateStrategy::ForceBump, &*fee_estimator, &*logger,
971980
) {
972981
match bump_claim {
973982
OnchainClaim::Tx(bump_tx) => {
@@ -1048,7 +1057,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
10481057
// `height` is the height being disconnected, so our `current_height` is 1 lower.
10491058
let current_height = height - 1;
10501059
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(
1051-
current_height, &request, true /* force_feerate_bump */, fee_estimator, logger
1060+
current_height, &request, &FeerateStrategy::ForceBump, fee_estimator, logger
10521061
) {
10531062
request.set_timer(new_timer);
10541063
request.set_feerate(new_feerate);

lightning/src/chain/package.rs

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint};
2929
use crate::ln::msgs::DecodeError;
3030
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT, compute_feerate_sat_per_1000_weight, FEERATE_FLOOR_SATS_PER_KW};
3131
use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
32-
use crate::chain::onchaintx::{ExternalHTLCClaim, OnchainTxHandler};
32+
use crate::chain::onchaintx::{FeerateStrategy, ExternalHTLCClaim, OnchainTxHandler};
3333
use crate::util::logger::Logger;
3434
use crate::util::ser::{Readable, Writer, Writeable, RequiredWrapper};
3535

@@ -963,7 +963,7 @@ impl PackageTemplate {
963963
/// which was used to generate the value. Will not return less than `dust_limit_sats` for the
964964
/// value.
965965
pub(crate) fn compute_package_output<F: Deref, L: Logger>(
966-
&self, predicted_weight: u64, dust_limit_sats: u64, force_feerate_bump: bool,
966+
&self, predicted_weight: u64, dust_limit_sats: u64, feerate_strategy: &FeerateStrategy,
967967
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
968968
) -> Option<(u64, u64)>
969969
where F::Target: FeeEstimator,
@@ -974,7 +974,7 @@ impl PackageTemplate {
974974
// If old feerate is 0, first iteration of this claim, use normal fee calculation
975975
if self.feerate_previous != 0 {
976976
if let Some((new_fee, feerate)) = feerate_bump(
977-
predicted_weight, input_amounts, self.feerate_previous, force_feerate_bump,
977+
predicted_weight, input_amounts, self.feerate_previous, feerate_strategy,
978978
fee_estimator, logger,
979979
) {
980980
return Some((cmp::max(input_amounts as i64 - new_fee as i64, dust_limit_sats as i64) as u64, feerate));
@@ -987,32 +987,31 @@ impl PackageTemplate {
987987
None
988988
}
989989

990-
/// Computes a feerate based on the given confirmation target. If a previous feerate was used,
991-
/// the new feerate is below it, and `force_feerate_bump` is set, we'll use a 25% increase of
992-
/// the previous feerate instead of the new feerate.
990+
/// Computes a feerate based on the given confirmation target and feerate strategy.
993991
pub(crate) fn compute_package_feerate<F: Deref>(
994992
&self, fee_estimator: &LowerBoundedFeeEstimator<F>, conf_target: ConfirmationTarget,
995-
force_feerate_bump: bool,
993+
feerate_strategy: &FeerateStrategy,
996994
) -> u32 where F::Target: FeeEstimator {
997995
let feerate_estimate = fee_estimator.bounded_sat_per_1000_weight(conf_target);
998996
if self.feerate_previous != 0 {
999-
// Use the new fee estimate if it's higher than the one previously used.
1000-
if feerate_estimate as u64 > self.feerate_previous {
1001-
feerate_estimate
1002-
} else if !force_feerate_bump {
1003-
self.feerate_previous.try_into().unwrap_or(u32::max_value())
1004-
} else {
1005-
// Our fee estimate has decreased, but our transaction remains unconfirmed after
1006-
// using our previous fee estimate. This may point to an unreliable fee estimator,
1007-
// so we choose to bump our previous feerate by 25%, making sure we don't use a
1008-
// lower feerate or overpay by a large margin by limiting it to 5x the new fee
1009-
// estimate.
1010-
let previous_feerate = self.feerate_previous.try_into().unwrap_or(u32::max_value());
1011-
let mut new_feerate = previous_feerate.saturating_add(previous_feerate / 4);
1012-
if new_feerate > feerate_estimate * 5 {
1013-
new_feerate = cmp::max(feerate_estimate * 5, previous_feerate);
1014-
}
1015-
new_feerate
997+
let previous_feerate = self.feerate_previous.try_into().unwrap_or(u32::max_value());
998+
match feerate_strategy {
999+
FeerateStrategy::HighestOfPreviousOrNew => cmp::max(previous_feerate, feerate_estimate),
1000+
FeerateStrategy::ForceBump => if feerate_estimate > previous_feerate {
1001+
feerate_estimate
1002+
} else {
1003+
// Our fee estimate has decreased, but our transaction remains unconfirmed after
1004+
// using our previous fee estimate. This may point to an unreliable fee estimator,
1005+
// so we choose to bump our previous feerate by 25%, making sure we don't use a
1006+
// lower feerate or overpay by a large margin by limiting it to 5x the new fee
1007+
// estimate.
1008+
let previous_feerate = self.feerate_previous.try_into().unwrap_or(u32::max_value());
1009+
let mut new_feerate = previous_feerate.saturating_add(previous_feerate / 4);
1010+
if new_feerate > feerate_estimate * 5 {
1011+
new_feerate = cmp::max(feerate_estimate * 5, previous_feerate);
1012+
}
1013+
new_feerate
1014+
},
10161015
}
10171016
} else {
10181017
feerate_estimate
@@ -1128,33 +1127,38 @@ fn compute_fee_from_spent_amounts<F: Deref, L: Logger>(input_amounts: u64, predi
11281127

11291128
/// Attempt to propose a bumping fee for a transaction from its spent output's values and predicted
11301129
/// weight. If feerates proposed by the fee-estimator have been increasing since last fee-bumping
1131-
/// attempt, use them. If `force_feerate_bump` is set, we bump the feerate by 25% of the previous
1132-
/// feerate, or just use the previous feerate otherwise. If a feerate bump did happen, we also
1133-
/// verify that those bumping heuristics respect BIP125 rules 3) and 4) and if required adjust the
1134-
/// new fee to meet the RBF policy requirement.
1130+
/// attempt, use them. If we need to force a feerate bump, we manually bump the feerate by 25% of
1131+
/// the previous feerate. If a feerate bump did happen, we also verify that those bumping heuristics
1132+
/// respect BIP125 rules 3) and 4) and if required adjust the new fee to meet the RBF policy
1133+
/// requirement.
11351134
fn feerate_bump<F: Deref, L: Logger>(
1136-
predicted_weight: u64, input_amounts: u64, previous_feerate: u64, force_feerate_bump: bool,
1135+
predicted_weight: u64, input_amounts: u64, previous_feerate: u64, feerate_strategy: &FeerateStrategy,
11371136
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
11381137
) -> Option<(u64, u64)>
11391138
where
11401139
F::Target: FeeEstimator,
11411140
{
11421141
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
11431142
let (new_fee, new_feerate) = if let Some((new_fee, new_feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
1144-
if new_feerate > previous_feerate {
1145-
(new_fee, new_feerate)
1146-
} else if !force_feerate_bump {
1147-
let previous_fee = previous_feerate * predicted_weight / 1000;
1148-
(previous_fee, previous_feerate)
1149-
} else {
1150-
// ...else just increase the previous feerate by 25% (because that's a nice number)
1151-
let bumped_feerate = previous_feerate + (previous_feerate / 4);
1152-
let bumped_fee = bumped_feerate * predicted_weight / 1000;
1153-
if input_amounts <= bumped_fee {
1154-
log_warn!(logger, "Can't 25% bump new claiming tx, amount {} is too small", input_amounts);
1155-
return None;
1156-
}
1157-
(bumped_fee, bumped_feerate)
1143+
match feerate_strategy {
1144+
FeerateStrategy::HighestOfPreviousOrNew => if new_feerate > previous_feerate {
1145+
(new_fee, new_feerate)
1146+
} else {
1147+
let previous_fee = previous_feerate * predicted_weight / 1000;
1148+
(previous_fee, previous_feerate)
1149+
},
1150+
FeerateStrategy::ForceBump => if new_feerate > previous_feerate {
1151+
(new_fee, new_feerate)
1152+
} else {
1153+
// ...else just increase the previous feerate by 25% (because that's a nice number)
1154+
let bumped_feerate = previous_feerate + (previous_feerate / 4);
1155+
let bumped_fee = bumped_feerate * predicted_weight / 1000;
1156+
if input_amounts <= bumped_fee {
1157+
log_warn!(logger, "Can't 25% bump new claiming tx, amount {} is too small", input_amounts);
1158+
return None;
1159+
}
1160+
(bumped_fee, bumped_feerate)
1161+
},
11581162
}
11591163
} else {
11601164
log_warn!(logger, "Can't new-estimation bump new claiming tx, amount {} is too small", input_amounts);

0 commit comments

Comments
 (0)