Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 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
a54a4d9
ln: store incoming mpp data in PendingHTLCRouting
carlaKC Jan 27, 2026
98f00cb
f: don't rebind incoming_multipath_data in destructure
carlaKC May 12, 2026
7d012ae
ln: use total_msat to calculate the amount for our next trampoline
carlaKC Feb 25, 2026
c266716
f: clarify total_msat in check_blinded_forward for MPP
carlaKC May 12, 2026
bbeec58
ln: use outer onion values in PendingHTLCInfo for trampoline
carlaKC May 12, 2026
6b50ad6
ln: store next trampoline amount and cltv in PendingHTLCRouting
carlaKC May 12, 2026
75b316b
ln: use outer onion values for trampoline NextPacketDetails
carlaKC Feb 12, 2026
7b17d4f
ln: add awaiting_trampoline_forwards to accumulate inbound MPP
carlaKC Mar 30, 2026
05d9791
ln: add trampoline mpp accumulation with rejection on completion
carlaKC Apr 10, 2026
4b26edc
f: use doc comment for handle_trampoline_htlc
carlaKC May 12, 2026
dd81ad9
f: use compute_fees and document per-trampoline-hop fee
carlaKC May 12, 2026
4bac8b0
f: add trampoline mpp accumulation with rejection on completion
carlaKC May 12, 2026
164f203
ln: double encrypt errors received from downstream failures
carlaKC Mar 12, 2026
fd5dc1e
ln: handle DecodedOnionFailure for local trampoline failures
carlaKC Mar 12, 2026
90a0a4b
f: replace decoded_onion_failure macro with closure
carlaKC May 12, 2026
4e53faf
ln: process added trampoline htlcs with CLTV validation in tests
carlaKC Feb 25, 2026
ff4f993
ln/test: add test coverage for MPP trampoline
carlaKC Mar 17, 2026
5ba7094
ln/test: add tests for mpp accumulation of trampoline forwards
carlaKC Apr 27, 2026
e48b8f8
f: dispatch trampoline MPP via public send_payment_with_route
carlaKC May 12, 2026
473242c
ln: add trampoline forward info to PendingOutboundPayment::Retryable
carlaKC Jan 16, 2026
36efdc5
ln: thread trampoline routing information through payment methods
carlaKC Feb 10, 2026
2f175a9
ln: add blinding point to new_trampoline_entry
carlaKC Feb 10, 2026
3bba5f2
ln function to build trampoline forwarding onions
carlaKC Jan 28, 2026
c14a469
ln: support trampoline in send_payment_along_path
carlaKC May 1, 2026
6fe311c
ln: add send trampoline payment functionality
carlaKC Jan 16, 2026
c9755b5
[wip] Clauded error handling
carlaKC May 4, 2026
6d03b12
[wip] ln: add trampoline htlc failure logic to outbound payments
carlaKC Mar 17, 2026
87712a8
ln: add claim_trampoline_forward to mark trampoline complete
carlaKC Feb 18, 2026
47bdb91
ln: handle trampoline payments in finalize_claims
carlaKC Feb 18, 2026
5982a3f
ln: only fail trampoline payments backwards when payment state ready
carlaKC Mar 12, 2026
6fb748f
ln: claim trampoline payment on completion
carlaKC Feb 18, 2026
b760884
ln: use correct blinding point for trampoline payload decodes
carlaKC Feb 2, 2026
581f953
ln: allow reading HTLCSource::TrampolineForward
carlaKC Feb 24, 2026
ff63248
ln: add trampoline payment dispatch after inbound accumulation
carlaKC May 12, 2026
f1bb42a
ln/test: only use replacement onion in trampoline tests when needed
carlaKC Feb 10, 2026
37fd8c4
[deleteme]: remove assertion that fails on unblinded test
carlaKC Feb 3, 2026
ccad557
[wip]ln: pass trampoline secret to construct_pending_htlc_fail_msg
carlaKC Mar 17, 2026
148ef00
[wip]: forwarding tests with messy replacement onion code
carlaKC May 12, 2026
0c35faa
[wip]: track already_forwarded_htlcs by full HTLCSource
carlaKC Mar 4, 2026
499ab97
[wip]: support muti-out sources in inbound_forwarded_htlcs
carlaKC Mar 4, 2026
930b108
[wip]: pass full HTLCSource through in committed_outbound_htlc_sources
carlaKC Mar 4, 2026
e1bc228
[wip] dedup trampoline forwards with failed_htlcs
carlaKC Mar 4, 2026
b92f291
[wip] persist trampoline information in InboundUpdateAdd
carlaKC Mar 4, 2026
9f53e04
[wip] return trampoline forwards in inbound_forwarded_htlcs
carlaKC Mar 4, 2026
0149f8d
[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