}
}
+impl HTLCFailReason {
+ pub(super) fn reason(failure_code: u16, data: Vec<u8>) -> Self {
+ Self::Reason { failure_code, data }
+ }
+
+ pub(super) fn from_failure_code(failure_code: u16) -> Self {
+ Self::Reason { failure_code, data: Vec::new() }
+ }
+}
+
struct ReceiveError {
err_code: u16,
err_data: Vec<u8>,
channel_state: Mutex<ChannelHolder<<K::Target as KeysInterface>::Signer>>,
/// Storage for PaymentSecrets and any requirements on future inbound payments before we will
- /// expose them to users via a PaymentReceived event. HTLCs which do not meet the requirements
+ /// expose them to users via a PaymentClaimable event. HTLCs which do not meet the requirements
/// here are failed when we process them as pending-forwardable-HTLCs, and entries are removed
- /// after we generate a PaymentReceived upon receipt of all MPP parts or when they time out.
+ /// after we generate a PaymentClaimable upon receipt of all MPP parts or when they time out.
///
/// See `ChannelManager` struct-level documentation for lock order requirements.
pending_inbound_payments: Mutex<HashMap<PaymentHash, PendingInboundPayment>>,
};
for htlc_source in failed_htlcs.drain(..) {
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: *channel_id };
- self.fail_htlc_backwards_internal(htlc_source.0, &htlc_source.1, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ self.fail_htlc_backwards_internal(&htlc_source.0, &htlc_source.1, &reason, receiver);
}
let _ = handle_error!(self, result, *counterparty_node_id);
log_debug!(self.logger, "Finishing force-closure of channel with {} HTLCs to fail", failed_htlcs.len());
for htlc_source in failed_htlcs.drain(..) {
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
- self.fail_htlc_backwards_internal(source, &payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
}
if let Some((funding_txo, monitor_update)) = monitor_update_option {
// There isn't anything we can do if we get an update failure - we're already
None => { // unknown_next_peer
// Note that this is likely a timing oracle for detecting whether an scid is a
// phantom or an intercept.
- if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) ||
- fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
+ if (self.default_configuration.accept_intercept_htlcs &&
+ fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)) ||
+ fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
{
None
} else {
let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
let onion_keys = onion_utils::construct_onion_keys(&self.secp_ctx, &path, &session_priv)
- .map_err(|_| APIError::RouteError{err: "Pubkey along hop was maliciously selected"})?;
+ .map_err(|_| APIError::InvalidRoute{err: "Pubkey along hop was maliciously selected"})?;
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(path, total_value, payment_secret, cur_height, keysend_preimage)?;
if onion_utils::route_size_insane(&onion_payloads) {
- return Err(APIError::RouteError{err: "Route size too large considering onion data"});
+ return Err(APIError::InvalidRoute{err: "Route size too large considering onion data"});
}
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
match {
if chan.get().get_counterparty_node_id() != path.first().unwrap().pubkey {
- return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
+ return Err(APIError::InvalidRoute{err: "Node ID mismatch on first hop!"});
}
if !chan.get().is_live() {
return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!".to_owned()});
/// fields for more info.
///
/// If a pending payment is currently in-flight with the same [`PaymentId`] provided, this
- /// method will error with an [`APIError::RouteError`]. Note, however, that once a payment
+ /// method will error with an [`APIError::InvalidRoute`]. Note, however, that once a payment
/// is no longer pending (either via [`ChannelManager::abandon_payment`], or handling of an
/// [`Event::PaymentSent`]) LDK will not stop you from sending a second payment with the same
/// [`PaymentId`].
/// PaymentSendFailure for more info.
///
/// In general, a path may raise:
- /// * [`APIError::RouteError`] when an invalid route or forwarding parameter (cltv_delta, fee,
+ /// * [`APIError::InvalidRoute`] when an invalid route or forwarding parameter (cltv_delta, fee,
/// node public key) is specified.
/// * [`APIError::ChannelUnavailable`] if the next-hop channel is not available for updates
/// (including due to previous monitor update failure or new permanent monitor update
fn send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: Vec<[u8; 32]>) -> Result<(), PaymentSendFailure> {
if route.paths.len() < 1 {
- return Err(PaymentSendFailure::ParameterError(APIError::RouteError{err: "There must be at least one path to send over"}));
+ return Err(PaymentSendFailure::ParameterError(APIError::InvalidRoute{err: "There must be at least one path to send over"}));
}
if payment_secret.is_none() && route.paths.len() > 1 {
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_string()}));
let mut path_errs = Vec::with_capacity(route.paths.len());
'path_check: for path in route.paths.iter() {
if path.len() < 1 || path.len() > 20 {
- path_errs.push(Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"}));
+ path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size"}));
continue 'path_check;
}
for (idx, hop) in path.iter().enumerate() {
if idx != path.len() - 1 && hop.pubkey == our_node_id {
- path_errs.push(Err(APIError::RouteError{err: "Path went through us but wasn't a simple rebalance loop to us"}));
+ path_errs.push(Err(APIError::InvalidRoute{err: "Path went through us but wasn't a simple rebalance loop to us"}));
continue 'path_check;
}
}
/// Intercepted HTLCs can be useful for Lightning Service Providers (LSPs) to open a just-in-time
/// channel to a receiving node if the node lacks sufficient inbound liquidity.
///
- /// To make use of intercepted HTLCs, use [`ChannelManager::get_intercept_scid`] to generate short
- /// channel id(s) to put in the receiver's invoice route hints. These route hints will signal to
- /// LDK to generate an [`HTLCIntercepted`] event when it receives the forwarded HTLC, and this
- /// method or [`ChannelManager::fail_intercepted_htlc`] MUST be called in response to the event.
+ /// To make use of intercepted HTLCs, set [`UserConfig::accept_intercept_htlcs`] and use
+ /// [`ChannelManager::get_intercept_scid`] to generate short channel id(s) to put in the
+ /// receiver's invoice route hints. These route hints will signal to LDK to generate an
+ /// [`HTLCIntercepted`] event when it receives the forwarded HTLC, and this method or
+ /// [`ChannelManager::fail_intercepted_htlc`] MUST be called in response to the event.
///
/// Note that LDK does not enforce fee requirements in `amt_to_forward_msat`, and will not stop
/// you from forwarding more than you received.
///
+ /// Errors if the event was not handled in time, in which case the HTLC was automatically failed
+ /// backwards.
+ ///
+ /// [`UserConfig::accept_intercept_htlcs`]: crate::util::config::UserConfig::accept_intercept_htlcs
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
// TODO: when we move to deciding the best outbound channel at forward time, only take
// `next_node_id` and not `next_hop_channel_id`
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let next_hop_scid = match self.channel_state.lock().unwrap().by_id.get(next_hop_channel_id) {
- Some(chan) => chan.get_short_channel_id().unwrap_or(chan.outbound_scid_alias()),
- None => return Err(APIError::APIMisuseError {
- err: format!("Channel with id {:?} not found", next_hop_channel_id)
+ Some(chan) => {
+ if !chan.is_usable() {
+ return Err(APIError::ChannelUnavailable {
+ err: format!("Channel with id {} not fully established", log_bytes!(*next_hop_channel_id))
+ })
+ }
+ chan.get_short_channel_id().unwrap_or(chan.outbound_scid_alias())
+ },
+ None => return Err(APIError::ChannelUnavailable {
+ err: format!("Channel with id {} not found", log_bytes!(*next_hop_channel_id))
})
};
let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
.ok_or_else(|| APIError::APIMisuseError {
- err: format!("Payment with intercept id {:?} not found", intercept_id.0)
+ err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
})?;
let routing = match payment.forward_info.routing {
/// Fails the intercepted HTLC indicated by intercept_id. Should only be called in response to
/// an [`HTLCIntercepted`] event. See [`ChannelManager::forward_intercepted_htlc`].
///
+ /// Errors if the event was not handled in time, in which case the HTLC was automatically failed
+ /// backwards.
+ ///
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
pub fn fail_intercepted_htlc(&self, intercept_id: InterceptId) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
.ok_or_else(|| APIError::APIMisuseError {
- err: format!("Payment with InterceptId {:?} not found", intercept_id)
+ err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
})?;
if let PendingHTLCRouting::Forward { short_channel_id, .. } = payment.forward_info.routing {
phantom_shared_secret: None,
});
- let failure_reason = HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() };
+ let failure_reason = HTLCFailReason::from_failure_code(0x4000 | 10);
let destination = HTLCDestination::UnknownNextHop { requested_forward_scid: short_channel_id };
- self.fail_htlc_backwards_internal(htlc_source, &payment.forward_info.payment_hash, failure_reason, destination);
+ self.fail_htlc_backwards_internal(&htlc_source, &payment.forward_info.payment_hash, &failure_reason, destination);
} else { unreachable!() } // Only `PendingHTLCRouting::Forward`s are intercepted
Ok(())
};
failed_forwards.push((htlc_source, payment_hash,
- HTLCFailReason::Reason { failure_code: $err_code, data: $err_data },
+ HTLCFailReason::reason($err_code, $err_data),
reason
));
continue;
}
let (failure_code, data) = self.get_htlc_temp_fail_err_and_data(0x1000|7, short_chan_id, chan.get());
failed_forwards.push((htlc_source, payment_hash,
- HTLCFailReason::Reason { failure_code, data },
+ HTLCFailReason::reason(failure_code, data),
HTLCDestination::NextHopChannel { node_id: Some(chan.get().get_counterparty_node_id()), channel_id: forward_chan_id }
));
continue;
incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
phantom_shared_secret,
}), payment_hash,
- HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data },
+ HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
HTLCDestination::FailedPayment { payment_hash: $payment_hash },
));
}
} else if total_value == $payment_data.total_msat {
let prev_channel_id = prev_funding_outpoint.to_channel_id();
htlcs.push(claimable_htlc);
- new_events.push(events::Event::PaymentReceived {
+ new_events.push(events::Event::PaymentClaimable {
receiver_node_id: Some(receiver_node_id),
payment_hash,
purpose: purpose(),
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
e.insert((purpose.clone(), vec![claimable_htlc]));
let prev_channel_id = prev_funding_outpoint.to_channel_id();
- new_events.push(events::Event::PaymentReceived {
+ new_events.push(events::Event::PaymentClaimable {
receiver_node_id: Some(receiver_node_id),
payment_hash,
amount_msat: outgoing_amt_msat,
}
for (htlc_source, payment_hash, failure_reason, destination) in failed_forwards.drain(..) {
- self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
+ self.fail_htlc_backwards_internal(&htlc_source, &payment_hash, &failure_reason, destination);
}
self.forward_htlcs(&mut phantom_receives);
});
for htlc_source in timed_out_mpp_htlcs.drain(..) {
+ let source = HTLCSource::PreviousHopData(htlc_source.0.clone());
+ let reason = HTLCFailReason::from_failure_code(23);
let receiver = HTLCDestination::FailedPayment { payment_hash: htlc_source.1 };
- self.fail_htlc_backwards_internal(HTLCSource::PreviousHopData(htlc_source.0.clone()), &htlc_source.1, HTLCFailReason::Reason { failure_code: 23, data: Vec::new() }, receiver );
+ self.fail_htlc_backwards_internal(&source, &htlc_source.1, &reason, receiver);
}
for (err, counterparty_node_id) in handle_errors.drain(..) {
}
/// Indicates that the preimage for payment_hash is unknown or the received amount is incorrect
- /// after a PaymentReceived event, failing the HTLC back to its origin and freeing resources
+ /// after a PaymentClaimable event, failing the HTLC back to its origin and freeing resources
/// along the path (including in our own channel on which we received it).
///
/// Note that in some cases around unclean shutdown, it is possible the payment may have
/// already been claimed by you via [`ChannelManager::claim_funds`] prior to you seeing (a
- /// second copy of) the [`events::Event::PaymentReceived`] event. Alternatively, the payment
+ /// second copy of) the [`events::Event::PaymentClaimable`] event. Alternatively, the payment
/// may have already been failed automatically by LDK if it was nearing its expiration time.
///
/// While LDK will never claim a payment automatically on your behalf (i.e. without you calling
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(
self.best_block.read().unwrap().height()));
- self.fail_htlc_backwards_internal(
- HTLCSource::PreviousHopData(htlc.prev_hop), payment_hash,
- HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data },
- HTLCDestination::FailedPayment { payment_hash: *payment_hash });
+ let source = HTLCSource::PreviousHopData(htlc.prev_hop);
+ let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
+ let receiver = HTLCDestination::FailedPayment { payment_hash: *payment_hash };
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
}
}
}
&self, mut htlcs_to_fail: Vec<(HTLCSource, PaymentHash)>, channel_id: [u8; 32],
counterparty_node_id: &PublicKey
) {
- for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) {
- let (failure_code, onion_failure_data) =
- match self.channel_state.lock().unwrap().by_id.entry(channel_id) {
- hash_map::Entry::Occupied(chan_entry) => {
- self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
- },
- hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
- };
+ let (failure_code, onion_failure_data) =
+ match self.channel_state.lock().unwrap().by_id.entry(channel_id) {
+ hash_map::Entry::Occupied(chan_entry) => {
+ self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
+ },
+ hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
+ };
+ for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) {
+ let reason = HTLCFailReason::reason(failure_code, onion_failure_data.clone());
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id };
- self.fail_htlc_backwards_internal(htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data }, receiver);
+ self.fail_htlc_backwards_internal(&htlc_src, &payment_hash, &reason, receiver);
}
}
/// Fails an HTLC backwards to the sender of it to us.
/// Note that we do not assume that channels corresponding to failed HTLCs are still available.
- fn fail_htlc_backwards_internal(&self, source: HTLCSource, payment_hash: &PaymentHash, onion_error: HTLCFailReason,destination: HTLCDestination) {
+ fn fail_htlc_backwards_internal(&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, destination: HTLCDestination) {
#[cfg(debug_assertions)]
{
// Ensure that the `channel_state` lock is not held when calling this function.
// from block_connected which may run during initialization prior to the chain_monitor
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
- HTLCSource::OutboundRoute { ref path, session_priv, payment_id, ref payment_params, .. } => {
+ HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, ref payment_params, .. } => {
let mut session_priv_bytes = [0; 32];
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 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));
return;
all_paths_failed = true;
if payment.get().abandoned() {
full_failure_ev = Some(events::Event::PaymentFailed {
- payment_id,
+ payment_id: *payment_id,
payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
});
payment.remove();
if self.payment_is_probe(payment_hash, &payment_id) {
if !payment_retryable {
events::Event::ProbeSuccessful {
- payment_id,
+ payment_id: *payment_id,
payment_hash: payment_hash.clone(),
path: path.clone(),
}
} else {
events::Event::ProbeFailed {
- payment_id,
+ payment_id: *payment_id,
payment_hash: payment_hash.clone(),
path: path.clone(),
short_channel_id,
retry.as_mut().map(|r| r.payment_params.previously_failed_channels.push(scid));
}
events::Event::PaymentPathFailed {
- payment_id: Some(payment_id),
+ payment_id: Some(*payment_id),
payment_hash: payment_hash.clone(),
payment_failed_permanently: !payment_retryable,
network_update,
if self.payment_is_probe(payment_hash, &payment_id) {
events::Event::ProbeFailed {
- payment_id,
+ payment_id: *payment_id,
payment_hash: payment_hash.clone(),
path: path.clone(),
short_channel_id: Some(scid),
}
} else {
events::Event::PaymentPathFailed {
- payment_id: Some(payment_id),
+ payment_id: Some(*payment_id),
payment_hash: payment_hash.clone(),
payment_failed_permanently: false,
network_update: None,
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, phantom_shared_secret, outpoint }) => {
+ HTLCSource::PreviousHopData(HTLCPreviousHopData { ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret, ref phantom_shared_secret, ref outpoint }) => {
let err_packet = match onion_error {
- HTLCFailReason::Reason { failure_code, data } => {
+ HTLCFailReason::Reason { ref failure_code, ref data } => {
log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with code {}", log_bytes!(payment_hash.0), failure_code);
if let Some(phantom_ss) = phantom_shared_secret {
- let phantom_packet = onion_utils::build_failure_packet(&phantom_ss, failure_code, &data[..]).encode();
- let encrypted_phantom_packet = onion_utils::encrypt_failure_packet(&phantom_ss, &phantom_packet);
- onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &encrypted_phantom_packet.data[..])
+ let phantom_packet = onion_utils::build_failure_packet(phantom_ss, *failure_code, &data[..]).encode();
+ let encrypted_phantom_packet = onion_utils::encrypt_failure_packet(phantom_ss, &phantom_packet);
+ onion_utils::encrypt_failure_packet(incoming_packet_shared_secret, &encrypted_phantom_packet.data[..])
} else {
- let packet = onion_utils::build_failure_packet(&incoming_packet_shared_secret, failure_code, &data[..]).encode();
- onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &packet)
+ let packet = onion_utils::build_failure_packet(incoming_packet_shared_secret, *failure_code, &data[..]).encode();
+ onion_utils::encrypt_failure_packet(incoming_packet_shared_secret, &packet)
}
},
HTLCFailReason::LightningError { err } => {
log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards with pre-built LightningError", log_bytes!(payment_hash.0));
- onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &err.data)
+ onion_utils::encrypt_failure_packet(incoming_packet_shared_secret, &err.data)
}
};
if forward_htlcs.is_empty() {
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
}
- match forward_htlcs.entry(short_channel_id) {
+ match forward_htlcs.entry(*short_channel_id) {
hash_map::Entry::Occupied(mut entry) => {
- entry.get_mut().push(HTLCForwardInfo::FailHTLC { htlc_id, err_packet });
+ entry.get_mut().push(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet });
},
hash_map::Entry::Vacant(entry) => {
- entry.insert(vec!(HTLCForwardInfo::FailHTLC { htlc_id, err_packet }));
+ entry.insert(vec!(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet }));
}
}
mem::drop(forward_htlcs);
}
pending_events.push(events::Event::HTLCHandlingFailed {
prev_channel_id: outpoint.to_channel_id(),
- failed_next_destination: destination
+ failed_next_destination: destination,
});
},
}
}
- /// Provides a payment preimage in response to [`Event::PaymentReceived`], generating any
+ /// Provides a payment preimage in response to [`Event::PaymentClaimable`], generating any
/// [`MessageSendEvent`]s needed to claim the payment.
///
/// Note that calling this method does *not* guarantee that the payment has been claimed. You
/// provided to your [`EventHandler`] when [`process_pending_events`] is next called.
///
/// 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`
+ /// [`create_inbound_payment_for_hash`] you must check that the amount in the `PaymentClaimable`
/// 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.
///
- /// [`Event::PaymentReceived`]: crate::util::events::Event::PaymentReceived
+ /// [`Event::PaymentClaimable`]: crate::util::events::Event::PaymentClaimable
/// [`Event::PaymentClaimed`]: crate::util::events::Event::PaymentClaimed
/// [`process_pending_events`]: EventsProvider::process_pending_events
/// [`create_inbound_payment`]: Self::create_inbound_payment
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(
self.best_block.read().unwrap().height()));
- self.fail_htlc_backwards_internal(
- HTLCSource::PreviousHopData(htlc.prev_hop), &payment_hash,
- HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_height_data },
- HTLCDestination::FailedPayment { payment_hash } );
+ let source = HTLCSource::PreviousHopData(htlc.prev_hop);
+ let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
+ let receiver = HTLCDestination::FailedPayment { payment_hash };
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
}
}
self.finalize_claims(finalized_claims);
for failure in pending_failures.drain(..) {
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id: funding_txo.to_channel_id() };
- self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver);
+ self.fail_htlc_backwards_internal(&failure.0, &failure.1, &failure.2, receiver);
}
}
};
for htlc_source in dropped_htlcs.drain(..) {
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id: msg.channel_id };
- self.fail_htlc_backwards_internal(htlc_source.0, &htlc_source.1, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+ self.fail_htlc_backwards_internal(&htlc_source.0, &htlc_source.1, &reason, receiver);
}
let _ = handle_error!(self, result, *counterparty_node_id);
let chan_err: ChannelError = ChannelError::Close("Got update_fail_malformed_htlc with BADONION not set".to_owned());
try_chan_entry!(self, Err(chan_err), chan);
}
- try_chan_entry!(self, chan.get_mut().update_fail_malformed_htlc(&msg, HTLCFailReason::Reason { failure_code: msg.failure_code, data: Vec::new() }), chan);
+ try_chan_entry!(self, chan.get_mut().update_fail_malformed_htlc(&msg, HTLCFailReason::from_failure_code(msg.failure_code)), chan);
Ok(())
},
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id))
});
failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
- HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() },
+ HTLCFailReason::from_failure_code(0x4000 | 10),
HTLCDestination::InvalidForward { requested_forward_scid: scid },
));
}
}
for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
- self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
+ self.fail_htlc_backwards_internal(&htlc_source, &payment_hash, &failure_reason, destination);
}
if !new_intercept_events.is_empty() {
{
for failure in pending_failures.drain(..) {
let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: channel_outpoint.to_channel_id() };
- self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver);
+ self.fail_htlc_backwards_internal(&failure.0, &failure.1, &failure.2, receiver);
}
self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, user_channel_id, pending_forwards)]);
self.finalize_claims(finalized_claim_htlcs);
} else {
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", log_bytes!(htlc_update.payment_hash.0));
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
- self.fail_htlc_backwards_internal(htlc_update.source, &htlc_update.payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+ self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
}
},
MonitorEvent::CommitmentTxConfirmed(funding_outpoint) |
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
///
- /// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentReceived`], which
- /// will have the [`PaymentReceived::payment_preimage`] field filled in. That should then be
+ /// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentClaimable`], which
+ /// will have the [`PaymentClaimable::payment_preimage`] field filled in. That should then be
/// passed directly to [`claim_funds`].
///
/// See [`create_inbound_payment_for_hash`] for detailed documentation on behavior and requirements.
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// [`claim_funds`]: Self::claim_funds
- /// [`PaymentReceived`]: events::Event::PaymentReceived
- /// [`PaymentReceived::payment_preimage`]: events::Event::PaymentReceived::payment_preimage
+ /// [`PaymentClaimable`]: events::Event::PaymentClaimable
+ /// [`PaymentClaimable::payment_preimage`]: events::Event::PaymentClaimable::payment_preimage
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<(PaymentHash, PaymentSecret), ()> {
inbound_payment::create(&self.inbound_payment_key, min_value_msat, invoice_expiry_delta_secs, &self.keys_manager, self.highest_seen_timestamp.load(Ordering::Acquire) as u64)
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
///
- /// A [`PaymentReceived`] event will only be generated if the [`PaymentSecret`] matches a
+ /// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
/// the `min_value_msat` provided here, if one is provided.
///
///
/// `min_value_msat` should be set if the invoice being generated contains a value. Any payment
/// received for the returned [`PaymentHash`] will be required to be at least `min_value_msat`
- /// before a [`PaymentReceived`] event will be generated, ensuring that we do not provide the
+ /// before a [`PaymentClaimable`] event will be generated, ensuring that we do not provide the
/// sender "proof-of-payment" unless they have paid the required amount.
///
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
///
/// Note that we use block header time to time-out pending inbound payments (with some margin
/// to compensate for the inaccuracy of block header timestamps). Thus, in practice we will
- /// accept a payment and generate a [`PaymentReceived`] event for some time after the expiry.
+ /// accept a payment and generate a [`PaymentClaimable`] event for some time after the expiry.
/// If you need exact expiry semantics, you should enforce them upon receipt of
- /// [`PaymentReceived`].
+ /// [`PaymentClaimable`].
///
/// Note that invoices generated for inbound payments should have their `min_final_cltv_expiry`
/// set to at least [`MIN_FINAL_CLTV_EXPIRY`].
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
- /// [`PaymentReceived`]: events::Event::PaymentReceived
+ /// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32) -> Result<PaymentSecret, ()> {
inbound_payment::create_from_hash(&self.inbound_payment_key, min_value_msat, payment_hash, invoice_expiry_delta_secs, self.highest_seen_timestamp.load(Ordering::Acquire) as u64)
}
if let Ok((channel_ready_opt, mut timed_out_pending_htlcs, announcement_sigs)) = res {
for (source, payment_hash) in timed_out_pending_htlcs.drain(..) {
let (failure_code, data) = self.get_htlc_inbound_temp_fail_err_and_data(0x1000|14 /* expiry_too_soon */, &channel);
- timed_out_htlcs.push((source, payment_hash, HTLCFailReason::Reason {
- failure_code, data,
- }, HTLCDestination::NextHopChannel { node_id: Some(channel.get_counterparty_node_id()), channel_id: channel.channel_id() }));
+ timed_out_htlcs.push((source, payment_hash, HTLCFailReason::reason(failure_code, data),
+ HTLCDestination::NextHopChannel { node_id: Some(channel.get_counterparty_node_id()), channel_id: channel.channel_id() }));
}
if let Some(channel_ready) = channel_ready_opt {
send_channel_ready!(self, pending_msg_events, channel, channel_ready);
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height));
- timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason {
- failure_code: 0x4000 | 15,
- data: htlc_msat_height_data
- }, HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }));
+ timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(),
+ HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
+ HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }));
false
} else { true }
});
!htlcs.is_empty() // Only retain this entry if htlcs has at least one entry.
});
+
+ let mut intercepted_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
+ intercepted_htlcs.retain(|_, htlc| {
+ if height >= htlc.forward_info.outgoing_cltv_value - HTLC_FAIL_BACK_BUFFER {
+ let prev_hop_data = HTLCSource::PreviousHopData(HTLCPreviousHopData {
+ short_channel_id: htlc.prev_short_channel_id,
+ htlc_id: htlc.prev_htlc_id,
+ incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret,
+ phantom_shared_secret: None,
+ outpoint: htlc.prev_funding_outpoint,
+ });
+
+ let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing {
+ PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
+ _ => unreachable!(),
+ };
+ timed_out_htlcs.push((prev_hop_data, htlc.forward_info.payment_hash,
+ HTLCFailReason::from_failure_code(0x2000 | 2),
+ HTLCDestination::InvalidForward { requested_forward_scid }));
+ log_trace!(self.logger, "Timing out intercepted HTLC with requested forward scid {}", requested_forward_scid);
+ false
+ } else { true }
+ });
}
self.handle_init_event_channel_failures(failed_channels);
for (source, payment_hash, reason, destination) in timed_out_htlcs.drain(..) {
- self.fail_htlc_backwards_internal(source, &payment_hash, reason, destination);
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, destination);
}
}
for htlc_source in failed_htlcs.drain(..) {
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
- channel_manager.fail_htlc_backwards_internal(source, &payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+ let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+ channel_manager.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
}
//TODO: Broadcast channel update for closed channels, but only after we've made a
$node_b.handle_revoke_and_ack(&$node_a.get_our_node_id(), &get_event_msg!(NodeHolder { node: &$node_a }, MessageSendEvent::SendRevokeAndACK, $node_b.get_our_node_id()));
expect_pending_htlcs_forwardable!(NodeHolder { node: &$node_b });
- expect_payment_received!(NodeHolder { node: &$node_b }, payment_hash, payment_secret, 10_000);
+ expect_payment_claimable!(NodeHolder { node: &$node_b }, payment_hash, payment_secret, 10_000);
$node_b.claim_funds(payment_preimage);
expect_payment_claimed!(NodeHolder { node: &$node_b }, payment_hash, 10_000);