Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add min feerate checks #1552

Merged
merged 2 commits into from Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unit conversions are still really useful.

/// 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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Minimum feerate that takes a sane approach to bitcoind weight-to-vbytes rounding"

I don't know if this subtlety is documented anywhere well, so feel free to ref Rusty's commit : ElementsProject/lightning@2e687b9


/// 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