33//! [`BlindedPath`]: crate::blinded_path::BlindedPath 
44
55use  bitcoin:: secp256k1:: { self ,  PublicKey ,  Secp256k1 ,  SecretKey } ; 
6- 
6+ use  core :: convert :: TryFrom ; 
77use  crate :: blinded_path:: BlindedHop ; 
88use  crate :: blinded_path:: utils; 
99use  crate :: io; 
1010use  crate :: ln:: PaymentSecret ; 
1111use  crate :: ln:: features:: BlindedHopFeatures ; 
1212use  crate :: ln:: msgs:: DecodeError ; 
13+ use  crate :: offers:: invoice:: BlindedPayInfo ; 
1314use  crate :: prelude:: * ; 
1415use  crate :: util:: ser:: { Readable ,  Writeable ,  Writer } ; 
1516
@@ -45,6 +46,36 @@ pub enum BlindedPaymentTlvs {
4546	} , 
4647} 
4748
49+ impl  BlindedPaymentTlvs  { 
50+ 	// The fee used to get from the current hop to the next hop in the path. 
51+ 	fn  fee_base_msat ( & self )  -> u32  { 
52+ 		match  self  { 
53+ 			Self :: Forward  {  payment_relay,  .. }  => payment_relay. fee_base_msat , 
54+ 			_ => 0 , 
55+ 		} 
56+ 	} 
57+ 	// The fee used to get from the current hop to the next hop in the path. 
58+ 	fn  fee_proportional_millionths ( & self )  -> u32  { 
59+ 		match  self  { 
60+ 			Self :: Forward  {  payment_relay,  .. }  => payment_relay. fee_proportional_millionths , 
61+ 			_ => 0 , 
62+ 		} 
63+ 	} 
64+ 	// The delta used to get from the current hop to the next hop in the path. 
65+ 	fn  cltv_expiry_delta ( & self )  -> u16  { 
66+ 		match  self  { 
67+ 			Self :: Forward  {  payment_relay,  .. }  => payment_relay. cltv_expiry_delta , 
68+ 			_ => 0 , 
69+ 		} 
70+ 	} 
71+ 	fn  htlc_minimum_msat ( & self )  -> u64  { 
72+ 		match  self  { 
73+ 			Self :: Forward  {  payment_constraints,  .. }  | Self :: Receive  {  payment_constraints,  .. }  =>
74+ 				payment_constraints. htlc_minimum_msat , 
75+ 		} 
76+ 	} 
77+ } 
78+ 
4879/// Parameters for relaying over a given [`BlindedHop`]. 
4980/// 
5081/// [`BlindedHop`]: crate::blinded_path::BlindedHop 
@@ -148,6 +179,34 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
148179	Ok ( blinded_hops) 
149180} 
150181
182+ pub ( super )  fn  compute_payinfo ( 
183+ 	path :  & [ ( PublicKey ,  BlindedPaymentTlvs ) ] 
184+ )  -> Result < BlindedPayInfo ,  ( ) >  { 
185+ 	let  mut  curr_base_fee:  u128  = 0 ; 
186+ 	let  mut  curr_prop_mil:  u128  = 0 ; 
187+ 	for  ( _,  payment_tlvs)  in  path. iter ( ) . rev ( ) . skip ( 1 )  { 
188+ 		let  next_base_fee = payment_tlvs. fee_base_msat ( )  as  u128 ; 
189+ 		let  next_prop_mil = payment_tlvs. fee_proportional_millionths ( )  as  u128 ; 
190+ 		curr_base_fee =
191+ 			( ( next_base_fee *  1_000_000  + ( curr_base_fee *  ( 1_000_000  + next_prop_mil) ) )  + 1_000_000  - 1 ) 
192+ 			 / 1_000_000 ; 
193+ 		curr_prop_mil =
194+ 			( ( ( curr_prop_mil + next_prop_mil)  *  1_000_000  + curr_prop_mil *  next_prop_mil)  + 1_000_000  - 1 ) 
195+ 			 / 1_000_000 ; 
196+ 	} 
197+ 	Ok ( BlindedPayInfo  { 
198+ 		fee_base_msat :  u32:: try_from ( curr_base_fee) . map_err ( |_| ( ) ) ?, 
199+ 		fee_proportional_millionths :  u32:: try_from ( curr_prop_mil) . map_err ( |_| ( ) ) ?, 
200+ 		cltv_expiry_delta :  path. iter ( ) . map ( |( _,  tlvs) | tlvs. cltv_expiry_delta ( ) ) 
201+ 			. try_fold ( 0u16 ,  |acc,  delta| acc. checked_add ( delta) ) . ok_or ( ( ) ) ?, 
202+ 		htlc_minimum_msat :  path. iter ( ) . map ( |( _,  tlvs) | tlvs. htlc_minimum_msat ( ) ) . max ( ) . unwrap_or ( 0 ) , 
203+ 		// TODO: this field isn't present in route blinding encrypted data 
204+ 		htlc_maximum_msat :  21_000_000  *  100_000_000  *  1_000 ,  // Total bitcoin supply 
205+ 		// TODO: when there are blinded hop features, take the subset of them here 
206+ 		features :  BlindedHopFeatures :: empty ( ) , 
207+ 	} ) 
208+ } 
209+ 
151210impl_writeable_msg ! ( PaymentRelay ,  { 
152211	cltv_expiry_delta, 
153212	fee_proportional_millionths, 
@@ -158,3 +217,72 @@ impl_writeable_msg!(PaymentConstraints, {
158217	max_cltv_expiry, 
159218	htlc_minimum_msat
160219} ,  { } ) ; 
220+ 
221+ #[ cfg( test) ]  
222+ mod  tests { 
223+ 	use  bitcoin:: secp256k1:: PublicKey ; 
224+ 	use  crate :: blinded_path:: payment:: { BlindedPaymentTlvs ,  PaymentConstraints ,  PaymentRelay } ; 
225+ 	use  crate :: ln:: PaymentSecret ; 
226+ 	use  crate :: ln:: features:: BlindedHopFeatures ; 
227+ 
228+ 	#[ test]  
229+ 	fn  compute_payinfo ( )  { 
230+ 		// Taken from the spec example for aggregating blinded payment info. 
231+ 		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
232+ 		let  path = vec ! [ ( dummy_pk,  BlindedPaymentTlvs :: Forward  { 
233+ 			short_channel_id:  0 , 
234+ 			payment_relay:  PaymentRelay  { 
235+ 				cltv_expiry_delta:  144 , 
236+ 				fee_proportional_millionths:  500 , 
237+ 				fee_base_msat:  100 , 
238+ 			} , 
239+ 			payment_constraints:  PaymentConstraints  { 
240+ 				max_cltv_expiry:  0 , 
241+ 				htlc_minimum_msat:  100 , 
242+ 			} , 
243+ 			features:  BlindedHopFeatures :: empty( ) , 
244+ 		} ) ,  ( dummy_pk,  BlindedPaymentTlvs :: Forward  { 
245+ 			short_channel_id:  0 , 
246+ 			payment_relay:  PaymentRelay  { 
247+ 				cltv_expiry_delta:  144 , 
248+ 				fee_proportional_millionths:  500 , 
249+ 				fee_base_msat:  100 , 
250+ 			} , 
251+ 			payment_constraints:  PaymentConstraints  { 
252+ 				max_cltv_expiry:  0 , 
253+ 				htlc_minimum_msat:  1_000 , 
254+ 			} , 
255+ 			features:  BlindedHopFeatures :: empty( ) , 
256+ 		} ) ,  ( dummy_pk,  BlindedPaymentTlvs :: Receive  { 
257+ 			payment_secret:  PaymentSecret ( [ 0 ;  32 ] ) , 
258+ 			payment_constraints:  PaymentConstraints  { 
259+ 				max_cltv_expiry:  0 , 
260+ 				htlc_minimum_msat:  1 , 
261+ 			} , 
262+ 			features:  BlindedHopFeatures :: empty( ) , 
263+ 		} ) ] ; 
264+ 		let  blinded_payinfo = super :: compute_payinfo ( & path[ ..] ) . unwrap ( ) ; 
265+ 		assert_eq ! ( blinded_payinfo. fee_base_msat,  201 ) ; 
266+ 		assert_eq ! ( blinded_payinfo. fee_proportional_millionths,  1001 ) ; 
267+ 		assert_eq ! ( blinded_payinfo. cltv_expiry_delta,  288 ) ; 
268+ 		assert_eq ! ( blinded_payinfo. htlc_minimum_msat,  1_000 ) ; 
269+ 	} 
270+ 
271+ 	#[ test]  
272+ 	fn  compute_payinfo_1_hop ( )  { 
273+ 		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
274+ 		let  path = vec ! [ ( dummy_pk,  BlindedPaymentTlvs :: Receive  { 
275+ 			payment_secret:  PaymentSecret ( [ 0 ;  32 ] ) , 
276+ 			payment_constraints:  PaymentConstraints  { 
277+ 				max_cltv_expiry:  0 , 
278+ 				htlc_minimum_msat:  1 , 
279+ 			} , 
280+ 			features:  BlindedHopFeatures :: empty( ) , 
281+ 		} ) ] ; 
282+ 		let  blinded_payinfo = super :: compute_payinfo ( & path[ ..] ) . unwrap ( ) ; 
283+ 		assert_eq ! ( blinded_payinfo. fee_base_msat,  0 ) ; 
284+ 		assert_eq ! ( blinded_payinfo. fee_proportional_millionths,  0 ) ; 
285+ 		assert_eq ! ( blinded_payinfo. cltv_expiry_delta,  0 ) ; 
286+ 		assert_eq ! ( blinded_payinfo. htlc_minimum_msat,  1 ) ; 
287+ 	} 
288+ } 
0 commit comments