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
@@ -40,6 +41,36 @@ pub enum BlindedPaymentTlvs {
4041 } ,
4142}
4243
44+ impl BlindedPaymentTlvs {
45+ // The fee used to get from the current hop to the next hop in the path.
46+ fn fee_base_msat ( & self ) -> u32 {
47+ match self {
48+ Self :: Forward { payment_relay, .. } => payment_relay. fee_base_msat ,
49+ _ => 0 ,
50+ }
51+ }
52+ // The fee used to get from the current hop to the next hop in the path.
53+ fn fee_proportional_millionths ( & self ) -> u32 {
54+ match self {
55+ Self :: Forward { payment_relay, .. } => payment_relay. fee_proportional_millionths ,
56+ _ => 0 ,
57+ }
58+ }
59+ // The delta used to get from the current hop to the next hop in the path.
60+ fn cltv_expiry_delta ( & self ) -> u16 {
61+ match self {
62+ Self :: Forward { payment_relay, .. } => payment_relay. cltv_expiry_delta ,
63+ _ => 0 ,
64+ }
65+ }
66+ fn htlc_minimum_msat ( & self ) -> u64 {
67+ match self {
68+ Self :: Forward { payment_constraints, .. } | Self :: Receive { payment_constraints, .. } =>
69+ payment_constraints. htlc_minimum_msat ,
70+ }
71+ }
72+ }
73+
4374/// Parameters for relaying over a given [`BlindedHop`].
4475///
4576/// [`BlindedHop`]: crate::blinded_path::BlindedHop
@@ -131,6 +162,34 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
131162 unblinded_path. iter ( ) . map ( |( _, tlvs) | tlvs) , session_priv)
132163}
133164
165+ pub ( super ) fn compute_payinfo (
166+ path : & [ ( PublicKey , BlindedPaymentTlvs ) ]
167+ ) -> Result < BlindedPayInfo , ( ) > {
168+ let mut curr_base_fee: u128 = 0 ;
169+ let mut curr_prop_mil: u128 = 0 ;
170+ for ( _, payment_tlvs) in path. iter ( ) . rev ( ) . skip ( 1 ) {
171+ let next_base_fee = payment_tlvs. fee_base_msat ( ) as u128 ;
172+ let next_prop_mil = payment_tlvs. fee_proportional_millionths ( ) as u128 ;
173+ curr_base_fee =
174+ ( ( next_base_fee * 1_000_000 + ( curr_base_fee * ( 1_000_000 + next_prop_mil) ) ) + 1_000_000 - 1 )
175+ / 1_000_000 ;
176+ curr_prop_mil =
177+ ( ( ( curr_prop_mil + next_prop_mil) * 1_000_000 + curr_prop_mil * next_prop_mil) + 1_000_000 - 1 )
178+ / 1_000_000 ;
179+ }
180+ Ok ( BlindedPayInfo {
181+ fee_base_msat : u32:: try_from ( curr_base_fee) . map_err ( |_| ( ) ) ?,
182+ fee_proportional_millionths : u32:: try_from ( curr_prop_mil) . map_err ( |_| ( ) ) ?,
183+ cltv_expiry_delta : path. iter ( ) . map ( |( _, tlvs) | tlvs. cltv_expiry_delta ( ) )
184+ . try_fold ( 0u16 , |acc, delta| acc. checked_add ( delta) ) . ok_or ( ( ) ) ?,
185+ htlc_minimum_msat : path. iter ( ) . map ( |( _, tlvs) | tlvs. htlc_minimum_msat ( ) ) . max ( ) . unwrap_or ( 0 ) ,
186+ // TODO: this field isn't present in route blinding encrypted data
187+ htlc_maximum_msat : 21_000_000 * 100_000_000 * 1_000 , // Total bitcoin supply
188+ // TODO: when there are blinded hop features, take the subset of them here
189+ features : BlindedHopFeatures :: empty ( ) ,
190+ } )
191+ }
192+
134193impl_writeable_msg ! ( PaymentRelay , {
135194 cltv_expiry_delta,
136195 fee_proportional_millionths,
@@ -141,3 +200,70 @@ impl_writeable_msg!(PaymentConstraints, {
141200 max_cltv_expiry,
142201 htlc_minimum_msat
143202} , { } ) ;
203+
204+ #[ cfg( test) ]
205+ mod tests {
206+ use bitcoin:: secp256k1:: PublicKey ;
207+ use crate :: blinded_path:: payment:: { BlindedPaymentTlvs , PaymentConstraints , PaymentRelay } ;
208+ use crate :: ln:: PaymentSecret ;
209+ use crate :: ln:: features:: BlindedHopFeatures ;
210+
211+ #[ test]
212+ fn compute_payinfo ( ) {
213+ // Taken from the spec example for aggregating blinded payment info.
214+ let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
215+ let path = vec ! [ ( dummy_pk, BlindedPaymentTlvs :: Forward {
216+ short_channel_id: 0 ,
217+ payment_relay: PaymentRelay {
218+ cltv_expiry_delta: 144 ,
219+ fee_proportional_millionths: 500 ,
220+ fee_base_msat: 100 ,
221+ } ,
222+ payment_constraints: PaymentConstraints {
223+ max_cltv_expiry: 0 ,
224+ htlc_minimum_msat: 100 ,
225+ } ,
226+ features: BlindedHopFeatures :: empty( ) ,
227+ } ) , ( dummy_pk, BlindedPaymentTlvs :: Forward {
228+ short_channel_id: 0 ,
229+ payment_relay: PaymentRelay {
230+ cltv_expiry_delta: 144 ,
231+ fee_proportional_millionths: 500 ,
232+ fee_base_msat: 100 ,
233+ } ,
234+ payment_constraints: PaymentConstraints {
235+ max_cltv_expiry: 0 ,
236+ htlc_minimum_msat: 1_000 ,
237+ } ,
238+ features: BlindedHopFeatures :: empty( ) ,
239+ } ) , ( dummy_pk, BlindedPaymentTlvs :: Receive {
240+ payment_secret: PaymentSecret ( [ 0 ; 32 ] ) ,
241+ payment_constraints: PaymentConstraints {
242+ max_cltv_expiry: 0 ,
243+ htlc_minimum_msat: 1 ,
244+ } ,
245+ } ) ] ;
246+ let blinded_payinfo = super :: compute_payinfo ( & path[ ..] ) . unwrap ( ) ;
247+ assert_eq ! ( blinded_payinfo. fee_base_msat, 201 ) ;
248+ assert_eq ! ( blinded_payinfo. fee_proportional_millionths, 1001 ) ;
249+ assert_eq ! ( blinded_payinfo. cltv_expiry_delta, 288 ) ;
250+ assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 1_000 ) ;
251+ }
252+
253+ #[ test]
254+ fn compute_payinfo_1_hop ( ) {
255+ let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
256+ let path = vec ! [ ( 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+ } ) ] ;
263+ let blinded_payinfo = super :: compute_payinfo ( & path[ ..] ) . unwrap ( ) ;
264+ assert_eq ! ( blinded_payinfo. fee_base_msat, 0 ) ;
265+ assert_eq ! ( blinded_payinfo. fee_proportional_millionths, 0 ) ;
266+ assert_eq ! ( blinded_payinfo. cltv_expiry_delta, 0 ) ;
267+ assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 1 ) ;
268+ }
269+ }
0 commit comments