use crate::ln::chan_utils::{self, TxCreationKeys, HTLCOutputInCommitment};
use crate::ln::features::ChannelTypeFeatures;
use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint};
+use crate::ln::channelmanager::MIN_CLTV_EXPIRY_DELTA;
use crate::ln::msgs::DecodeError;
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::chain::transaction::MaybeSignedTransaction;
// number_of_witness_elements + sig_length + revocation_sig + true_length + op_true + witness_script_length + witness_script
pub(crate) const WEIGHT_REVOKED_OUTPUT: u64 = 1 + 1 + 73 + 1 + 1 + 1 + 77;
+#[cfg(not(test))]
/// Height delay at which transactions are fee-bumped/rebroadcasted with a low priority.
const LOW_FREQUENCY_BUMP_INTERVAL: u32 = 15;
+#[cfg(test)]
+/// Height delay at which transactions are fee-bumped/rebroadcasted with a low priority.
+pub(crate) const LOW_FREQUENCY_BUMP_INTERVAL: u32 = 15;
+
/// Height delay at which transactions are fee-bumped/rebroadcasted with a middle priority.
const MIDDLE_FREQUENCY_BUMP_INTERVAL: u32 = 3;
/// Height delay at which transactions are fee-bumped/rebroadcasted with a high priority.
return None;
} else { panic!("API Error: Package must not be inputs empty"); }
}
- /// In LN, output claimed are time-sensitive, which means we have to spend them before reaching some timelock expiration. At in-channel
- /// output detection, we generate a first version of a claim tx and associate to it a height timer. A height timer is an absolute block
- /// height that once reached we should generate a new bumped "version" of the claim tx to be sure that we safely claim outputs before
- /// that our counterparty can do so. If timelock expires soon, height timer is going to be scaled down in consequence to increase
- /// frequency of the bump and so increase our bets of success.
+ /// Gets the next height at which we should fee-bump this package, assuming we can do so and
+ /// the package is last fee-bumped at `current_height`.
+ ///
+ /// As the deadline with which to get a claim confirmed approaches, the rate at which the timer
+ /// ticks increases.
pub(crate) fn get_height_timer(&self, current_height: u32) -> u32 {
- if self.soonest_conf_deadline <= current_height + MIDDLE_FREQUENCY_BUMP_INTERVAL {
- return current_height + HIGH_FREQUENCY_BUMP_INTERVAL
- } else if self.soonest_conf_deadline - current_height <= LOW_FREQUENCY_BUMP_INTERVAL {
- return current_height + MIDDLE_FREQUENCY_BUMP_INTERVAL
+ let mut height_timer = current_height + LOW_FREQUENCY_BUMP_INTERVAL;
+ let timer_for_target_conf = |target_conf| -> u32 {
+ if target_conf <= current_height + MIDDLE_FREQUENCY_BUMP_INTERVAL {
+ current_height + HIGH_FREQUENCY_BUMP_INTERVAL
+ } else if target_conf <= current_height + LOW_FREQUENCY_BUMP_INTERVAL {
+ current_height + MIDDLE_FREQUENCY_BUMP_INTERVAL
+ } else {
+ current_height + LOW_FREQUENCY_BUMP_INTERVAL
+ }
+ };
+ for (_, input) in self.inputs.iter() {
+ match input {
+ PackageSolvingData::RevokedOutput(_) => {
+ // Revoked Outputs will become spendable by our counterparty at the height
+ // where the CSV expires, which is also our `soonest_conf_deadline`.
+ height_timer = cmp::min(
+ height_timer,
+ timer_for_target_conf(self.soonest_conf_deadline),
+ );
+ },
+ PackageSolvingData::RevokedHTLCOutput(_) => {
+ // Revoked HTLC Outputs may be spendable by our counterparty right now, but
+ // after they spend them they still have to wait for an additional CSV delta
+ // before they can claim the full funds. Thus, we leave the timer at
+ // `LOW_FREQUENCY_BUMP_INTERVAL` until the HTLC output is spent, creating a
+ // `RevokedOutput`.
+ },
+ PackageSolvingData::CounterpartyOfferedHTLCOutput(outp) => {
+ // Incoming HTLCs being claimed by preimage should be claimed by the time their
+ // CLTV unlocks.
+ height_timer = cmp::min(
+ height_timer,
+ timer_for_target_conf(outp.htlc.cltv_expiry),
+ );
+ },
+ PackageSolvingData::HolderHTLCOutput(outp) if outp.preimage.is_some() => {
+ // We have the same deadline here as for `CounterpartyOfferedHTLCOutput`. Note
+ // that `outp.cltv_expiry` is always 0 in this case, but
+ // `soonest_conf_deadline` holds the real HTLC expiry.
+ height_timer = cmp::min(
+ height_timer,
+ timer_for_target_conf(self.soonest_conf_deadline),
+ );
+ },
+ PackageSolvingData::CounterpartyReceivedHTLCOutput(outp) => {
+ // Outgoing HTLCs being claimed through their timeout should be claimed fast
+ // enough to allow us to claim before the CLTV lock expires on the inbound
+ // edge (assuming the HTLC was forwarded).
+ height_timer = cmp::min(
+ height_timer,
+ timer_for_target_conf(outp.htlc.cltv_expiry + MIN_CLTV_EXPIRY_DELTA as u32),
+ );
+ },
+ PackageSolvingData::HolderHTLCOutput(outp) => {
+ // We have the same deadline for holder timeout claims as for
+ // `CounterpartyReceivedHTLCOutput`
+ height_timer = cmp::min(
+ height_timer,
+ timer_for_target_conf(outp.cltv_expiry + MIN_CLTV_EXPIRY_DELTA as u32),
+ );
+ },
+ PackageSolvingData::HolderFundingOutput(_) => {
+ // We should apply a smart heuristic here based on the HTLCs in the commitment
+ // transaction, but we don't currently have that information available so
+ // instead we just bump once per block.
+ height_timer =
+ cmp::min(height_timer, current_height + HIGH_FREQUENCY_BUMP_INTERVAL);
+ },
+ }
}
- current_height + LOW_FREQUENCY_BUMP_INTERVAL
+ height_timer
}
/// Returns value in satoshis to be included as package outgoing output amount and feerate
}
// Connecting more blocks should result in the HTLC transactions being rebroadcast.
- connect_blocks(&nodes[0], 6);
+ connect_blocks(&nodes[0], crate::chain::package::LOW_FREQUENCY_BUMP_INTERVAL);
if check_old_monitor_retries_after_upgrade {
check_added_monitors(&nodes[0], 1);
}
{
let txn = nodes[0].tx_broadcaster.txn_broadcast();
- if !nodes[0].connect_style.borrow().skips_blocks() {
- assert_eq!(txn.len(), 6);
- } else {
- assert!(txn.len() < 6);
- }
+ assert_eq!(txn.len(), 1);
for tx in txn {
assert_eq!(tx.input.len(), htlc_timeout_tx.input.len());
assert_eq!(tx.output.len(), htlc_timeout_tx.output.len());
connect_blocks(&nodes[0], 1);
check_htlc_retry(true, false);
- // Connect one more block, expecting a retry with a fee bump. Unfortunately, we cannot bump HTLC
- // transactions pre-anchors.
- connect_blocks(&nodes[0], 1);
+ // Connect a few more blocks, expecting a retry with a fee bump. Unfortunately, we cannot bump
+ // HTLC transactions pre-anchors.
+ connect_blocks(&nodes[0], crate::chain::package::LOW_FREQUENCY_BUMP_INTERVAL);
check_htlc_retry(true, anchors);
// Trigger a call and we should have another retry, but without a bump.
nodes[0].chain_monitor.chain_monitor.rebroadcast_pending_claims();
check_htlc_retry(true, anchors);
- // Connect one more block, expecting a retry with a fee bump. Unfortunately, we cannot bump HTLC
- // transactions pre-anchors.
- connect_blocks(&nodes[0], 1);
+ // Connect a few more blocks, expecting a retry with a fee bump. Unfortunately, we cannot bump
+ // HTLC transactions pre-anchors.
+ connect_blocks(&nodes[0], crate::chain::package::LOW_FREQUENCY_BUMP_INTERVAL);
let htlc_tx = check_htlc_retry(true, anchors).unwrap();
// Mine the HTLC transaction to ensure we don't retry claims while they're confirmed.
mine_transaction(&nodes[0], &htlc_tx);
- // If we have a `ConnectStyle` that advertises the new block first without the transactions,
- // we'll receive an extra bumped claim.
- if nodes[0].connect_style.borrow().updates_best_block_first() {
- nodes[0].wallet_source.add_utxo(bitcoin::OutPoint { txid: coinbase_tx.compute_txid(), vout: 0 }, coinbase_tx.output[0].value);
- nodes[0].wallet_source.remove_utxo(bitcoin::OutPoint { txid: htlc_tx.compute_txid(), vout: 1 });
- check_htlc_retry(true, anchors);
- }
nodes[0].chain_monitor.chain_monitor.rebroadcast_pending_claims();
check_htlc_retry(false, false);
}