From 49bafc02078788c11d8db5c80e601b3141136914 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Tue, 7 Mar 2023 18:39:26 +0800 Subject: [PATCH] routing: handle switch error when `sendAttempt` fails This commit starts handling switch error inside `sendAttempt` when an error is returned from sending the HTLC. To make sure the updated `HTLCAttempt` is always returned to the callsite, `handleSwitchErr` now also returns a `attemptResult`. --- routing/payment_lifecycle.go | 41 +++++++++++++++++++++--------------- routing/router.go | 2 +- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index facb9168b..7f64f0eeb 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -300,7 +300,7 @@ lifecycle: // We must inspect the error to know whether it was // critical or not, to decide whether we should // continue trying. - err := p.handleSwitchErr( + _, err := p.handleSwitchErr( attempt, outcome.err, ) if err != nil { @@ -392,7 +392,7 @@ func (p *paymentLifecycle) launchShard(rt *route.Route, // Now that the attempt is created and checkpointed to the DB, we send // it. - sendErr := p.sendAttempt(attempt) + _, sendErr := p.sendAttempt(attempt) if sendErr != nil { // TODO(joostjager): Distinguish unexpected internal errors // from real send errors. @@ -460,7 +460,7 @@ func (p *paymentLifecycle) collectResultAsync(attempt *channeldb.HTLCAttempt) { // Overwrite the param errToSend and return so that the // defer function will use the param to proceed. Notice // that the errToSend could be nil here. - errToSend = p.handleSwitchErr(attempt, result.err) + _, errToSend = p.handleSwitchErr(attempt, result.err) return } }() @@ -657,7 +657,7 @@ func (p *paymentLifecycle) createNewPaymentAttempt(rt *route.Route, // the payment. If this attempt fails, then we'll continue on to the next // available route. func (p *paymentLifecycle) sendAttempt( - attempt *channeldb.HTLCAttempt) error { + attempt *channeldb.HTLCAttempt) (*attemptResult, error) { log.Tracef("Attempting to send payment %v (pid=%v), "+ "using route: %v", p.identifier, attempt.AttemptID, @@ -687,8 +687,13 @@ func (p *paymentLifecycle) sendAttempt( &rt, attempt.Hash[:], attempt.SessionKey(), ) if err != nil { - return err + log.Errorf("Failed to create onion blob: attempt=%d in "+ + "payment=%v, err:%v", attempt.AttemptID, + p.identifier, err) + + return p.failAttempt(attempt.AttemptID, err) } + copy(htlcAdd.OnionBlob[:], onionBlob) // Send it to the Switch. When this method returns we assume @@ -700,13 +705,15 @@ func (p *paymentLifecycle) sendAttempt( log.Errorf("Failed sending attempt %d for payment %v to "+ "switch: %v", attempt.AttemptID, p.identifier, err) - return err + return p.handleSwitchErr(attempt, err) } log.Debugf("Payment %v (pid=%v) successfully sent to switch, route: %v", p.identifier, attempt.AttemptID, &attempt.Route) - return nil + return &attemptResult{ + attempt: attempt, + }, nil } // failAttemptAndPayment fails both the payment and its attempt via the @@ -742,7 +749,7 @@ func (p *paymentLifecycle) failPaymentAndAttempt( // need to continue with an alternative route. A final outcome is indicated by // a non-nil reason value. func (p *paymentLifecycle) handleSwitchErr(attempt *channeldb.HTLCAttempt, - sendErr error) error { + sendErr error) (*attemptResult, error) { internalErrorReason := channeldb.FailureReasonError attemptID := attempt.AttemptID @@ -776,10 +783,13 @@ func (p *paymentLifecycle) handleSwitchErr(attempt *channeldb.HTLCAttempt, } if sendErr == htlcswitch.ErrUnreadableFailureMessage { - log.Tracef("Unreadable failure when sending htlc") + log.Warn("Unreadable failure when sending htlc: id=%v, hash=%v", + attempt.AttemptID, attempt.Hash) - _, err := reportAndFail(nil, nil) - return err + // Since this error message cannot be decrypted, we will send a + // nil error message to our mission controller and fail the + // payment. + return reportAndFail(nil, nil) } // If the error is a ClearTextError, we have received a valid wire @@ -789,10 +799,9 @@ func (p *paymentLifecycle) handleSwitchErr(attempt *channeldb.HTLCAttempt, // occurred. rtErr, ok := sendErr.(htlcswitch.ClearTextError) if !ok { - _, err := p.failPaymentAndAttempt( + return p.failPaymentAndAttempt( attemptID, &internalErrorReason, sendErr, ) - return err } // failureSourceIdx is the index of the node that the failure occurred @@ -815,17 +824,15 @@ func (p *paymentLifecycle) handleSwitchErr(attempt *channeldb.HTLCAttempt, &attempt.Route, failureSourceIdx, failureMessage, ) if err != nil { - _, err := p.failPaymentAndAttempt( + return p.failPaymentAndAttempt( attemptID, &internalErrorReason, sendErr, ) - return err } log.Tracef("Node=%v reported failure when sending htlc", failureSourceIdx) - _, err = reportAndFail(&failureSourceIdx, failureMessage) - return err + return reportAndFail(&failureSourceIdx, failureMessage) } // handleFailureMessage tries to apply a channel update present in the failure diff --git a/routing/router.go b/routing/router.go index d20932c28..08700526e 100644 --- a/routing/router.go +++ b/routing/router.go @@ -2558,7 +2558,7 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, // the error to check if it maps into a terminal error code, if not use // a generic NO_ROUTE error. var failureReason *channeldb.FailureReason - err = p.handleSwitchErr(attempt, shardError) + _, err = p.handleSwitchErr(attempt, shardError) switch { // If a non-terminal error is returned and `skipTempErr` is false, then