Skip to content

Fix a panic in Route's new fee-calculation methods and clean up #1088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 21, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions lightning/src/routing/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,22 @@ pub struct Route {
}

impl Route {
/// Returns the total amount of fees paid on this Route.
/// This doesn't include any extra payment made to the recipient,
/// which can happen in excess of the amount passed to `get_route`'s `final_value_msat`.
/// Returns the total amount of fees paid on this [`Route`].
///
/// This doesn't include any extra payment made to the recipient, which can happen in excess of
/// the amount passed to [`get_route`]'s `final_value_msat`.
pub fn get_total_fees(&self) -> u64 {
// Do not count last hop of each path since that's the full value of the payment
return self.paths.iter()
.flat_map(|path| path.split_last().unwrap().1)
.flat_map(|path| path.split_last().map(|(_, path_prefix)| path_prefix).unwrap_or(&[]))
.map(|hop| &hop.fee_msat)
.sum();
}
/// Returns the total amount paid on this Route, excluding the fees.

/// Returns the total amount paid on this [`Route`], excluding the fees.
pub fn get_total_amount(&self) -> u64 {
return self.paths.iter()
.map(|path| path.split_last().unwrap().0)
.map(|hop| &hop.fee_msat)
.map(|path| path.split_last().map(|(hop, _)| hop.fee_msat).unwrap_or(0))
.sum();
}
}
Expand Down Expand Up @@ -4226,17 +4227,17 @@ mod tests {
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
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
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
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
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
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
short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0
},
]],
};
Expand All @@ -4252,23 +4253,23 @@ mod tests {
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
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
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
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
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
},
],vec![
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
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
short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
},
RouteHop {
pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
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
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
},
]],
};
Expand All @@ -4277,6 +4278,17 @@ mod tests {
assert_eq!(route.get_total_amount(), 300);
}

#[test]
fn total_empty_route_no_panic() {
// In an earlier version of `Route::get_total_fees` and `Route::get_total_amount`, they
// would both panic if the route was completely empty. We test to ensure they return 0
// here, even though its somewhat nonsensical as a route.
let route = Route { paths: Vec::new() };

assert_eq!(route.get_total_fees(), 0);
assert_eq!(route.get_total_amount(), 0);
}

#[cfg(not(feature = "no-std"))]
pub(super) fn random_init_seed() -> u64 {
// Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
Expand Down