Skip to content

Commit 4535c5f

Browse files
authored
Merge pull request #352 from tnull/2024-08-fee-estimator-refactor
2 parents ea448a1 + 42a695e commit 4535c5f

File tree

3 files changed

+90
-45
lines changed

3 files changed

+90
-45
lines changed

src/event.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
};
77

88
use crate::connection::ConnectionManager;
9+
use crate::fee_estimator::ConfirmationTarget;
910

1011
use crate::payment::store::{
1112
PaymentDetails, PaymentDetailsUpdate, PaymentDirection, PaymentKind, PaymentStatus,
@@ -18,7 +19,6 @@ use crate::io::{
1819
};
1920
use crate::logger::{log_debug, log_error, log_info, Logger};
2021

21-
use lightning::chain::chaininterface::ConfirmationTarget;
2222
use lightning::events::bump_transaction::BumpTransactionEvent;
2323
use lightning::events::{ClosureReason, PaymentPurpose};
2424
use lightning::events::{Event as LdkEvent, PaymentFailureReason};
@@ -398,7 +398,7 @@ where
398398
} => {
399399
// Construct the raw transaction with the output that is paid the amount of the
400400
// channel.
401-
let confirmation_target = ConfirmationTarget::NonAnchorChannelFee;
401+
let confirmation_target = ConfirmationTarget::ChannelFunding;
402402

403403
// We set nLockTime to the current height to discourage fee sniping.
404404
let cur_height = self.channel_manager.current_best_block().height;

src/fee_estimator.rs

Lines changed: 80 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use crate::config::FEE_RATE_CACHE_UPDATE_TIMEOUT_SECS;
22
use crate::logger::{log_error, log_trace, Logger};
33
use crate::{Config, Error};
44

5-
use lightning::chain::chaininterface::{
6-
ConfirmationTarget, FeeEstimator, FEERATE_FLOOR_SATS_PER_KW,
7-
};
5+
use lightning::chain::chaininterface::ConfirmationTarget as LdkConfirmationTarget;
6+
use lightning::chain::chaininterface::FeeEstimator as LdkFeeEstimator;
7+
use lightning::chain::chaininterface::FEERATE_FLOOR_SATS_PER_KW;
88

99
use bdk::FeeRate;
1010
use esplora_client::AsyncClient as EsploraClient;
@@ -17,6 +17,26 @@ use std::ops::Deref;
1717
use std::sync::{Arc, RwLock};
1818
use std::time::Duration;
1919

20+
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
21+
pub(crate) enum ConfirmationTarget {
22+
/// The default target for onchain payments.
23+
OnchainPayment,
24+
/// The target used for funding transactions.
25+
ChannelFunding,
26+
/// Targets used by LDK.
27+
Lightning(LdkConfirmationTarget),
28+
}
29+
30+
pub(crate) trait FeeEstimator {
31+
fn estimate_fee_rate(&self, confirmation_target: ConfirmationTarget) -> FeeRate;
32+
}
33+
34+
impl From<LdkConfirmationTarget> for ConfirmationTarget {
35+
fn from(value: LdkConfirmationTarget) -> Self {
36+
Self::Lightning(value)
37+
}
38+
}
39+
2040
pub(crate) struct OnchainFeeEstimator<L: Deref>
2141
where
2242
L::Target: Logger,
@@ -61,23 +81,30 @@ where
6181
}
6282

6383
let confirmation_targets = vec![
64-
ConfirmationTarget::OnChainSweep,
65-
ConfirmationTarget::MinAllowedAnchorChannelRemoteFee,
66-
ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee,
67-
ConfirmationTarget::AnchorChannelFee,
68-
ConfirmationTarget::NonAnchorChannelFee,
69-
ConfirmationTarget::ChannelCloseMinimum,
70-
ConfirmationTarget::OutputSpendingFee,
84+
ConfirmationTarget::OnchainPayment,
85+
ConfirmationTarget::ChannelFunding,
86+
LdkConfirmationTarget::OnChainSweep.into(),
87+
LdkConfirmationTarget::MinAllowedAnchorChannelRemoteFee.into(),
88+
LdkConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee.into(),
89+
LdkConfirmationTarget::AnchorChannelFee.into(),
90+
LdkConfirmationTarget::NonAnchorChannelFee.into(),
91+
LdkConfirmationTarget::ChannelCloseMinimum.into(),
92+
LdkConfirmationTarget::OutputSpendingFee.into(),
7193
];
94+
7295
for target in confirmation_targets {
7396
let num_blocks = match target {
74-
ConfirmationTarget::OnChainSweep => 6,
75-
ConfirmationTarget::MinAllowedAnchorChannelRemoteFee => 1008,
76-
ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => 144,
77-
ConfirmationTarget::AnchorChannelFee => 1008,
78-
ConfirmationTarget::NonAnchorChannelFee => 12,
79-
ConfirmationTarget::ChannelCloseMinimum => 144,
80-
ConfirmationTarget::OutputSpendingFee => 12,
97+
ConfirmationTarget::OnchainPayment => 6,
98+
ConfirmationTarget::ChannelFunding => 12,
99+
ConfirmationTarget::Lightning(ldk_target) => match ldk_target {
100+
LdkConfirmationTarget::OnChainSweep => 6,
101+
LdkConfirmationTarget::MinAllowedAnchorChannelRemoteFee => 1008,
102+
LdkConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => 144,
103+
LdkConfirmationTarget::AnchorChannelFee => 1008,
104+
LdkConfirmationTarget::NonAnchorChannelFee => 12,
105+
LdkConfirmationTarget::ChannelCloseMinimum => 144,
106+
LdkConfirmationTarget::OutputSpendingFee => 12,
107+
},
81108
};
82109

83110
let converted_estimates =
@@ -96,7 +123,9 @@ where
96123
// LDK 0.0.118 introduced changes to the `ConfirmationTarget` semantics that
97124
// require some post-estimation adjustments to the fee rates, which we do here.
98125
let adjusted_fee_rate = match target {
99-
ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => {
126+
ConfirmationTarget::Lightning(
127+
LdkConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee,
128+
) => {
100129
let slightly_less_than_background =
101130
fee_rate.fee_wu(Weight::from_wu(1000)) - 250;
102131
FeeRate::from_sat_per_kwu(slightly_less_than_background as f32)
@@ -115,33 +144,53 @@ where
115144
}
116145
Ok(())
117146
}
147+
}
118148

119-
pub(crate) fn estimate_fee_rate(&self, confirmation_target: ConfirmationTarget) -> FeeRate {
149+
impl<L: Deref> FeeEstimator for OnchainFeeEstimator<L>
150+
where
151+
L::Target: Logger,
152+
{
153+
fn estimate_fee_rate(&self, confirmation_target: ConfirmationTarget) -> FeeRate {
120154
let locked_fee_rate_cache = self.fee_rate_cache.read().unwrap();
121155

122156
let fallback_sats_kwu = match confirmation_target {
123-
ConfirmationTarget::OnChainSweep => 5000,
124-
ConfirmationTarget::MinAllowedAnchorChannelRemoteFee => FEERATE_FLOOR_SATS_PER_KW,
125-
ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => FEERATE_FLOOR_SATS_PER_KW,
126-
ConfirmationTarget::AnchorChannelFee => 500,
127-
ConfirmationTarget::NonAnchorChannelFee => 1000,
128-
ConfirmationTarget::ChannelCloseMinimum => 500,
129-
ConfirmationTarget::OutputSpendingFee => 1000,
157+
ConfirmationTarget::OnchainPayment => 5000,
158+
ConfirmationTarget::ChannelFunding => 1000,
159+
ConfirmationTarget::Lightning(ldk_target) => match ldk_target {
160+
LdkConfirmationTarget::OnChainSweep => 5000,
161+
LdkConfirmationTarget::MinAllowedAnchorChannelRemoteFee => {
162+
FEERATE_FLOOR_SATS_PER_KW
163+
},
164+
LdkConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => {
165+
FEERATE_FLOOR_SATS_PER_KW
166+
},
167+
LdkConfirmationTarget::AnchorChannelFee => 500,
168+
LdkConfirmationTarget::NonAnchorChannelFee => 1000,
169+
LdkConfirmationTarget::ChannelCloseMinimum => 500,
170+
LdkConfirmationTarget::OutputSpendingFee => 1000,
171+
},
130172
};
131173

132174
// We'll fall back on this, if we really don't have any other information.
133175
let fallback_rate = FeeRate::from_sat_per_kwu(fallback_sats_kwu as f32);
134176

135-
*locked_fee_rate_cache.get(&confirmation_target).unwrap_or(&fallback_rate)
177+
let estimate = *locked_fee_rate_cache.get(&confirmation_target).unwrap_or(&fallback_rate);
178+
179+
// Currently we assume every transaction needs to at least be relayable, which is why we
180+
// enforce a lower bound of `FEERATE_FLOOR_SATS_PER_KW`.
181+
let weight_units = Weight::from_wu(1000);
182+
FeeRate::from_wu(
183+
estimate.fee_wu(weight_units).max(FEERATE_FLOOR_SATS_PER_KW as u64),
184+
weight_units,
185+
)
136186
}
137187
}
138188

139-
impl<L: Deref> FeeEstimator for OnchainFeeEstimator<L>
189+
impl<L: Deref> LdkFeeEstimator for OnchainFeeEstimator<L>
140190
where
141191
L::Target: Logger,
142192
{
143-
fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 {
144-
(self.estimate_fee_rate(confirmation_target).fee_wu(Weight::from_wu(1000)) as u32)
145-
.max(FEERATE_FLOOR_SATS_PER_KW)
193+
fn get_est_sat_per_1000_weight(&self, confirmation_target: LdkConfirmationTarget) -> u32 {
194+
self.estimate_fee_rate(confirmation_target.into()).fee_wu(Weight::from_wu(1000)) as u32
146195
}
147196
}

src/wallet.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use crate::logger::{log_error, log_info, log_trace, Logger};
22

33
use crate::config::BDK_WALLET_SYNC_TIMEOUT_SECS;
4+
use crate::fee_estimator::{ConfirmationTarget, FeeEstimator};
45
use crate::Error;
56

6-
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
7+
use lightning::chain::chaininterface::BroadcasterInterface;
78

89
use lightning::events::bump_transaction::{Utxo, WalletSource};
910
use lightning::ln::msgs::{DecodeError, UnsignedGossipMessage};
@@ -18,8 +19,7 @@ use lightning::util::message_signing;
1819
use bdk::blockchain::EsploraBlockchain;
1920
use bdk::database::BatchDatabase;
2021
use bdk::wallet::AddressIndex;
21-
use bdk::{Balance, FeeRate};
22-
use bdk::{SignOptions, SyncOptions};
22+
use bdk::{Balance, SignOptions, SyncOptions};
2323

2424
use bitcoin::address::{Payload, WitnessVersion};
2525
use bitcoin::bech32::u5;
@@ -43,7 +43,7 @@ enum WalletSyncStatus {
4343
InProgress { subscribers: tokio::sync::broadcast::Sender<Result<(), Error>> },
4444
}
4545

46-
pub struct Wallet<D, B: Deref, E: Deref, L: Deref>
46+
pub(crate) struct Wallet<D, B: Deref, E: Deref, L: Deref>
4747
where
4848
D: BatchDatabase,
4949
B::Target: BroadcasterInterface,
@@ -153,9 +153,7 @@ where
153153
&self, output_script: ScriptBuf, value_sats: u64, confirmation_target: ConfirmationTarget,
154154
locktime: LockTime,
155155
) -> Result<Transaction, Error> {
156-
let fee_rate = FeeRate::from_sat_per_kwu(
157-
self.fee_estimator.get_est_sat_per_1000_weight(confirmation_target) as f32,
158-
);
156+
let fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target);
159157

160158
let locked_wallet = self.inner.lock().unwrap();
161159
let mut tx_builder = locked_wallet.build_tx();
@@ -240,10 +238,8 @@ where
240238
pub(crate) fn send_to_address(
241239
&self, address: &bitcoin::Address, amount_msat_or_drain: Option<u64>,
242240
) -> Result<Txid, Error> {
243-
let confirmation_target = ConfirmationTarget::OutputSpendingFee;
244-
let fee_rate = FeeRate::from_sat_per_kwu(
245-
self.fee_estimator.get_est_sat_per_1000_weight(confirmation_target) as f32,
246-
);
241+
let confirmation_target = ConfirmationTarget::OnchainPayment;
242+
let fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target);
247243

248244
let tx = {
249245
let locked_wallet = self.inner.lock().unwrap();
@@ -485,7 +481,7 @@ where
485481

486482
/// Similar to [`KeysManager`], but overrides the destination and shutdown scripts so they are
487483
/// directly spendable by the BDK wallet.
488-
pub struct WalletKeysManager<D, B: Deref, E: Deref, L: Deref>
484+
pub(crate) struct WalletKeysManager<D, B: Deref, E: Deref, L: Deref>
489485
where
490486
D: BatchDatabase,
491487
B::Target: BroadcasterInterface,

0 commit comments

Comments
 (0)