@@ -18,10 +18,9 @@ use lightning::events::bump_transaction::BumpTransactionEvent;
1818#[ cfg( not( feature = "uniffi" ) ) ]
1919use lightning:: events:: PaidBolt12Invoice ;
2020use lightning:: events:: {
21- ClosureReason , Event as LdkEvent , FundingInfo , PaymentFailureReason , PaymentPurpose ,
22- ReplayEvent ,
21+ ClosureReason , Event as LdkEvent , FundingInfo , HTLCLocator as LdkHTLCLocator ,
22+ PaymentFailureReason , PaymentPurpose , ReplayEvent ,
2323} ;
24- use lightning:: impl_writeable_tlv_based_enum;
2524use lightning:: ln:: channelmanager:: PaymentId ;
2625use lightning:: ln:: types:: ChannelId ;
2726use lightning:: routing:: gossip:: NodeId ;
@@ -32,6 +31,7 @@ use lightning::util::config::{
3231use lightning:: util:: errors:: APIError ;
3332use lightning:: util:: persist:: KVStore ;
3433use lightning:: util:: ser:: { Readable , ReadableArgs , Writeable , Writer } ;
34+ use lightning:: { impl_writeable_tlv_based, impl_writeable_tlv_based_enum} ;
3535use lightning_liquidity:: lsps2:: utils:: compute_opening_fee;
3636use lightning_types:: payment:: { PaymentHash , PaymentPreimage } ;
3737
@@ -61,6 +61,50 @@ use crate::{
6161 UserChannelId ,
6262} ;
6363
64+ /// Identifies the channel and counterparty that a HTLC was processed with.
65+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
66+ #[ cfg_attr( feature = "uniffi" , derive( uniffi:: Record ) ) ]
67+ pub struct HTLCLocator {
68+ /// The channel that the HTLC was sent or received on.
69+ pub channel_id : ChannelId ,
70+ /// The `user_channel_id` for the channel.
71+ ///
72+ /// Will only be `None` for events serialized with LDK Node v0.3.0 or prior, or if the
73+ /// payment was settled via an on-chain transaction.
74+ pub user_channel_id : Option < UserChannelId > ,
75+ /// The node id of the counterparty for this HTLC.
76+ ///
77+ /// This is only `None` for HTLCs received prior to LDK Node v0.5 or for events serialized by
78+ /// versions prior to v0.5.
79+ pub node_id : Option < PublicKey > ,
80+ }
81+
82+ impl_writeable_tlv_based ! ( HTLCLocator , {
83+ ( 1 , channel_id, required) ,
84+ ( 3 , user_channel_id, option) ,
85+ ( 5 , node_id, option) ,
86+ } ) ;
87+
88+ impl From < LdkHTLCLocator > for HTLCLocator {
89+ fn from ( value : LdkHTLCLocator ) -> Self {
90+ HTLCLocator {
91+ channel_id : value. channel_id ,
92+ user_channel_id : value. user_channel_id . map ( |u| UserChannelId ( u) ) ,
93+ node_id : value. node_id ,
94+ }
95+ }
96+ }
97+
98+ impl From < HTLCLocator > for LdkHTLCLocator {
99+ fn from ( value : HTLCLocator ) -> Self {
100+ LdkHTLCLocator {
101+ channel_id : value. channel_id ,
102+ user_channel_id : value. user_channel_id . map ( |u| u. 0 ) ,
103+ node_id : value. node_id ,
104+ }
105+ }
106+ }
107+
64108/// An event emitted by [`Node`], which should be handled by the user.
65109///
66110/// [`Node`]: [`crate::Node`]
@@ -128,29 +172,14 @@ pub enum Event {
128172 } ,
129173 /// A payment has been forwarded.
130174 PaymentForwarded {
131- /// The channel id of the incoming channel between the previous node and us.
132- prev_channel_id : ChannelId ,
133- /// The channel id of the outgoing channel between the next node and us.
134- next_channel_id : ChannelId ,
135- /// The `user_channel_id` of the incoming channel between the previous node and us.
136- ///
137- /// Will only be `None` for events serialized with LDK Node v0.3.0 or prior.
138- prev_user_channel_id : Option < UserChannelId > ,
139- /// The `user_channel_id` of the outgoing channel between the next node and us.
140- ///
141- /// This will be `None` if the payment was settled via an on-chain transaction. See the
142- /// caveat described for the `total_fee_earned_msat` field.
143- next_user_channel_id : Option < UserChannelId > ,
144- /// The node id of the previous node.
145- ///
146- /// This is only `None` for HTLCs received prior to LDK Node v0.5 or for events serialized by
147- /// versions prior to v0.5.
148- prev_node_id : Option < PublicKey > ,
149- /// The node id of the next node.
150- ///
151- /// This is only `None` for HTLCs received prior to LDK Node v0.5 or for events serialized by
152- /// versions prior to v0.5.
153- next_node_id : Option < PublicKey > ,
175+ /// The set of incoming HTLCs that were forwarded to our node. Contains a single HTLC for
176+ /// source-routed payments, and may contain multiple HTLCs when we acted as a trampoline
177+ /// router.
178+ prev_htlcs : Vec < HTLCLocator > ,
179+ /// The set of outgoing HTLCs forwarded by our node. Contains a single HTLC for regular
180+ /// source-routed payments, and may contain multiple HTLCs when we acted as a trampoline
181+ /// router.
182+ next_htlcs : Vec < HTLCLocator > ,
154183 /// The total fee, in milli-satoshis, which was earned as a result of the payment.
155184 ///
156185 /// Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC
@@ -323,16 +352,63 @@ impl_writeable_tlv_based_enum!(Event,
323352 ( 7 , custom_records, optional_vec) ,
324353 } ,
325354 ( 7 , PaymentForwarded ) => {
326- ( 0 , prev_channel_id, required) ,
327- ( 1 , prev_node_id, option) ,
328- ( 2 , next_channel_id, required) ,
329- ( 3 , next_node_id, option) ,
330- ( 4 , prev_user_channel_id, option) ,
331- ( 6 , next_user_channel_id, option) ,
355+ // We don't write our legacy types because we don't need to support downgrades, but we do
356+ // read them so that we can forwards compatibly fill prev/next_htlcs on upgrade.
357+ ( 0 , legacy_prev_channel_id, ( legacy, ChannelId , |_| Ok ( ( ) ) , |_: & Event | None :: <Option <ChannelId >>) ) ,
358+ ( 1 , legacy_prev_node_id, ( legacy, PublicKey , |_| Ok ( ( ) ) , |_: & Event | None :: <Option <PublicKey >>) ) ,
359+ ( 2 , legacy_next_channel_id, ( legacy, ChannelId , |_| Ok ( ( ) ) , |_: & Event | None :: <Option <ChannelId >>) ) ,
360+ ( 3 , legacy_next_node_id, ( legacy, PublicKey , |_| Ok ( ( ) ) , |_: & Event | None :: <Option <PublicKey >>) ) ,
361+ ( 4 , legacy_prev_user_channel_id, ( legacy, u128 , |_| Ok ( ( ) ) , |_: & Event | None :: <Option <u128 >>) ) ,
362+ ( 6 , legacy_next_user_channel_id, ( legacy, u128 , |_| Ok ( ( ) ) , |_: & Event | None :: <Option <u128 >>) ) ,
332363 ( 8 , total_fee_earned_msat, option) ,
333364 ( 10 , skimmed_fee_msat, option) ,
334365 ( 12 , claim_from_onchain_tx, required) ,
335366 ( 14 , outbound_amount_forwarded_msat, option) ,
367+ // We cannot implement Readable/Writeable for Vec<HTLCLocator> because we do not own the
368+ // trait or the type (Vec). To work around this, and prevent duplicating serialization code,
369+ // we map to the underlying LdkHTLCLocator type for serialization.
370+ ( 15 , prev_htlcs, ( custom, Vec <LdkHTLCLocator >,
371+ |v: Option <Vec <LdkHTLCLocator >>| {
372+ let res: Result <Vec <HTLCLocator >, lightning:: ln:: msgs:: DecodeError > =
373+ Ok ( v. map( |ldk_vec| ldk_vec. into_iter( ) . map( HTLCLocator :: from) . collect( ) )
374+ . unwrap_or_else( || {
375+ legacy_prev_channel_id. map( |ch| vec![ HTLCLocator {
376+ channel_id: ch,
377+ user_channel_id: legacy_prev_user_channel_id. map( UserChannelId ) ,
378+ node_id: legacy_prev_node_id,
379+ } ] ) . unwrap_or_default( )
380+ } ) ) ;
381+ res
382+ } ,
383+ |us: & Event | {
384+ if let Event :: PaymentForwarded { ref prev_htlcs, .. } = us {
385+ if !prev_htlcs. is_empty( ) {
386+ Some ( prev_htlcs. iter( ) . cloned( ) . map( LdkHTLCLocator :: from) . collect:: <Vec <_>>( ) )
387+ } else { None }
388+ } else { unreachable!( ) }
389+ }
390+ ) ) ,
391+ ( 17 , next_htlcs, ( custom, Vec <LdkHTLCLocator >,
392+ |v: Option <Vec <LdkHTLCLocator >>| {
393+ let res: Result <Vec <HTLCLocator >, lightning:: ln:: msgs:: DecodeError > =
394+ Ok ( v. map( |ldk_vec| ldk_vec. into_iter( ) . map( HTLCLocator :: from) . collect( ) )
395+ . unwrap_or_else( || {
396+ legacy_next_channel_id. map( |ch| vec![ HTLCLocator {
397+ channel_id: ch,
398+ user_channel_id: legacy_next_user_channel_id. map( UserChannelId ) ,
399+ node_id: legacy_next_node_id,
400+ } ] ) . unwrap_or_default( )
401+ } ) ) ;
402+ res
403+ } ,
404+ |us: & Event | {
405+ if let Event :: PaymentForwarded { ref next_htlcs, .. } = us {
406+ if !next_htlcs. is_empty( ) {
407+ Some ( next_htlcs. iter( ) . cloned( ) . map( LdkHTLCLocator :: from) . collect:: <Vec <_>>( ) )
408+ } else { None }
409+ } else { unreachable!( ) }
410+ }
411+ ) ) ,
336412 } ,
337413 ( 8 , SplicePending ) => {
338414 ( 1 , channel_id, required) ,
@@ -1306,12 +1382,8 @@ where
13061382 }
13071383 } ,
13081384 LdkEvent :: PaymentForwarded {
1309- prev_channel_id,
1310- next_channel_id,
1311- prev_user_channel_id,
1312- next_user_channel_id,
1313- prev_node_id,
1314- next_node_id,
1385+ prev_htlcs,
1386+ next_htlcs,
13151387 total_fee_earned_msat,
13161388 skimmed_fee_msat,
13171389 claim_from_onchain_tx,
@@ -1322,11 +1394,10 @@ where
13221394 let nodes = read_only_network_graph. nodes ( ) ;
13231395 let channels = self . channel_manager . list_channels ( ) ;
13241396
1325- let node_str = |channel_id : & Option < ChannelId > | {
1326- channel_id
1327- . and_then ( |channel_id| {
1328- channels. iter ( ) . find ( |c| c. channel_id == channel_id)
1329- } )
1397+ let node_str = |channel_id : & ChannelId | {
1398+ channels
1399+ . iter ( )
1400+ . find ( |c| c. channel_id == * channel_id)
13301401 . and_then ( |channel| {
13311402 nodes. get ( & NodeId :: from_pubkey ( & channel. counterparty . node_id ) )
13321403 } )
@@ -1338,21 +1409,21 @@ where
13381409 } )
13391410 } )
13401411 } ;
1341- let channel_str = | channel_id : & Option < ChannelId > | {
1342- channel_id
1343- . map ( |channel_id| format ! ( " with channel {}" , channel_id ) )
1344- . unwrap_or_default ( )
1345- } ;
1346- let from_prev_str = format ! (
1347- " from {}{}" ,
1348- node_str ( & prev_channel_id ) ,
1349- channel_str ( & prev_channel_id )
1350- ) ;
1351- let to_next_str = format ! (
1352- " to {} {}",
1353- node_str ( & next_channel_id ) ,
1354- channel_str ( & next_channel_id )
1355- ) ;
1412+ let from_prev_str : String = prev_htlcs
1413+ . iter ( )
1414+ . map ( |htlc| {
1415+ format ! ( "with {} on {}" , node_str ( & htlc . channel_id ) , htlc . channel_id )
1416+ } )
1417+ . collect :: < Vec < _ > > ( )
1418+ . join ( ", " ) ;
1419+
1420+ let to_next_str : String = next_htlcs
1421+ . iter ( )
1422+ . map ( |htlc| {
1423+ format ! ( "with {} on {}", node_str ( & htlc . channel_id ) , htlc . channel_id )
1424+ } )
1425+ . collect :: < Vec < _ > > ( )
1426+ . join ( ", " ) ;
13561427
13571428 let fee_earned = total_fee_earned_msat. unwrap_or ( 0 ) ;
13581429 if claim_from_onchain_tx {
@@ -1367,8 +1438,10 @@ where
13671438 } else {
13681439 log_info ! (
13691440 self . logger,
1370- "Forwarded payment{}{} of {}msat, earning {}msat in fees." ,
1441+ "Forwarded payment with {} inbound HTLC(s) ({}) and {} outbound HTLC(s) ({}) of {}msat, earning {}msat in fees." ,
1442+ prev_htlcs. len( ) ,
13711443 from_prev_str,
1444+ next_htlcs. len( ) ,
13721445 to_next_str,
13731446 outbound_amount_forwarded_msat. unwrap_or( 0 ) ,
13741447 fee_earned,
@@ -1378,18 +1451,16 @@ where
13781451
13791452 if let Some ( liquidity_source) = self . liquidity_source . as_ref ( ) {
13801453 let skimmed_fee_msat = skimmed_fee_msat. unwrap_or ( 0 ) ;
1381- liquidity_source
1382- . handle_payment_forwarded ( next_channel_id, skimmed_fee_msat)
1383- . await ;
1454+ for next_htlc in next_htlcs. iter ( ) {
1455+ liquidity_source
1456+ . handle_payment_forwarded ( Some ( next_htlc. channel_id ) , skimmed_fee_msat)
1457+ . await ;
1458+ }
13841459 }
13851460
13861461 let event = Event :: PaymentForwarded {
1387- prev_channel_id : prev_channel_id. expect ( "prev_channel_id expected for events generated by LDK versions greater than 0.0.107." ) ,
1388- next_channel_id : next_channel_id. expect ( "next_channel_id expected for events generated by LDK versions greater than 0.0.107." ) ,
1389- prev_user_channel_id : prev_user_channel_id. map ( UserChannelId ) ,
1390- next_user_channel_id : next_user_channel_id. map ( UserChannelId ) ,
1391- prev_node_id,
1392- next_node_id,
1462+ prev_htlcs : prev_htlcs. into_iter ( ) . map ( |h| h. into ( ) ) . collect ( ) ,
1463+ next_htlcs : next_htlcs. into_iter ( ) . map ( |h| h. into ( ) ) . collect ( ) ,
13931464 total_fee_earned_msat,
13941465 skimmed_fee_msat,
13951466 claim_from_onchain_tx,
0 commit comments