X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;ds=sidebyside;f=lightning%2Fsrc%2Fchain%2Fonchaintx.rs;h=21b4717e1d9786dfd2b0eb68092e41f7b3d19afd;hb=0ecb4b093ac1134cbf06275cc48dd79ef7524774;hp=aece19c8371566bee10913d7f0404bbdba3ea62c;hpb=e496d62b98b7d7cd1800fe0dbf70684866fed7e7;p=rust-lightning diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs index aece19c8..21b4717e 100644 --- a/lightning/src/chain/onchaintx.rs +++ b/lightning/src/chain/onchaintx.rs @@ -23,7 +23,7 @@ use bitcoin::hash_types::{Txid, BlockHash}; use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature}; use bitcoin::secp256k1; -use crate::chain::keysinterface::{ChannelSigner, EntropySource, SignerProvider}; +use crate::sign::{ChannelSigner, EntropySource, SignerProvider}; use crate::ln::msgs::DecodeError; use crate::ln::PaymentPreimage; #[cfg(anchors)] @@ -33,7 +33,7 @@ use crate::ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransa use crate::chain::chaininterface::ConfirmationTarget; use crate::chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator}; use crate::chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER}; -use crate::chain::keysinterface::WriteableEcdsaChannelSigner; +use crate::sign::WriteableEcdsaChannelSigner; #[cfg(anchors)] use crate::chain::package::PackageSolvingData; use crate::chain::package::PackageTemplate; @@ -481,6 +481,59 @@ impl OnchainTxHandler events.into_iter().map(|(_, event)| event).collect() } + /// Triggers rebroadcasts/fee-bumps of pending claims from a force-closed channel. This is + /// crucial in preventing certain classes of pinning attacks, detecting substantial mempool + /// feerate changes between blocks, and ensuring reliability if broadcasting fails. We recommend + /// invoking this every 30 seconds, or lower if running in an environment with spotty + /// connections, like on mobile. + pub(crate) fn rebroadcast_pending_claims( + &mut self, current_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator, + logger: &L, + ) + where + B::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + { + let mut bump_requests = Vec::with_capacity(self.pending_claim_requests.len()); + for (package_id, request) in self.pending_claim_requests.iter() { + let inputs = request.outpoints(); + log_info!(logger, "Triggering rebroadcast/fee-bump for request with inputs {:?}", inputs); + bump_requests.push((*package_id, request.clone())); + } + for (package_id, request) in bump_requests { + self.generate_claim(current_height, &request, false /* force_feerate_bump */, fee_estimator, logger) + .map(|(_, new_feerate, claim)| { + let mut bumped_feerate = false; + if let Some(mut_request) = self.pending_claim_requests.get_mut(&package_id) { + bumped_feerate = request.previous_feerate() > new_feerate; + mut_request.set_feerate(new_feerate); + } + match claim { + OnchainClaim::Tx(tx) => { + let log_start = if bumped_feerate { "Broadcasting RBF-bumped" } else { "Rebroadcasting" }; + log_info!(logger, "{} onchain {}", log_start, log_tx!(tx)); + broadcaster.broadcast_transaction(&tx); + }, + #[cfg(anchors)] + OnchainClaim::Event(event) => { + let log_start = if bumped_feerate { "Yielding fee-bumped" } else { "Replaying" }; + log_info!(logger, "{} onchain event to spend inputs {:?}", log_start, + request.outpoints()); + #[cfg(debug_assertions)] { + debug_assert!(request.requires_external_funding()); + let num_existing = self.pending_claim_events.iter() + .filter(|entry| entry.0 == package_id).count(); + assert!(num_existing == 0 || num_existing == 1); + } + self.pending_claim_events.retain(|event| event.0 != package_id); + self.pending_claim_events.push((package_id, event)); + } + } + }); + } + } + /// Lightning security model (i.e being able to redeem/timeout HTLC or penalize counterparty /// onchain) lays on the assumption of claim transactions getting confirmed before timelock /// expiration (CSV or CLTV following cases). In case of high-fee spikes, claim tx may get stuck @@ -695,8 +748,8 @@ impl OnchainTxHandler preprocessed_requests.push(req); } - // Claim everything up to and including cur_height + 1 - let remaining_locked_packages = self.locktimed_packages.split_off(&(cur_height + 2)); + // Claim everything up to and including `cur_height` + let remaining_locked_packages = self.locktimed_packages.split_off(&(cur_height + 1)); for (pop_height, mut entry) in self.locktimed_packages.iter_mut() { log_trace!(logger, "Restoring delayed claim of package(s) at their timelock at {}.", pop_height); preprocessed_requests.append(&mut entry); @@ -983,8 +1036,10 @@ impl OnchainTxHandler } } for ((_package_id, _), ref mut request) in bump_candidates.iter_mut() { + // `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( - height, &request, true /* force_feerate_bump */, fee_estimator, &&*logger + current_height, &request, true /* force_feerate_bump */, fee_estimator, &&*logger ) { request.set_timer(new_timer); request.set_feerate(new_feerate);