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,33 @@ pub enum BlindedPaymentTlvs {
4546	} , 
4647} 
4748
49+ impl  BlindedPaymentTlvs  { 
50+ 	fn  fee_base_msat ( & self )  -> u32  { 
51+ 		match  self  { 
52+ 			Self :: Forward  {  payment_relay,  .. }  => payment_relay. fee_base_msat , 
53+ 			_ => 0 , 
54+ 		} 
55+ 	} 
56+ 	fn  fee_proportional_millionths ( & self )  -> u32  { 
57+ 		match  self  { 
58+ 			Self :: Forward  {  payment_relay,  .. }  => payment_relay. fee_proportional_millionths , 
59+ 			_ => 0 , 
60+ 		} 
61+ 	} 
62+ 	fn  cltv_expiry_delta ( & self )  -> u16  { 
63+ 		match  self  { 
64+ 			Self :: Forward  {  payment_relay,  .. }  => payment_relay. cltv_expiry_delta , 
65+ 			_ => 0 , 
66+ 		} 
67+ 	} 
68+ 	fn  htlc_minimum_msat ( & self )  -> u64  { 
69+ 		match  self  { 
70+ 			Self :: Forward  {  payment_constraints,  .. }  | Self :: Receive  {  payment_constraints,  .. }  =>
71+ 				payment_constraints. htlc_minimum_msat , 
72+ 		} 
73+ 	} 
74+ } 
75+ 
4876/// Parameters for relaying over a given [`BlindedHop`]. 
4977/// 
5078/// [`BlindedHop`]: crate::blinded_path::BlindedHop 
@@ -148,6 +176,34 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
148176	Ok ( blinded_hops) 
149177} 
150178
179+ pub ( super )  fn  compute_payinfo ( 
180+ 	path :  & [ ( PublicKey ,  BlindedPaymentTlvs ) ] 
181+ )  -> Result < BlindedPayInfo ,  ( ) >  { 
182+ 	let  mut  curr_base_fee:  u128  = 0 ; 
183+ 	let  mut  curr_prop_mil:  u128  = 0 ; 
184+ 	for  ( _,  payment_tlvs)  in  path. iter ( ) . rev ( ) . skip ( 1 )  { 
185+ 		let  next_base_fee = payment_tlvs. fee_base_msat ( )  as  u128 ; 
186+ 		let  next_prop_mil = payment_tlvs. fee_proportional_millionths ( )  as  u128 ; 
187+ 		curr_base_fee =
188+ 			( ( next_base_fee *  1_000_000  + ( curr_base_fee *  ( 1_000_000  + next_prop_mil) ) )  + 1_000_000  - 1 ) 
189+ 			 / 1_000_000 ; 
190+ 		curr_prop_mil =
191+ 			( ( ( curr_prop_mil + next_prop_mil)  *  1_000_000  + curr_prop_mil *  next_prop_mil)  + 1_000_000  - 1 ) 
192+ 			 / 1_000_000 ; 
193+ 	} 
194+ 	Ok ( BlindedPayInfo  { 
195+ 		fee_base_msat :  u32:: try_from ( curr_base_fee) . map_err ( |_| ( ) ) ?, 
196+ 		fee_proportional_millionths :  u32:: try_from ( curr_prop_mil) . map_err ( |_| ( ) ) ?, 
197+ 		cltv_expiry_delta :  path. iter ( ) . map ( |( _,  tlvs) | tlvs. cltv_expiry_delta ( ) ) 
198+ 			. try_fold ( 0u16 ,  |acc,  delta| acc. checked_add ( delta) ) . ok_or ( ( ) ) ?, 
199+ 		htlc_minimum_msat :  path. iter ( ) . map ( |( _,  tlvs) | tlvs. htlc_minimum_msat ( ) ) . max ( ) . unwrap_or ( 0 ) , 
200+ 		// TODO: this field isn't present in route blinding encrypted data 
201+ 		htlc_maximum_msat :  21_000_000  *  1_0000_0000 ,  // Total bitcoin supply 
202+ 		// TODO: when there are blinded hop features, take the subset of them here 
203+ 		features :  BlindedHopFeatures :: empty ( ) , 
204+ 	} ) 
205+ } 
206+ 
151207impl_writeable_msg ! ( PaymentRelay ,  { 
152208	cltv_expiry_delta, 
153209	fee_proportional_millionths, 
@@ -158,3 +214,72 @@ impl_writeable_msg!(PaymentConstraints, {
158214	max_cltv_expiry, 
159215	htlc_minimum_msat
160216} ,  { } ) ; 
217+ 
218+ #[ cfg( test) ]  
219+ mod  tests { 
220+ 	use  bitcoin:: secp256k1:: PublicKey ; 
221+ 	use  crate :: blinded_path:: payment:: { BlindedPaymentTlvs ,  PaymentConstraints ,  PaymentRelay } ; 
222+ 	use  crate :: ln:: PaymentSecret ; 
223+ 	use  crate :: ln:: features:: BlindedHopFeatures ; 
224+ 
225+ 	#[ test]  
226+ 	fn  compute_payinfo ( )  { 
227+ 		// Taken from the spec example for aggregating blinded payment info. 
228+ 		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
229+ 		let  path = vec ! [ ( dummy_pk,  BlindedPaymentTlvs :: Forward  { 
230+ 			short_channel_id:  0 , 
231+ 			payment_relay:  PaymentRelay  { 
232+ 				cltv_expiry_delta:  144 , 
233+ 				fee_proportional_millionths:  500 , 
234+ 				fee_base_msat:  100 , 
235+ 			} , 
236+ 			payment_constraints:  PaymentConstraints  { 
237+ 				max_cltv_expiry:  0 , 
238+ 				htlc_minimum_msat:  100 , 
239+ 			} , 
240+ 			features:  BlindedHopFeatures :: empty( ) , 
241+ 		} ) ,  ( dummy_pk,  BlindedPaymentTlvs :: Forward  { 
242+ 			short_channel_id:  0 , 
243+ 			payment_relay:  PaymentRelay  { 
244+ 				cltv_expiry_delta:  144 , 
245+ 				fee_proportional_millionths:  500 , 
246+ 				fee_base_msat:  100 , 
247+ 			} , 
248+ 			payment_constraints:  PaymentConstraints  { 
249+ 				max_cltv_expiry:  0 , 
250+ 				htlc_minimum_msat:  1_000 , 
251+ 			} , 
252+ 			features:  BlindedHopFeatures :: empty( ) , 
253+ 		} ) ,  ( dummy_pk,  BlindedPaymentTlvs :: Receive  { 
254+ 			payment_secret:  PaymentSecret ( [ 0 ;  32 ] ) , 
255+ 			payment_constraints:  PaymentConstraints  { 
256+ 				max_cltv_expiry:  0 , 
257+ 				htlc_minimum_msat:  1 , 
258+ 			} , 
259+ 			features:  BlindedHopFeatures :: empty( ) , 
260+ 		} ) ] ; 
261+ 		let  blinded_payinfo = super :: compute_payinfo ( & path[ ..] ) . unwrap ( ) ; 
262+ 		assert_eq ! ( blinded_payinfo. fee_base_msat,  201 ) ; 
263+ 		assert_eq ! ( blinded_payinfo. fee_proportional_millionths,  1001 ) ; 
264+ 		assert_eq ! ( blinded_payinfo. cltv_expiry_delta,  288 ) ; 
265+ 		assert_eq ! ( blinded_payinfo. htlc_minimum_msat,  1_000 ) ; 
266+ 	} 
267+ 
268+ 	#[ test]  
269+ 	fn  compute_payinfo_1_hop ( )  { 
270+ 		let  dummy_pk = PublicKey :: from_slice ( & [ 2 ;  33 ] ) . unwrap ( ) ; 
271+ 		let  path = vec ! [ ( dummy_pk,  BlindedPaymentTlvs :: Receive  { 
272+ 			payment_secret:  PaymentSecret ( [ 0 ;  32 ] ) , 
273+ 			payment_constraints:  PaymentConstraints  { 
274+ 				max_cltv_expiry:  0 , 
275+ 				htlc_minimum_msat:  1 , 
276+ 			} , 
277+ 			features:  BlindedHopFeatures :: empty( ) , 
278+ 		} ) ] ; 
279+ 		let  blinded_payinfo = super :: compute_payinfo ( & path[ ..] ) . unwrap ( ) ; 
280+ 		assert_eq ! ( blinded_payinfo. fee_base_msat,  0 ) ; 
281+ 		assert_eq ! ( blinded_payinfo. fee_proportional_millionths,  0 ) ; 
282+ 		assert_eq ! ( blinded_payinfo. cltv_expiry_delta,  0 ) ; 
283+ 		assert_eq ! ( blinded_payinfo. htlc_minimum_msat,  1 ) ; 
284+ 	} 
285+ } 
0 commit comments