Skip to content

Commit

Permalink
Merge pull request #1552 from dunxen/2022-06-checkminrelayfee
Browse files Browse the repository at this point in the history
Add min feerate checks
  • Loading branch information
TheBlueMatt committed Jul 13, 2022
2 parents fda3819 + 7bc6d0e commit 2a3bf03
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 74 deletions.
77 changes: 72 additions & 5 deletions lightning/src/chain/chaininterface.rs
Expand Up @@ -13,6 +13,8 @@
//! Includes traits for monitoring and receiving notifications of new blocks and block
//! disconnections, transaction broadcasting, and feerate information requests.

use core::{cmp, ops::Deref};

use bitcoin::blockdata::transaction::Transaction;

/// An interface to send a transaction to the Bitcoin network.
Expand Down Expand Up @@ -41,14 +43,79 @@ pub enum ConfirmationTarget {
pub trait FeeEstimator {
/// Gets estimated satoshis of fee required per 1000 Weight-Units.
///
/// Must return a value no smaller than 253 (ie 1 satoshi-per-byte rounded up to ensure later
/// round-downs don't put us below 1 satoshi-per-byte).
/// LDK will wrap this method and ensure that the value returned is no smaller than 253
/// (ie 1 satoshi-per-byte rounded up to ensure later round-downs don't put us below 1 satoshi-per-byte).
///
/// This method can be implemented with the following unit conversions:
/// * max(satoshis-per-byte * 250, 253)
/// * max(satoshis-per-kbyte / 4, 253)
/// The following unit conversions can be used to convert to sats/KW:
/// * satoshis-per-byte * 250
/// * satoshis-per-kbyte / 4
fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32;
}

// We need `FeeEstimator` implemented so that in some places where we only have a shared
// reference to a `Deref` to a `FeeEstimator`, we can still wrap it.
impl<D: Deref> FeeEstimator for D where D::Target: FeeEstimator {
fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 {
(**self).get_est_sat_per_1000_weight(confirmation_target)
}
}

/// Minimum relay fee as required by bitcoin network mempool policy.
pub const MIN_RELAY_FEE_SAT_PER_1000_WEIGHT: u64 = 4000;
/// Minimum feerate that takes a sane approach to bitcoind weight-to-vbytes rounding.
/// See the following Core Lightning commit for an explanation:
/// https://github.com/ElementsProject/lightning/commit/2e687b9b352c9092b5e8bd4a688916ac50b44af0
pub const FEERATE_FLOOR_SATS_PER_KW: u32 = 253;

/// Wraps a `Deref` to a `FeeEstimator` so that any fee estimations provided by it
/// are bounded below by `FEERATE_FLOOR_SATS_PER_KW` (253 sats/KW)
pub(crate) struct LowerBoundedFeeEstimator<F: Deref>(pub F) where F::Target: FeeEstimator;

impl<F: Deref> LowerBoundedFeeEstimator<F> where F::Target: FeeEstimator {
/// Creates a new `LowerBoundedFeeEstimator` which wraps the provided fee_estimator
pub fn new(fee_estimator: F) -> Self {
LowerBoundedFeeEstimator(fee_estimator)
}
}

impl<F: Deref> FeeEstimator for LowerBoundedFeeEstimator<F> where F::Target: FeeEstimator {
fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 {
cmp::max(
self.0.get_est_sat_per_1000_weight(confirmation_target),
FEERATE_FLOOR_SATS_PER_KW,
)
}
}

#[cfg(test)]
mod tests {
use super::{FEERATE_FLOOR_SATS_PER_KW, LowerBoundedFeeEstimator, ConfirmationTarget, FeeEstimator};

struct TestFeeEstimator {
sat_per_kw: u32,
}

impl FeeEstimator for TestFeeEstimator {
fn get_est_sat_per_1000_weight(&self, _: ConfirmationTarget) -> u32 {
self.sat_per_kw
}
}

#[test]
fn test_fee_estimator_less_than_floor() {
let sat_per_kw = FEERATE_FLOOR_SATS_PER_KW - 1;
let test_fee_estimator = &TestFeeEstimator { sat_per_kw };
let fee_estimator = LowerBoundedFeeEstimator::new(test_fee_estimator);

assert_eq!(fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background), FEERATE_FLOOR_SATS_PER_KW);
}

#[test]
fn test_fee_estimator_greater_than_floor() {
let sat_per_kw = FEERATE_FLOOR_SATS_PER_KW + 1;
let test_fee_estimator = &TestFeeEstimator { sat_per_kw };
let fee_estimator = LowerBoundedFeeEstimator::new(test_fee_estimator);

assert_eq!(fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background), sat_per_kw);
}
}
41 changes: 26 additions & 15 deletions lightning/src/chain/channelmonitor.rs
Expand Up @@ -40,7 +40,7 @@ use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLC
use ln::channelmanager::HTLCSource;
use chain;
use chain::{BestBlock, WatchedOutput};
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
use chain::transaction::{OutPoint, TransactionData};
use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, Sign, KeysInterface};
use chain::onchaintx::OnchainTxHandler;
Expand Down Expand Up @@ -1104,7 +1104,7 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
payment_hash: &PaymentHash,
payment_preimage: &PaymentPreimage,
broadcaster: &B,
fee_estimator: &F,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: &L,
) where
B::Target: BroadcasterInterface,
Expand Down Expand Up @@ -1300,8 +1300,9 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
F::Target: FeeEstimator,
L::Target: Logger,
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
self.inner.lock().unwrap().transactions_confirmed(
header, txdata, height, broadcaster, fee_estimator, logger)
header, txdata, height, broadcaster, &bounded_fee_estimator, logger)
}

/// Processes a transaction that was reorganized out of the chain.
Expand All @@ -1321,8 +1322,9 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
F::Target: FeeEstimator,
L::Target: Logger,
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
self.inner.lock().unwrap().transaction_unconfirmed(
txid, broadcaster, fee_estimator, logger);
txid, broadcaster, &bounded_fee_estimator, logger);
}

/// Updates the monitor with the current best chain tip, returning new outputs to watch. See
Expand All @@ -1345,8 +1347,9 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
F::Target: FeeEstimator,
L::Target: Logger,
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
self.inner.lock().unwrap().best_block_updated(
header, height, broadcaster, fee_estimator, logger)
header, height, broadcaster, &bounded_fee_estimator, logger)
}

/// Returns the set of txids that should be monitored for re-organization out of the chain.
Expand Down Expand Up @@ -1882,7 +1885,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
/// commitment_tx_infos which contain the payment hash have been revoked.
fn provide_payment_preimage<B: Deref, F: Deref, L: Deref>(&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage, broadcaster: &B, fee_estimator: &F, logger: &L)
fn provide_payment_preimage<B: Deref, F: Deref, L: Deref>(
&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage, broadcaster: &B,
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L)
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
Expand Down Expand Up @@ -1980,7 +1985,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
},
ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage } => {
log_trace!(logger, "Updating ChannelMonitor with payment preimage");
self.provide_payment_preimage(&PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()), &payment_preimage, broadcaster, fee_estimator, logger)
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
self.provide_payment_preimage(&PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()), &payment_preimage, broadcaster, &bounded_fee_estimator, logger)
},
ChannelMonitorUpdateStep::CommitmentSecret { idx, secret } => {
log_trace!(logger, "Updating ChannelMonitor with commitment secret");
Expand Down Expand Up @@ -2406,15 +2412,16 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let block_hash = header.block_hash();
self.best_block = BestBlock::new(block_hash, height);

self.transactions_confirmed(header, txdata, height, broadcaster, fee_estimator, logger)
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
self.transactions_confirmed(header, txdata, height, broadcaster, &bounded_fee_estimator, logger)
}

fn best_block_updated<B: Deref, F: Deref, L: Deref>(
&mut self,
header: &BlockHeader,
height: u32,
broadcaster: B,
fee_estimator: F,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: L,
) -> Vec<TransactionOutputs>
where
Expand All @@ -2441,7 +2448,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
txdata: &TransactionData,
height: u32,
broadcaster: B,
fee_estimator: F,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: L,
) -> Vec<TransactionOutputs>
where
Expand Down Expand Up @@ -2538,7 +2545,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
mut watch_outputs: Vec<TransactionOutputs>,
mut claimable_outpoints: Vec<PackageTemplate>,
broadcaster: &B,
fee_estimator: &F,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: &L,
) -> Vec<TransactionOutputs>
where
Expand Down Expand Up @@ -2676,7 +2683,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
//- maturing spendable output has transaction paying us has been disconnected
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height);

self.onchain_tx_handler.block_disconnected(height, broadcaster, fee_estimator, logger);
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
self.onchain_tx_handler.block_disconnected(height, broadcaster, &bounded_fee_estimator, logger);

self.best_block = BestBlock::new(header.prev_blockhash, height - 1);
}
Expand All @@ -2685,7 +2693,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
&mut self,
txid: &Txid,
broadcaster: B,
fee_estimator: F,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: L,
) where
B::Target: BroadcasterInterface,
Expand Down Expand Up @@ -3428,6 +3436,8 @@ mod tests {

use hex;

use crate::chain::chaininterface::LowerBoundedFeeEstimator;

use super::ChannelMonitorUpdateStep;
use ::{check_added_monitors, check_closed_broadcast, check_closed_event, check_spends, get_local_commitment_txn, get_monitor, get_route_and_payment_hash, unwrap_send_err};
use chain::{BestBlock, Confirm};
Expand Down Expand Up @@ -3549,7 +3559,7 @@ mod tests {
let secp_ctx = Secp256k1::new();
let logger = Arc::new(TestLogger::new());
let broadcaster = Arc::new(TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new()), blocks: Arc::new(Mutex::new(Vec::new()))});
let fee_estimator = Arc::new(TestFeeEstimator { sat_per_kw: Mutex::new(253) });
let fee_estimator = TestFeeEstimator { sat_per_kw: Mutex::new(253) };

let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
Expand Down Expand Up @@ -3648,7 +3658,8 @@ mod tests {
monitor.provide_latest_counterparty_commitment_tx(dummy_txid, preimages_slice_to_htlc_outputs!(preimages[17..20]), 281474976710653, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(dummy_txid, preimages_slice_to_htlc_outputs!(preimages[18..20]), 281474976710652, dummy_key, &logger);
for &(ref preimage, ref hash) in preimages.iter() {
monitor.provide_payment_preimage(hash, preimage, &broadcaster, &fee_estimator, &logger);
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(&fee_estimator);
monitor.provide_payment_preimage(hash, preimage, &broadcaster, &bounded_fee_estimator, &logger);
}

// Now provide a secret, pruning preimages 10-15
Expand Down
12 changes: 6 additions & 6 deletions lightning/src/chain/onchaintx.rs
Expand Up @@ -24,7 +24,7 @@ use bitcoin::secp256k1;
use ln::msgs::DecodeError;
use ln::PaymentPreimage;
use ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransaction};
use chain::chaininterface::{FeeEstimator, BroadcasterInterface};
use chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
use chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
use chain::keysinterface::{Sign, KeysInterface};
use chain::package::PackageTemplate;
Expand Down Expand Up @@ -377,7 +377,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
/// (CSV or CLTV following cases). In case of high-fee spikes, claim tx may stuck in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or Child-Pay-For-Parent.
/// Panics if there are signing errors, because signing operations in reaction to on-chain events
/// are not expected to fail, and if they do, we may lose funds.
fn generate_claim_tx<F: Deref, L: Deref>(&mut self, cur_height: u32, cached_request: &PackageTemplate, fee_estimator: &F, logger: &L) -> Option<(Option<u32>, u64, Transaction)>
fn generate_claim_tx<F: Deref, L: Deref>(&mut self, cur_height: u32, cached_request: &PackageTemplate, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(Option<u32>, u64, Transaction)>
where F::Target: FeeEstimator,
L::Target: Logger,
{
Expand Down Expand Up @@ -415,7 +415,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
/// `conf_height` represents the height at which the transactions in `txn_matched` were
/// confirmed. This does not need to equal the current blockchain tip height, which should be
/// provided via `cur_height`, however it must never be higher than `cur_height`.
pub(crate) fn update_claims_view<B: Deref, F: Deref, L: Deref>(&mut self, txn_matched: &[&Transaction], requests: Vec<PackageTemplate>, conf_height: u32, cur_height: u32, broadcaster: &B, fee_estimator: &F, logger: &L)
pub(crate) fn update_claims_view<B: Deref, F: Deref, L: Deref>(&mut self, txn_matched: &[&Transaction], requests: Vec<PackageTemplate>, conf_height: u32, cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L)
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
Expand Down Expand Up @@ -617,7 +617,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
&mut self,
txid: &Txid,
broadcaster: B,
fee_estimator: F,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: L,
) where
B::Target: BroadcasterInterface,
Expand All @@ -637,7 +637,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
}
}

pub(crate) fn block_disconnected<B: Deref, F: Deref, L: Deref>(&mut self, height: u32, broadcaster: B, fee_estimator: F, logger: L)
pub(crate) fn block_disconnected<B: Deref, F: Deref, L: Deref>(&mut self, height: u32, broadcaster: B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: L)
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
Expand Down Expand Up @@ -667,7 +667,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
}
}
for (_, request) in bump_candidates.iter_mut() {
if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, &request, &&*fee_estimator, &&*logger) {
if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, &request, fee_estimator, &&*logger) {
request.set_timer(new_timer);
request.set_feerate(new_feerate);
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
Expand Down
8 changes: 5 additions & 3 deletions lightning/src/chain/package.rs
Expand Up @@ -38,6 +38,8 @@ use core::mem;
use core::ops::Deref;
use bitcoin::Witness;

use super::chaininterface::LowerBoundedFeeEstimator;

const MAX_ALLOC_SIZE: usize = 64*1024;


Expand Down Expand Up @@ -665,7 +667,7 @@ impl PackageTemplate {
/// Returns value in satoshis to be included as package outgoing output amount and feerate
/// which was used to generate the value. Will not return less than `dust_limit_sats` for the
/// value.
pub(crate) fn compute_package_output<F: Deref, L: Deref>(&self, predicted_weight: usize, dust_limit_sats: u64, fee_estimator: &F, logger: &L) -> Option<(u64, u64)>
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)>
where F::Target: FeeEstimator,
L::Target: Logger,
{
Expand Down Expand Up @@ -772,7 +774,7 @@ impl Readable for PackageTemplate {
/// If the proposed fee is less than the available spent output's values, we return the proposed
/// fee and the corresponding updated feerate. If the proposed fee is equal or more than the
/// available spent output's values, we return nothing
fn compute_fee_from_spent_amounts<F: Deref, L: Deref>(input_amounts: u64, predicted_weight: usize, fee_estimator: &F, logger: &L) -> Option<(u64, u64)>
fn compute_fee_from_spent_amounts<F: Deref, L: Deref>(input_amounts: u64, predicted_weight: usize, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u64, u64)>
where F::Target: FeeEstimator,
L::Target: Logger,
{
Expand Down Expand Up @@ -808,7 +810,7 @@ fn compute_fee_from_spent_amounts<F: Deref, L: Deref>(input_amounts: u64, predic
/// attempt, use them. Otherwise, blindly bump the feerate by 25% of the previous feerate. We also
/// verify that those bumping heuristics respect BIP125 rules 3) and 4) and if required adjust
/// the new fee to meet the RBF policy requirement.
fn feerate_bump<F: Deref, L: Deref>(predicted_weight: usize, input_amounts: u64, previous_feerate: u64, fee_estimator: &F, logger: &L) -> Option<(u64, u64)>
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)>
where F::Target: FeeEstimator,
L::Target: Logger,
{
Expand Down

0 comments on commit 2a3bf03

Please sign in to comment.