// always return a HighPriority feerate here which is >= the maximum Normal feerate and a
// Background feerate which is <= the minimum Normal feerate.
match conf_target {
- ConfirmationTarget::MaximumFeeEstimate | ConfirmationTarget::OnChainSweep => MAX_FEE,
+ ConfirmationTarget::MaximumFeeEstimate | ConfirmationTarget::UrgentOnChainSweep => {
+ MAX_FEE
+ },
ConfirmationTarget::ChannelCloseMinimum
| ConfirmationTarget::AnchorChannelFee
| ConfirmationTarget::MinAllowedAnchorChannelRemoteFee
/// trying to burn channel balance to dust.
MaximumFeeEstimate,
/// We have some funds available on chain which we need to spend prior to some expiry time at
- /// which point our counterparty may be able to steal them. Generally we have in the high tens
- /// to low hundreds of blocks to get our transaction on-chain, but we shouldn't risk too low a
- /// fee - this should be a relatively high priority feerate.
- OnChainSweep,
+ /// which point our counterparty may be able to steal them.
+ ///
+ /// Generally we have in the high tens to low hundreds of blocks to get our transaction
+ /// on-chain (it doesn't have to happen in the next few blocks!), but we shouldn't risk too low
+ /// a fee - this should be a relatively high priority feerate.
+ UrgentOnChainSweep,
/// This is the lowest feerate we will allow our channel counterparty to have in an anchor
/// channel in order to close the channel if a channel party goes away.
///
///
/// [`ChannelManager::close_channel_with_feerate_and_script`]: crate::ln::channelmanager::ChannelManager::close_channel_with_feerate_and_script
ChannelCloseMinimum,
- /// The feerate [`OutputSweeper`] will use on transactions spending
- /// [`SpendableOutputDescriptor`]s after a channel closure.
+ /// The feerate used to claim on-chain funds when there is no particular urgency to do so.
+ ///
+ /// It is used to get commitment transactions without any HTLCs confirmed in [`ChannelMonitor`]
+ /// and by [`OutputSweeper`] on transactions spending [`SpendableOutputDescriptor`]s after a
+ /// channel closure.
///
/// Generally spending these outputs is safe as long as they eventually confirm, so a value
/// (slightly above) the mempool minimum should suffice. However, as this value will influence
/// how long funds will be unavailable after channel closure, [`FeeEstimator`] implementors
/// might want to choose a higher feerate to regain control over funds faster.
///
+ /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
/// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
/// [`SpendableOutputDescriptor`]: crate::sign::SpendableOutputDescriptor
OutputSpendingFee,
use crate::ln::channelmanager::{HTLCSource, SentHTLCId};
use crate::chain;
use crate::chain::{BestBlock, WatchedOutput};
-use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
+use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
use crate::chain::transaction::{OutPoint, TransactionData};
use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::EcdsaChannelSigner, SignerProvider, EntropySource};
use crate::chain::onchaintx::{ClaimEvent, FeerateStrategy, OnchainTxHandler};
let mut inner = self.inner.lock().unwrap();
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
let current_height = inner.best_block.height;
+ let conf_target = inner.closure_conf_target();
inner.onchain_tx_handler.rebroadcast_pending_claims(
- current_height, FeerateStrategy::HighestOfPreviousOrNew, &broadcaster, &fee_estimator, &logger,
+ current_height, FeerateStrategy::HighestOfPreviousOrNew, &broadcaster, conf_target, &fee_estimator, &logger,
);
}
let mut inner = self.inner.lock().unwrap();
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
let current_height = inner.best_block.height;
+ let conf_target = inner.closure_conf_target();
inner.onchain_tx_handler.rebroadcast_pending_claims(
- current_height, FeerateStrategy::RetryPrevious, &broadcaster, &fee_estimator, &logger,
+ current_height, FeerateStrategy::RetryPrevious, &broadcaster, conf_target, &fee_estimator, &logger,
);
}
}
impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
+ /// Gets the [`ConfirmationTarget`] we should use when selecting feerates for channel closure
+ /// transactions for this channel right now.
+ fn closure_conf_target(&self) -> ConfirmationTarget {
+ // Treat the sweep as urgent as long as there is at least one HTLC which is pending on a
+ // valid commitment transaction.
+ if !self.current_holder_commitment_tx.htlc_outputs.is_empty() {
+ return ConfirmationTarget::UrgentOnChainSweep;
+ }
+ if self.prev_holder_signed_commitment_tx.as_ref().map(|t| !t.htlc_outputs.is_empty()).unwrap_or(false) {
+ return ConfirmationTarget::UrgentOnChainSweep;
+ }
+ if let Some(txid) = self.current_counterparty_commitment_txid {
+ if !self.counterparty_claimable_outpoints.get(&txid).unwrap().is_empty() {
+ return ConfirmationTarget::UrgentOnChainSweep;
+ }
+ }
+ if let Some(txid) = self.prev_counterparty_commitment_txid {
+ if !self.counterparty_claimable_outpoints.get(&txid).unwrap().is_empty() {
+ return ConfirmationTarget::UrgentOnChainSweep;
+ }
+ }
+ ConfirmationTarget::OutputSpendingFee
+ }
+
/// Inserts a revocation secret into this channel monitor. Prunes old preimages if neither
/// needed by holder commitment transactions HTCLs nor by counterparty ones. Unless we haven't already seen
/// counterparty commitment transaction's secret, they are de facto pruned (we can use revocation key).
macro_rules! claim_htlcs {
($commitment_number: expr, $txid: expr, $htlcs: expr) => {
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None, $htlcs);
- self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height, self.best_block.height, broadcaster, fee_estimator, logger);
+ let conf_target = self.closure_conf_target();
+ self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height, self.best_block.height, broadcaster, conf_target, fee_estimator, logger);
}
}
if let Some(txid) = self.current_counterparty_commitment_txid {
// block. Even if not, its a reasonable metric for the bump criteria on the HTLC
// transactions.
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&holder_commitment_tx, self.best_block.height);
- self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height, self.best_block.height, broadcaster, fee_estimator, logger);
+ let conf_target = self.closure_conf_target();
+ self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height, self.best_block.height, broadcaster, conf_target, fee_estimator, logger);
}
}
}
L::Target: Logger,
{
let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) });
+ let conf_target = self.closure_conf_target();
self.onchain_tx_handler.update_claims_view_from_requests(
claimable_outpoints, self.best_block.height, self.best_block.height, broadcaster,
- fee_estimator, logger
+ conf_target, fee_estimator, logger,
);
}
self.best_block = BestBlock::new(block_hash, height);
log_trace!(logger, "Best block re-orged, replaced with new block {} at height {}", block_hash, height);
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height);
- self.onchain_tx_handler.block_disconnected(height + 1, broadcaster, fee_estimator, logger);
+ let conf_target = self.closure_conf_target();
+ self.onchain_tx_handler.block_disconnected(height + 1, broadcaster, conf_target, fee_estimator, logger);
Vec::new()
} else { Vec::new() }
}
}
}
- self.onchain_tx_handler.update_claims_view_from_requests(claimable_outpoints, conf_height, self.best_block.height, broadcaster, fee_estimator, logger);
- self.onchain_tx_handler.update_claims_view_from_matched_txn(&txn_matched, conf_height, conf_hash, self.best_block.height, broadcaster, fee_estimator, logger);
+ let conf_target = self.closure_conf_target();
+ self.onchain_tx_handler.update_claims_view_from_requests(claimable_outpoints, conf_height, self.best_block.height, broadcaster, conf_target, fee_estimator, logger);
+ self.onchain_tx_handler.update_claims_view_from_matched_txn(&txn_matched, conf_height, conf_hash, self.best_block.height, broadcaster, conf_target, fee_estimator, logger);
// Determine new outputs to watch by comparing against previously known outputs to watch,
// updating the latter in the process.
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height);
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
- self.onchain_tx_handler.block_disconnected(height, broadcaster, &bounded_fee_estimator, logger);
+ let conf_target = self.closure_conf_target();
+ self.onchain_tx_handler.block_disconnected(height, broadcaster, conf_target, &bounded_fee_estimator, logger);
self.best_block = BestBlock::new(header.prev_blockhash, height - 1);
}
debug_assert!(!self.onchain_events_awaiting_threshold_conf.iter().any(|ref entry| entry.txid == *txid));
- self.onchain_tx_handler.transaction_unconfirmed(txid, broadcaster, fee_estimator, logger);
+ let conf_target = self.closure_conf_target();
+ self.onchain_tx_handler.transaction_unconfirmed(txid, broadcaster, conf_target, fee_estimator, logger);
}
/// Filters a block's `txdata` for transactions spending watched outputs or for any child
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1;
-use crate::chain::chaininterface::compute_feerate_sat_per_1000_weight;
+use crate::chain::chaininterface::{ConfirmationTarget, compute_feerate_sat_per_1000_weight};
use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, ChannelSigner, EntropySource, SignerProvider, ecdsa::EcdsaChannelSigner};
use crate::ln::msgs::DecodeError;
use crate::ln::types::PaymentPreimage;
use crate::ln::chan_utils::{self, ChannelTransactionParameters, HTLCOutputInCommitment, HolderCommitmentTransaction};
use crate::chain::ClaimId;
-use crate::chain::chaininterface::{ConfirmationTarget, FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
+use crate::chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
use crate::chain::package::{PackageSolvingData, PackageTemplate};
use crate::chain::transaction::MaybeSignedTransaction;
/// connections, like on mobile.
pub(super) fn rebroadcast_pending_claims<B: Deref, F: Deref, L: Logger>(
&mut self, current_height: u32, feerate_strategy: FeerateStrategy, broadcaster: &B,
- fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
+ conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
)
where
B::Target: BroadcasterInterface,
bump_requests.push((*claim_id, request.clone()));
}
for (claim_id, request) in bump_requests {
- self.generate_claim(current_height, &request, &feerate_strategy, fee_estimator, logger)
+ self.generate_claim(current_height, &request, &feerate_strategy, conf_target, fee_estimator, logger)
.map(|(_, new_feerate, claim)| {
let mut bumped_feerate = false;
if let Some(mut_request) = self.pending_claim_requests.get_mut(&claim_id) {
/// events are not expected to fail, and if they do, we may lose funds.
fn generate_claim<F: Deref, L: Logger>(
&mut self, cur_height: u32, cached_request: &PackageTemplate, feerate_strategy: &FeerateStrategy,
- fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
+ conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
) -> Option<(u32, u64, OnchainClaim)>
where F::Target: FeeEstimator,
{
if cached_request.is_malleable() {
if cached_request.requires_external_funding() {
let target_feerate_sat_per_1000_weight = cached_request.compute_package_feerate(
- fee_estimator, ConfirmationTarget::OnChainSweep, feerate_strategy,
+ fee_estimator, conf_target, feerate_strategy,
);
if let Some(htlcs) = cached_request.construct_malleable_package_with_external_funding(self) {
return Some((
let predicted_weight = cached_request.package_weight(&self.destination_script);
if let Some((output_value, new_feerate)) = cached_request.compute_package_output(
predicted_weight, self.destination_script.minimal_non_dust().to_sat(),
- feerate_strategy, fee_estimator, logger,
+ feerate_strategy, conf_target, fee_estimator, logger,
) {
assert!(new_feerate != 0);
debug_assert_eq!(tx.0.compute_txid(), self.holder_commitment.trust().txid(),
"Holder commitment transaction mismatch");
- let conf_target = ConfirmationTarget::OnChainSweep;
let package_target_feerate_sat_per_1000_weight = cached_request
.compute_package_feerate(fee_estimator, conf_target, feerate_strategy);
if let Some(input_amount_sat) = output.funding_amount {
/// `cur_height`, however it must never be higher than `cur_height`.
pub(super) fn update_claims_view_from_requests<B: Deref, F: Deref, L: Logger>(
&mut self, requests: Vec<PackageTemplate>, conf_height: u32, cur_height: u32,
- broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
+ broadcaster: &B, conf_target: ConfirmationTarget,
+ fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
// height timer expiration (i.e in how many blocks we're going to take action).
for mut req in preprocessed_requests {
if let Some((new_timer, new_feerate, claim)) = self.generate_claim(
- cur_height, &req, &FeerateStrategy::ForceBump, &*fee_estimator, &*logger,
+ cur_height, &req, &FeerateStrategy::ForceBump, conf_target, &*fee_estimator, &*logger,
) {
req.set_timer(new_timer);
req.set_feerate(new_feerate);
/// provided via `cur_height`, however it must never be higher than `cur_height`.
pub(super) fn update_claims_view_from_matched_txn<B: Deref, F: Deref, L: Logger>(
&mut self, txn_matched: &[&Transaction], conf_height: u32, conf_hash: BlockHash,
- cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
+ cur_height: u32, broadcaster: &B, conf_target: ConfirmationTarget,
+ fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
for (claim_id, request) in bump_candidates.iter() {
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(
- cur_height, &request, &FeerateStrategy::ForceBump, &*fee_estimator, &*logger,
+ cur_height, &request, &FeerateStrategy::ForceBump, conf_target, &*fee_estimator, &*logger,
) {
match bump_claim {
OnchainClaim::Tx(bump_tx) => {
&mut self,
txid: &Txid,
broadcaster: B,
+ conf_target: ConfirmationTarget,
fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: &L,
) where
}
if let Some(height) = height {
- self.block_disconnected(height, broadcaster, fee_estimator, logger);
+ self.block_disconnected(height, broadcaster, conf_target, fee_estimator, logger);
}
}
- pub(super) fn block_disconnected<B: Deref, F: Deref, L: Logger>(&mut self, height: u32, broadcaster: B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L)
+ pub(super) fn block_disconnected<B: Deref, F: Deref, L: Logger>(
+ &mut self, height: u32, broadcaster: B, conf_target: ConfirmationTarget,
+ fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
+ )
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
{
// `height` is the height being disconnected, so our `current_height` is 1 lower.
let current_height = height - 1;
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(
- current_height, &request, &FeerateStrategy::ForceBump, fee_estimator, logger
+ current_height, &request, &FeerateStrategy::ForceBump, conf_target, fee_estimator, logger
) {
request.set_timer(new_timer);
request.set_feerate(new_feerate);
(0, funding_redeemscript, required),
(1, channel_type_features, option),
(2, _legacy_deserialization_prevention_marker, option),
- (3, funding_amount, option)
+ (3, funding_amount, option),
});
verify_channel_type_features(&channel_type_features, None)?;
/// value.
pub(crate) fn compute_package_output<F: Deref, L: Logger>(
&self, predicted_weight: u64, dust_limit_sats: u64, feerate_strategy: &FeerateStrategy,
- fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
+ conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
) -> Option<(u64, u64)>
where F::Target: FeeEstimator,
{
if self.feerate_previous != 0 {
if let Some((new_fee, feerate)) = feerate_bump(
predicted_weight, input_amounts, self.feerate_previous, feerate_strategy,
- fee_estimator, logger,
+ conf_target, fee_estimator, logger,
) {
return Some((cmp::max(input_amounts as i64 - new_fee as i64, dust_limit_sats as i64) as u64, feerate));
}
} else {
- if let Some((new_fee, feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
+ if let Some((new_fee, feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, conf_target, fee_estimator, logger) {
return Some((cmp::max(input_amounts as i64 - new_fee as i64, dust_limit_sats as i64) as u64, feerate));
}
}
}
/// Attempt to propose a bumping fee for a transaction from its spent output's values and predicted
-/// weight. We first try our [`OnChainSweep`] feerate, if it's not enough we try to sweep half of
-/// the input amounts.
+/// weight. We first try our estimator's feerate, if it's not enough we try to sweep half of the
+/// input amounts.
///
/// If the proposed fee is less than the available spent output's values, we return the proposed
/// fee and the corresponding updated feerate. If fee is under [`FEERATE_FLOOR_SATS_PER_KW`], we
/// return nothing.
///
-/// [`OnChainSweep`]: crate::chain::chaininterface::ConfirmationTarget::OnChainSweep
/// [`FEERATE_FLOOR_SATS_PER_KW`]: crate::chain::chaininterface::MIN_RELAY_FEE_SAT_PER_1000_WEIGHT
-fn compute_fee_from_spent_amounts<F: Deref, L: Logger>(input_amounts: u64, predicted_weight: u64, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u64, u64)>
+fn compute_fee_from_spent_amounts<F: Deref, L: Logger>(
+ input_amounts: u64, predicted_weight: u64, conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
+) -> Option<(u64, u64)>
where F::Target: FeeEstimator,
{
- let sweep_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::OnChainSweep);
+ let sweep_feerate = fee_estimator.bounded_sat_per_1000_weight(conf_target);
let fee_rate = cmp::min(sweep_feerate, compute_feerate_sat_per_1000_weight(input_amounts / 2, predicted_weight));
let fee = fee_rate as u64 * (predicted_weight) / 1000;
/// requirement.
fn feerate_bump<F: Deref, L: Logger>(
predicted_weight: u64, input_amounts: u64, previous_feerate: u64, feerate_strategy: &FeerateStrategy,
- fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
+ conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
) -> Option<(u64, u64)>
where
F::Target: FeeEstimator,
{
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
- let (new_fee, new_feerate) = if let Some((new_fee, new_feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
+ let (new_fee, new_feerate) = if let Some((new_fee, new_feerate)) =
+ compute_fee_from_spent_amounts(input_amounts, predicted_weight, conf_target, fee_estimator, logger)
+ {
match feerate_strategy {
FeerateStrategy::RetryPrevious => {
let previous_fee = previous_feerate * predicted_weight / 1000;