Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e3e8836
ln/refactor: use amount_msat and counterparty_skimmed_fee_msat vars
carlaKC Apr 14, 2026
b2928cd
ln: remove incoming trampoline secret from HTLCSource
carlaKC Mar 12, 2026
5d0007c
ln: store incoming mpp data in PendingHTLCRouting
carlaKC Jan 27, 2026
f9cb46e
ln: use total_msat to calculate the amount for our next trampoline
carlaKC Feb 25, 2026
c482fd5
ln: use outer onion values in PendingHTLCInfo for trampoline
carlaKC Feb 25, 2026
08247a7
ln: store next trampoline amount and cltv in PendingHTLCRouting
carlaKC Feb 25, 2026
9684e57
ln: use outer onion values for trampoline NextPacketDetails
carlaKC Feb 12, 2026
ade0ef4
ln: add awaiting_trampoline_forwards to accumulate inbound MPP
carlaKC Mar 30, 2026
b370a26
ln: add trampoline mpp accumulation with rejection on completion
carlaKC Apr 10, 2026
6f538dc
ln: double encrypt errors received from downstream failures
carlaKC Mar 12, 2026
99d0eed
ln: handle DecodedOnionFailure for local trampoline failures
carlaKC Mar 12, 2026
5c5df21
ln: process added trampoline htlcs with CLTV validation in tests
carlaKC Feb 25, 2026
5397c1f
ln/test: add test coverage for MPP trampoline
carlaKC Mar 17, 2026
7e428bd
ln/test: add tests for mpp accumulation of trampoline forwards
carlaKC Apr 27, 2026
18de4be
ln: add trampoline forward info to PendingOutboundPayment::Retryable
carlaKC Jan 16, 2026
b020421
ln: thread trampoline routing information through payment methods
carlaKC Feb 10, 2026
dd34630
ln: add blinding point to new_trampoline_entry
carlaKC Feb 10, 2026
4c1bf98
ln function to build trampoline forwarding onions
carlaKC Jan 28, 2026
d9598b3
ln: support trampoline in send_payment_along_path
carlaKC Feb 11, 2026
5a72ca4
ln: add send trampoline payment functionality
carlaKC Jan 16, 2026
7fdc77b
ln: surface trampoline error packet it could not decrypt
carlaKC Mar 17, 2026
a3b8d4a
[wip] ln: add trampoline htlc failure logic to outbound payments
carlaKC Mar 17, 2026
a8dcb81
ln: add claim_trampoline_forward to mark trampoline complete
carlaKC Feb 18, 2026
badc25a
ln: handle trampoline payments in finalize_claims
carlaKC Feb 18, 2026
94886b6
ln: only fail trampoline payments backwards when payment state ready
carlaKC Mar 12, 2026
33c6435
ln: claim trampoline payment on completion
carlaKC Feb 18, 2026
437ad28
ln: use correct blinding point for trampoline payload decodes
carlaKC Feb 2, 2026
a194e39
ln: allow reading HTLCSource::TrampolineForward
carlaKC Feb 24, 2026
d73b672
ln: add trampoline payment dispatch after inbound accumulation
carlaKC Apr 27, 2026
fa84923
ln/test: only use replacement onion in trampoline tests when needed
carlaKC Feb 10, 2026
17bcdcc
[deleteme]: remove assertion that fails on unblinded test
carlaKC Feb 3, 2026
1677b12
[wip]ln: pass trampoline secret to construct_pending_htlc_fail_msg
carlaKC Mar 17, 2026
4732c9a
[wip]: forwarding tests with messy replacement onion code
carlaKC Mar 17, 2026
d80e959
[wip]: track already_forwarded_htlcs by full HTLCSource
carlaKC Mar 4, 2026
311e846
[wip]: support muti-out sources in inbound_forwarded_htlcs
carlaKC Mar 4, 2026
8ab2a55
[wip]: pass full HTLCSource through in committed_outbound_htlc_sources
carlaKC Mar 4, 2026
fe647a1
[wip] dedup trampoline forwards with failed_htlcs
carlaKC Mar 4, 2026
32d29d4
[wip] persist trampoline information in InboundUpdateAdd
carlaKC Mar 4, 2026
d7befa3
[wip] return trampoline forwards in inbound_forwarded_htlcs
carlaKC Mar 4, 2026
9d4fc86
[wip]: return trampoline forwards from outbound_htlc_forwards
carlaKC Mar 4, 2026
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
847 changes: 630 additions & 217 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

130 changes: 107 additions & 23 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ use crate::ln::channel_state::{
use crate::ln::channelmanager::{
self, BlindedFailure, ChannelReadyOrder, FundingConfirmedMessage, HTLCFailureMsg,
HTLCPreviousHopData, HTLCSource, OpenChannelMessage, PaymentClaimDetails, PendingHTLCInfo,
PendingHTLCStatus, RAACommitmentOrder, SentHTLCId, TrustedChannelFeatures, BREAKDOWN_TIMEOUT,
MAX_LOCAL_BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA,
PendingHTLCStatus, RAACommitmentOrder, SentHTLCId, TrampolineDispatch, TrustedChannelFeatures,
BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA,
};
use crate::ln::funding::{
FeeRateAdjustmentError, FundingContribution, FundingTemplate, FundingTxInput, PriorContribution,
Expand Down Expand Up @@ -357,6 +357,15 @@ enum InboundUpdateAdd {
blinded_failure: Option<BlindedFailure>,
outbound_hop: OutboundHop,
},
/// This inbound HTLC is a forward that was irrevocably committed to outbound edge(s) as part
/// of a trampoline forward, allowing its onion to be pruned and no longer persisted.
///
/// Contains data that is useful if we need to fail or claim this HTLC backwards after a
/// restart and it's missing in the outbound edge.
TrampolineForwarded {
previous_hop_data: Vec<HTLCPreviousHopData>,
outbound_hops: Vec<(OutboundHop, TrampolineDispatch)>,
},
/// This HTLC was received pre-LDK 0.3, before we started persisting the onion for inbound
/// committed HTLCs.
Legacy,
Expand All @@ -374,6 +383,10 @@ impl_writeable_tlv_based_enum_upgradable!(InboundUpdateAdd,
(6, trampoline_shared_secret, option),
(8, blinded_failure, option),
},
(6, TrampolineForwarded) => {
(0, previous_hop_data, required_vec),
(2, outbound_hops, required_vec),
},
);

impl_writeable_for_vec!(&InboundUpdateAdd);
Expand Down Expand Up @@ -1197,7 +1210,7 @@ pub(super) struct MonitorRestoreUpdates {
/// The sources of outbound HTLCs that were forwarded and irrevocably committed on this channel
/// (the outbound edge), along with their outbound amounts. Useful to store in the inbound HTLC
/// to ensure it gets resolved.
pub committed_outbound_htlc_sources: Vec<(HTLCPreviousHopData, u64)>,
pub committed_outbound_htlc_sources: Vec<(HTLCSource, u64)>,
}

/// The return value of `signer_maybe_unblocked`
Expand Down Expand Up @@ -7988,7 +8001,7 @@ where
/// when reconstructing the set of pending HTLCs when deserializing the `ChannelManager`.
pub(super) fn inbound_forwarded_htlcs(
&self,
) -> impl Iterator<Item = (PaymentHash, HTLCPreviousHopData, OutboundHop)> + '_ {
) -> impl Iterator<Item = (PaymentHash, Vec<(HTLCSource, OutboundHop)>)> + '_ {
// We don't want to return an HTLC as needing processing if it already has a resolution that's
// pending in the holding cell.
let htlc_resolution_in_holding_cell = |id: u64| -> bool {
Expand Down Expand Up @@ -8037,7 +8050,32 @@ where
counterparty_node_id: Some(counterparty_node_id),
cltv_expiry: Some(htlc.cltv_expiry),
};
Some((htlc.payment_hash, prev_hop_data, *outbound_hop))
Some((
htlc.payment_hash,
vec![(HTLCSource::PreviousHopData(prev_hop_data), *outbound_hop)],
))
},
InboundHTLCState::Committed {
update_add_htlc:
InboundUpdateAdd::TrampolineForwarded { previous_hop_data, outbound_hops },
} => {
if htlc_resolution_in_holding_cell(htlc.htlc_id) {
return None;
}
let trampoline_sources: Vec<(HTLCSource, OutboundHop)> = outbound_hops
.iter()
.map(|(hop, dispatch)| {
(
HTLCSource::TrampolineForward {
previous_hop_data: previous_hop_data.clone(),
outbound_payment: Some(dispatch.clone()),
},
*hop,
)
})
.collect();

Some((htlc.payment_hash, trampoline_sources))
},
_ => None,
})
Expand All @@ -8048,21 +8086,21 @@ where
/// present in the outbound edge, or else we'll double-forward.
pub(super) fn outbound_htlc_forwards(
&self,
) -> impl Iterator<Item = (PaymentHash, HTLCPreviousHopData)> + '_ {
) -> impl Iterator<Item = (PaymentHash, HTLCSource)> + '_ {
let holding_cell_outbounds =
self.context.holding_cell_htlc_updates.iter().filter_map(|htlc| match htlc {
HTLCUpdateAwaitingACK::AddHTLC { source, payment_hash, .. } => match source {
HTLCSource::PreviousHopData(prev_hop_data) => {
Some((*payment_hash, prev_hop_data.clone()))
HTLCSource::PreviousHopData(_) | HTLCSource::TrampolineForward { .. } => {
Some((*payment_hash, source.clone()))
},
_ => None,
},
_ => None,
});
let committed_outbounds =
self.context.pending_outbound_htlcs.iter().filter_map(|htlc| match &htlc.source {
HTLCSource::PreviousHopData(prev_hop_data) => {
Some((htlc.payment_hash, prev_hop_data.clone()))
HTLCSource::PreviousHopData(_) | HTLCSource::TrampolineForward { .. } => {
Some((htlc.payment_hash, htlc.source.clone()))
},
_ => None,
});
Expand All @@ -8087,20 +8125,66 @@ where
/// This inbound HTLC was irrevocably forwarded to the outbound edge, so we no longer need to
/// persist its onion.
pub(super) fn prune_inbound_htlc_onion(
&mut self, htlc_id: u64, prev_hop_data: &HTLCPreviousHopData,
outbound_hop_data: OutboundHop,
&mut self, htlc_id: u64, htlc_source: &HTLCSource, outbound_hop_data: OutboundHop,
) {
for htlc in self.context.pending_inbound_htlcs.iter_mut() {
// TODO: all these returns are super mif
if htlc.htlc_id == htlc_id {
if let InboundHTLCState::Committed { ref mut update_add_htlc } = htlc.state {
*update_add_htlc = InboundUpdateAdd::Forwarded {
incoming_packet_shared_secret: prev_hop_data.incoming_packet_shared_secret,
phantom_shared_secret: prev_hop_data.phantom_shared_secret,
trampoline_shared_secret: prev_hop_data.trampoline_shared_secret,
blinded_failure: prev_hop_data.blinded_failure,
outbound_hop: outbound_hop_data,
};
return;
match &mut htlc.state {
InboundHTLCState::Committed {
update_add_htlc: InboundUpdateAdd::TrampolineForwarded { outbound_hops, .. },
} => {
if let HTLCSource::TrampolineForward {
outbound_payment: Some(trampoline_dispatch),
..
} = htlc_source
{
if !outbound_hops.iter().any(|(_, dispatch)| {
dispatch.session_priv == trampoline_dispatch.session_priv
}) {
outbound_hops.push((outbound_hop_data, trampoline_dispatch.clone()))
}
return;
} else {
debug_assert!(false, "prune inbound onion called for trampoline with no dispatch or on non-trampoline inbound");
return;
}
},
InboundHTLCState::Committed { update_add_htlc } => {
*update_add_htlc = match htlc_source {
HTLCSource::PreviousHopData(prev_hop_data) => {
InboundUpdateAdd::Forwarded {
incoming_packet_shared_secret: prev_hop_data
.incoming_packet_shared_secret,
phantom_shared_secret: prev_hop_data.phantom_shared_secret,
trampoline_shared_secret: prev_hop_data
.trampoline_shared_secret,
blinded_failure: prev_hop_data.blinded_failure,
outbound_hop: outbound_hop_data,
}
},
HTLCSource::TrampolineForward {
previous_hop_data,
outbound_payment,
} => {
InboundUpdateAdd::TrampolineForwarded {
previous_hop_data: previous_hop_data.to_vec(),
outbound_hops: vec![(outbound_hop_data, outbound_payment
.clone() // TODO: no clone / expect
.expect("trampoline shouldn't be pruned with no payment data"))],
}
},
_ => {
debug_assert!(
false,
"outbound route should not prune inbound htlc"
);
return;
},
};
return;
},
_ => {},
}
}
}
Expand Down Expand Up @@ -9787,8 +9871,8 @@ where
mem::swap(&mut pending_update_adds, &mut self.context.monitor_pending_update_adds);
let committed_outbound_htlc_sources = self.context.pending_outbound_htlcs.iter().filter_map(|htlc| {
if let &OutboundHTLCState::LocalAnnounced(_) = &htlc.state {
if let HTLCSource::PreviousHopData(prev_hop_data) = &htlc.source {
return Some((prev_hop_data.clone(), htlc.amount_msat))
if let HTLCSource::PreviousHopData(_) | HTLCSource::TrampolineForward { .. } = &htlc.source {
return Some((htlc.source.clone(), htlc.amount_msat))
}
}
None
Expand Down
Loading
Loading