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
8925925
ln/refactor: use amount_msat and counterparty_skimmed_fee_msat vars
carlaKC Apr 14, 2026
6168bed
ln: remove incoming trampoline secret from HTLCSource
carlaKC Mar 12, 2026
b9c3601
ln: store incoming mpp data in PendingHTLCRouting
carlaKC Jan 27, 2026
d2ece0d
ln: use total_msat to calculate the amount for our next trampoline
carlaKC Feb 25, 2026
e253953
ln: use outer onion values in PendingHTLCInfo for trampoline
carlaKC May 12, 2026
3d0309f
ln: store next trampoline amount and cltv in PendingHTLCRouting
carlaKC May 12, 2026
0cae5b5
ln: use outer onion values for trampoline NextPacketDetails
carlaKC Feb 12, 2026
07d3248
ln: add awaiting_trampoline_forwards to accumulate inbound MPP
carlaKC Mar 30, 2026
f25382b
ln: add trampoline mpp accumulation with rejection on completion
carlaKC Apr 10, 2026
163da0a
ln: double encrypt errors received from downstream failures
carlaKC Mar 12, 2026
8bc2e85
ln: handle DecodedOnionFailure for local trampoline failures
carlaKC Mar 12, 2026
764458a
ln: process added trampoline htlcs with CLTV validation in tests
carlaKC Feb 25, 2026
7fce959
ln/test: add test coverage for MPP trampoline
carlaKC Mar 17, 2026
33a2e6e
ln/test: add tests for mpp accumulation of trampoline forwards
carlaKC Apr 27, 2026
a72e72b
ln: add trampoline forward info to PendingOutboundPayment::Retryable
carlaKC Jan 16, 2026
e561664
ln: thread trampoline routing information through payment methods
carlaKC Feb 10, 2026
6f29fa4
ln: add blinding point to new_trampoline_entry
carlaKC Feb 10, 2026
10ea6b6
ln function to build trampoline forwarding onions
carlaKC Jan 28, 2026
aae0d08
ln: support trampoline in send_payment_along_path
carlaKC May 1, 2026
ca34a50
ln: add send trampoline payment functionality
carlaKC Jan 16, 2026
326dd57
[wip] Clauded error handling
carlaKC May 4, 2026
8d5da62
[wip] ln: add trampoline htlc failure logic to outbound payments
carlaKC Mar 17, 2026
0a6055d
ln: add claim_trampoline_forward to mark trampoline complete
carlaKC Feb 18, 2026
d3a8c79
ln: handle trampoline payments in finalize_claims
carlaKC Feb 18, 2026
50cf716
ln: only fail trampoline payments backwards when payment state ready
carlaKC Mar 12, 2026
8df6640
ln: claim trampoline payment on completion
carlaKC Feb 18, 2026
b65c990
ln: use correct blinding point for trampoline payload decodes
carlaKC Feb 2, 2026
3839863
ln: allow reading HTLCSource::TrampolineForward
carlaKC Feb 24, 2026
141bbcf
ln: add trampoline payment dispatch after inbound accumulation
carlaKC May 12, 2026
12061ee
ln/test: only use replacement onion in trampoline tests when needed
carlaKC Feb 10, 2026
be1bf39
[deleteme]: remove assertion that fails on unblinded test
carlaKC Feb 3, 2026
82488ec
[wip]ln: pass trampoline secret to construct_pending_htlc_fail_msg
carlaKC Mar 17, 2026
54129f0
[wip]: forwarding tests with messy replacement onion code
carlaKC May 12, 2026
71e0612
[wip]: track already_forwarded_htlcs by full HTLCSource
carlaKC Mar 4, 2026
6526d8f
[wip]: support muti-out sources in inbound_forwarded_htlcs
carlaKC Mar 4, 2026
4fb3f18
[wip]: pass full HTLCSource through in committed_outbound_htlc_sources
carlaKC Mar 4, 2026
5682900
[wip] dedup trampoline forwards with failed_htlcs
carlaKC Mar 4, 2026
cf96b2c
[wip] persist trampoline information in InboundUpdateAdd
carlaKC Mar 4, 2026
6d6841b
[wip] return trampoline forwards in inbound_forwarded_htlcs
carlaKC Mar 4, 2026
2044b32
[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
837 changes: 619 additions & 218 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 @@ -1194,7 +1207,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 @@ -7977,7 +7990,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 @@ -8026,7 +8039,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 @@ -8037,21 +8075,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 @@ -8076,20 +8114,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 @@ -9777,8 +9861,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