@@ -15,7 +15,7 @@ use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
15
15
use crate :: ln:: msgs;
16
16
use crate :: offers:: invoice_request:: InvoiceRequest ;
17
17
use crate :: routing:: gossip:: NetworkUpdate ;
18
- use crate :: routing:: router:: { Path , RouteHop , RouteParameters } ;
18
+ use crate :: routing:: router:: { Path , RouteHop , RouteParameters , TrampolineHop } ;
19
19
use crate :: sign:: NodeSigner ;
20
20
use crate :: types:: features:: { ChannelFeatures , NodeFeatures } ;
21
21
use crate :: types:: payment:: { PaymentHash , PaymentPreimage } ;
@@ -176,6 +176,122 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(
176
176
Ok ( res)
177
177
}
178
178
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
+
179
295
/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
180
296
pub ( super ) fn build_onion_payloads < ' a > (
181
297
path : & ' a Path , total_msat : u64 , recipient_onion : & ' a RecipientOnionFields ,
0 commit comments