From: Matt Corallo Date: Thu, 22 Aug 2024 19:15:34 +0000 (+0000) Subject: Split `ConfirmationTarget::OnChainSweep` into urgent and non-urgent X-Git-Tag: v0.0.124-rc1~6^2~3 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=14720190b05c81283e22b32f9d95b11f08f393e5;p=rust-lightning Split `ConfirmationTarget::OnChainSweep` into urgent and non-urgent When we force-close a channel, occasionally its due to feerate disagreements or other non-HTLC-related issues. In those cases, there's no reason to use a very urgent feerate estimate - we don't have any timers expiring soon. Instead, we should give users the information they need to be more economical on fees in this case, which we do here by splitting `OnChainSweep` into `UrgentOnChainSweep` and `NonUrgentOnChainSweep` `ConfirmationTarget`s. --- diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 7c50a5125..b1b916250 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -97,7 +97,9 @@ impl FeeEstimator for FuzzEstimator { // 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 diff --git a/lightning/src/chain/chaininterface.rs b/lightning/src/chain/chaininterface.rs index 0db327470..84281df1d 100644 --- a/lightning/src/chain/chaininterface.rs +++ b/lightning/src/chain/chaininterface.rs @@ -56,10 +56,12 @@ pub enum ConfirmationTarget { /// 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. /// @@ -130,14 +132,18 @@ pub enum ConfirmationTarget { /// /// [`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, diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 4cfb4bf12..deb9cc868 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -40,7 +40,7 @@ use crate::ln::chan_utils::{self,CommitmentTransaction, CounterpartyCommitmentSe 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}; @@ -1902,8 +1902,9 @@ impl ChannelMonitor { 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, ); } @@ -1927,8 +1928,9 @@ impl ChannelMonitor { 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, ); } @@ -2684,6 +2686,30 @@ pub fn deliberately_bogus_accepted_htlc_witness() -> Vec> { } impl ChannelMonitorImpl { + /// 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). @@ -2928,7 +2954,8 @@ impl ChannelMonitorImpl { 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 { @@ -2976,7 +3003,8 @@ impl ChannelMonitorImpl { // 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); } } } @@ -3036,9 +3064,10 @@ impl ChannelMonitorImpl { 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, ); } @@ -3851,7 +3880,8 @@ impl ChannelMonitorImpl { 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() } } @@ -4116,8 +4146,9 @@ impl ChannelMonitorImpl { } } - 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. @@ -4156,7 +4187,8 @@ impl ChannelMonitorImpl { 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); } @@ -4190,7 +4222,8 @@ impl ChannelMonitorImpl { 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 diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs index eff827983..00910a9d5 100644 --- a/lightning/src/chain/onchaintx.rs +++ b/lightning/src/chain/onchaintx.rs @@ -24,13 +24,13 @@ use bitcoin::secp256k1::PublicKey; 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; @@ -487,7 +487,7 @@ impl OnchainTxHandler { /// connections, like on mobile. pub(super) fn rebroadcast_pending_claims( &mut self, current_height: u32, feerate_strategy: FeerateStrategy, broadcaster: &B, - fee_estimator: &LowerBoundedFeeEstimator, logger: &L, + conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator, logger: &L, ) where B::Target: BroadcasterInterface, @@ -500,7 +500,7 @@ impl OnchainTxHandler { 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) { @@ -552,7 +552,7 @@ impl OnchainTxHandler { /// events are not expected to fail, and if they do, we may lose funds. fn generate_claim( &mut self, cur_height: u32, cached_request: &PackageTemplate, feerate_strategy: &FeerateStrategy, - fee_estimator: &LowerBoundedFeeEstimator, logger: &L, + conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator, logger: &L, ) -> Option<(u32, u64, OnchainClaim)> where F::Target: FeeEstimator, { @@ -600,7 +600,7 @@ impl OnchainTxHandler { 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(( @@ -620,7 +620,7 @@ impl OnchainTxHandler { 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); @@ -650,7 +650,6 @@ impl OnchainTxHandler { 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 { @@ -728,7 +727,8 @@ impl OnchainTxHandler { /// `cur_height`, however it must never be higher than `cur_height`. pub(super) fn update_claims_view_from_requests( &mut self, requests: Vec, conf_height: u32, cur_height: u32, - broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator, logger: &L + broadcaster: &B, conf_target: ConfirmationTarget, + fee_estimator: &LowerBoundedFeeEstimator, logger: &L ) where B::Target: BroadcasterInterface, F::Target: FeeEstimator, @@ -798,7 +798,7 @@ impl OnchainTxHandler { // 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); @@ -863,7 +863,8 @@ impl OnchainTxHandler { /// provided via `cur_height`, however it must never be higher than `cur_height`. pub(super) fn update_claims_view_from_matched_txn( &mut self, txn_matched: &[&Transaction], conf_height: u32, conf_hash: BlockHash, - cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator, logger: &L + cur_height: u32, broadcaster: &B, conf_target: ConfirmationTarget, + fee_estimator: &LowerBoundedFeeEstimator, logger: &L ) where B::Target: BroadcasterInterface, F::Target: FeeEstimator, @@ -1014,7 +1015,7 @@ impl OnchainTxHandler { 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) => { @@ -1049,6 +1050,7 @@ impl OnchainTxHandler { &mut self, txid: &Txid, broadcaster: B, + conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator, logger: &L, ) where @@ -1064,11 +1066,14 @@ impl OnchainTxHandler { } 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(&mut self, height: u32, broadcaster: B, fee_estimator: &LowerBoundedFeeEstimator, logger: &L) + pub(super) fn block_disconnected( + &mut self, height: u32, broadcaster: B, conf_target: ConfirmationTarget, + fee_estimator: &LowerBoundedFeeEstimator, logger: &L, + ) where B::Target: BroadcasterInterface, F::Target: FeeEstimator, { @@ -1100,7 +1105,7 @@ impl OnchainTxHandler { // `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); diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index 361e12fa5..3dedf2914 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -473,7 +473,7 @@ impl Readable for HolderFundingOutput { (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)?; @@ -966,7 +966,7 @@ impl PackageTemplate { /// value. pub(crate) fn compute_package_output( &self, predicted_weight: u64, dust_limit_sats: u64, feerate_strategy: &FeerateStrategy, - fee_estimator: &LowerBoundedFeeEstimator, logger: &L, + conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator, logger: &L, ) -> Option<(u64, u64)> where F::Target: FeeEstimator, { @@ -977,12 +977,12 @@ impl PackageTemplate { 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)); } } @@ -1102,19 +1102,20 @@ impl Readable for PackageTemplate { } /// 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(input_amounts: u64, predicted_weight: u64, fee_estimator: &LowerBoundedFeeEstimator, logger: &L) -> Option<(u64, u64)> +fn compute_fee_from_spent_amounts( + input_amounts: u64, predicted_weight: u64, conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator, 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; @@ -1136,13 +1137,15 @@ fn compute_fee_from_spent_amounts(input_amounts: u64, predi /// requirement. fn feerate_bump( predicted_weight: u64, input_amounts: u64, previous_feerate: u64, feerate_strategy: &FeerateStrategy, - fee_estimator: &LowerBoundedFeeEstimator, logger: &L, + conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator, 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;