routing: don't return an error from failPayment within handleSendError
In this commit, we fix a regression introduced by a recent bug fix in this area. Before this change, we'd inspect the error returned by `processSendError`, and then fail the payment from the PoV of mission control using the returned error. A recent refactoring removed `processSendError` and combined the logic with `tryApplyChannelUpdate` in order to introduce a new `handleSendError` method that consolidates the logic within the `shardHandler`. Along the way, the behavior of the prior check was replicated in the form of a new internal `failPayment` closure. However, the new function closure ends up returning a `channeldb.FailureReason` instance, which is actually an `error`. In the wild, when `SendToRoute` fails due to an error at the destination, then this new logic caused the `handleSendErorr` method to fail with an error, returning an unstructured error back to the caller, instead of the usual payment failure details. We fix this by no longer checking the `handleSendErorr` for an error as normal. The `handleSendErorr` function as is will always return an error of type `*channeldb.FailureReason`, therefore we don't need to treat it as a normal error. Instead, we check for the type of error returned, and update the control tower state accordingly. With this commit, the test added in the prior commit now passes. Fixes #5477.
This commit is contained in:
parent
b24edb2833
commit
525ef594c7
@ -782,10 +782,13 @@ func (p *shardHandler) handleSendError(attempt *channeldb.HTLCAttemptInfo,
|
|||||||
if err := p.router.cfg.Control.Fail(
|
if err := p.router.cfg.Control.Fail(
|
||||||
p.identifier, *reason); err != nil {
|
p.identifier, *reason); err != nil {
|
||||||
|
|
||||||
return err
|
log.Errorf("unable to report failure to control "+
|
||||||
|
"tower: %v", err)
|
||||||
|
|
||||||
|
return &internalErrorReason
|
||||||
}
|
}
|
||||||
|
|
||||||
return *reason
|
return reason
|
||||||
}
|
}
|
||||||
|
|
||||||
// reportFail is a helper closure that reports the failure to the
|
// reportFail is a helper closure that reports the failure to the
|
||||||
|
@ -2,6 +2,7 @@ package routing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
goErrors "errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -2163,13 +2164,23 @@ func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, rt *route.Route) (
|
|||||||
// mark the payment failed with the control tower immediately. Process
|
// mark the payment failed with the control tower immediately. Process
|
||||||
// the error to check if it maps into a terminal error code, if not use
|
// the error to check if it maps into a terminal error code, if not use
|
||||||
// a generic NO_ROUTE error.
|
// a generic NO_ROUTE error.
|
||||||
if err := sh.handleSendError(attempt, shardError); err != nil {
|
var failureReason *channeldb.FailureReason
|
||||||
return nil, err
|
err = sh.handleSendError(attempt, shardError)
|
||||||
}
|
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// If we weren't able to extract a proper failure reason (which can
|
||||||
|
// happen if the second chance logic is triggered), then we'll use the
|
||||||
|
// normal no route error.
|
||||||
|
case err == nil:
|
||||||
err = r.cfg.Control.Fail(
|
err = r.cfg.Control.Fail(
|
||||||
paymentIdentifier, channeldb.FailureReasonNoRoute,
|
paymentIdentifier, channeldb.FailureReasonNoRoute,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// If this is a failure reason, then we'll apply the failure directly
|
||||||
|
// to the control tower, and return the normal response to the caller.
|
||||||
|
case goErrors.As(err, &failureReason):
|
||||||
|
err = r.cfg.Control.Fail(paymentIdentifier, *failureReason)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user