Skip to content

Commit 2c79bf9

Browse files
committed
routing: refactor backward and forward pass
1 parent c18694f commit 2c79bf9

File tree

2 files changed

+91
-31
lines changed

2 files changed

+91
-31
lines changed

routing/router.go

Lines changed: 84 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,69 +1545,123 @@ func getEdgeUnifiers(source route.Vertex, hops []route.Vertex,
15451545
// determines the amount that should be sent to fulfill min HTLC requirements.
15461546
// The minimal sender amount can be searched for by activating useMinAmt.
15471547
func senderAmtBackwardPass(unifiers []*edgeUnifier, useMinAmt bool,
1548-
runningAmt lnwire.MilliSatoshi,
1548+
receiverAmt lnwire.MilliSatoshi,
15491549
bandwidthHints bandwidthHints) ([]*unifiedEdge, lnwire.MilliSatoshi,
15501550
error) {
15511551

1552+
if len(unifiers) == 0 {
1553+
return nil, 0, fmt.Errorf("no unifiers provided")
1554+
}
1555+
15521556
var unifiedEdges = make([]*unifiedEdge, len(unifiers))
15531557

1554-
// Traverse hops backwards to accumulate fees in the running amounts.
1555-
for i := len(unifiers) - 1; i >= 0; i-- {
1556-
localChan := i == 0
1557-
edgeUnifier := unifiers[i]
1558+
// We traverse the route backwards and handle the last hop separately.
1559+
edgeUnifier := unifiers[len(unifiers)-1]
1560+
1561+
// incomingAmt tracks the amount that is forwarded on the edges of a
1562+
// route. The last hop only forwards the amount that the receiver should
1563+
// receive, as there are no fees paid to the last node.
1564+
incomingAmt := receiverAmt
1565+
1566+
// If using min amt, increase the amount if needed to fulfill min HTLC
1567+
// requirements.
1568+
if useMinAmt {
1569+
min := edgeUnifier.minAmt()
1570+
if min > incomingAmt {
1571+
incomingAmt = min
1572+
}
1573+
}
15581574

1559-
// If using min amt, increase amt if needed.
1575+
// Get an edge for the specific amount that we want to forward.
1576+
edge := edgeUnifier.getEdge(incomingAmt, bandwidthHints, 0)
1577+
if edge == nil {
1578+
log.Errorf("Cannot find policy with amt=%v "+
1579+
"for hop %v", incomingAmt, len(unifiers)-1)
1580+
1581+
return nil, 0, ErrNoChannel{position: len(unifiers) - 1}
1582+
}
1583+
1584+
unifiedEdges[len(unifiers)-1] = edge
1585+
1586+
// Handle the rest of the route except the last hop.
1587+
for i := len(unifiers) - 2; i >= 0; i-- {
1588+
edgeUnifier = unifiers[i]
1589+
1590+
// If using min amt, increase the amount if needed to fulfill
1591+
// min HTLC requirements.
15601592
if useMinAmt {
15611593
min := edgeUnifier.minAmt()
1562-
if min > runningAmt {
1563-
runningAmt = min
1594+
if min > incomingAmt {
1595+
incomingAmt = min
15641596
}
15651597
}
15661598

1567-
// Get an edge for the specific amount that we want to forward.
1568-
edge := edgeUnifier.getEdge(runningAmt, bandwidthHints, 0)
1569-
if edge == nil {
1570-
log.Errorf("Cannot find policy with amt=%v for hop %v",
1571-
runningAmt, i)
1599+
// A --current hop-- B --next hop: incomingAmt-- C
1600+
// The outbound fee paid to the current end node B is based on
1601+
// the amount that the next hop forwards and B's policy for that
1602+
// hop.
1603+
outboundFee := unifiedEdges[i+1].policy.ComputeFee(
1604+
incomingAmt,
1605+
)
15721606

1573-
return nil, 0, ErrNoChannel{
1574-
position: i,
1575-
}
1576-
}
1607+
netAmount := incomingAmt + outboundFee
15771608

1578-
// Add fee for this hop.
1579-
if !localChan {
1580-
runningAmt += edge.policy.ComputeFee(runningAmt)
1609+
// We need to select an edge that can forward the requested
1610+
// amount.
1611+
edge = edgeUnifier.getEdge(
1612+
netAmount, bandwidthHints, outboundFee,
1613+
)
1614+
if edge == nil {
1615+
return nil, 0, ErrNoChannel{position: i}
15811616
}
15821617

1618+
fee := outboundFee
1619+
15831620
log.Tracef("Select channel %v at position %v",
15841621
edge.policy.ChannelID, i)
15851622

1623+
// Finally, we update the amount that needs to flow into node B
1624+
// from A, which is the next hop's forwarding amount plus the
1625+
// fee for B: A --current hop: incomingAmt-- B --next hop-- C
1626+
incomingAmt += fee
1627+
15861628
unifiedEdges[i] = edge
15871629
}
15881630

1589-
return unifiedEdges, runningAmt, nil
1631+
return unifiedEdges, incomingAmt, nil
15901632
}
15911633

15921634
// receiverAmtForwardPass returns the amount that a receiver will receive after
15931635
// deducting all fees from the sender amount.
15941636
func receiverAmtForwardPass(runningAmt lnwire.MilliSatoshi,
15951637
unifiedEdges []*unifiedEdge) (lnwire.MilliSatoshi, error) {
15961638

1639+
if len(unifiedEdges) == 0 {
1640+
return 0, fmt.Errorf("no edges to forward through")
1641+
}
1642+
1643+
inEdge := unifiedEdges[0]
1644+
if !inEdge.amtInRange(runningAmt) {
1645+
log.Errorf("Amount %v not in range for hop index %v",
1646+
runningAmt, 0)
1647+
1648+
return 0, ErrNoChannel{position: 0}
1649+
}
1650+
15971651
// Now that we arrived at the start of the route and found out the route
15981652
// total amount, we make a forward pass. Because the amount may have
15991653
// been increased in the backward pass, fees need to be recalculated and
16001654
// amount ranges re-checked.
1601-
for i, edge := range unifiedEdges {
1602-
if i > 0 {
1603-
// Decrease the amount to send while going forward.
1604-
runningAmt -= edge.policy.ComputeFeeFromIncoming(
1605-
runningAmt,
1606-
)
1607-
}
1655+
for i := 1; i < len(unifiedEdges); i++ {
1656+
outEdge := unifiedEdges[i]
1657+
1658+
// Decrease the amount to send while going forward.
1659+
runningAmt -= outEdge.policy.ComputeFeeFromIncoming(
1660+
runningAmt,
1661+
)
16081662

1609-
if !edge.amtInRange(runningAmt) {
1610-
log.Errorf("Amount %v not in range for hop %v",
1663+
if !outEdge.amtInRange(runningAmt) {
1664+
log.Errorf("Amount %v not in range for hop index %v",
16111665
runningAmt, i)
16121666

16131667
return 0, ErrNoChannel{position: i}

routing/router_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,11 @@ func TestBuildRoute(t *testing.T) {
16191619
_, err = rand.Read(payAddr[:])
16201620
require.NoError(t, err)
16211621

1622+
// Test that we can't build a route when no hops are given.
1623+
hops = []route.Vertex{}
1624+
_, err = ctx.router.BuildRoute(nil, hops, nil, 40, nil)
1625+
require.Error(t, err)
1626+
16221627
// Create hop list for an unknown destination.
16231628
hops := []route.Vertex{ctx.aliases["b"], ctx.aliases["y"]}
16241629
_, err = ctx.router.BuildRoute(nil, hops, nil, 40, &payAddr)
@@ -1698,7 +1703,8 @@ func TestReceiverAmtForwardPass(t *testing.T) {
16981703
expectedErr string
16991704
}{
17001705
{
1701-
name: "empty",
1706+
name: "empty",
1707+
expectedErr: "no edges to forward through",
17021708
},
17031709
{
17041710
name: "single edge, no valid policy",

0 commit comments

Comments
 (0)