@@ -33,7 +33,7 @@ use lightning_liquidity::lsps2::utils::compute_opening_fee;
3333use lightning_types:: payment:: { PaymentHash , PaymentPreimage } ;
3434use rand:: { rng, Rng } ;
3535
36- use crate :: config:: { may_announce_channel, Config } ;
36+ use crate :: config:: { may_announce_channel, Config , ForwardedPaymentTrackingMode } ;
3737use crate :: connection:: ConnectionManager ;
3838use crate :: data_store:: DataStoreUpdateResult ;
3939use crate :: fee_estimator:: ConfirmationTarget ;
@@ -46,12 +46,13 @@ use crate::logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger
4646use crate :: payment:: asynchronous:: om_mailbox:: OnionMessageMailbox ;
4747use crate :: payment:: asynchronous:: static_invoice_store:: StaticInvoiceStore ;
4848use crate :: payment:: store:: {
49- ForwardedPaymentDetails , ForwardedPaymentId , PaymentDetails , PaymentDetailsUpdate ,
50- PaymentDirection , PaymentKind , PaymentStatus ,
49+ ChannelForwardingStats , ForwardedPaymentDetails , ForwardedPaymentId , PaymentDetails ,
50+ PaymentDetailsUpdate , PaymentDirection , PaymentKind , PaymentStatus ,
5151} ;
5252use crate :: runtime:: Runtime ;
5353use crate :: types:: {
54- CustomTlvRecord , DynStore , ForwardedPaymentStore , OnionMessenger , PaymentStore , Sweeper , Wallet ,
54+ ChannelForwardingStatsStore , CustomTlvRecord , DynStore , ForwardedPaymentStore , OnionMessenger ,
55+ PaymentStore , Sweeper , Wallet ,
5556} ;
5657use crate :: {
5758 hex_utils, BumpTransactionEventHandler , ChannelManager , Error , Graph , PeerInfo , PeerStore ,
@@ -492,6 +493,7 @@ where
492493 liquidity_source : Option < Arc < LiquiditySource < Arc < Logger > > > > ,
493494 payment_store : Arc < PaymentStore > ,
494495 forwarded_payment_store : Arc < ForwardedPaymentStore > ,
496+ channel_forwarding_stats_store : Arc < ChannelForwardingStatsStore > ,
495497 peer_store : Arc < PeerStore < L > > ,
496498 runtime : Arc < Runtime > ,
497499 logger : L ,
@@ -512,6 +514,7 @@ where
512514 output_sweeper : Arc < Sweeper > , network_graph : Arc < Graph > ,
513515 liquidity_source : Option < Arc < LiquiditySource < Arc < Logger > > > > ,
514516 payment_store : Arc < PaymentStore > , forwarded_payment_store : Arc < ForwardedPaymentStore > ,
517+ channel_forwarding_stats_store : Arc < ChannelForwardingStatsStore > ,
515518 peer_store : Arc < PeerStore < L > > , static_invoice_store : Option < StaticInvoiceStore > ,
516519 onion_messenger : Arc < OnionMessenger > , om_mailbox : Option < Arc < OnionMessageMailbox > > ,
517520 runtime : Arc < Runtime > , logger : L , config : Arc < Config > ,
@@ -527,6 +530,7 @@ where
527530 liquidity_source,
528531 payment_store,
529532 forwarded_payment_store,
533+ channel_forwarding_stats_store,
530534 peer_store,
531535 logger,
532536 runtime,
@@ -1370,40 +1374,99 @@ where
13701374 . await ;
13711375 }
13721376
1373- // Store the forwarded payment details
13741377 let prev_channel_id_value = prev_channel_id
13751378 . expect ( "prev_channel_id expected for events generated by LDK versions greater than 0.0.107." ) ;
13761379 let next_channel_id_value = next_channel_id
13771380 . expect ( "next_channel_id expected for events generated by LDK versions greater than 0.0.107." ) ;
13781381
1379- // PaymentForwarded does not have a unique id, so we generate a random one here.
1380- let mut id_bytes = [ 0u8 ; 32 ] ;
1381- rng ( ) . fill ( & mut id_bytes) ;
1382-
13831382 let forwarded_at_timestamp = SystemTime :: now ( )
13841383 . duration_since ( UNIX_EPOCH )
13851384 . expect ( "SystemTime::now() should come after SystemTime::UNIX_EPOCH" )
13861385 . as_secs ( ) ;
13871386
1388- let forwarded_payment = ForwardedPaymentDetails {
1389- id : ForwardedPaymentId ( id_bytes) ,
1390- prev_channel_id : prev_channel_id_value,
1391- next_channel_id : next_channel_id_value,
1392- prev_user_channel_id : prev_user_channel_id. map ( UserChannelId ) ,
1393- next_user_channel_id : next_user_channel_id. map ( UserChannelId ) ,
1394- prev_node_id,
1395- next_node_id,
1396- total_fee_earned_msat,
1397- skimmed_fee_msat,
1398- claim_from_onchain_tx,
1399- outbound_amount_forwarded_msat,
1400- forwarded_at_timestamp,
1387+ // Calculate inbound amount (outbound + fee)
1388+ let inbound_amount_msat = outbound_amount_forwarded_msat
1389+ . unwrap_or ( 0 )
1390+ . saturating_add ( total_fee_earned_msat. unwrap_or ( 0 ) ) ;
1391+
1392+ // Update per-channel forwarding stats for the inbound channel (prev_channel)
1393+ // For new entries, this becomes the initial value; for existing entries,
1394+ // these values are used as increments via the to_update() -> update() pattern.
1395+ let inbound_stats = ChannelForwardingStats {
1396+ channel_id : prev_channel_id_value,
1397+ counterparty_node_id : prev_node_id,
1398+ inbound_payments_forwarded : 1 ,
1399+ outbound_payments_forwarded : 0 ,
1400+ total_inbound_amount_msat : inbound_amount_msat,
1401+ total_outbound_amount_msat : 0 ,
1402+ total_fee_earned_msat : total_fee_earned_msat. unwrap_or ( 0 ) ,
1403+ total_skimmed_fee_msat : skimmed_fee_msat. unwrap_or ( 0 ) ,
1404+ onchain_claims_count : if claim_from_onchain_tx { 1 } else { 0 } ,
1405+ first_forwarded_at_timestamp : forwarded_at_timestamp,
1406+ last_forwarded_at_timestamp : forwarded_at_timestamp,
1407+ } ;
1408+ self . channel_forwarding_stats_store
1409+ . insert_or_update ( inbound_stats)
1410+ . map_err ( |e| {
1411+ log_error ! (
1412+ self . logger,
1413+ "Failed to update inbound channel forwarding stats: {e}"
1414+ ) ;
1415+ ReplayEvent ( )
1416+ } ) ?;
1417+
1418+ // Update per-channel forwarding stats for the outbound channel (next_channel)
1419+ let outbound_stats = ChannelForwardingStats {
1420+ channel_id : next_channel_id_value,
1421+ counterparty_node_id : next_node_id,
1422+ inbound_payments_forwarded : 0 ,
1423+ outbound_payments_forwarded : 1 ,
1424+ total_inbound_amount_msat : 0 ,
1425+ total_outbound_amount_msat : outbound_amount_forwarded_msat. unwrap_or ( 0 ) ,
1426+ total_fee_earned_msat : 0 ,
1427+ total_skimmed_fee_msat : 0 ,
1428+ onchain_claims_count : 0 ,
1429+ first_forwarded_at_timestamp : forwarded_at_timestamp,
1430+ last_forwarded_at_timestamp : forwarded_at_timestamp,
14011431 } ;
1432+ self . channel_forwarding_stats_store
1433+ . insert_or_update ( outbound_stats)
1434+ . map_err ( |e| {
1435+ log_error ! (
1436+ self . logger,
1437+ "Failed to update outbound channel forwarding stats: {e}"
1438+ ) ;
1439+ ReplayEvent ( )
1440+ } ) ?;
14021441
1403- self . forwarded_payment_store . insert ( forwarded_payment) . map_err ( |e| {
1404- log_error ! ( self . logger, "Failed to store forwarded payment: {e}" ) ;
1405- ReplayEvent ( )
1406- } ) ?;
1442+ // Only store individual forwarded payment details in Detailed mode
1443+ if self . config . forwarded_payment_tracking_mode
1444+ == ForwardedPaymentTrackingMode :: Detailed
1445+ {
1446+ // PaymentForwarded does not have a unique id, so we generate a random one here.
1447+ let mut id_bytes = [ 0u8 ; 32 ] ;
1448+ rng ( ) . fill ( & mut id_bytes) ;
1449+
1450+ let forwarded_payment = ForwardedPaymentDetails {
1451+ id : ForwardedPaymentId ( id_bytes) ,
1452+ prev_channel_id : prev_channel_id_value,
1453+ next_channel_id : next_channel_id_value,
1454+ prev_user_channel_id : prev_user_channel_id. map ( UserChannelId ) ,
1455+ next_user_channel_id : next_user_channel_id. map ( UserChannelId ) ,
1456+ prev_node_id,
1457+ next_node_id,
1458+ total_fee_earned_msat,
1459+ skimmed_fee_msat,
1460+ claim_from_onchain_tx,
1461+ outbound_amount_forwarded_msat,
1462+ forwarded_at_timestamp,
1463+ } ;
1464+
1465+ self . forwarded_payment_store . insert ( forwarded_payment) . map_err ( |e| {
1466+ log_error ! ( self . logger, "Failed to store forwarded payment: {e}" ) ;
1467+ ReplayEvent ( )
1468+ } ) ?;
1469+ }
14071470
14081471 let event = Event :: PaymentForwarded {
14091472 prev_channel_id : prev_channel_id_value,
0 commit comments