Skip to content

Commit 563b680

Browse files
committed
Extend OnchainTxHandler::generate_claim to not manually bump feerates
In the next commit, we plan to extend the `OnchainTxHandler` to retry pending claims on every timer tick. These timer ticks may fire with much more frequency than incoming blocks, so we want to avoid manually bumping feerates (currently by 25%) each time our fee estimator provides a lower feerate than before.
1 parent 9d5adfc commit 563b680

File tree

3 files changed

+65
-29
lines changed

3 files changed

+65
-29
lines changed

lightning/src/chain/onchaintx.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -489,9 +489,13 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
489489
///
490490
/// Panics if there are signing errors, because signing operations in reaction to on-chain
491491
/// events are not expected to fail, and if they do, we may lose funds.
492-
fn generate_claim<F: Deref, L: Deref>(&mut self, cur_height: u32, cached_request: &PackageTemplate, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u32, u64, OnchainClaim)>
493-
where F::Target: FeeEstimator,
494-
L::Target: Logger,
492+
fn generate_claim<F: Deref, L: Deref>(
493+
&mut self, cur_height: u32, cached_request: &PackageTemplate, manually_bump_feerate: bool,
494+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
495+
) -> Option<(u32, u64, OnchainClaim)>
496+
where
497+
F::Target: FeeEstimator,
498+
L::Target: Logger,
495499
{
496500
let request_outpoints = cached_request.outpoints();
497501
if request_outpoints.is_empty() {
@@ -538,8 +542,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
538542
#[cfg(anchors)]
539543
{ // Attributes are not allowed on if expressions on our current MSRV of 1.41.
540544
if cached_request.requires_external_funding() {
541-
let target_feerate_sat_per_1000_weight = cached_request
542-
.compute_package_feerate(fee_estimator, ConfirmationTarget::HighPriority);
545+
let target_feerate_sat_per_1000_weight = cached_request.compute_package_feerate(
546+
fee_estimator, ConfirmationTarget::HighPriority, manually_bump_feerate
547+
);
543548
if let Some(htlcs) = cached_request.construct_malleable_package_with_external_funding(self) {
544549
return Some((
545550
new_timer,
@@ -558,7 +563,8 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
558563

559564
let predicted_weight = cached_request.package_weight(&self.destination_script);
560565
if let Some((output_value, new_feerate)) = cached_request.compute_package_output(
561-
predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger,
566+
predicted_weight, self.destination_script.dust_value().to_sat(),
567+
manually_bump_feerate, fee_estimator, logger,
562568
) {
563569
assert!(new_feerate != 0);
564570

@@ -601,7 +607,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
601607
// counterparty's latest commitment don't have any HTLCs present.
602608
let conf_target = ConfirmationTarget::HighPriority;
603609
let package_target_feerate_sat_per_1000_weight = cached_request
604-
.compute_package_feerate(fee_estimator, conf_target);
610+
.compute_package_feerate(fee_estimator, conf_target, manually_bump_feerate);
605611
Some((
606612
new_timer,
607613
package_target_feerate_sat_per_1000_weight as u64,
@@ -700,7 +706,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
700706
// Generate claim transactions and track them to bump if necessary at
701707
// height timer expiration (i.e in how many blocks we're going to take action).
702708
for mut req in preprocessed_requests {
703-
if let Some((new_timer, new_feerate, claim)) = self.generate_claim(cur_height, &req, &*fee_estimator, &*logger) {
709+
if let Some((new_timer, new_feerate, claim)) = self.generate_claim(
710+
cur_height, &req, true /* manually_bump_feerate */, &*fee_estimator, &*logger,
711+
) {
704712
req.set_timer(new_timer);
705713
req.set_feerate(new_feerate);
706714
let package_id = match claim {
@@ -893,7 +901,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
893901
// Build, bump and rebroadcast tx accordingly
894902
log_trace!(logger, "Bumping {} candidates", bump_candidates.len());
895903
for (package_id, request) in bump_candidates.iter() {
896-
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(cur_height, &request, &*fee_estimator, &*logger) {
904+
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(
905+
cur_height, &request, true /* manually_bump_feerate */, &*fee_estimator, &*logger,
906+
) {
897907
match bump_claim {
898908
OnchainClaim::Tx(bump_tx) => {
899909
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
@@ -973,7 +983,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
973983
}
974984
}
975985
for ((_package_id, _), ref mut request) in bump_candidates.iter_mut() {
976-
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(height, &request, fee_estimator, &&*logger) {
986+
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(
987+
height, &request, true /* manually_bump_feerate */, fee_estimator, &&*logger
988+
) {
977989
request.set_timer(new_timer);
978990
request.set_feerate(new_feerate);
979991
match bump_claim {

lightning/src/chain/package.rs

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -762,16 +762,23 @@ impl PackageTemplate {
762762
/// Returns value in satoshis to be included as package outgoing output amount and feerate
763763
/// which was used to generate the value. Will not return less than `dust_limit_sats` for the
764764
/// value.
765-
pub(crate) fn compute_package_output<F: Deref, L: Deref>(&self, predicted_weight: usize, dust_limit_sats: u64, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u64, u64)>
766-
where F::Target: FeeEstimator,
767-
L::Target: Logger,
765+
pub(crate) fn compute_package_output<F: Deref, L: Deref>(
766+
&self, predicted_weight: usize, dust_limit_sats: u64, manually_bump_feerate: bool,
767+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
768+
) -> Option<(u64, u64)>
769+
where
770+
F::Target: FeeEstimator,
771+
L::Target: Logger,
768772
{
769773
debug_assert!(self.malleability == PackageMalleability::Malleable, "The package output is fixed for non-malleable packages");
770774
let input_amounts = self.package_amount();
771775
assert!(dust_limit_sats as i64 > 0, "Output script must be broadcastable/have a 'real' dust limit.");
772776
// If old feerate is 0, first iteration of this claim, use normal fee calculation
773777
if self.feerate_previous != 0 {
774-
if let Some((new_fee, feerate)) = feerate_bump(predicted_weight, input_amounts, self.feerate_previous, fee_estimator, logger) {
778+
if let Some((new_fee, feerate)) = feerate_bump(
779+
predicted_weight, input_amounts, self.feerate_previous, manually_bump_feerate,
780+
fee_estimator, logger,
781+
) {
775782
return Some((cmp::max(input_amounts as i64 - new_fee as i64, dust_limit_sats as i64) as u64, feerate));
776783
}
777784
} else {
@@ -784,16 +791,19 @@ impl PackageTemplate {
784791

785792
#[cfg(anchors)]
786793
/// Computes a feerate based on the given confirmation target. If a previous feerate was used,
787-
/// and the new feerate is below it, we'll use a 25% increase of the previous feerate instead of
788-
/// the new one.
794+
/// the new feerate is below it, and `manually_bump_feerate` is set, we'll use a 25% increase of
795+
/// the previous feerate instead of the new feerate.
789796
pub(crate) fn compute_package_feerate<F: Deref>(
790797
&self, fee_estimator: &LowerBoundedFeeEstimator<F>, conf_target: ConfirmationTarget,
798+
manually_bump_feerate: bool,
791799
) -> u32 where F::Target: FeeEstimator {
792800
let feerate_estimate = fee_estimator.bounded_sat_per_1000_weight(conf_target);
793801
if self.feerate_previous != 0 {
794802
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
795803
if feerate_estimate as u64 > self.feerate_previous {
796804
feerate_estimate
805+
} else if !manually_bump_feerate {
806+
self.feerate_previous.try_into().unwrap_or(u32::max_value())
797807
} else {
798808
// ...else just increase the previous feerate by 25% (because that's a nice number)
799809
(self.feerate_previous + (self.feerate_previous / 4)).try_into().unwrap_or(u32::max_value())
@@ -948,29 +958,43 @@ fn compute_fee_from_spent_amounts<F: Deref, L: Deref>(input_amounts: u64, predic
948958
/// attempt, use them. Otherwise, blindly bump the feerate by 25% of the previous feerate. We also
949959
/// verify that those bumping heuristics respect BIP125 rules 3) and 4) and if required adjust
950960
/// the new fee to meet the RBF policy requirement.
951-
fn feerate_bump<F: Deref, L: Deref>(predicted_weight: usize, input_amounts: u64, previous_feerate: u64, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u64, u64)>
952-
where F::Target: FeeEstimator,
953-
L::Target: Logger,
961+
fn feerate_bump<F: Deref, L: Deref>(
962+
predicted_weight: usize, input_amounts: u64, previous_feerate: u64, manually_bump_feerate: bool,
963+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
964+
) -> Option<(u64, u64)>
965+
where
966+
F::Target: FeeEstimator,
967+
L::Target: Logger,
954968
{
955969
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
956-
let new_fee = if let Some((new_fee, _)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
957-
let updated_feerate = new_fee / (predicted_weight as u64 * 1000);
958-
if updated_feerate > previous_feerate {
959-
new_fee
970+
let (new_fee, new_feerate) = if let Some((new_fee, new_feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
971+
if new_feerate > previous_feerate {
972+
(new_fee, new_feerate)
973+
} else if !manually_bump_feerate {
974+
let previous_fee = previous_feerate * (predicted_weight as u64) / 1000;
975+
(previous_fee, previous_feerate)
960976
} else {
961977
// ...else just increase the previous feerate by 25% (because that's a nice number)
962-
let new_fee = previous_feerate * (predicted_weight as u64) / 750;
963-
if input_amounts <= new_fee {
978+
let bumped_feerate = previous_feerate + (previous_feerate / 4);
979+
let bumped_fee = bumped_feerate * (predicted_weight as u64) / 1000;
980+
if input_amounts <= bumped_fee {
964981
log_warn!(logger, "Can't 25% bump new claiming tx, amount {} is too small", input_amounts);
965982
return None;
966983
}
967-
new_fee
984+
(bumped_fee, bumped_feerate)
968985
}
969986
} else {
970987
log_warn!(logger, "Can't new-estimation bump new claiming tx, amount {} is too small", input_amounts);
971988
return None;
972989
};
973990

991+
// Our feerates should never decrease. If it hasn't changed though, we just need to
992+
// rebroadcast/re-sign the previous claim.
993+
debug_assert!(new_feerate >= previous_feerate);
994+
if new_feerate == previous_feerate {
995+
return Some((new_fee, new_feerate));
996+
}
997+
974998
let previous_fee = previous_feerate * (predicted_weight as u64) / 1000;
975999
let min_relay_fee = MIN_RELAY_FEE_SAT_PER_1000_WEIGHT * (predicted_weight as u64) / 1000;
9761000
// BIP 125 Opt-in Full Replace-by-Fee Signaling

lightning/src/ln/functional_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2991,9 +2991,9 @@ fn do_test_htlc_on_chain_timeout(connect_style: ConnectStyle) {
29912991
if nodes[1].connect_style.borrow().skips_blocks() {
29922992
assert_eq!(txn.len(), 1);
29932993
} else {
2994-
assert_eq!(txn.len(), 2); // Extra rebroadcast of timeout transaction
2994+
assert_eq!(txn.len(), 3); // Two extra fee bumps for timeout transaction
29952995
}
2996-
check_spends!(txn[0], commitment_tx[0]);
2996+
txn.iter().for_each(|tx| check_spends!(tx, commitment_tx[0]));
29972997
assert_eq!(txn[0].clone().input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
29982998
txn.remove(0)
29992999
};
@@ -7510,7 +7510,7 @@ fn test_bump_penalty_txn_on_remote_commitment() {
75107510
assert_ne!(feerate_preimage, 0);
75117511

75127512
// After exhaustion of height timer, new bumped claim txn should have been broadcast, check it
7513-
connect_blocks(&nodes[1], 15);
7513+
connect_blocks(&nodes[1], 1);
75147514
{
75157515
let mut node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
75167516
assert_eq!(node_txn.len(), 1);

0 commit comments

Comments
 (0)