Skip to content

Commit 5583b98

Browse files
committed
Trampoline payload construction method
1 parent de9f85a commit 5583b98

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
@@ -14,7 +14,7 @@ use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
1414
use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
1515
use crate::ln::msgs;
1616
use crate::routing::gossip::NetworkUpdate;
17-
use crate::routing::router::{Path, RouteHop, RouteParameters};
17+
use crate::routing::router::{Path, RouteHop, RouteParameters, TrampolineHop};
1818
use crate::sign::NodeSigner;
1919
use crate::types::features::{ChannelFeatures, NodeFeatures};
2020
use crate::types::payment::{PaymentHash, PaymentPreimage};
@@ -175,6 +175,122 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(
175175
Ok(res)
176176
}
177177

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

0 commit comments

Comments
 (0)