Skip to content

Commit b992a6a

Browse files
committed
chaininterface: Add LowerBoundedFeeEstimator wrapper
The LowerBoundedFeeEstimator wrapper is used internally to wrap a user-provided FeeEstimator to ensure that we clip fee estimates to no less than 253 sats/KW.
1 parent abf6564 commit b992a6a

File tree

4 files changed

+79
-10
lines changed

4 files changed

+79
-10
lines changed

lightning/src/chain/chaininterface.rs

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
//! Includes traits for monitoring and receiving notifications of new blocks and block
1414
//! disconnections, transaction broadcasting, and feerate information requests.
1515
16+
use core::{cmp, ops::Deref};
17+
1618
use bitcoin::blockdata::transaction::Transaction;
1719

1820
/// An interface to send a transaction to the Bitcoin network.
@@ -41,14 +43,75 @@ pub enum ConfirmationTarget {
4143
pub trait FeeEstimator {
4244
/// Gets estimated satoshis of fee required per 1000 Weight-Units.
4345
///
44-
/// Must return a value no smaller than 253 (ie 1 satoshi-per-byte rounded up to ensure later
45-
/// round-downs don't put us below 1 satoshi-per-byte).
46-
///
47-
/// This method can be implemented with the following unit conversions:
48-
/// * max(satoshis-per-byte * 250, 253)
49-
/// * max(satoshis-per-kbyte / 4, 253)
46+
/// LDK will wrap this method and ensure that the value returned is no smaller than 253
47+
/// (ie 1 satoshi-per-byte rounded up to ensure later round-downs don't put us below 1 satoshi-per-byte).
5048
fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32;
5149
}
5250

5351
/// Minimum relay fee as required by bitcoin network mempool policy.
5452
pub const MIN_RELAY_FEE_SAT_PER_1000_WEIGHT: u64 = 4000;
53+
/// Minimum feerate that takes a sane approach to rounding
54+
pub const FEERATE_FLOOR_SATS_PER_KW: u32 = 253;
55+
56+
/// Wraps a `FeeEstimator` so that any fee estimations provided by it
57+
/// are bounded below by `FEERATE_FLOOR_SATS_PER_KW` (253 sats/KW)
58+
pub(crate) struct LowerBoundedFeeEstimator<'a, F: Deref>(pub(crate) &'a F)
59+
where
60+
F::Target: FeeEstimator;
61+
62+
impl<'a, F: Deref> LowerBoundedFeeEstimator<'a, F>
63+
where
64+
F::Target: FeeEstimator,
65+
{
66+
pub(crate) fn new(fee_estimator: &'a F) -> Self
67+
where
68+
F::Target: FeeEstimator,
69+
{
70+
LowerBoundedFeeEstimator(fee_estimator)
71+
}
72+
}
73+
74+
impl<F: Deref> FeeEstimator for LowerBoundedFeeEstimator<'_, F>
75+
where
76+
F::Target: FeeEstimator,
77+
{
78+
fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 {
79+
cmp::max(
80+
self.0.get_est_sat_per_1000_weight(confirmation_target),
81+
FEERATE_FLOOR_SATS_PER_KW,
82+
)
83+
}
84+
}
85+
86+
#[cfg(test)]
87+
mod tests {
88+
use super::{FEERATE_FLOOR_SATS_PER_KW, LowerBoundedFeeEstimator, ConfirmationTarget, FeeEstimator};
89+
90+
struct TestFeeEstimator {
91+
sat_per_kw: u32,
92+
}
93+
94+
impl FeeEstimator for TestFeeEstimator {
95+
fn get_est_sat_per_1000_weight(&self, _: ConfirmationTarget) -> u32 {
96+
self.sat_per_kw
97+
}
98+
}
99+
100+
#[test]
101+
fn test_fee_estimator_less_than_floor() {
102+
let sat_per_kw = FEERATE_FLOOR_SATS_PER_KW - 1;
103+
let test_fee_estimator = &TestFeeEstimator { sat_per_kw };
104+
let fee_estimator = LowerBoundedFeeEstimator::new(&test_fee_estimator);
105+
106+
assert_eq!(fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background), FEERATE_FLOOR_SATS_PER_KW);
107+
}
108+
109+
#[test]
110+
fn test_fee_estimator_greater_than_floor() {
111+
let sat_per_kw = FEERATE_FLOOR_SATS_PER_KW + 1;
112+
let test_fee_estimator = &TestFeeEstimator { sat_per_kw };
113+
let fee_estimator = LowerBoundedFeeEstimator::new(&test_fee_estimator);
114+
115+
assert_eq!(fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background), sat_per_kw);
116+
}
117+
}

lightning/src/chain/package.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ use core::mem;
3838
use core::ops::Deref;
3939
use bitcoin::Witness;
4040

41+
use super::chaininterface::LowerBoundedFeeEstimator;
42+
4143
const MAX_ALLOC_SIZE: usize = 64*1024;
4244

4345

@@ -776,6 +778,7 @@ fn compute_fee_from_spent_amounts<F: Deref, L: Deref>(input_amounts: u64, predic
776778
where F::Target: FeeEstimator,
777779
L::Target: Logger,
778780
{
781+
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
779782
let mut updated_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) as u64;
780783
let mut fee = updated_feerate * (predicted_weight as u64) / 1000;
781784
if input_amounts <= fee {

lightning/src/ln/channel.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use ln::channelmanager::{CounterpartyForwardingInfo, PendingHTLCStatus, HTLCSour
3131
use ln::chan_utils::{CounterpartyCommitmentSecrets, TxCreationKeys, HTLCOutputInCommitment, htlc_success_tx_weight, htlc_timeout_tx_weight, make_funding_redeemscript, ChannelPublicKeys, CommitmentTransaction, HolderCommitmentTransaction, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, MAX_HTLCS, get_commitment_transaction_number_obscure_factor, ClosingTransaction};
3232
use ln::chan_utils;
3333
use chain::BestBlock;
34-
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
34+
use chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator};
3535
use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS};
3636
use chain::transaction::{OutPoint, TransactionData};
3737
use chain::keysinterface::{Sign, KeysInterface};
@@ -888,6 +888,7 @@ impl<Signer: Sign> Channel<Signer> {
888888
let holder_selected_contest_delay = config.channel_handshake_config.our_to_self_delay;
889889
let holder_signer = keys_provider.get_channel_signer(false, channel_value_satoshis);
890890
let pubkeys = holder_signer.pubkeys().clone();
891+
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
891892

892893
if !their_features.supports_wumbo() && channel_value_satoshis > MAX_FUNDING_SATOSHIS_NO_WUMBO {
893894
return Err(APIError::APIMisuseError{err: format!("funding_value must not exceed {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, channel_value_satoshis)});
@@ -1047,6 +1048,7 @@ impl<Signer: Sign> Channel<Signer> {
10471048
fn check_remote_fee<F: Deref>(fee_estimator: &F, feerate_per_kw: u32) -> Result<(), ChannelError>
10481049
where F::Target: FeeEstimator
10491050
{
1051+
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
10501052
// We only bound the fee updates on the upper side to prevent completely absurd feerates,
10511053
// always accepting up to 25 sat/vByte or 10x our fee estimator's "High Priority" fee.
10521054
// We generally don't care too much if they set the feerate to something very high, but it
@@ -3987,6 +3989,7 @@ impl<Signer: Sign> Channel<Signer> {
39873989
where F::Target: FeeEstimator
39883990
{
39893991
if let Some((min, max)) = self.closing_fee_limits { return (min, max); }
3992+
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
39903993

39913994
// Propose a range from our current Background feerate to our Normal feerate plus our
39923995
// force_close_avoidance_max_fee_satoshis.

lightning/src/ln/channelmanager.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use bitcoin::secp256k1;
3636

3737
use chain;
3838
use chain::{Confirm, ChannelMonitorUpdateErr, Watch, BestBlock};
39-
use chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
39+
use chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
4040
use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, HTLC_FAIL_BACK_BUFFER, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY, MonitorEvent, CLOSED_CHANNEL_UPDATE_ID};
4141
use chain::transaction::{OutPoint, TransactionData};
4242
// Since this struct is returned in `list_channels` methods, expose it here in case users want to
@@ -3436,7 +3436,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
34363436
PersistenceNotifierGuard::optionally_notify(&self.total_consistency_lock, &self.persistence_notifier, || {
34373437
let mut should_persist = NotifyOption::SkipPersist;
34383438

3439-
let new_feerate = self.fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
3439+
let new_feerate = LowerBoundedFeeEstimator::new(&self.fee_estimator).get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
34403440

34413441
let mut handle_errors = Vec::new();
34423442
{
@@ -3473,7 +3473,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
34733473
let mut should_persist = NotifyOption::SkipPersist;
34743474
if self.process_background_events() { should_persist = NotifyOption::DoPersist; }
34753475

3476-
let new_feerate = self.fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
3476+
let new_feerate = LowerBoundedFeeEstimator::new(&self.fee_estimator).get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
34773477

34783478
let mut handle_errors = Vec::new();
34793479
let mut timed_out_mpp_htlcs = Vec::new();

0 commit comments

Comments
 (0)