Skip to content
139 changes: 117 additions & 22 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use crate::ln::channel_state::{
use crate::ln::channelmanager::{
self, BlindedFailure, ChannelReadyOrder, FundingConfirmedMessage, HTLCFailureMsg,
HTLCPreviousHopData, HTLCSource, OpenChannelMessage, PaymentClaimDetails, PendingHTLCInfo,
PendingHTLCStatus, RAACommitmentOrder, SentHTLCId, BREAKDOWN_TIMEOUT,
PendingHTLCStatus, RAACommitmentOrder, SentHTLCId, TrampolineDispatch, BREAKDOWN_TIMEOUT,
MAX_LOCAL_BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA,
};
use crate::ln::funding::{FundingContribution, FundingTemplate, FundingTxInput};
Expand Down Expand Up @@ -356,6 +356,16 @@ 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)>,
incoming_trampoline_shared_secret: [u8; 32],
},
/// This HTLC was received pre-LDK 0.3, before we started persisting the onion for inbound
/// committed HTLCs.
Legacy,
Expand All @@ -373,6 +383,11 @@ 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),
(4, incoming_trampoline_shared_secret, required),
},
);

impl_writeable_for_vec!(&InboundUpdateAdd);
Expand Down Expand Up @@ -1193,7 +1208,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 @@ -7451,7 +7466,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 @@ -7500,7 +7515,38 @@ 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,
incoming_trampoline_shared_secret,
},
} => {
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(),
incoming_trampoline_shared_secret:
*incoming_trampoline_shared_secret,
outbound_payment: Some(dispatch.clone()),
},
*hop,
)
})
.collect();

Some((htlc.payment_hash, trampoline_sources))
},
_ => None,
})
Expand All @@ -7511,21 +7557,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 @@ -7550,20 +7596,69 @@ 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,
incoming_trampoline_shared_secret,
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"))],
incoming_trampoline_shared_secret:
*incoming_trampoline_shared_secret,
}
},
_ => {
debug_assert!(
false,
"outbound route should not prune inbound htlc"
);
return;
},
};
return;
},
_ => {},
}
}
}
Expand Down Expand Up @@ -9184,8 +9279,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