@@ -14,7 +14,7 @@ use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
1414use crate :: ln:: channelmanager:: { HTLCSource , RecipientOnionFields } ;
1515use crate :: ln:: msgs;
1616use crate :: routing:: gossip:: NetworkUpdate ;
17- use crate :: routing:: router:: { Path , RouteHop , RouteParameters } ;
17+ use crate :: routing:: router:: { Path , RouteHop , RouteParameters , TrampolineHop } ;
1818use crate :: sign:: NodeSigner ;
1919use crate :: types:: features:: { ChannelFeatures , NodeFeatures } ;
2020use 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.
179295pub ( super ) fn build_onion_payloads < ' a > (
180296 path : & ' a Path , total_msat : u64 , recipient_onion : & ' a RecipientOnionFields ,
0 commit comments