Skip to content

Commit 33804c3

Browse files
committed
Trampoline payload construction method
1 parent a85aca9 commit 33804c3

File tree

1 file changed

+117
-1
lines changed

1 file changed

+117
-1
lines changed

lightning/src/ln/onion_utils.rs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
1515
use crate::ln::msgs;
1616
use crate::offers::invoice_request::InvoiceRequest;
1717
use crate::routing::gossip::NetworkUpdate;
18-
use crate::routing::router::{Path, RouteHop, RouteParameters};
18+
use crate::routing::router::{Path, RouteHop, RouteParameters, TrampolineHop};
1919
use crate::sign::NodeSigner;
2020
use crate::types::features::{ChannelFeatures, NodeFeatures};
2121
use crate::types::payment::{PaymentHash, PaymentPreimage};
@@ -176,6 +176,122 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(
176176
Ok(res)
177177
}
178178

179+
fn build_trampoline_onion_payloads<'a>(
180+
path: &'a Path, total_msat: u64, recipient_onion: &'a RecipientOnionFields,
181+
starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>,
182+
) -> Result<(Vec<msgs::OutboundTrampolinePayload<'a>>, u64, u32), APIError> {
183+
let mut res: Vec<msgs::OutboundTrampolinePayload> = Vec::with_capacity(
184+
path.trampoline_hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()),
185+
);
186+
let blinded_tail = path.blinded_tail.as_ref().ok_or(APIError::InvalidRoute {
187+
err: "Routes using Trampoline must terminate blindly.".to_string(),
188+
})?;
189+
let blinded_tail_with_hop_iter = BlindedTailHopIter {
190+
hops: blinded_tail.hops.iter(),
191+
blinding_point: blinded_tail.blinding_point,
192+
final_value_msat: blinded_tail.final_value_msat,
193+
excess_final_cltv_expiry_delta: blinded_tail.excess_final_cltv_expiry_delta,
194+
};
195+
196+
let (value_msat, cltv) = build_trampoline_onion_payloads_callback(
197+
path.trampoline_hops.iter(),
198+
blinded_tail_with_hop_iter,
199+
total_msat,
200+
recipient_onion,
201+
starting_htlc_offset,
202+
keysend_preimage,
203+
|action, payload| match action {
204+
PayloadCallbackAction::PushBack => res.push(payload),
205+
PayloadCallbackAction::PushFront => res.insert(0, payload),
206+
},
207+
)?;
208+
Ok((res, value_msat, cltv))
209+
}
210+
211+
fn build_trampoline_onion_payloads_callback<'a, H, B, F>(
212+
hops: H, mut blinded_tail: BlindedTailHopIter<'a, B>, total_msat: u64,
213+
recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32,
214+
keysend_preimage: &Option<PaymentPreimage>, mut callback: F,
215+
) -> Result<(u64, u32), APIError>
216+
where
217+
H: DoubleEndedIterator<Item = &'a TrampolineHop>,
218+
B: ExactSizeIterator<Item = &'a BlindedHop>,
219+
F: FnMut(PayloadCallbackAction, msgs::OutboundTrampolinePayload<'a>),
220+
{
221+
let mut cur_value_msat = 0u64;
222+
let mut cur_cltv = starting_htlc_offset;
223+
let mut last_node_id = None;
224+
225+
// appeasing the borrow checker
226+
let mut blinded_tail_option = Some(blinded_tail);
227+
228+
for (idx, hop) in hops.rev().enumerate() {
229+
// First hop gets special values so that it can check, on receipt, that everything is
230+
// exactly as it should be (and the next hop isn't trying to probe to find out if we're
231+
// the intended recipient).
232+
let value_msat = if cur_value_msat == 0 { hop.fee_msat } else { cur_value_msat };
233+
let cltv = if cur_cltv == starting_htlc_offset {
234+
hop.cltv_expiry_delta + starting_htlc_offset
235+
} else {
236+
cur_cltv
237+
};
238+
if idx == 0 {
239+
if let Some(BlindedTailHopIter {
240+
blinding_point,
241+
hops,
242+
final_value_msat,
243+
excess_final_cltv_expiry_delta,
244+
}) = blinded_tail_option.take()
245+
{
246+
let mut blinding_point = Some(blinding_point);
247+
let hops_len = hops.len();
248+
for (i, blinded_hop) in hops.enumerate() {
249+
if i == hops_len - 1 {
250+
cur_value_msat += final_value_msat;
251+
callback(
252+
PayloadCallbackAction::PushBack,
253+
msgs::OutboundTrampolinePayload::BlindedReceive {
254+
sender_intended_htlc_amt_msat: final_value_msat,
255+
total_msat,
256+
cltv_expiry_height: cur_cltv + excess_final_cltv_expiry_delta,
257+
encrypted_tlvs: &blinded_hop.encrypted_payload,
258+
intro_node_blinding_point: blinding_point.take(),
259+
keysend_preimage: *keysend_preimage,
260+
custom_tlvs: &recipient_onion.custom_tlvs,
261+
},
262+
);
263+
} else {
264+
callback(
265+
PayloadCallbackAction::PushBack,
266+
msgs::OutboundTrampolinePayload::BlindedForward {
267+
encrypted_tlvs: &blinded_hop.encrypted_payload,
268+
intro_node_blinding_point: blinding_point.take(),
269+
},
270+
);
271+
}
272+
}
273+
}
274+
} else {
275+
let payload = msgs::OutboundTrampolinePayload::Forward {
276+
outgoing_node_id: last_node_id.unwrap(),
277+
amt_to_forward: value_msat,
278+
outgoing_cltv_value: cltv,
279+
};
280+
callback(PayloadCallbackAction::PushFront, payload);
281+
}
282+
cur_value_msat += hop.fee_msat;
283+
if cur_value_msat >= 21000000 * 100000000 * 1000 {
284+
return Err(APIError::InvalidRoute { err: "Channel fees overflowed?".to_owned() });
285+
}
286+
cur_cltv += hop.cltv_expiry_delta as u32;
287+
if cur_cltv >= 500000000 {
288+
return Err(APIError::InvalidRoute { err: "Channel CLTV overflowed?".to_owned() });
289+
}
290+
last_node_id = Some(hop.pubkey);
291+
}
292+
Ok((cur_value_msat, cur_cltv))
293+
}
294+
179295
/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
180296
pub(super) fn build_onion_payloads<'a>(
181297
path: &'a Path, total_msat: u64, recipient_onion: &'a RecipientOnionFields,

0 commit comments

Comments
 (0)