Skip to content

Commit 03aaa9d

Browse files
committed
Add method to count total fees in a Route #999
1 parent 4368b56 commit 03aaa9d

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

lightning/src/routing/router.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ pub struct Route {
7171
pub paths: Vec<Vec<RouteHop>>,
7272
}
7373

74+
impl Route {
75+
/// Returns the total amount of fees payed on this Route.
76+
fn get_total_fees(&self) -> u64 {
77+
// Do not count last hop of each path since that's the full value of the payment
78+
return self.paths.iter()
79+
.flat_map(|path| path.split_last().unwrap().1)
80+
.map(|hop| &hop.fee_msat)
81+
.sum();
82+
}
83+
}
84+
7485
const SERIALIZATION_VERSION: u8 = 1;
7586
const MIN_SERIALIZATION_VERSION: u8 = 1;
7687

@@ -3425,6 +3436,7 @@ mod tests {
34253436
total_amount_paid_msat += path.last().unwrap().fee_msat;
34263437
}
34273438
assert_eq!(total_amount_paid_msat, 200_000);
3439+
assert_eq!(route.get_total_fees(), 150_000);
34283440
}
34293441

34303442
}
@@ -3831,6 +3843,121 @@ mod tests {
38313843
}
38323844
}
38333845

3846+
// Tweaked from available_liquidity_last_hop_test
3847+
#[test]
3848+
fn total_fees_single_path() {
3849+
// Check that total fees in single path payment are calculated properly
3850+
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
3851+
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
3852+
3853+
// Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
3854+
// {12, 13, 11} have the capacities of 100, {6} has a capacity of 50.
3855+
// Total capacity: 50 sats.
3856+
// Fee in first channel does not count since that's opened by the originating node.
3857+
// So, only the fees of the last 3 channels count towards total fees.
3858+
3859+
// Disable other potential paths.
3860+
update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
3861+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
3862+
short_channel_id: 2,
3863+
timestamp: 2,
3864+
flags: 2,
3865+
cltv_expiry_delta: 0,
3866+
htlc_minimum_msat: 0,
3867+
htlc_maximum_msat: OptionalField::Present(100_000),
3868+
fee_base_msat: 0,
3869+
fee_proportional_millionths: 0,
3870+
excess_data: Vec::new()
3871+
});
3872+
update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
3873+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
3874+
short_channel_id: 7,
3875+
timestamp: 2,
3876+
flags: 2,
3877+
cltv_expiry_delta: 0,
3878+
htlc_minimum_msat: 0,
3879+
htlc_maximum_msat: OptionalField::Present(100_000),
3880+
fee_base_msat: 0,
3881+
fee_proportional_millionths: 0,
3882+
excess_data: Vec::new()
3883+
});
3884+
3885+
// Limit capacities
3886+
3887+
update_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
3888+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
3889+
short_channel_id: 12,
3890+
timestamp: 2,
3891+
flags: 0,
3892+
cltv_expiry_delta: 0,
3893+
htlc_minimum_msat: 0,
3894+
htlc_maximum_msat: OptionalField::Present(100_000),
3895+
fee_base_msat: 210,
3896+
fee_proportional_millionths: 0,
3897+
excess_data: Vec::new()
3898+
});
3899+
update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
3900+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
3901+
short_channel_id: 13,
3902+
timestamp: 2,
3903+
flags: 0,
3904+
cltv_expiry_delta: 0,
3905+
htlc_minimum_msat: 0,
3906+
htlc_maximum_msat: OptionalField::Present(100_000),
3907+
fee_base_msat: 220,
3908+
fee_proportional_millionths: 0,
3909+
excess_data: Vec::new()
3910+
});
3911+
3912+
update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
3913+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
3914+
short_channel_id: 6,
3915+
timestamp: 2,
3916+
flags: 0,
3917+
cltv_expiry_delta: 0,
3918+
htlc_minimum_msat: 0,
3919+
htlc_maximum_msat: OptionalField::Present(50_000),
3920+
fee_base_msat: 280,
3921+
fee_proportional_millionths: 0,
3922+
excess_data: Vec::new()
3923+
});
3924+
update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
3925+
chain_hash: genesis_block(Network::Testnet).header.block_hash(),
3926+
short_channel_id: 11,
3927+
timestamp: 2,
3928+
flags: 0,
3929+
cltv_expiry_delta: 0,
3930+
htlc_minimum_msat: 0,
3931+
htlc_maximum_msat: OptionalField::Present(100_000),
3932+
fee_base_msat: 290,
3933+
fee_proportional_millionths: 0,
3934+
excess_data: Vec::new()
3935+
});
3936+
3937+
{
3938+
// Now, attempt to route 49 sats (just a bit below the capacity).
3939+
let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
3940+
Some(InvoiceFeatures::known()), None, &Vec::new(), 49_000, 42, Arc::clone(&logger)).unwrap();
3941+
assert_eq!(route.paths.len(), 1);
3942+
3943+
let mut total_value_transferred_msat = 0;
3944+
let mut total_paid_msat = 0;
3945+
for path in &route.paths {
3946+
assert_eq!(path.len(), 4);
3947+
assert_eq!(path.last().unwrap().pubkey, nodes[3]);
3948+
total_value_transferred_msat += path.last().unwrap().fee_msat;
3949+
for hop in path {
3950+
total_paid_msat += hop.fee_msat;
3951+
}
3952+
}
3953+
// If we paid fee, this would be higher.
3954+
assert_eq!(total_value_transferred_msat, 49_000);
3955+
let total_fees_paid = total_paid_msat - total_value_transferred_msat;
3956+
assert_eq!(total_fees_paid, 790);
3957+
assert_eq!(route.get_total_fees(), 790);
3958+
}
3959+
}
3960+
38343961
#[cfg(not(feature = "no-std"))]
38353962
pub(super) fn random_init_seed() -> u64 {
38363963
// Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.

0 commit comments

Comments
 (0)