+ /// Get the list of HTLCs who's status has been updated on chain. This should be called by
+ /// ChannelManager via [`chain::Watch::release_pending_monitor_events`].
+ pub fn get_and_clear_pending_monitor_events(&self) -> Vec<MonitorEvent> {
+ self.inner.lock().unwrap().get_and_clear_pending_monitor_events()
+ }
+
+ /// Gets the list of pending events which were generated by previous actions, clearing the list
+ /// in the process.
+ ///
+ /// This is called by ChainMonitor::get_and_clear_pending_events() and is equivalent to
+ /// EventsProvider::get_and_clear_pending_events() except that it requires &mut self as we do
+ /// no internal locking in ChannelMonitors.
+ pub fn get_and_clear_pending_events(&self) -> Vec<Event> {
+ self.inner.lock().unwrap().get_and_clear_pending_events()
+ }
+
+ pub(crate) fn get_min_seen_secret(&self) -> u64 {
+ self.inner.lock().unwrap().get_min_seen_secret()
+ }
+
+ pub(crate) fn get_cur_counterparty_commitment_number(&self) -> u64 {
+ self.inner.lock().unwrap().get_cur_counterparty_commitment_number()
+ }
+
+ pub(crate) fn get_cur_holder_commitment_number(&self) -> u64 {
+ self.inner.lock().unwrap().get_cur_holder_commitment_number()
+ }
+
+ /// Gets the `node_id` of the counterparty for this channel.
+ ///
+ /// Will be `None` for channels constructed on LDK versions prior to 0.0.110 and always `Some`
+ /// otherwise.
+ pub fn get_counterparty_node_id(&self) -> Option<PublicKey> {
+ self.inner.lock().unwrap().counterparty_node_id
+ }
+
+ /// Used by ChannelManager deserialization to broadcast the latest holder state if its copy of
+ /// the Channel was out-of-date. You may use it to get a broadcastable holder toxic tx in case of
+ /// fallen-behind, i.e when receiving a channel_reestablish with a proof that our counterparty side knows
+ /// a higher revocation secret than the holder commitment number we are aware of. Broadcasting these
+ /// transactions are UNSAFE, as they allow counterparty side to punish you. Nevertheless you may want to
+ /// broadcast them if counterparty don't close channel with his higher commitment transaction after a
+ /// substantial amount of time (a month or even a year) to get back funds. Best may be to contact
+ /// out-of-band the other node operator to coordinate with him if option is available to you.
+ /// In any-case, choice is up to the user.
+ pub fn get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
+ where L::Target: Logger {
+ self.inner.lock().unwrap().get_latest_holder_commitment_txn(logger)
+ }
+
+ /// Unsafe test-only version of get_latest_holder_commitment_txn used by our test framework
+ /// to bypass HolderCommitmentTransaction state update lockdown after signature and generate
+ /// revoked commitment transaction.
+ #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))]
+ pub fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
+ where L::Target: Logger {
+ self.inner.lock().unwrap().unsafe_get_latest_holder_commitment_txn(logger)
+ }
+
+ /// Processes transactions in a newly connected block, which may result in any of the following:
+ /// - update the monitor's state against resolved HTLCs
+ /// - punish the counterparty in the case of seeing a revoked commitment transaction
+ /// - force close the channel and claim/timeout incoming/outgoing HTLCs if near expiration
+ /// - detect settled outputs for later spending
+ /// - schedule and bump any in-flight claims
+ ///
+ /// Returns any new outputs to watch from `txdata`; after called, these are also included in
+ /// [`get_outputs_to_watch`].
+ ///
+ /// [`get_outputs_to_watch`]: #method.get_outputs_to_watch
+ pub fn block_connected<B: Deref, F: Deref, L: Deref>(
+ &self,
+ header: &BlockHeader,
+ txdata: &TransactionData,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) -> Vec<TransactionOutputs>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ self.inner.lock().unwrap().block_connected(
+ header, txdata, height, broadcaster, fee_estimator, logger)
+ }
+
+ /// Determines if the disconnected block contained any transactions of interest and updates
+ /// appropriately.
+ pub fn block_disconnected<B: Deref, F: Deref, L: Deref>(
+ &self,
+ header: &BlockHeader,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ self.inner.lock().unwrap().block_disconnected(
+ header, height, broadcaster, fee_estimator, logger)
+ }
+
+ /// Processes transactions confirmed in a block with the given header and height, returning new
+ /// outputs to watch. See [`block_connected`] for details.
+ ///
+ /// Used instead of [`block_connected`] by clients that are notified of transactions rather than
+ /// blocks. See [`chain::Confirm`] for calling expectations.
+ ///
+ /// [`block_connected`]: Self::block_connected
+ pub fn transactions_confirmed<B: Deref, F: Deref, L: Deref>(
+ &self,
+ header: &BlockHeader,
+ txdata: &TransactionData,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) -> Vec<TransactionOutputs>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
+ self.inner.lock().unwrap().transactions_confirmed(
+ header, txdata, height, broadcaster, &bounded_fee_estimator, logger)
+ }
+
+ /// Processes a transaction that was reorganized out of the chain.
+ ///
+ /// Used instead of [`block_disconnected`] by clients that are notified of transactions rather
+ /// than blocks. See [`chain::Confirm`] for calling expectations.
+ ///
+ /// [`block_disconnected`]: Self::block_disconnected
+ pub fn transaction_unconfirmed<B: Deref, F: Deref, L: Deref>(
+ &self,
+ txid: &Txid,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
+ self.inner.lock().unwrap().transaction_unconfirmed(
+ txid, broadcaster, &bounded_fee_estimator, logger);
+ }
+
+ /// Updates the monitor with the current best chain tip, returning new outputs to watch. See
+ /// [`block_connected`] for details.
+ ///
+ /// Used instead of [`block_connected`] by clients that are notified of transactions rather than
+ /// blocks. See [`chain::Confirm`] for calling expectations.
+ ///
+ /// [`block_connected`]: Self::block_connected
+ pub fn best_block_updated<B: Deref, F: Deref, L: Deref>(
+ &self,
+ header: &BlockHeader,
+ height: u32,
+ broadcaster: B,
+ fee_estimator: F,
+ logger: L,
+ ) -> Vec<TransactionOutputs>
+ where
+ B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
+ {
+ let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
+ self.inner.lock().unwrap().best_block_updated(
+ header, height, broadcaster, &bounded_fee_estimator, logger)
+ }
+
+ /// Returns the set of txids that should be monitored for re-organization out of the chain.
+ pub fn get_relevant_txids(&self) -> Vec<Txid> {
+ let inner = self.inner.lock().unwrap();
+ let mut txids: Vec<Txid> = inner.onchain_events_awaiting_threshold_conf
+ .iter()
+ .map(|entry| entry.txid)
+ .chain(inner.onchain_tx_handler.get_relevant_txids().into_iter())
+ .collect();
+ txids.sort_unstable();
+ txids.dedup();
+ txids
+ }
+
+ /// Gets the latest best block which was connected either via the [`chain::Listen`] or
+ /// [`chain::Confirm`] interfaces.
+ pub fn current_best_block(&self) -> BestBlock {
+ self.inner.lock().unwrap().best_block.clone()
+ }
+}
+
+impl<Signer: Sign> ChannelMonitorImpl<Signer> {
+ /// Helper for get_claimable_balances which does the work for an individual HTLC, generating up
+ /// to one `Balance` for the HTLC.
+ fn get_htlc_balance(&self, htlc: &HTLCOutputInCommitment, holder_commitment: bool,
+ counterparty_revoked_commitment: bool, confirmed_txid: Option<Txid>)
+ -> Option<Balance> {
+ let htlc_commitment_tx_output_idx =
+ if let Some(v) = htlc.transaction_output_index { v } else { return None; };
+
+ let mut htlc_spend_txid_opt = None;
+ let mut holder_timeout_spend_pending = None;
+ let mut htlc_spend_pending = None;
+ let mut holder_delayed_output_pending = None;
+ for event in self.onchain_events_awaiting_threshold_conf.iter() {
+ match event.event {
+ OnchainEvent::HTLCUpdate { commitment_tx_output_idx, htlc_value_satoshis, .. }
+ if commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) => {
+ debug_assert!(htlc_spend_txid_opt.is_none());
+ htlc_spend_txid_opt = event.transaction.as_ref().map(|tx| tx.txid());
+ debug_assert!(holder_timeout_spend_pending.is_none());
+ debug_assert_eq!(htlc_value_satoshis.unwrap(), htlc.amount_msat / 1000);
+ holder_timeout_spend_pending = Some(event.confirmation_threshold());
+ },
+ OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. }
+ if commitment_tx_output_idx == htlc_commitment_tx_output_idx => {
+ debug_assert!(htlc_spend_txid_opt.is_none());
+ htlc_spend_txid_opt = event.transaction.as_ref().map(|tx| tx.txid());
+ debug_assert!(htlc_spend_pending.is_none());
+ htlc_spend_pending = Some((event.confirmation_threshold(), preimage.is_some()));
+ },
+ OnchainEvent::MaturingOutput {
+ descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) }
+ if descriptor.outpoint.index as u32 == htlc_commitment_tx_output_idx => {
+ debug_assert!(holder_delayed_output_pending.is_none());
+ holder_delayed_output_pending = Some(event.confirmation_threshold());
+ },
+ _ => {},
+ }
+ }
+ let htlc_resolved = self.htlcs_resolved_on_chain.iter()
+ .find(|v| if v.commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) {
+ debug_assert!(htlc_spend_txid_opt.is_none());
+ htlc_spend_txid_opt = v.resolving_txid;
+ true
+ } else { false });
+ debug_assert!(holder_timeout_spend_pending.is_some() as u8 + htlc_spend_pending.is_some() as u8 + htlc_resolved.is_some() as u8 <= 1);
+
+ let htlc_output_to_spend =
+ if let Some(txid) = htlc_spend_txid_opt {
+ debug_assert!(
+ self.onchain_tx_handler.channel_transaction_parameters.opt_anchors.is_none(),
+ "This code needs updating for anchors");
+ BitcoinOutPoint::new(txid, 0)
+ } else {
+ BitcoinOutPoint::new(confirmed_txid.unwrap(), htlc_commitment_tx_output_idx)
+ };
+ let htlc_output_spend_pending = self.onchain_tx_handler.is_output_spend_pending(&htlc_output_to_spend);
+
+ if let Some(conf_thresh) = holder_delayed_output_pending {
+ debug_assert!(holder_commitment);
+ return Some(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ confirmation_height: conf_thresh,
+ });
+ } else if htlc_resolved.is_some() && !htlc_output_spend_pending {
+ // Funding transaction spends should be fully confirmed by the time any
+ // HTLC transactions are resolved, unless we're talking about a holder
+ // commitment tx, whose resolution is delayed until the CSV timeout is
+ // reached, even though HTLCs may be resolved after only
+ // ANTI_REORG_DELAY confirmations.
+ debug_assert!(holder_commitment || self.funding_spend_confirmed.is_some());
+ } else if counterparty_revoked_commitment {
+ let htlc_output_claim_pending = self.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::MaturingOutput {
+ descriptor: SpendableOutputDescriptor::StaticOutput { .. }
+ } = &event.event {
+ if event.transaction.as_ref().map(|tx| tx.input.iter().any(|inp| {
+ if let Some(htlc_spend_txid) = htlc_spend_txid_opt {
+ Some(tx.txid()) == htlc_spend_txid_opt ||
+ inp.previous_output.txid == htlc_spend_txid
+ } else {
+ Some(inp.previous_output.txid) == confirmed_txid &&
+ inp.previous_output.vout == htlc_commitment_tx_output_idx
+ }
+ })).unwrap_or(false) {
+ Some(())
+ } else { None }
+ } else { None }
+ });
+ if htlc_output_claim_pending.is_some() {
+ // We already push `Balance`s onto the `res` list for every
+ // `StaticOutput` in a `MaturingOutput` in the revoked
+ // counterparty commitment transaction case generally, so don't
+ // need to do so again here.
+ } else {
+ debug_assert!(holder_timeout_spend_pending.is_none(),
+ "HTLCUpdate OnchainEvents should never appear for preimage claims");
+ debug_assert!(!htlc.offered || htlc_spend_pending.is_none() || !htlc_spend_pending.unwrap().1,
+ "We don't (currently) generate preimage claims against revoked outputs, where did you get one?!");
+ return Some(Balance::CounterpartyRevokedOutputClaimable {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ });
+ }
+ } else if htlc.offered == holder_commitment {
+ // If the payment was outbound, check if there's an HTLCUpdate
+ // indicating we have spent this HTLC with a timeout, claiming it back
+ // and awaiting confirmations on it.
+ if let Some(conf_thresh) = holder_timeout_spend_pending {
+ return Some(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ confirmation_height: conf_thresh,
+ });
+ } else {
+ return Some(Balance::MaybeTimeoutClaimableHTLC {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ claimable_height: htlc.cltv_expiry,
+ });
+ }
+ } else if self.payment_preimages.get(&htlc.payment_hash).is_some() {
+ // Otherwise (the payment was inbound), only expose it as claimable if
+ // we know the preimage.
+ // Note that if there is a pending claim, but it did not use the
+ // preimage, we lost funds to our counterparty! We will then continue
+ // to show it as ContentiousClaimable until ANTI_REORG_DELAY.
+ debug_assert!(holder_timeout_spend_pending.is_none());
+ if let Some((conf_thresh, true)) = htlc_spend_pending {
+ return Some(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ confirmation_height: conf_thresh,
+ });
+ } else {
+ return Some(Balance::ContentiousClaimable {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ timeout_height: htlc.cltv_expiry,
+ });
+ }
+ } else if htlc_resolved.is_none() {
+ return Some(Balance::MaybePreimageClaimableHTLC {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ expiry_height: htlc.cltv_expiry,
+ });
+ }
+ None
+ }
+}
+
+impl<Signer: Sign> ChannelMonitor<Signer> {
+ /// Gets the balances in this channel which are either claimable by us if we were to
+ /// force-close the channel now or which are claimable on-chain (possibly awaiting
+ /// confirmation).
+ ///
+ /// Any balances in the channel which are available on-chain (excluding on-chain fees) are
+ /// included here until an [`Event::SpendableOutputs`] event has been generated for the
+ /// balance, or until our counterparty has claimed the balance and accrued several
+ /// confirmations on the claim transaction.
+ ///
+ /// Note that for `ChannelMonitors` which track a channel which went on-chain with versions of
+ /// LDK prior to 0.0.111, balances may not be fully captured if our counterparty broadcasted
+ /// a revoked state.
+ ///
+ /// See [`Balance`] for additional details on the types of claimable balances which
+ /// may be returned here and their meanings.
+ pub fn get_claimable_balances(&self) -> Vec<Balance> {
+ let mut res = Vec::new();
+ let us = self.inner.lock().unwrap();
+
+ let mut confirmed_txid = us.funding_spend_confirmed;
+ let mut confirmed_counterparty_output = us.confirmed_commitment_tx_counterparty_output;
+ let mut pending_commitment_tx_conf_thresh = None;
+ let funding_spend_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::FundingSpendConfirmation { commitment_tx_to_counterparty_output, .. } =
+ event.event
+ {
+ confirmed_counterparty_output = commitment_tx_to_counterparty_output;
+ Some((event.txid, event.confirmation_threshold()))
+ } else { None }
+ });
+ if let Some((txid, conf_thresh)) = funding_spend_pending {
+ debug_assert!(us.funding_spend_confirmed.is_none(),
+ "We have a pending funding spend awaiting anti-reorg confirmation, we can't have confirmed it already!");
+ confirmed_txid = Some(txid);
+ pending_commitment_tx_conf_thresh = Some(conf_thresh);
+ }
+
+ macro_rules! walk_htlcs {
+ ($holder_commitment: expr, $counterparty_revoked_commitment: expr, $htlc_iter: expr) => {
+ for htlc in $htlc_iter {
+ if htlc.transaction_output_index.is_some() {
+
+ if let Some(bal) = us.get_htlc_balance(htlc, $holder_commitment, $counterparty_revoked_commitment, confirmed_txid) {
+ res.push(bal);
+ }
+ }
+ }
+ }
+ }
+
+ if let Some(txid) = confirmed_txid {
+ let mut found_commitment_tx = false;
+ if let Some(counterparty_tx_htlcs) = us.counterparty_claimable_outpoints.get(&txid) {
+ // First look for the to_remote output back to us.
+ if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
+ if let Some(value) = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::MaturingOutput {
+ descriptor: SpendableOutputDescriptor::StaticPaymentOutput(descriptor)
+ } = &event.event {
+ Some(descriptor.output.value)
+ } else { None }
+ }) {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: value,
+ confirmation_height: conf_thresh,
+ });
+ } else {
+ // If a counterparty commitment transaction is awaiting confirmation, we
+ // should either have a StaticPaymentOutput MaturingOutput event awaiting
+ // confirmation with the same height or have never met our dust amount.
+ }
+ }
+ if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
+ walk_htlcs!(false, false, counterparty_tx_htlcs.iter().map(|(a, _)| a));
+ } else {
+ walk_htlcs!(false, true, counterparty_tx_htlcs.iter().map(|(a, _)| a));
+ // The counterparty broadcasted a revoked state!
+ // Look for any StaticOutputs first, generating claimable balances for those.
+ // If any match the confirmed counterparty revoked to_self output, skip
+ // generating a CounterpartyRevokedOutputClaimable.
+ let mut spent_counterparty_output = false;
+ for event in us.onchain_events_awaiting_threshold_conf.iter() {
+ if let OnchainEvent::MaturingOutput {
+ descriptor: SpendableOutputDescriptor::StaticOutput { output, .. }
+ } = &event.event {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: output.value,
+ confirmation_height: event.confirmation_threshold(),
+ });
+ if let Some(confirmed_to_self_idx) = confirmed_counterparty_output.map(|(idx, _)| idx) {
+ if event.transaction.as_ref().map(|tx|
+ tx.input.iter().any(|inp| inp.previous_output.vout == confirmed_to_self_idx)
+ ).unwrap_or(false) {
+ spent_counterparty_output = true;
+ }
+ }
+ }
+ }
+
+ if spent_counterparty_output {
+ } else if let Some((confirmed_to_self_idx, amt)) = confirmed_counterparty_output {
+ let output_spendable = us.onchain_tx_handler
+ .is_output_spend_pending(&BitcoinOutPoint::new(txid, confirmed_to_self_idx));
+ if output_spendable {
+ res.push(Balance::CounterpartyRevokedOutputClaimable {
+ claimable_amount_satoshis: amt,
+ });
+ }
+ } else {
+ // Counterparty output is missing, either it was broadcasted on a
+ // previous version of LDK or the counterparty hadn't met dust.
+ }
+ }
+ found_commitment_tx = true;
+ } else if txid == us.current_holder_commitment_tx.txid {
+ walk_htlcs!(true, false, us.current_holder_commitment_tx.htlc_outputs.iter().map(|(a, _, _)| a));
+ if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
+ confirmation_height: conf_thresh,
+ });
+ }
+ found_commitment_tx = true;
+ } else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
+ if txid == prev_commitment.txid {
+ walk_htlcs!(true, false, prev_commitment.htlc_outputs.iter().map(|(a, _, _)| a));
+ if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: prev_commitment.to_self_value_sat,
+ confirmation_height: conf_thresh,
+ });
+ }
+ found_commitment_tx = true;
+ }
+ }
+ if !found_commitment_tx {
+ if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
+ // We blindly assume this is a cooperative close transaction here, and that
+ // neither us nor our counterparty misbehaved. At worst we've under-estimated
+ // the amount we can claim as we'll punish a misbehaving counterparty.
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
+ confirmation_height: conf_thresh,
+ });
+ }
+ }
+ } else {
+ let mut claimable_inbound_htlc_value_sat = 0;
+ for (htlc, _, _) in us.current_holder_commitment_tx.htlc_outputs.iter() {
+ if htlc.transaction_output_index.is_none() { continue; }
+ if htlc.offered {
+ res.push(Balance::MaybeTimeoutClaimableHTLC {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ claimable_height: htlc.cltv_expiry,
+ });
+ } else if us.payment_preimages.get(&htlc.payment_hash).is_some() {
+ claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000;
+ } else {
+ // As long as the HTLC is still in our latest commitment state, treat
+ // it as potentially claimable, even if it has long-since expired.
+ res.push(Balance::MaybePreimageClaimableHTLC {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ expiry_height: htlc.cltv_expiry,
+ });
+ }
+ }
+ res.push(Balance::ClaimableOnChannelClose {
+ claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat + claimable_inbound_htlc_value_sat,
+ });
+ }
+
+ res
+ }
+
+ /// Gets the set of outbound HTLCs which are pending resolution in this channel.
+ /// This is used to reconstruct pending outbound payments on restart in the ChannelManager.
+ pub(crate) fn get_pending_outbound_htlcs(&self) -> HashMap<HTLCSource, HTLCOutputInCommitment> {
+ let mut res = HashMap::new();
+ let us = self.inner.lock().unwrap();
+
+ macro_rules! walk_htlcs {
+ ($holder_commitment: expr, $htlc_iter: expr) => {
+ for (htlc, source) in $htlc_iter {
+ if us.htlcs_resolved_on_chain.iter().any(|v| v.commitment_tx_output_idx == htlc.transaction_output_index) {
+ // We should assert that funding_spend_confirmed is_some() here, but we
+ // have some unit tests which violate HTLC transaction CSVs entirely and
+ // would fail.
+ // TODO: Once tests all connect transactions at consensus-valid times, we
+ // should assert here like we do in `get_claimable_balances`.
+ } else if htlc.offered == $holder_commitment {
+ // If the payment was outbound, check if there's an HTLCUpdate
+ // indicating we have spent this HTLC with a timeout, claiming it back
+ // and awaiting confirmations on it.
+ let htlc_update_confd = us.onchain_events_awaiting_threshold_conf.iter().any(|event| {
+ if let OnchainEvent::HTLCUpdate { commitment_tx_output_idx: Some(commitment_tx_output_idx), .. } = event.event {
+ // If the HTLC was timed out, we wait for ANTI_REORG_DELAY blocks
+ // before considering it "no longer pending" - this matches when we
+ // provide the ChannelManager an HTLC failure event.
+ Some(commitment_tx_output_idx) == htlc.transaction_output_index &&
+ us.best_block.height() >= event.height + ANTI_REORG_DELAY - 1
+ } else if let OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, .. } = event.event {
+ // If the HTLC was fulfilled with a preimage, we consider the HTLC
+ // immediately non-pending, matching when we provide ChannelManager
+ // the preimage.
+ Some(commitment_tx_output_idx) == htlc.transaction_output_index
+ } else { false }
+ });
+ if !htlc_update_confd {
+ res.insert(source.clone(), htlc.clone());
+ }
+ }
+ }
+ }
+ }
+
+ // We're only concerned with the confirmation count of HTLC transactions, and don't
+ // actually care how many confirmations a commitment transaction may or may not have. Thus,
+ // we look for either a FundingSpendConfirmation event or a funding_spend_confirmed.
+ let confirmed_txid = us.funding_spend_confirmed.or_else(|| {
+ us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::FundingSpendConfirmation { .. } = event.event {
+ Some(event.txid)
+ } else { None }
+ })
+ });
+ if let Some(txid) = confirmed_txid {
+ if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
+ walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().filter_map(|(a, b)| {
+ if let &Some(ref source) = b {
+ Some((a, &**source))
+ } else { None }
+ }));
+ } else if txid == us.current_holder_commitment_tx.txid {
+ walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().filter_map(|(a, _, c)| {
+ if let Some(source) = c { Some((a, source)) } else { None }
+ }));
+ } else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
+ if txid == prev_commitment.txid {
+ walk_htlcs!(true, prev_commitment.htlc_outputs.iter().filter_map(|(a, _, c)| {
+ if let Some(source) = c { Some((a, source)) } else { None }
+ }));
+ }
+ }
+ } else {
+ // If we have not seen a commitment transaction on-chain (ie the channel is not yet
+ // closed), just examine the available counterparty commitment transactions. See docs
+ // on `fail_unbroadcast_htlcs`, below, for justification.
+ macro_rules! walk_counterparty_commitment {
+ ($txid: expr) => {
+ if let Some(ref latest_outpoints) = us.counterparty_claimable_outpoints.get($txid) {
+ for &(ref htlc, ref source_option) in latest_outpoints.iter() {
+ if let &Some(ref source) = source_option {
+ res.insert((**source).clone(), htlc.clone());
+ }
+ }
+ }
+ }
+ }
+ if let Some(ref txid) = us.current_counterparty_commitment_txid {
+ walk_counterparty_commitment!(txid);
+ }
+ if let Some(ref txid) = us.prev_counterparty_commitment_txid {
+ walk_counterparty_commitment!(txid);
+ }
+ }
+
+ res
+ }
+
+ pub(crate) fn get_stored_preimages(&self) -> HashMap<PaymentHash, PaymentPreimage> {
+ self.inner.lock().unwrap().payment_preimages.clone()
+ }
+}
+
+/// Compares a broadcasted commitment transaction's HTLCs with those in the latest state,
+/// failing any HTLCs which didn't make it into the broadcasted commitment transaction back
+/// after ANTI_REORG_DELAY blocks.
+///
+/// We always compare against the set of HTLCs in counterparty commitment transactions, as those
+/// are the commitment transactions which are generated by us. The off-chain state machine in
+/// `Channel` will automatically resolve any HTLCs which were never included in a commitment
+/// transaction when it detects channel closure, but it is up to us to ensure any HTLCs which were
+/// included in a remote commitment transaction are failed back if they are not present in the
+/// broadcasted commitment transaction.
+///
+/// Specifically, the removal process for HTLCs in `Channel` is always based on the counterparty
+/// sending a `revoke_and_ack`, which causes us to clear `prev_counterparty_commitment_txid`. Thus,
+/// as long as we examine both the current counterparty commitment transaction and, if it hasn't
+/// been revoked yet, the previous one, we we will never "forget" to resolve an HTLC.
+macro_rules! fail_unbroadcast_htlcs {
+ ($self: expr, $commitment_tx_type: expr, $commitment_txid_confirmed: expr, $commitment_tx_confirmed: expr,
+ $commitment_tx_conf_height: expr, $confirmed_htlcs_list: expr, $logger: expr) => { {
+ debug_assert_eq!($commitment_tx_confirmed.txid(), $commitment_txid_confirmed);
+
+ macro_rules! check_htlc_fails {
+ ($txid: expr, $commitment_tx: expr) => {
+ if let Some(ref latest_outpoints) = $self.counterparty_claimable_outpoints.get($txid) {
+ for &(ref htlc, ref source_option) in latest_outpoints.iter() {
+ if let &Some(ref source) = source_option {
+ // Check if the HTLC is present in the commitment transaction that was
+ // broadcast, but not if it was below the dust limit, which we should
+ // fail backwards immediately as there is no way for us to learn the
+ // payment_preimage.
+ // Note that if the dust limit were allowed to change between
+ // commitment transactions we'd want to be check whether *any*
+ // broadcastable commitment transaction has the HTLC in it, but it
+ // cannot currently change after channel initialization, so we don't
+ // need to here.
+ let confirmed_htlcs_iter: &mut Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> = &mut $confirmed_htlcs_list;
+
+ let mut matched_htlc = false;
+ for (ref broadcast_htlc, ref broadcast_source) in confirmed_htlcs_iter {
+ if broadcast_htlc.transaction_output_index.is_some() &&
+ (Some(&**source) == *broadcast_source ||
+ (broadcast_source.is_none() &&
+ broadcast_htlc.payment_hash == htlc.payment_hash &&
+ broadcast_htlc.amount_msat == htlc.amount_msat)) {
+ matched_htlc = true;
+ break;
+ }
+ }
+ if matched_htlc { continue; }
+ $self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
+ if entry.height != $commitment_tx_conf_height { return true; }
+ match entry.event {
+ OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
+ *update_source != **source
+ },
+ _ => true,
+ }
+ });
+ let entry = OnchainEventEntry {
+ txid: $commitment_txid_confirmed,
+ transaction: Some($commitment_tx_confirmed.clone()),
+ height: $commitment_tx_conf_height,
+ event: OnchainEvent::HTLCUpdate {
+ source: (**source).clone(),
+ payment_hash: htlc.payment_hash.clone(),
+ htlc_value_satoshis: Some(htlc.amount_msat / 1000),
+ commitment_tx_output_idx: None,
+ },
+ };
+ log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction {}, waiting for confirmation (at height {})",
+ log_bytes!(htlc.payment_hash.0), $commitment_tx, $commitment_tx_type,
+ $commitment_txid_confirmed, entry.confirmation_threshold());
+ $self.onchain_events_awaiting_threshold_conf.push(entry);
+ }
+ }
+ }
+ }
+ }
+ if let Some(ref txid) = $self.current_counterparty_commitment_txid {
+ check_htlc_fails!(txid, "current");
+ }
+ if let Some(ref txid) = $self.prev_counterparty_commitment_txid {
+ check_htlc_fails!(txid, "previous");
+ }
+ } }
+}
+
+// In the `test_invalid_funding_tx` test, we need a bogus script which matches the HTLC-Accepted
+// witness length match (ie is 136 bytes long). We generate one here which we also use in some
+// in-line tests later.
+
+#[cfg(test)]
+pub fn deliberately_bogus_accepted_htlc_witness_program() -> Vec<u8> {
+ let mut ret = [opcodes::all::OP_NOP.to_u8(); 136];
+ ret[131] = opcodes::all::OP_DROP.to_u8();
+ ret[132] = opcodes::all::OP_DROP.to_u8();
+ ret[133] = opcodes::all::OP_DROP.to_u8();
+ ret[134] = opcodes::all::OP_DROP.to_u8();
+ ret[135] = opcodes::OP_TRUE.to_u8();
+ Vec::from(&ret[..])
+}
+
+#[cfg(test)]
+pub fn deliberately_bogus_accepted_htlc_witness() -> Vec<Vec<u8>> {
+ vec![Vec::new(), Vec::new(), Vec::new(), Vec::new(), deliberately_bogus_accepted_htlc_witness_program().into()].into()
+}
+
+impl<Signer: Sign> ChannelMonitorImpl<Signer> {