X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannelmanager.rs;h=b9ee78499bcc6a5fecf749086af81b69b4277212;hb=1d516a6fc505783ce752f0289322ce73ae10bcf4;hp=0eaf244d0e47653cc04b2cd0c508f519820f5c78;hpb=d47aebca38ca8bfbd06f57052ed2712121593d6f;p=rust-lightning diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 0eaf244d..b9ee7849 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -67,10 +67,11 @@ use io::{Cursor, Read}; use sync::{Arc, Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard}; use core::sync::atomic::{AtomicUsize, Ordering}; use core::time::Duration; -#[cfg(any(test, feature = "allow_wallclock_use"))] -use std::time::Instant; use core::ops::Deref; +#[cfg(any(test, feature = "std"))] +use std::time::Instant; + // We hold various information about HTLC relay in the HTLC objects in Channel itself: // // Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should @@ -454,6 +455,17 @@ pub(crate) enum PendingOutboundPayment { session_privs: HashSet<[u8; 32]>, payment_hash: Option, }, + /// When a payer gives up trying to retry a payment, they inform us, letting us generate a + /// `PaymentFailed` event when all HTLCs have irrevocably failed. This avoids a number of race + /// conditions in MPP-aware payment retriers (1), where the possibility of multiple + /// `PaymentPathFailed` events with `all_paths_failed` can be pending at once, confusing a + /// downstream event handler as to when a payment has actually failed. + /// + /// (1) https://github.com/lightningdevkit/rust-lightning/issues/1164 + Abandoned { + session_privs: HashSet<[u8; 32]>, + payment_hash: PaymentHash, + }, } impl PendingOutboundPayment { @@ -469,6 +481,12 @@ impl PendingOutboundPayment { _ => false, } } + fn abandoned(&self) -> bool { + match self { + PendingOutboundPayment::Abandoned { .. } => true, + _ => false, + } + } fn get_pending_fee_msat(&self) -> Option { match self { PendingOutboundPayment::Retryable { pending_fee_msat, .. } => pending_fee_msat.clone(), @@ -481,6 +499,7 @@ impl PendingOutboundPayment { PendingOutboundPayment::Legacy { .. } => None, PendingOutboundPayment::Retryable { payment_hash, .. } => Some(*payment_hash), PendingOutboundPayment::Fulfilled { payment_hash, .. } => *payment_hash, + PendingOutboundPayment::Abandoned { payment_hash, .. } => Some(*payment_hash), } } @@ -489,19 +508,38 @@ impl PendingOutboundPayment { core::mem::swap(&mut session_privs, match self { PendingOutboundPayment::Legacy { session_privs } | PendingOutboundPayment::Retryable { session_privs, .. } | - PendingOutboundPayment::Fulfilled { session_privs, .. } - => session_privs + PendingOutboundPayment::Fulfilled { session_privs, .. } | + PendingOutboundPayment::Abandoned { session_privs, .. } + => session_privs, }); let payment_hash = self.payment_hash(); *self = PendingOutboundPayment::Fulfilled { session_privs, payment_hash }; } + fn mark_abandoned(&mut self) -> Result<(), ()> { + let mut session_privs = HashSet::new(); + let our_payment_hash; + core::mem::swap(&mut session_privs, match self { + PendingOutboundPayment::Legacy { .. } | + PendingOutboundPayment::Fulfilled { .. } => + return Err(()), + PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } | + PendingOutboundPayment::Abandoned { session_privs, payment_hash, .. } => { + our_payment_hash = *payment_hash; + session_privs + }, + }); + *self = PendingOutboundPayment::Abandoned { session_privs, payment_hash: our_payment_hash }; + Ok(()) + } + /// panics if path is None and !self.is_fulfilled fn remove(&mut self, session_priv: &[u8; 32], path: Option<&Vec>) -> bool { let remove_res = match self { PendingOutboundPayment::Legacy { session_privs } | PendingOutboundPayment::Retryable { session_privs, .. } | - PendingOutboundPayment::Fulfilled { session_privs, .. } => { + PendingOutboundPayment::Fulfilled { session_privs, .. } | + PendingOutboundPayment::Abandoned { session_privs, .. } => { session_privs.remove(session_priv) } }; @@ -524,7 +562,8 @@ impl PendingOutboundPayment { PendingOutboundPayment::Retryable { session_privs, .. } => { session_privs.insert(session_priv) } - PendingOutboundPayment::Fulfilled { .. } => false + PendingOutboundPayment::Fulfilled { .. } => false, + PendingOutboundPayment::Abandoned { .. } => false, }; if insert_res { if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self { @@ -542,7 +581,8 @@ impl PendingOutboundPayment { match self { PendingOutboundPayment::Legacy { session_privs } | PendingOutboundPayment::Retryable { session_privs, .. } | - PendingOutboundPayment::Fulfilled { session_privs, .. } => { + PendingOutboundPayment::Fulfilled { session_privs, .. } | + PendingOutboundPayment::Abandoned { session_privs, .. } => { session_privs.len() } } @@ -799,6 +839,10 @@ const CHECK_CLTV_EXPIRY_SANITY: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRA #[allow(dead_code)] const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - 2*CLTV_CLAIM_BUFFER; +/// The number of blocks before we consider an outbound payment for expiry if it doesn't have any +/// pending HTLCs in flight. +pub(crate) const PAYMENT_EXPIRY_BLOCKS: u32 = 3; + /// Information needed for constructing an invoice route hint for this channel. #[derive(Clone, Debug, PartialEq)] pub struct CounterpartyForwardingInfo { @@ -868,17 +912,30 @@ pub struct ChannelDetails { pub unspendable_punishment_reserve: Option, /// The `user_channel_id` passed in to create_channel, or 0 if the channel was inbound. pub user_channel_id: u64, + /// Our total balance. This is the amount we would get if we close the channel. + /// This value is not exact. Due to various in-flight changes and feerate changes, exactly this + /// amount is not likely to be recoverable on close. + /// + /// This does not include any pending HTLCs which are not yet fully resolved (and, thus, whose + /// balance is not available for inclusion in new outbound HTLCs). This further does not include + /// any pending outgoing HTLCs which are awaiting some other resolution to be sent. + /// This does not consider any on-chain fees. + /// + /// See also [`ChannelDetails::outbound_capacity_msat`] + pub balance_msat: u64, /// The available outbound capacity for sending HTLCs to the remote peer. This does not include - /// any pending HTLCs which are not yet fully resolved (and, thus, who's balance is not + /// any pending HTLCs which are not yet fully resolved (and, thus, whose balance is not /// available for inclusion in new outbound HTLCs). This further does not include any pending /// outgoing HTLCs which are awaiting some other resolution to be sent. /// + /// See also [`ChannelDetails::balance_msat`] + /// /// This value is not exact. Due to various in-flight changes, feerate changes, and our /// conflict-avoidance policy, exactly this amount is not likely to be spendable. However, we /// should be able to spend nearly this amount. pub outbound_capacity_msat: u64, /// The available inbound capacity for the remote peer to send HTLCs to us. This does not - /// include any pending HTLCs which are not yet fully resolved (and, thus, who's balance is not + /// include any pending HTLCs which are not yet fully resolved (and, thus, whose balance is not /// available for inclusion in new inbound HTLCs). /// Note that there are some corner cases not fully handled here, so the actual available /// inbound capacity may be slightly higher than this. @@ -1449,6 +1506,7 @@ impl ChannelMana res.reserve(channel_state.by_id.len()); for (channel_id, channel) in channel_state.by_id.iter().filter(f) { let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat(); + let balance_msat = channel.get_balance_msat(); let (to_remote_reserve_satoshis, to_self_reserve_satoshis) = channel.get_holder_counterparty_selected_channel_reserve_satoshis(); res.push(ChannelDetails { @@ -1463,6 +1521,7 @@ impl ChannelMana short_channel_id: channel.get_short_channel_id(), channel_value_satoshis: channel.get_value_satoshis(), unspendable_punishment_reserve: to_self_reserve_satoshis, + balance_msat, inbound_capacity_msat, outbound_capacity_msat, user_channel_id: channel.get_user_id(), @@ -2313,10 +2372,12 @@ impl ChannelMana /// /// Errors returned are a superset of those returned from [`send_payment`], so see /// [`send_payment`] documentation for more details on errors. This method will also error if the - /// retry amount puts the payment more than 10% over the payment's total amount, or if the payment - /// for the given `payment_id` cannot be found (likely due to timeout or success). + /// retry amount puts the payment more than 10% over the payment's total amount, if the payment + /// for the given `payment_id` cannot be found (likely due to timeout or success), or if + /// further retries have been disabled with [`abandon_payment`]. /// /// [`send_payment`]: [`ChannelManager::send_payment`] + /// [`abandon_payment`]: [`ChannelManager::abandon_payment`] pub fn retry_payment(&self, route: &Route, payment_id: PaymentId) -> Result<(), PaymentSendFailure> { const RETRY_OVERFLOW_PERCENTAGE: u64 = 10; for path in route.paths.iter() { @@ -2348,8 +2409,13 @@ impl ChannelMana })) }, PendingOutboundPayment::Fulfilled { .. } => { - return Err(PaymentSendFailure::ParameterError(APIError::RouteError { - err: "Payment already completed" + return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { + err: "Payment already completed".to_owned() + })); + }, + PendingOutboundPayment::Abandoned { .. } => { + return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError { + err: "Payment already abandoned (with some HTLCs still pending)".to_owned() })); }, } @@ -2362,6 +2428,37 @@ impl ChannelMana return self.send_payment_internal(route, payment_hash, &payment_secret, None, Some(payment_id), Some(total_msat)).map(|_| ()) } + /// Signals that no further retries for the given payment will occur. + /// + /// After this method returns, any future calls to [`retry_payment`] for the given `payment_id` + /// will fail with [`PaymentSendFailure::ParameterError`]. If no such event has been generated, + /// an [`Event::PaymentFailed`] event will be generated as soon as there are no remaining + /// pending HTLCs for this payment. + /// + /// Note that calling this method does *not* prevent a payment from succeeding. You must still + /// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to + /// determine the ultimate status of a payment. + /// + /// [`retry_payment`]: Self::retry_payment + /// [`Event::PaymentFailed`]: events::Event::PaymentFailed + /// [`Event::PaymentSent`]: events::Event::PaymentSent + pub fn abandon_payment(&self, payment_id: PaymentId) { + let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier); + + let mut outbounds = self.pending_outbound_payments.lock().unwrap(); + if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) { + if let Ok(()) = payment.get_mut().mark_abandoned() { + if payment.get().remaining_parts() == 0 { + self.pending_events.lock().unwrap().push(events::Event::PaymentFailed { + payment_id, + payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"), + }); + payment.remove(); + } + } + } + } + /// Send a spontaneous payment, which is a payment that does not require the recipient to have /// generated an invoice. Optionally, you may specify the preimage. If you do choose to specify /// the preimage, it must be a cryptographically secure random value that no intermediate node @@ -2443,7 +2540,8 @@ impl ChannelMana /// Returns an [`APIError::APIMisuseError`] if the funding_transaction spent non-SegWit outputs /// or if no output was found which matches the parameters in [`Event::FundingGenerationReady`]. /// - /// Panics if a funding transaction has already been provided for this channel. + /// Returns [`APIError::ChannelUnavailable`] if a funding transaction has already been provided + /// for the channel or if the channel has been closed as indicated by [`Event::ChannelClosed`]. /// /// May panic if the output found in the funding transaction is duplicative with some other /// channel (note that this should be trivially prevented by using unique funding transaction @@ -2458,6 +2556,7 @@ impl ChannelMana /// create a new channel with a conflicting funding transaction. /// /// [`Event::FundingGenerationReady`]: crate::util::events::Event::FundingGenerationReady + /// [`Event::ChannelClosed`]: crate::util::events::Event::ChannelClosed pub fn funding_transaction_generated(&self, temporary_channel_id: &[u8; 32], funding_transaction: Transaction) -> Result<(), APIError> { let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier); @@ -2814,6 +2913,59 @@ impl ChannelMana } } + macro_rules! check_total_value { + ($payment_data_total_msat: expr, $payment_secret: expr, $payment_preimage: expr) => {{ + let mut total_value = 0; + let mut payment_received_generated = false; + let htlcs = channel_state.claimable_htlcs.entry(payment_hash) + .or_insert(Vec::new()); + if htlcs.len() == 1 { + if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload { + log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash", log_bytes!(payment_hash.0)); + fail_htlc!(claimable_htlc); + continue + } + } + htlcs.push(claimable_htlc); + for htlc in htlcs.iter() { + total_value += htlc.value; + match &htlc.onion_payload { + OnionPayload::Invoice(htlc_payment_data) => { + if htlc_payment_data.total_msat != $payment_data_total_msat { + log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})", + log_bytes!(payment_hash.0), $payment_data_total_msat, htlc_payment_data.total_msat); + total_value = msgs::MAX_VALUE_MSAT; + } + if total_value >= msgs::MAX_VALUE_MSAT { break; } + }, + _ => unreachable!(), + } + } + if total_value >= msgs::MAX_VALUE_MSAT || total_value > $payment_data_total_msat { + log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)", + log_bytes!(payment_hash.0), total_value, $payment_data_total_msat); + for htlc in htlcs.iter() { + fail_htlc!(htlc); + } + } else if total_value == $payment_data_total_msat { + new_events.push(events::Event::PaymentReceived { + payment_hash, + purpose: events::PaymentPurpose::InvoicePayment { + payment_preimage: $payment_preimage, + payment_secret: $payment_secret, + }, + amt: total_value, + }); + payment_received_generated = true; + } else { + // Nothing to do - we haven't reached the total + // payment value yet, wait until we receive more + // MPP parts. + } + payment_received_generated + }} + } + // Check that the payment hash and secret are known. Note that we // MUST take care to handle the "unknown payment hash" and // "incorrect payment secret" cases here identically or we'd expose @@ -2863,54 +3015,9 @@ impl ChannelMana log_bytes!(payment_hash.0), payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap()); fail_htlc!(claimable_htlc); } else { - let mut total_value = 0; - let htlcs = channel_state.claimable_htlcs.entry(payment_hash) - .or_insert(Vec::new()); - if htlcs.len() == 1 { - if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload { - log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash", log_bytes!(payment_hash.0)); - fail_htlc!(claimable_htlc); - continue - } - } - htlcs.push(claimable_htlc); - for htlc in htlcs.iter() { - total_value += htlc.value; - match &htlc.onion_payload { - OnionPayload::Invoice(htlc_payment_data) => { - if htlc_payment_data.total_msat != payment_data.total_msat { - log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})", - log_bytes!(payment_hash.0), payment_data.total_msat, htlc_payment_data.total_msat); - total_value = msgs::MAX_VALUE_MSAT; - } - if total_value >= msgs::MAX_VALUE_MSAT { break; } - }, - _ => unreachable!(), - } - } - if total_value >= msgs::MAX_VALUE_MSAT || total_value > payment_data.total_msat { - log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)", - log_bytes!(payment_hash.0), total_value, payment_data.total_msat); - for htlc in htlcs.iter() { - fail_htlc!(htlc); - } - } else if total_value == payment_data.total_msat { - new_events.push(events::Event::PaymentReceived { - payment_hash, - purpose: events::PaymentPurpose::InvoicePayment { - payment_preimage: inbound_payment.get().payment_preimage, - payment_secret: payment_data.payment_secret, - }, - amt: total_value, - }); - // Only ever generate at most one PaymentReceived - // per registered payment_hash, even if it isn't - // claimed. + let payment_received_generated = check_total_value!(payment_data.total_msat, payment_data.payment_secret, inbound_payment.get().payment_preimage); + if payment_received_generated { inbound_payment.remove_entry(); - } else { - // Nothing to do - we haven't reached the total - // payment value yet, wait until we receive more - // MPP parts. } } }, @@ -3187,22 +3294,28 @@ impl ChannelMana final_cltv_expiry_delta: path_last_hop.cltv_expiry_delta, }) } else { None }; - self.pending_events.lock().unwrap().push( - events::Event::PaymentPathFailed { - payment_id: Some(payment_id), - payment_hash, - rejected_by_dest: false, - network_update: None, - all_paths_failed: payment.get().remaining_parts() == 0, - path: path.clone(), - short_channel_id: None, - retry, - #[cfg(test)] - error_code: None, - #[cfg(test)] - error_data: None, - } - ); + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push(events::Event::PaymentPathFailed { + payment_id: Some(payment_id), + payment_hash, + rejected_by_dest: false, + network_update: None, + all_paths_failed: payment.get().remaining_parts() == 0, + path: path.clone(), + short_channel_id: None, + retry, + #[cfg(test)] + error_code: None, + #[cfg(test)] + error_data: None, + }); + if payment.get().abandoned() && payment.get().remaining_parts() == 0 { + pending_events.push(events::Event::PaymentFailed { + payment_id, + payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"), + }); + payment.remove(); + } } } else { log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0)); @@ -3233,6 +3346,7 @@ impl ChannelMana session_priv_bytes.copy_from_slice(&session_priv[..]); let mut outbounds = self.pending_outbound_payments.lock().unwrap(); let mut all_paths_failed = false; + let mut full_failure_ev = None; if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) { if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) { log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0)); @@ -3244,6 +3358,13 @@ impl ChannelMana } if payment.get().remaining_parts() == 0 { all_paths_failed = true; + if payment.get().abandoned() { + full_failure_ev = Some(events::Event::PaymentFailed { + payment_id, + payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"), + }); + payment.remove(); + } } } else { log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0)); @@ -3259,7 +3380,8 @@ impl ChannelMana }) } else { None }; log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0)); - match &onion_error { + + let path_failure = match &onion_error { &HTLCFailReason::LightningError { ref err } => { #[cfg(test)] let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone()); @@ -3268,22 +3390,20 @@ impl ChannelMana // TODO: If we decided to blame ourselves (or one of our channels) in // process_onion_failure we should close that channel as it implies our // next-hop is needlessly blaming us! - self.pending_events.lock().unwrap().push( - events::Event::PaymentPathFailed { - payment_id: Some(payment_id), - payment_hash: payment_hash.clone(), - rejected_by_dest: !payment_retryable, - network_update, - all_paths_failed, - path: path.clone(), - short_channel_id, - retry, + events::Event::PaymentPathFailed { + payment_id: Some(payment_id), + payment_hash: payment_hash.clone(), + rejected_by_dest: !payment_retryable, + network_update, + all_paths_failed, + path: path.clone(), + short_channel_id, + retry, #[cfg(test)] - error_code: onion_error_code, + error_code: onion_error_code, #[cfg(test)] - error_data: onion_error_data - } - ); + error_data: onion_error_data + } }, &HTLCFailReason::Reason { #[cfg(test)] @@ -3298,24 +3418,25 @@ impl ChannelMana // ChannelDetails. // TODO: For non-temporary failures, we really should be closing the // channel here as we apparently can't relay through them anyway. - self.pending_events.lock().unwrap().push( - events::Event::PaymentPathFailed { - payment_id: Some(payment_id), - payment_hash: payment_hash.clone(), - rejected_by_dest: path.len() == 1, - network_update: None, - all_paths_failed, - path: path.clone(), - short_channel_id: Some(path.first().unwrap().short_channel_id), - retry, + events::Event::PaymentPathFailed { + payment_id: Some(payment_id), + payment_hash: payment_hash.clone(), + rejected_by_dest: path.len() == 1, + network_update: None, + all_paths_failed, + path: path.clone(), + short_channel_id: Some(path.first().unwrap().short_channel_id), + retry, #[cfg(test)] - error_code: Some(*failure_code), + error_code: Some(*failure_code), #[cfg(test)] - error_data: Some(data.clone()), - } - ); + error_data: Some(data.clone()), + } } - } + }; + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push(path_failure); + if let Some(ev) = full_failure_ev { pending_events.push(ev); } }, HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id, htlc_id, incoming_packet_shared_secret, .. }) => { let err_packet = match onion_error { @@ -3353,19 +3474,21 @@ impl ChannelMana } } - /// Provides a payment preimage in response to a PaymentReceived event, returning true and - /// generating message events for the net layer to claim the payment, if possible. Thus, you - /// should probably kick the net layer to go send messages if this returns true! + /// Provides a payment preimage in response to [`Event::PaymentReceived`], generating any + /// [`MessageSendEvent`]s needed to claim the payment. /// /// Note that if you did not set an `amount_msat` when calling [`create_inbound_payment`] or /// [`create_inbound_payment_for_hash`] you must check that the amount in the `PaymentReceived` /// event matches your expectation. If you fail to do so and call this method, you may provide /// the sender "proof-of-payment" when they did not fulfill the full expected payment. /// - /// May panic if called except in response to a PaymentReceived event. + /// Returns whether any HTLCs were claimed, and thus if any new [`MessageSendEvent`]s are now + /// pending for processing via [`get_and_clear_pending_msg_events`]. /// + /// [`Event::PaymentReceived`]: crate::util::events::Event::PaymentReceived /// [`create_inbound_payment`]: Self::create_inbound_payment /// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash + /// [`get_and_clear_pending_msg_events`]: MessageSendEventsProvider::get_and_clear_pending_msg_events pub fn claim_funds(&self, payment_preimage: PaymentPreimage) -> bool { let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner()); @@ -4842,14 +4965,19 @@ where inbound_payment.expiry_time > header.time as u64 }); + let mut pending_events = self.pending_events.lock().unwrap(); let mut outbounds = self.pending_outbound_payments.lock().unwrap(); - outbounds.retain(|_, payment| { - const PAYMENT_EXPIRY_BLOCKS: u32 = 3; + outbounds.retain(|payment_id, payment| { if payment.remaining_parts() != 0 { return true } - if let PendingOutboundPayment::Retryable { starting_block_height, .. } = payment { - return *starting_block_height + PAYMENT_EXPIRY_BLOCKS > height - } - true + if let PendingOutboundPayment::Retryable { starting_block_height, payment_hash, .. } = payment { + if *starting_block_height + PAYMENT_EXPIRY_BLOCKS <= height { + log_info!(self.logger, "Timing out payment with id {} and hash {}", log_bytes!(payment_id.0), log_bytes!(payment_hash.0)); + pending_events.push(events::Event::PaymentFailed { + payment_id: *payment_id, payment_hash: *payment_hash, + }); + false + } else { true } + } else { true } }); } @@ -4991,8 +5119,9 @@ where /// indicating whether persistence is necessary. Only one listener on /// `await_persistable_update` or `await_persistable_update_timeout` is guaranteed to be woken /// up. - /// Note that the feature `allow_wallclock_use` must be enabled to use this function. - #[cfg(any(test, feature = "allow_wallclock_use"))] + /// + /// Note that this method is not available with the `no-std` feature. + #[cfg(any(test, feature = "std"))] pub fn await_persistable_update_timeout(&self, max_wait: Duration) -> bool { self.persistence_notifier.wait_timeout(max_wait) } @@ -5287,7 +5416,7 @@ impl PersistenceNotifier { } } - #[cfg(any(test, feature = "allow_wallclock_use"))] + #[cfg(any(test, feature = "std"))] fn wait_timeout(&self, max_wait: Duration) -> bool { let current_time = Instant::now(); loop { @@ -5601,6 +5730,10 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (8, pending_amt_msat, required), (10, starting_block_height, required), }, + (3, Abandoned) => { + (0, session_privs, required), + (2, payment_hash, required), + }, ); impl Writeable for ChannelManager @@ -5694,7 +5827,7 @@ impl Writeable f // For backwards compat, write the session privs and their total length. let mut num_pending_outbounds_compat: u64 = 0; for (_, outbound) in pending_outbound_payments.iter() { - if !outbound.is_fulfilled() { + if !outbound.is_fulfilled() && !outbound.abandoned() { num_pending_outbounds_compat += outbound.remaining_parts() as u64; } } @@ -5708,6 +5841,7 @@ impl Writeable f } } PendingOutboundPayment::Fulfilled { .. } => {}, + PendingOutboundPayment::Abandoned { .. } => {}, } }