@@ -71,6 +71,26 @@ pub struct Route {
7171 pub paths : Vec < Vec < RouteHop > > ,
7272}
7373
74+ impl Route {
75+ /// Returns the total amount of fees paid on this Route.
76+ /// This doesn't include any extra payment made to the recipient,
77+ /// which can happen in excess of the amount passed to `get_route`'s `final_value_msat`.
78+ pub fn get_total_fees ( & self ) -> u64 {
79+ // Do not count last hop of each path since that's the full value of the payment
80+ return self . paths . iter ( )
81+ . flat_map ( |path| path. split_last ( ) . unwrap ( ) . 1 )
82+ . map ( |hop| & hop. fee_msat )
83+ . sum ( ) ;
84+ }
85+ /// Returns the total amount paid on this Route, excluding the fees.
86+ pub fn get_total_amount ( & self ) -> u64 {
87+ return self . paths . iter ( )
88+ . map ( |path| path. split_last ( ) . unwrap ( ) . 0 )
89+ . map ( |hop| & hop. fee_msat )
90+ . sum ( ) ;
91+ }
92+ }
93+
7494const SERIALIZATION_VERSION : u8 = 1 ;
7595const MIN_SERIALIZATION_VERSION : u8 = 1 ;
7696
@@ -1191,7 +1211,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
11911211
11921212#[ cfg( test) ]
11931213mod tests {
1194- use routing:: router:: { get_route, Route , RouteHint , RouteHintHop , RoutingFees } ;
1214+ use routing:: router:: { get_route, Route , RouteHint , RouteHintHop , RouteHop , RoutingFees } ;
11951215 use routing:: network_graph:: { NetworkGraph , NetGraphMsgHandler } ;
11961216 use chain:: transaction:: OutPoint ;
11971217 use ln:: features:: { ChannelFeatures , InitFeatures , InvoiceFeatures , NodeFeatures } ;
@@ -3425,6 +3445,7 @@ mod tests {
34253445 total_amount_paid_msat += path. last ( ) . unwrap ( ) . fee_msat ;
34263446 }
34273447 assert_eq ! ( total_amount_paid_msat, 200_000 ) ;
3448+ assert_eq ! ( route. get_total_fees( ) , 150_000 ) ;
34283449 }
34293450
34303451 }
@@ -3831,6 +3852,64 @@ mod tests {
38313852 }
38323853 }
38333854
3855+ #[ test]
3856+ fn total_fees_single_path ( ) {
3857+ let route = Route {
3858+ paths : vec ! [ vec![
3859+ RouteHop {
3860+ pubkey: PublicKey :: from_slice( & hex:: decode( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap( ) [ ..] ) . unwrap( ) ,
3861+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
3862+ short_channel_id: 0 , fee_msat: 100 , cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
3863+ } ,
3864+ RouteHop {
3865+ pubkey: PublicKey :: from_slice( & hex:: decode( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) [ ..] ) . unwrap( ) ,
3866+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
3867+ short_channel_id: 0 , fee_msat: 150 , cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
3868+ } ,
3869+ RouteHop {
3870+ pubkey: PublicKey :: from_slice( & hex:: decode( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) [ ..] ) . unwrap( ) ,
3871+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
3872+ short_channel_id: 0 , fee_msat: 225 , cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
3873+ } ,
3874+ ] ] ,
3875+ } ;
3876+
3877+ assert_eq ! ( route. get_total_fees( ) , 250 ) ;
3878+ assert_eq ! ( route. get_total_amount( ) , 225 ) ;
3879+ }
3880+
3881+ #[ test]
3882+ fn total_fees_multi_path ( ) {
3883+ let route = Route {
3884+ paths : vec ! [ vec![
3885+ RouteHop {
3886+ pubkey: PublicKey :: from_slice( & hex:: decode( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap( ) [ ..] ) . unwrap( ) ,
3887+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
3888+ short_channel_id: 0 , fee_msat: 100 , cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
3889+ } ,
3890+ RouteHop {
3891+ pubkey: PublicKey :: from_slice( & hex:: decode( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) [ ..] ) . unwrap( ) ,
3892+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
3893+ short_channel_id: 0 , fee_msat: 150 , cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
3894+ } ,
3895+ ] , vec![
3896+ RouteHop {
3897+ pubkey: PublicKey :: from_slice( & hex:: decode( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap( ) [ ..] ) . unwrap( ) ,
3898+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
3899+ short_channel_id: 0 , fee_msat: 100 , cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
3900+ } ,
3901+ RouteHop {
3902+ pubkey: PublicKey :: from_slice( & hex:: decode( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) [ ..] ) . unwrap( ) ,
3903+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
3904+ short_channel_id: 0 , fee_msat: 150 , cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
3905+ } ,
3906+ ] ] ,
3907+ } ;
3908+
3909+ assert_eq ! ( route. get_total_fees( ) , 200 ) ;
3910+ assert_eq ! ( route. get_total_amount( ) , 300 ) ;
3911+ }
3912+
38343913 #[ cfg( not( feature = "no-std" ) ) ]
38353914 pub ( super ) fn random_init_seed ( ) -> u64 {
38363915 // Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
0 commit comments