@@ -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
@@ -1245,7 +1265,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
12451265
12461266#[ cfg( test) ]
12471267mod tests {
1248- use routing:: router:: { get_route, Route , RouteHint , RouteHintHop , RoutingFees } ;
1268+ use routing:: router:: { get_route, Route , RouteHint , RouteHintHop , RouteHop , RoutingFees } ;
12491269 use routing:: network_graph:: { NetworkGraph , NetGraphMsgHandler } ;
12501270 use chain:: transaction:: OutPoint ;
12511271 use ln:: features:: { ChannelFeatures , InitFeatures , InvoiceFeatures , NodeFeatures } ;
@@ -3791,6 +3811,7 @@ mod tests {
37913811 total_amount_paid_msat += path. last ( ) . unwrap ( ) . fee_msat ;
37923812 }
37933813 assert_eq ! ( total_amount_paid_msat, 200_000 ) ;
3814+ assert_eq ! ( route. get_total_fees( ) , 150_000 ) ;
37943815 }
37953816
37963817 }
@@ -4198,6 +4219,64 @@ mod tests {
41984219 }
41994220 }
42004221
4222+ #[ test]
4223+ fn total_fees_single_path ( ) {
4224+ let route = Route {
4225+ paths : vec ! [ vec![
4226+ RouteHop {
4227+ pubkey: PublicKey :: from_slice( & hex:: decode( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap( ) [ ..] ) . unwrap( ) ,
4228+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
4229+ 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
4230+ } ,
4231+ RouteHop {
4232+ pubkey: PublicKey :: from_slice( & hex:: decode( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) [ ..] ) . unwrap( ) ,
4233+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
4234+ 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
4235+ } ,
4236+ RouteHop {
4237+ pubkey: PublicKey :: from_slice( & hex:: decode( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) [ ..] ) . unwrap( ) ,
4238+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
4239+ 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
4240+ } ,
4241+ ] ] ,
4242+ } ;
4243+
4244+ assert_eq ! ( route. get_total_fees( ) , 250 ) ;
4245+ assert_eq ! ( route. get_total_amount( ) , 225 ) ;
4246+ }
4247+
4248+ #[ test]
4249+ fn total_fees_multi_path ( ) {
4250+ let route = Route {
4251+ paths : vec ! [ vec![
4252+ RouteHop {
4253+ pubkey: PublicKey :: from_slice( & hex:: decode( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap( ) [ ..] ) . unwrap( ) ,
4254+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
4255+ 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
4256+ } ,
4257+ RouteHop {
4258+ pubkey: PublicKey :: from_slice( & hex:: decode( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) [ ..] ) . unwrap( ) ,
4259+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
4260+ 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
4261+ } ,
4262+ ] , vec![
4263+ RouteHop {
4264+ pubkey: PublicKey :: from_slice( & hex:: decode( "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619" ) . unwrap( ) [ ..] ) . unwrap( ) ,
4265+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
4266+ 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
4267+ } ,
4268+ RouteHop {
4269+ pubkey: PublicKey :: from_slice( & hex:: decode( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) [ ..] ) . unwrap( ) ,
4270+ channel_features: ChannelFeatures :: empty( ) , node_features: NodeFeatures :: empty( ) ,
4271+ 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
4272+ } ,
4273+ ] ] ,
4274+ } ;
4275+
4276+ assert_eq ! ( route. get_total_fees( ) , 200 ) ;
4277+ assert_eq ! ( route. get_total_amount( ) , 300 ) ;
4278+ }
4279+
42014280 #[ cfg( not( feature = "no-std" ) ) ]
42024281 pub ( super ) fn random_init_seed ( ) -> u64 {
42034282 // Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
0 commit comments