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