use crate::ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment};
use crate::ln::chan_utils;
use crate::ln::msgs::DecodeError;
-use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
+use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT, compute_feerate_sat_per_1000_weight, FEERATE_FLOOR_SATS_PER_KW};
use crate::sign::WriteableEcdsaChannelSigner;
-#[cfg(anchors)]
-use crate::chain::onchaintx::ExternalHTLCClaim;
-use crate::chain::onchaintx::OnchainTxHandler;
+use crate::chain::onchaintx::{ExternalHTLCClaim, OnchainTxHandler};
use crate::util::logger::Logger;
use crate::util::ser::{Readable, Writer, Writeable, RequiredWrapper};
use crate::io;
use crate::prelude::*;
use core::cmp;
-#[cfg(anchors)]
use core::convert::TryInto;
use core::mem;
use core::ops::Deref;
let mut counterparty_htlc_base_key = RequiredWrapper(None);
let mut preimage = RequiredWrapper(None);
let mut htlc = RequiredWrapper(None);
- let mut legacy_deserialization_prevention_marker: Option<()> = None;
+ let mut _legacy_deserialization_prevention_marker: Option<()> = None;
let mut channel_type_features = None;
read_tlv_fields!(reader, {
(4, counterparty_htlc_base_key, required),
(6, preimage, required),
(8, htlc, required),
- (10, legacy_deserialization_prevention_marker, option),
+ (10, _legacy_deserialization_prevention_marker, option),
(11, channel_type_features, option),
});
let mut counterparty_delayed_payment_base_key = RequiredWrapper(None);
let mut counterparty_htlc_base_key = RequiredWrapper(None);
let mut htlc = RequiredWrapper(None);
- let mut legacy_deserialization_prevention_marker: Option<()> = None;
+ let mut _legacy_deserialization_prevention_marker: Option<()> = None;
let mut channel_type_features = None;
read_tlv_fields!(reader, {
(2, counterparty_delayed_payment_base_key, required),
(4, counterparty_htlc_base_key, required),
(6, htlc, required),
- (8, legacy_deserialization_prevention_marker, option),
+ (8, _legacy_deserialization_prevention_marker, option),
(9, channel_type_features, option),
});
let mut amount_msat = RequiredWrapper(None);
let mut cltv_expiry = RequiredWrapper(None);
let mut preimage = None;
- let mut legacy_deserialization_prevention_marker: Option<()> = None;
+ let mut _legacy_deserialization_prevention_marker: Option<()> = None;
let mut channel_type_features = None;
read_tlv_fields!(reader, {
(0, amount_msat, required),
(2, cltv_expiry, required),
(4, preimage, option),
- (6, legacy_deserialization_prevention_marker, option),
+ (6, _legacy_deserialization_prevention_marker, option),
(7, channel_type_features, option),
});
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct HolderFundingOutput {
funding_redeemscript: Script,
- funding_amount: Option<u64>,
+ pub(crate) funding_amount: Option<u64>,
channel_type_features: ChannelTypeFeatures,
}
impl Readable for HolderFundingOutput {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
let mut funding_redeemscript = RequiredWrapper(None);
- let mut legacy_deserialization_prevention_marker: Option<()> = None;
+ let mut _legacy_deserialization_prevention_marker: Option<()> = None;
let mut channel_type_features = None;
let mut funding_amount = None;
read_tlv_fields!(reader, {
(0, funding_redeemscript, required),
(1, channel_type_features, option),
- (2, legacy_deserialization_prevention_marker, option),
+ (2, _legacy_deserialization_prevention_marker, option),
(3, funding_amount, option)
});
_ => { mem::discriminant(self) == mem::discriminant(&input) }
}
}
+ fn as_tx_input(&self, previous_output: BitcoinOutPoint) -> TxIn {
+ let sequence = match self {
+ PackageSolvingData::RevokedOutput(_) => Sequence::ENABLE_RBF_NO_LOCKTIME,
+ PackageSolvingData::RevokedHTLCOutput(_) => Sequence::ENABLE_RBF_NO_LOCKTIME,
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(outp) => if outp.channel_type_features.supports_anchors_zero_fee_htlc_tx() {
+ Sequence::from_consensus(1)
+ } else {
+ Sequence::ENABLE_RBF_NO_LOCKTIME
+ },
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(outp) => if outp.channel_type_features.supports_anchors_zero_fee_htlc_tx() {
+ Sequence::from_consensus(1)
+ } else {
+ Sequence::ENABLE_RBF_NO_LOCKTIME
+ },
+ _ => {
+ debug_assert!(false, "This should not be reachable by 'untractable' or 'malleable with external funding' packages");
+ Sequence::ENABLE_RBF_NO_LOCKTIME
+ },
+ };
+ TxIn {
+ previous_output,
+ script_sig: Script::new(),
+ sequence,
+ witness: Witness::new(),
+ }
+ }
fn finalize_input<Signer: WriteableEcdsaChannelSigner>(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler<Signer>) -> bool {
match self {
PackageSolvingData::RevokedOutput(ref outp) => {
let output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR;
inputs_weight + witnesses_weight + transaction_weight + output_weight
}
- #[cfg(anchors)]
pub(crate) fn construct_malleable_package_with_external_funding<Signer: WriteableEcdsaChannelSigner>(
&self, onchain_handler: &mut OnchainTxHandler<Signer>,
) -> Option<Vec<ExternalHTLCClaim>> {
value,
}],
};
- for (outpoint, _) in self.inputs.iter() {
- bumped_tx.input.push(TxIn {
- previous_output: *outpoint,
- script_sig: Script::new(),
- sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
- witness: Witness::new(),
- });
+ for (outpoint, outp) in self.inputs.iter() {
+ bumped_tx.input.push(outp.as_tx_input(*outpoint));
}
for (i, (outpoint, out)) in self.inputs.iter().enumerate() {
log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout);
None
}
- #[cfg(anchors)]
/// Computes a feerate based on the given confirmation target. If a previous feerate was used,
/// the new feerate is below it, and `force_feerate_bump` is set, we'll use a 25% increase of
/// the previous feerate instead of the new feerate.
) -> u32 where F::Target: FeeEstimator {
let feerate_estimate = fee_estimator.bounded_sat_per_1000_weight(conf_target);
if self.feerate_previous != 0 {
- // If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
+ // Use the new fee estimate if it's higher than the one previously used.
if feerate_estimate as u64 > self.feerate_previous {
feerate_estimate
} else if !force_feerate_bump {
self.feerate_previous.try_into().unwrap_or(u32::max_value())
} else {
- // ...else just increase the previous feerate by 25% (because that's a nice number)
- (self.feerate_previous + (self.feerate_previous / 4)).try_into().unwrap_or(u32::max_value())
+ // Our fee estimate has decreased, but our transaction remains unconfirmed after
+ // using our previous fee estimate. This may point to an unreliable fee estimator,
+ // so we choose to bump our previous feerate by 25%, making sure we don't use a
+ // lower feerate or overpay by a large margin by limiting it to 5x the new fee
+ // estimate.
+ let previous_feerate = self.feerate_previous.try_into().unwrap_or(u32::max_value());
+ let mut new_feerate = previous_feerate.saturating_add(previous_feerate / 4);
+ if new_feerate > feerate_estimate * 5 {
+ new_feerate = cmp::max(feerate_estimate * 5, previous_feerate);
+ }
+ new_feerate
}
} else {
feerate_estimate
}
/// Attempt to propose a bumping fee for a transaction from its spent output's values and predicted
-/// weight. We start with the highest priority feerate returned by the node's fee estimator then
-/// fall-back to lower priorities until we have enough value available to suck from.
+/// weight. We first try our [`OnChainSweep`] 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 the proposed fee is equal or more than the
-/// available spent output's values, we return nothing
+/// 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: Deref>(input_amounts: u64, predicted_weight: usize, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u64, u64)>
where F::Target: FeeEstimator,
L::Target: Logger,
{
- let mut updated_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::HighPriority) as u64;
- let mut fee = updated_feerate * (predicted_weight as u64) / 1000;
- if input_amounts <= fee {
- updated_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal) as u64;
- fee = updated_feerate * (predicted_weight as u64) / 1000;
- if input_amounts <= fee {
- updated_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Background) as u64;
- fee = updated_feerate * (predicted_weight as u64) / 1000;
- if input_amounts <= fee {
- log_error!(logger, "Failed to generate an on-chain punishment tx as even low priority fee ({} sat) was more than the entire claim balance ({} sat)",
- fee, input_amounts);
- None
- } else {
- log_warn!(logger, "Used low priority fee for on-chain punishment tx as high priority fee was more than the entire claim balance ({} sat)",
- input_amounts);
- Some((fee, updated_feerate))
- }
- } else {
- log_warn!(logger, "Used medium priority fee for on-chain punishment tx as high priority fee was more than the entire claim balance ({} sat)",
- input_amounts);
- Some((fee, updated_feerate))
- }
+ let sweep_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::OnChainSweep);
+ let fee_rate = cmp::min(sweep_feerate, compute_feerate_sat_per_1000_weight(input_amounts / 2, predicted_weight as u64)) as u64;
+ let fee = fee_rate * (predicted_weight as u64) / 1000;
+
+ // if the fee rate is below the floor, we don't sweep
+ if fee_rate < FEERATE_FLOOR_SATS_PER_KW as u64 {
+ log_error!(logger, "Failed to generate an on-chain tx with fee ({} sat/kw) was less than the floor ({} sat/kw)",
+ fee_rate, FEERATE_FLOOR_SATS_PER_KW);
+ None
} else {
- Some((fee, updated_feerate))
+ Some((fee, fee_rate))
}
}