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`.
This commit is contained in:
yyforyongyu 2023-03-07 18:39:26 +08:00
parent 568b977a1f
commit 49bafc0207
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
2 changed files with 25 additions and 18 deletions

View File

@ -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

View File

@ -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