@@ -1588,6 +1588,28 @@ func TestBuildRoute(t *testing.T) {
15881588 MaxHTLC : lnwire .MilliSatoshi (20100 ),
15891589 Features : paymentAddrFeatures ,
15901590 }, 8 ),
1591+
1592+ // Create a route with inbound fees.
1593+ symmetricTestChannel ("a" , "d" , chanCapSat , & testChannelPolicy {
1594+ Expiry : 144 ,
1595+ FeeRate : 20000 ,
1596+ MinHTLC : lnwire .NewMSatFromSatoshis (5 ),
1597+ MaxHTLC : lnwire .NewMSatFromSatoshis (
1598+ chanCapSat ,
1599+ ),
1600+ InboundFeeBaseMsat : - 1000 ,
1601+ InboundFeeRate : - 1000 ,
1602+ }, 9 ),
1603+ symmetricTestChannel ("d" , "f" , chanCapSat , & testChannelPolicy {
1604+ Expiry : 144 ,
1605+ FeeRate : 60000 ,
1606+ MinHTLC : lnwire .NewMSatFromSatoshis (20 ),
1607+ MaxHTLC : lnwire .NewMSatFromSatoshis (120 ),
1608+ Features : paymentAddrFeatures ,
1609+ // The inbound fee will not be active for the last hop.
1610+ InboundFeeBaseMsat : 2000 ,
1611+ InboundFeeRate : 2000 ,
1612+ }, 10 ),
15911613 }
15921614
15931615 testGraph , err := createTestGraphFromChannels (
@@ -1686,6 +1708,30 @@ func TestBuildRoute(t *testing.T) {
16861708 require .Equal (t , lnwire .MilliSatoshi (21200 ), rt .TotalAmount )
16871709 require .Equal (t , lnwire .MilliSatoshi (20000 ), rt .Hops [1 ].AmtToForward )
16881710
1711+ // Check that we compute a correct forwarding amount that involves
1712+ // inbound fees. We expect a similar amount as for the above case of
1713+ // b->c, but reduced by the inbound discount on the channel a->d.
1714+ // We get 106000 - 1000 (base in) - 0.001 * 106000 (rate in) = 104894.
1715+ hops = []route.Vertex {ctx .aliases ["d" ], ctx .aliases ["f" ]}
1716+ amt = lnwire .NewMSatFromSatoshis (100 )
1717+ rt , err = ctx .router .BuildRoute (& amt , hops , nil , 40 , & payAddr )
1718+ require .NoError (t , err )
1719+ checkHops (rt , []uint64 {9 , 10 }, payAddr )
1720+ require .EqualValues (t , 104894 , rt .TotalAmount )
1721+
1722+ // Also check the min amount with inbound fees. The min amount bumps
1723+ // this to 20000 msat for the last hop. The outbound fee is 1200 msat,
1724+ // the inbound fee is -1021.2 msat (rounded down). This results in a
1725+ // total fee of 179 msat, giving a sender amount of 20179 msat. The
1726+ // determined receiver amount however reduces this to 20001 msat again
1727+ // due to rounding. This would not be compatible with the sender amount
1728+ // of 20179 msat, which results in underpayment of 1 msat in fee. There
1729+ // is a third pass through newRoute in which this gets corrected to end
1730+ hops = []route.Vertex {ctx .aliases ["d" ], ctx .aliases ["f" ]}
1731+ rt , err = ctx .router .BuildRoute (nil , hops , nil , 40 , & payAddr )
1732+ require .NoError (t , err )
1733+ checkHops (rt , []uint64 {9 , 10 }, payAddr )
1734+ require .EqualValues (t , 20180 , rt .TotalAmount , "%v" , rt .TotalAmount )
16891735}
16901736
16911737// TestReceiverAmtForwardPass tests that the forward pass returns the expected
@@ -1831,6 +1877,9 @@ func TestSenderAmtBackwardPass(t *testing.T) {
18311877 policy : & models.CachedEdgePolicy {
18321878 FeeBaseMSat : 112 ,
18331879 },
1880+ inboundFees : models.InboundFee {
1881+ Base : 111 ,
1882+ },
18341883 capacity : capacity ,
18351884 },
18361885 },
@@ -1841,6 +1890,9 @@ func TestSenderAmtBackwardPass(t *testing.T) {
18411890 policy : & models.CachedEdgePolicy {
18421891 FeeBaseMSat : 222 ,
18431892 },
1893+ inboundFees : models.InboundFee {
1894+ Base : 222 ,
1895+ },
18441896 capacity : capacity ,
18451897 },
18461898 },
@@ -1852,6 +1904,12 @@ func TestSenderAmtBackwardPass(t *testing.T) {
18521904 FeeBaseMSat : 333 ,
18531905 MinHTLC : minHTLC ,
18541906 },
1907+ // In pathfinding, inbound fees are not
1908+ // populated for exit hops because the
1909+ // newNodeEdgeUnifier enforces this.
1910+ // This is important as otherwise we
1911+ // would not fail the min HTLC check in
1912+ // getEdge.
18551913 capacity : capacity ,
18561914 },
18571915 },
@@ -1870,20 +1928,51 @@ func TestSenderAmtBackwardPass(t *testing.T) {
18701928 edgeUnifiers , true , 1 , & bandwidthHints ,
18711929 )
18721930 require .NoError (t , err )
1873- require .Equal (t , minHTLC + 333 + 222 , senderAmount )
1931+ require .Equal (t , minHTLC + 333 + 222 + 222 + 111 , senderAmount )
18741932
18751933 // Do a search for a specific amount.
18761934 unifiedEdges , senderAmount , err = senderAmtBackwardPass (
18771935 edgeUnifiers , false , testReceiverAmt , & bandwidthHints ,
18781936 )
18791937 require .NoError (t , err )
1880- require .Equal (t , testReceiverAmt + 333 + 222 , senderAmount )
1938+ require .Equal (t , testReceiverAmt + 333 + 222 + 222 + 111 , senderAmount )
18811939
18821940 // Check that we arrive at the same receiver amount by doing a forward
18831941 // pass.
18841942 receiverAmt , err := receiverAmtForwardPass (senderAmount , unifiedEdges )
18851943 require .NoError (t , err )
18861944 require .Equal (t , testReceiverAmt , receiverAmt )
1945+
1946+ // Insert a policy that leads to rounding.
1947+ edgeUnifiers [1 ] = & edgeUnifier {
1948+ edges : []* unifiedEdge {
1949+ {
1950+ policy : & models.CachedEdgePolicy {
1951+ FeeBaseMSat : 20 ,
1952+ FeeProportionalMillionths : 100 ,
1953+ },
1954+ inboundFees : models.InboundFee {
1955+ Base : - 10 ,
1956+ Rate : - 50 ,
1957+ },
1958+ capacity : capacity ,
1959+ },
1960+ },
1961+ }
1962+
1963+ unifiedEdges , senderAmount , err = senderAmtBackwardPass (
1964+ edgeUnifiers , false , testReceiverAmt , & bandwidthHints ,
1965+ )
1966+ require .NoError (t , err )
1967+
1968+ // For this route, we have some rounding errors, so we can't expect the
1969+ // exact amount, but it should be higher than the exact amount, to not
1970+ // end up below a min HTLC constraint.
1971+ receiverAmt , err = receiverAmtForwardPass (senderAmount , unifiedEdges )
1972+ require .NoError (t , err )
1973+ require .NotEqual (t , testReceiverAmt , receiverAmt )
1974+ require .InDelta (t , int64 (testReceiverAmt ), int64 (receiverAmt ), 1 )
1975+ require .GreaterOrEqual (t , int64 (receiverAmt ), int64 (testReceiverAmt ))
18871976}
18881977
18891978// TestInboundOutbound tests the functions that computes the incoming and
0 commit comments