From dd7e2e9e0491eaeee3c5c8dcf451ba7cbdd917c3 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 24 Oct 2018 10:28:31 +0200 Subject: [PATCH 1/6] routing: move logging into applyChannelUpdate To remove code duplicated at all call sites to check err and log. --- routing/router.go | 53 ++++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/routing/router.go b/routing/router.go index 31a269e4..f575ef26 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1804,11 +1804,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // correct block height is. case *lnwire.FailExpiryTooSoon: update := onionErr.Update - err := r.applyChannelUpdate(&update, errSource) - if err != nil { - log.Errorf("unable to apply channel "+ - "update for onion error: %v", err) - } + r.applyChannelUpdate(&update, errSource) pruneVertexFailure( paySession, route, errSource, false, @@ -1830,11 +1826,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // and continue with the rest of the routes. case *lnwire.FailAmountBelowMinimum: update := onionErr.Update - err := r.applyChannelUpdate(&update, errSource) - if err != nil { - log.Errorf("unable to apply channel "+ - "update for onion error: %v", err) - } + r.applyChannelUpdate(&update, errSource) return preImage, nil, sendError @@ -1843,11 +1835,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // newly updated fees. case *lnwire.FailFeeInsufficient: update := onionErr.Update - err := r.applyChannelUpdate(&update, errSource) - if err != nil { - log.Errorf("unable to apply channel "+ - "update for onion error: %v", err) - + updateOk := r.applyChannelUpdate(&update, errSource) + if !updateOk { pruneEdgeFailure( paySession, route, errSource, ) @@ -1877,11 +1866,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // finding. case *lnwire.FailIncorrectCltvExpiry: update := onionErr.Update - err := r.applyChannelUpdate(&update, errSource) - if err != nil { - log.Errorf("unable to apply channel "+ - "update for onion error: %v", err) - } + r.applyChannelUpdate(&update, errSource) pruneVertexFailure( paySession, route, errSource, false, @@ -1893,11 +1878,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // the update and continue. case *lnwire.FailChannelDisabled: update := onionErr.Update - err := r.applyChannelUpdate(&update, errSource) - if err != nil { - log.Errorf("unable to apply channel "+ - "update for onion error: %v", err) - } + r.applyChannelUpdate(&update, errSource) pruneEdgeFailure(paySession, route, errSource) continue @@ -1907,11 +1888,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // now, and continue onwards with our path finding. case *lnwire.FailTemporaryChannelFailure: update := onionErr.Update - err := r.applyChannelUpdate(update, errSource) - if err != nil { - log.Errorf("unable to apply channel "+ - "update for onion error: %v", err) - } + r.applyChannelUpdate(update, errSource) pruneEdgeFailure(paySession, route, errSource) continue @@ -2048,17 +2025,18 @@ func pruneEdgeFailure(paySession *paymentSession, route *Route, } // applyChannelUpdate validates a channel update and if valid, applies it to the -// database. +// database. It returns a bool indicating whether the updates was successful. func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate, - pubKey *btcec.PublicKey) error { + pubKey *btcec.PublicKey) bool { // If we get passed a nil channel update (as it's optional with some - // onion errors), then we'll exit early with a nil error. + // onion errors), then we'll exit early with a success result. if msg == nil { - return nil + return true } if err := ValidateChannelUpdateAnn(pubKey, msg); err != nil { - return err + log.Errorf("Unable to validate channel update: %v", err) + return false } err := r.UpdateEdge(&channeldb.ChannelEdgePolicy{ @@ -2072,10 +2050,11 @@ func (r *ChannelRouter) applyChannelUpdate(msg *lnwire.ChannelUpdate, FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate), }) if err != nil && !IsError(err, ErrIgnored, ErrOutdated) { - return fmt.Errorf("Unable to apply channel update: %v", err) + log.Errorf("Unable to apply channel update: %v", err) + return false } - return nil + return true } // AddNode is used to add information about a node to the router database. If From aca136a91c4921729fe4c236ba1f9fde63f35ef8 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 24 Oct 2018 10:35:34 +0200 Subject: [PATCH 2/6] routing: remove unused pruneVertexFailure parameters --- routing/router.go | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/routing/router.go b/routing/router.go index f575ef26..29fbd06a 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1807,7 +1807,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, r.applyChannelUpdate(&update, errSource) pruneVertexFailure( - paySession, route, errSource, false, + paySession, errSource, ) continue @@ -1850,7 +1850,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, _, ok := errFailedFeeChans[chanID] if ok { pruneVertexFailure( - paySession, route, errSource, false, + paySession, errSource, ) continue } @@ -1869,7 +1869,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, r.applyChannelUpdate(&update, errSource) pruneVertexFailure( - paySession, route, errSource, false, + paySession, errSource, ) continue @@ -1898,7 +1898,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // continue. case *lnwire.FailRequiredNodeFeatureMissing: pruneVertexFailure( - paySession, route, errSource, false, + paySession, errSource, ) continue @@ -1907,7 +1907,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // continue. case *lnwire.FailRequiredChannelFeatureMissing: pruneVertexFailure( - paySession, route, errSource, false, + paySession, errSource, ) continue @@ -1927,13 +1927,13 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // routes. case *lnwire.FailTemporaryNodeFailure: pruneVertexFailure( - paySession, route, errSource, false, + paySession, errSource, ) continue case *lnwire.FailPermanentNodeFailure: pruneVertexFailure( - paySession, route, errSource, false, + paySession, errSource, ) continue @@ -1948,7 +1948,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // that node. case *lnwire.FailExpiryTooFar: pruneVertexFailure( - paySession, route, errSource, false, + paySession, errSource, ) continue @@ -1971,24 +1971,11 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // pruneVertexFailure will attempt to prune a vertex from the current available // vertexes of the target payment session in response to an encountered routing // error. -func pruneVertexFailure(paySession *paymentSession, route *Route, - errSource *btcec.PublicKey, nextNode bool) { - +func pruneVertexFailure(paySession *paymentSession, errSource *btcec.PublicKey) { // By default, we'll try to prune the node that actually sent us the // error. errNode := NewVertex(errSource) - // If this failure indicates that the node _after_ the source of the - // error was not found. As a result, we'll locate the vertex for that - // node itself. - if nextNode { - nodeToPrune, ok := route.nextHopVertex(errSource) - - if ok { - errNode = nodeToPrune - } - } - // Once we've located the vertex, we'll report this failure to // missionControl and restart path finding. paySession.ReportVertexFailure(errNode) From 71037969b70bcc4e3604f551da6c6064b20900ab Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 24 Oct 2018 10:41:15 +0200 Subject: [PATCH 3/6] routing: remove pruneVertexFailure function --- routing/router.go | 46 +++++++++------------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/routing/router.go b/routing/router.go index 29fbd06a..0d3cdc28 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1758,6 +1758,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, } errSource := fErr.ErrorSource + errVertex := NewVertex(errSource) log.Tracef("node=%x reported failure when sending "+ "htlc=%x", errSource.SerializeCompressed(), @@ -1806,9 +1807,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, update := onionErr.Update r.applyChannelUpdate(&update, errSource) - pruneVertexFailure( - paySession, errSource, - ) + paySession.ReportVertexFailure(errVertex) continue // If we hit an instance of onion payload corruption or @@ -1849,9 +1848,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, chanID := update.ShortChannelID _, ok := errFailedFeeChans[chanID] if ok { - pruneVertexFailure( - paySession, errSource, - ) + paySession.ReportVertexFailure(errVertex) continue } @@ -1868,9 +1865,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, update := onionErr.Update r.applyChannelUpdate(&update, errSource) - pruneVertexFailure( - paySession, errSource, - ) + paySession.ReportVertexFailure(errVertex) continue // The outgoing channel that this node was meant to @@ -1897,18 +1892,14 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // required features, then we'll note this error and // continue. case *lnwire.FailRequiredNodeFeatureMissing: - pruneVertexFailure( - paySession, errSource, - ) + paySession.ReportVertexFailure(errVertex) continue // If the send fail due to a node not having the // required features, then we'll note this error and // continue. case *lnwire.FailRequiredChannelFeatureMissing: - pruneVertexFailure( - paySession, errSource, - ) + paySession.ReportVertexFailure(errVertex) continue // If the next hop in the route wasn't known or @@ -1926,15 +1917,11 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // reason, then we'll note this and continue with the // routes. case *lnwire.FailTemporaryNodeFailure: - pruneVertexFailure( - paySession, errSource, - ) + paySession.ReportVertexFailure(errVertex) continue case *lnwire.FailPermanentNodeFailure: - pruneVertexFailure( - paySession, errSource, - ) + paySession.ReportVertexFailure(errVertex) continue // If we crafted a route that contains a too long time @@ -1947,9 +1934,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // that as a hint during future path finding through // that node. case *lnwire.FailExpiryTooFar: - pruneVertexFailure( - paySession, errSource, - ) + paySession.ReportVertexFailure(errVertex) continue // If we get a permanent channel or node failure, then @@ -1968,19 +1953,6 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, } } -// pruneVertexFailure will attempt to prune a vertex from the current available -// vertexes of the target payment session in response to an encountered routing -// error. -func pruneVertexFailure(paySession *paymentSession, errSource *btcec.PublicKey) { - // By default, we'll try to prune the node that actually sent us the - // error. - errNode := NewVertex(errSource) - - // Once we've located the vertex, we'll report this failure to - // missionControl and restart path finding. - paySession.ReportVertexFailure(errNode) -} - // pruneEdgeFailure will attempts to prune an edge from the current available // edges of the target payment session in response to an encountered routing // error. From ac04729cff28af9c3269d23badfb953a2271150e Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 24 Oct 2018 21:16:38 +0200 Subject: [PATCH 4/6] routing: use complete route in test Previously not all route fields were properly populated. Example: prev and next hop maps. --- routing/router_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/routing/router_test.go b/routing/router_test.go index 8bfebe82..7a62754e 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -421,9 +421,10 @@ func TestChannelUpdateValidation(t *testing.T) { }, } - route := &Route{ - Hops: hops, - } + route := NewRouteFromHops( + lnwire.MilliSatoshi(10000), 100, + NewVertex(ctx.aliases["a"]), hops, + ) // Set up a channel update message with an invalid signature to be // returned to the sender. From 6ba1144528c337bd4bf715d40c3e030b8f73a756 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 24 Oct 2018 09:59:38 +0200 Subject: [PATCH 5/6] routing: move failed channels map into payment session This is a small preparatory step towards moving mission control logic out of router and reusing the acquired routing result data. --- routing/missioncontrol.go | 24 ++++++++++++++++-------- routing/router.go | 12 +++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index b722b942..115f1cbe 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -164,6 +164,12 @@ type paymentSession struct { bandwidthHints map[uint64]lnwire.MilliSatoshi + // errFailedFeeChans is a map of the short channel ID's that were the + // source of policy related routing failures during this payment attempt. + // We'll use this map to prune out channels when the first error may not + // require pruning, but any subsequent ones do. + errFailedPolicyChans map[uint64]struct{} + mc *missionControl haveRoutes bool @@ -236,10 +242,11 @@ func (m *missionControl) NewPaymentSession(routeHints [][]HopHint, } return &paymentSession{ - pruneViewSnapshot: viewSnapshot, - additionalEdges: edges, - bandwidthHints: bandwidthHints, - mc: m, + pruneViewSnapshot: viewSnapshot, + additionalEdges: edges, + bandwidthHints: bandwidthHints, + errFailedPolicyChans: make(map[uint64]struct{}), + mc: m, }, nil } @@ -249,10 +256,11 @@ func (m *missionControl) NewPaymentSession(routeHints [][]HopHint, // used for things like channel rebalancing, and swaps. func (m *missionControl) NewPaymentSessionFromRoutes(routes []*Route) *paymentSession { return &paymentSession{ - pruneViewSnapshot: m.GraphPruneView(), - haveRoutes: true, - preBuiltRoutes: routes, - mc: m, + pruneViewSnapshot: m.GraphPruneView(), + haveRoutes: true, + preBuiltRoutes: routes, + errFailedPolicyChans: make(map[uint64]struct{}), + mc: m, } } diff --git a/routing/router.go b/routing/router.go index 0d3cdc28..508e1aa3 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1641,12 +1641,6 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, sendError error ) - // errFailedFeeChans is a map of the short channel ID's that were the - // source of fee related routing failures during this payment attempt. - // We'll use this map to prune out channels when the first error may - // not require pruning, but any subsequent ones do. - errFailedFeeChans := make(map[lnwire.ShortChannelID]struct{}) - // We'll also fetch the current block height so we can properly // calculate the required HTLC time locks within the route. _, currentHeight, err := r.cfg.Chain.GetBestBlock() @@ -1845,8 +1839,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // reported a fee related failure for this // node. If so, then we'll actually prune out // the vertex for now. - chanID := update.ShortChannelID - _, ok := errFailedFeeChans[chanID] + chanID := update.ShortChannelID.ToUint64() + _, ok := paySession.errFailedPolicyChans[chanID] if ok { paySession.ReportVertexFailure(errVertex) continue @@ -1854,7 +1848,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // Finally, we'll record a fee failure from // this node and move on. - errFailedFeeChans[chanID] = struct{}{} + paySession.errFailedPolicyChans[chanID] = struct{}{} continue // If we get the failure for an intermediate node that From b6ce03e569093e3ad8d1941cb868d6013d4fe0f6 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 24 Oct 2018 10:45:30 +0200 Subject: [PATCH 6/6] routing: make routing retry behaviour consistent Fixes the following issues: - If the channel update of FailFeeInsufficient contains an invalid channel update, it is not possible to properly add to the failed channels set. - FailAmountBelowMinimum may apply a channel update, but does not retry. - FailIncorrectCltvExpiry immediately prunes the vertex without trying one more time. In this commit, the logic for all three policy related errors is aligned. --- routing/missioncontrol.go | 25 +++++++ routing/router.go | 153 +++++++++++++++++++++----------------- 2 files changed, 108 insertions(+), 70 deletions(-) diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index 115f1cbe..09a6a027 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -339,6 +339,31 @@ func (p *paymentSession) ReportChannelFailure(e uint64) { p.mc.Unlock() } +// ReportChannelPolicyFailure handles a failure message that relates to a +// channel policy. For these types of failures, the policy is updated and we +// want to keep it included during path finding. This function does mark the +// edge as 'policy failed once'. The next time it fails, the whole node will be +// pruned. This is to prevent nodes from keeping us busy by continuously sending +// new channel updates. +func (p *paymentSession) ReportChannelPolicyFailure( + errSource Vertex, failedChanID uint64) { + + // Check to see if we've already reported a policy related failure for + // this channel. If so, then we'll prune out the vertex. + _, ok := p.errFailedPolicyChans[failedChanID] + if ok { + // TODO(joostjager): is this aggresive pruning still necessary? + // Just pruning edges may also work unless there is a huge + // number of failing channels from that node? + p.ReportVertexFailure(errSource) + + return + } + + // Finally, we'll record a policy failure from this node and move on. + p.errFailedPolicyChans[failedChanID] = struct{}{} +} + // RequestRoute returns a route which is likely to be capable for successfully // routing the specified HTLC payment to the target node. Initially the first // set of paths returned from this method may encounter routing failure along diff --git a/routing/router.go b/routing/router.go index 508e1aa3..f19893df 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1758,6 +1758,49 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, "htlc=%x", errSource.SerializeCompressed(), payment.PaymentHash[:]) + // Always determine chan id ourselves, because a channel + // update with id may not be available. + failedChanID, err := getFailedChannelID(route, errSource) + if err != nil { + return preImage, nil, err + } + + // processChannelUpdateAndRetry is a closure that + // handles a failure message containing a channel + // update. This function always tries to apply the + // channel update and passes on the result to the + // payment session to adjust its view on the reliability + // of the network. + // + // As channel id, the locally determined channel id is + // used. It does not rely on the channel id that is part + // of the channel update message, because the remote + // node may lie to us or the update may be corrupt. + processChannelUpdateAndRetry := func( + update *lnwire.ChannelUpdate, + pubKey *btcec.PublicKey) { + + // Try to apply the channel update. + updateOk := r.applyChannelUpdate(update, pubKey) + + // If the update could not be applied, prune the + // edge. There is no reason to continue trying + // this channel. + // + // TODO: Could even prune the node completely? + // Or is there a valid reason for the channel + // update to fail? + if !updateOk { + paySession.ReportChannelFailure( + failedChanID, + ) + } + + paySession.ReportChannelPolicyFailure( + NewVertex(errSource), failedChanID, + ) + } + switch onionErr := fErr.FailureMessage.(type) { // If the end destination didn't know they payment // hash, then we'll terminate immediately. @@ -1798,9 +1841,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // that sent us this error, as it doesn't now what the // correct block height is. case *lnwire.FailExpiryTooSoon: - update := onionErr.Update - r.applyChannelUpdate(&update, errSource) - + r.applyChannelUpdate(&onionErr.Update, errSource) paySession.ReportVertexFailure(errVertex) continue @@ -1814,72 +1855,47 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, case *lnwire.FailInvalidOnionKey: return preImage, nil, sendError - // If the onion error includes a channel update, and - // isn't necessarily fatal, then we'll apply the update - // and continue with the rest of the routes. + // If we get a failure due to violating the minimum + // amount, we'll apply the new minimum amount and retry + // routing. case *lnwire.FailAmountBelowMinimum: - update := onionErr.Update - r.applyChannelUpdate(&update, errSource) + processChannelUpdateAndRetry( + &onionErr.Update, errSource, + ) + continue - return preImage, nil, sendError - - // If we get a failure due to a fee, so we'll apply the + // If we get a failure due to a fee, we'll apply the // new fee update, and retry our attempt using the // newly updated fees. case *lnwire.FailFeeInsufficient: - update := onionErr.Update - updateOk := r.applyChannelUpdate(&update, errSource) - if !updateOk { - pruneEdgeFailure( - paySession, route, errSource, - ) - } - - // We'll now check to see if we've already - // reported a fee related failure for this - // node. If so, then we'll actually prune out - // the vertex for now. - chanID := update.ShortChannelID.ToUint64() - _, ok := paySession.errFailedPolicyChans[chanID] - if ok { - paySession.ReportVertexFailure(errVertex) - continue - } - - // Finally, we'll record a fee failure from - // this node and move on. - paySession.errFailedPolicyChans[chanID] = struct{}{} + processChannelUpdateAndRetry( + &onionErr.Update, errSource, + ) continue // If we get the failure for an intermediate node that // disagrees with our time lock values, then we'll - // prune it out for now, and continue with path - // finding. + // apply the new delta value and try it once more. case *lnwire.FailIncorrectCltvExpiry: - update := onionErr.Update - r.applyChannelUpdate(&update, errSource) - - paySession.ReportVertexFailure(errVertex) + processChannelUpdateAndRetry( + &onionErr.Update, errSource, + ) continue // The outgoing channel that this node was meant to // forward one is currently disabled, so we'll apply // the update and continue. case *lnwire.FailChannelDisabled: - update := onionErr.Update - r.applyChannelUpdate(&update, errSource) - - pruneEdgeFailure(paySession, route, errSource) + r.applyChannelUpdate(&onionErr.Update, errSource) + paySession.ReportChannelFailure(failedChanID) continue // It's likely that the outgoing channel didn't have // sufficient capacity, so we'll prune this edge for // now, and continue onwards with our path finding. case *lnwire.FailTemporaryChannelFailure: - update := onionErr.Update - r.applyChannelUpdate(update, errSource) - - pruneEdgeFailure(paySession, route, errSource) + r.applyChannelUpdate(onionErr.Update, errSource) + paySession.ReportChannelFailure(failedChanID) continue // If the send fail due to a node not having the @@ -1904,7 +1920,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // returning errors in order to attempt to black list // another node. case *lnwire.FailUnknownNextPeer: - pruneEdgeFailure(paySession, route, errSource) + paySession.ReportChannelFailure(failedChanID) continue // If the node wasn't able to forward for which ever @@ -1935,7 +1951,7 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, // we'll note this (exclude the vertex/edge), and // continue with the rest of the routes. case *lnwire.FailPermanentChannelFailure: - pruneEdgeFailure(paySession, route, errSource) + paySession.ReportChannelFailure(failedChanID) continue default: @@ -1947,34 +1963,31 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment, } } -// pruneEdgeFailure will attempts to prune an edge from the current available -// edges of the target payment session in response to an encountered routing -// error. -func pruneEdgeFailure(paySession *paymentSession, route *Route, - errSource *btcec.PublicKey) { +// getFailedChannelID tries to locate the failing channel given a route and the +// pubkey of the node that sent the error. It will assume that the error is +// associated with the outgoing channel of the error node. +func getFailedChannelID(route *Route, errSource *btcec.PublicKey) ( + uint64, error) { // As this error indicates that the target channel was unable to carry // this HTLC (for w/e reason), we'll query the index to find the // _outgoing_ channel the source of the error was meant to pass the // HTLC along to. - badChan, ok := route.nextHopChannel(errSource) - if !ok { - // If we weren't able to find the hop *after* this node, then - // we'll attempt to disable the previous channel. - prevChan, ok := route.prevHopChannel( - errSource, - ) - - if !ok { - return - } - - badChan = prevChan + if badChan, ok := route.nextHopChannel(errSource); ok { + return badChan.ChannelID, nil } - // If the channel was found, then we'll inform mission control of this - // failure so future attempts avoid this link temporarily. - paySession.ReportChannelFailure(badChan.ChannelID) + // If we weren't able to find the hop *after* this node, then we'll + // attempt to disable the previous channel. + // + // TODO(joostjager): errSource must be the final hop then? In that case, + // certain types of errors are not expected. For example + // FailUnknownNextPeer. This could be a reason to prune the node? + if prevChan, ok := route.prevHopChannel(errSource); ok { + return prevChan.ChannelID, nil + } + + return 0, fmt.Errorf("cannot find channel in route") } // applyChannelUpdate validates a channel update and if valid, applies it to the