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