Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions lightning-liquidity/src/lsps2/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::prelude::hash_map::Entry;
use crate::prelude::{new_hash_map, HashMap, String, ToString, Vec};
use crate::sync::{Arc, Mutex, MutexGuard, RwLock};

use lightning::events::HTLCDestination;
use lightning::events::HTLCHandlingType;
use lightning::ln::channelmanager::{AChannelManager, InterceptId};
use lightning::ln::msgs::{ErrorAction, LightningError};
use lightning::ln::types::ChannelId;
Expand Down Expand Up @@ -879,10 +879,8 @@ where
/// or if the payment queue is empty
///
/// [`Event::HTLCHandlingFailed`]: lightning::events::Event::HTLCHandlingFailed
pub fn htlc_handling_failed(
&self, failed_next_destination: HTLCDestination,
) -> Result<(), APIError> {
if let HTLCDestination::NextHopChannel { channel_id, .. } = failed_next_destination {
pub fn htlc_handling_failed(&self, handling_type: HTLCHandlingType) -> Result<(), APIError> {
if let HTLCHandlingType::ForwardFailed { channel_id, .. } = handling_type {
let peer_by_channel_id = self.peer_by_channel_id.read().unwrap();
if let Some(counterparty_node_id) = peer_by_channel_id.get(&channel_id) {
let outer_state_lock = self.per_peer_state.read().unwrap();
Expand Down
103 changes: 86 additions & 17 deletions lightning/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::chain::transaction;
use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
use crate::types::features::ChannelTypeFeatures;
use crate::ln::msgs;
use crate::ln::{msgs, LocalHTLCFailureReason};
use crate::ln::types::ChannelId;
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::offers::invoice::Bolt12Invoice;
Expand Down Expand Up @@ -465,12 +465,12 @@ impl_writeable_tlv_based_enum_upgradable!(ClosureReason,
},
);

/// Intended destination of a failed HTLC as indicated in [`Event::HTLCHandlingFailed`].
/// The type of HTLC that is being handled in [`Event::HTLCHandlingFailed`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HTLCDestination {
pub enum HTLCHandlingType {
/// We tried forwarding to a channel but failed to do so. An example of such an instance is when
/// there is insufficient capacity in our outbound channel.
NextHopChannel {
ForwardFailed {
/// The `node_id` of the next node. For backwards compatibility, this field is
/// marked as optional, versions prior to 0.0.110 may not always be able to provide
/// counterparty node information.
Expand All @@ -479,12 +479,16 @@ pub enum HTLCDestination {
channel_id: ChannelId,
},
/// Scenario where we are unsure of the next node to forward the HTLC to.
///
/// Deprecated: will only be used in versions before LDK v0.2.0.
UnknownNextHop {
/// Short channel id we are requesting to forward an HTLC to.
requested_forward_scid: u64,
},
/// We couldn't forward to the outgoing scid. An example would be attempting to send a duplicate
/// intercept HTLC.
///
/// In LDK v0.2.0 and greater, this variant replaces [`Self::UnknownNextHop`].
InvalidForward {
/// Short channel id we are requesting to forward an HTLC to.
requested_forward_scid: u64
Expand All @@ -501,14 +505,14 @@ pub enum HTLCDestination {
/// * The counterparty node modified the HTLC in transit,
/// * A probing attack where an intermediary node is trying to detect if we are the ultimate
/// recipient for a payment.
FailedPayment {
ReceiveFailed {
/// The payment hash of the payment we attempted to process.
payment_hash: PaymentHash
},
}

impl_writeable_tlv_based_enum_upgradable!(HTLCDestination,
(0, NextHopChannel) => {
impl_writeable_tlv_based_enum_upgradable!(HTLCHandlingType,
(0, ForwardFailed) => {
(0, node_id, required),
(2, channel_id, required),
},
Expand All @@ -519,11 +523,30 @@ impl_writeable_tlv_based_enum_upgradable!(HTLCDestination,
(0, requested_forward_scid, required),
},
(3, InvalidOnion) => {},
(4, FailedPayment) => {
(4, ReceiveFailed) => {
(0, payment_hash, required),
},
);

/// The reason for HTLC failures in [`Event::HTLCHandlingFailed`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HTLCHandlingFailureReason {
/// The forwarded HTLC was failed back by the downstream node with an encrypted error reason.
Downstream,
/// The HTLC was failed locally by our node.
Local {
/// The reason that our node chose to fail the HTLC.
reason: LocalHTLCFailureReason,
},
}

impl_writeable_tlv_based_enum!(HTLCHandlingFailureReason,
(0, Downstream) => {},
(1, Local) => {
(0, reason, required),
},
);

/// Will be used in [`Event::HTLCIntercepted`] to identify the next hop in the HTLC's path.
/// Currently only used in serialization for the sake of maintaining compatibility. More variants
/// will be added for general-purpose HTLC forward intercepts as well as trampoline forward
Expand Down Expand Up @@ -1447,8 +1470,12 @@ pub enum Event {
HTLCHandlingFailed {
/// The channel over which the HTLC was received.
prev_channel_id: ChannelId,
/// Destination of the HTLC that failed to be processed.
failed_next_destination: HTLCDestination,
/// The type of HTLC that was handled.
handling_type: HTLCHandlingType,
/// The reason that the HTLC failed.
///
/// This field will be `None` only for objects serialized prior to LDK 0.2.0.
handling_failure: Option<HTLCHandlingFailureReason>
},
/// Indicates that a transaction originating from LDK needs to have its fee bumped. This event
/// requires confirmed external funds to be readily available to spend.
Expand Down Expand Up @@ -1752,11 +1779,29 @@ impl Writeable for Event {
(8, path.blinded_tail, option),
})
},
&Event::HTLCHandlingFailed { ref prev_channel_id, ref failed_next_destination } => {
&Event::HTLCHandlingFailed { ref prev_channel_id, ref handling_type, ref handling_failure } => {
25u8.write(writer)?;

// The [`HTLCHandlingType::UnknownNextPeer`] variant is deprecated, but we want to
// continue writing it to allow downgrading. Detect the case where we're
// representing it as [`HTLCHandlingType::InvalidForward`] and
// [`LocalHTLCFailureReason::UnknownNextHop`] and write the old variant instead.
let downgradable_type = match (handling_type, handling_failure) {
(HTLCHandlingType::InvalidForward { requested_forward_scid },
Some(HTLCHandlingFailureReason::Local { reason }))
if *reason == LocalHTLCFailureReason::UnknownNextPeer =>
{
HTLCHandlingType::UnknownNextHop {
requested_forward_scid: *requested_forward_scid,
}
}
_ => handling_type.clone()
};

write_tlv_fields!(writer, {
(0, prev_channel_id, required),
(2, failed_next_destination, required),
(1, handling_failure, option),
(2, downgradable_type, required),
})
},
&Event::BumpTransaction(ref event)=> {
Expand Down Expand Up @@ -2201,15 +2246,39 @@ impl MaybeReadable for Event {
25u8 => {
let mut f = || {
let mut prev_channel_id = ChannelId::new_zero();
let mut failed_next_destination_opt = UpgradableRequired(None);
let mut handling_failure = None;
let mut handling_type_opt = UpgradableRequired(None);
read_tlv_fields!(reader, {
(0, prev_channel_id, required),
(2, failed_next_destination_opt, upgradable_required),
(1, handling_failure, option),
(2, handling_type_opt, upgradable_required),
});
Ok(Some(Event::HTLCHandlingFailed {

let mut event = Event::HTLCHandlingFailed {
prev_channel_id,
failed_next_destination: _init_tlv_based_struct_field!(failed_next_destination_opt, upgradable_required),
}))
handling_type: _init_tlv_based_struct_field!(handling_type_opt, upgradable_required),
handling_failure,
};

// The [`HTLCHandlingType::UnknownNextPeer`] variant is deprecated, but we
// continue writing it to allow downgrading. If it was written, upgrade
// it to its new representation of [`HTLCHandlingType::InvalidForward`] and
// [`LocalHTLCFailureReason::UnknownNextHop`]. This will cover both the case
// where we have a legacy event
match event {
Event::HTLCHandlingFailed { ref handling_type, .. } => {
if let HTLCHandlingType::UnknownNextHop { requested_forward_scid } = handling_type {
event = Event::HTLCHandlingFailed {
prev_channel_id,
handling_type: HTLCHandlingType::InvalidForward { requested_forward_scid: *requested_forward_scid },
handling_failure: Some(LocalHTLCFailureReason::UnknownNextPeer.into()),
}
}
}
_ => panic!("HTLCHandlingFailed wrong type")
}

Ok(Some(event))
};
f()
},
Expand Down
22 changes: 13 additions & 9 deletions lightning/src/ln/async_payments_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::blinded_path::message::{MessageContext, OffersContext};
use crate::blinded_path::payment::PaymentContext;
use crate::blinded_path::payment::{AsyncBolt12OfferContext, BlindedPaymentTlvs};
use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
use crate::events::{Event, HTLCDestination, PaymentFailureReason};
use crate::events::{Event, HTLCHandlingType, PaymentFailureReason};
use crate::ln::blinded_payment_tests::{fail_blinded_htlc_backwards, get_blinded_route_parameters};
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
use crate::ln::functional_test_utils::*;
Expand All @@ -20,7 +20,7 @@ use crate::ln::msgs::{
BaseMessageHandler, ChannelMessageHandler, MessageSendEvent, OnionMessageHandler,
};
use crate::ln::offers_tests;
use crate::ln::onion_utils::INVALID_ONION_BLINDING;
use crate::ln::onion_utils::LocalHTLCFailureReason;
use crate::ln::outbound_payment::PendingOutboundPayment;
use crate::ln::outbound_payment::Retry;
use crate::offers::invoice_request::InvoiceRequest;
Expand Down Expand Up @@ -172,14 +172,17 @@ fn invalid_keysend_payment_secret() {
PassAlongPathArgs::new(&nodes[0], &expected_route[0], amt_msat, payment_hash, ev.clone())
.with_payment_secret(invalid_payment_secret)
.with_payment_preimage(keysend_preimage)
.expect_failure(HTLCDestination::FailedPayment { payment_hash });
.expect_failure(HTLCHandlingType::ReceiveFailed { payment_hash });
do_pass_along_path(args);

let updates_2_1 = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
assert_eq!(updates_2_1.update_fail_malformed_htlcs.len(), 1);
let update_malformed = &updates_2_1.update_fail_malformed_htlcs[0];
assert_eq!(update_malformed.sha256_of_onion, [0; 32]);
assert_eq!(update_malformed.failure_code, INVALID_ONION_BLINDING);
assert_eq!(
update_malformed.failure_code,
LocalHTLCFailureReason::InvalidOnionBlinding.failure_code()
);
nodes[1]
.node
.handle_update_fail_malformed_htlc(nodes[2].node.get_our_node_id(), update_malformed);
Expand All @@ -196,7 +199,8 @@ fn invalid_keysend_payment_secret() {
&nodes[0],
payment_hash,
false,
PaymentFailedConditions::new().expected_htlc_error_data(INVALID_ONION_BLINDING, &[0; 32]),
PaymentFailedConditions::new()
.expected_htlc_error_data(LocalHTLCFailureReason::InvalidOnionBlinding, &[0; 32]),
);
}

Expand Down Expand Up @@ -694,7 +698,7 @@ fn amount_doesnt_match_invreq() {
let args = PassAlongPathArgs::new(&nodes[0], route[0], amt_msat, payment_hash, ev)
.with_payment_preimage(keysend_preimage)
.without_claimable_event()
.expect_failure(HTLCDestination::FailedPayment { payment_hash });
.expect_failure(HTLCHandlingType::ReceiveFailed { payment_hash });
do_pass_along_path(args);

// Modify the invoice request stored in our outbounds to be the correct one, to make sure the
Expand Down Expand Up @@ -910,7 +914,7 @@ fn invalid_async_receive_with_retry<F1, F2>(
nodes[2].node.fail_htlc_backwards(&payment_hash);
expect_pending_htlcs_forwardable_conditions(
nodes[2].node.get_and_clear_pending_events(),
&[HTLCDestination::FailedPayment { payment_hash }],
&[HTLCHandlingType::ReceiveFailed { payment_hash }],
);
nodes[2].node.process_pending_htlc_forwards();
check_added_monitors!(nodes[2], 1);
Expand All @@ -930,7 +934,7 @@ fn invalid_async_receive_with_retry<F1, F2>(
let args = PassAlongPathArgs::new(&nodes[0], route[0], amt_msat, payment_hash, ev)
.with_payment_preimage(keysend_preimage)
.without_claimable_event()
.expect_failure(HTLCDestination::FailedPayment { payment_hash });
.expect_failure(HTLCHandlingType::ReceiveFailed { payment_hash });
do_pass_along_path(args);
fail_blinded_htlc_backwards(payment_hash, 1, &[&nodes[0], &nodes[1], &nodes[2]], true);

Expand Down Expand Up @@ -1096,7 +1100,7 @@ fn expired_static_invoice_payment_path() {
let args = PassAlongPathArgs::new(&nodes[0], route[0], amt_msat, payment_hash, ev)
.with_payment_preimage(keysend_preimage)
.without_claimable_event()
.expect_failure(HTLCDestination::FailedPayment { payment_hash });
.expect_failure(HTLCHandlingType::ReceiveFailed { payment_hash });
do_pass_along_path(args);
fail_blinded_htlc_backwards(payment_hash, 1, &[&nodes[0], &nodes[1], &nodes[2]], false);
nodes[2].logger.assert_log_contains(
Expand Down
Loading
Loading