routing: only fail attempt inside handleSwitchErr

This commit makes sure we only fail attempt inside `handleSwitchErr` to
ensure the orders in failing payment and attempts. It refactors
`collectResult` to return `attemptResult`, and expands `handleSwitchErr`
to also handle the case where the attemptID is not found.
This commit is contained in:
yyforyongyu
2023-02-13 18:32:02 +08:00
parent 703ea08316
commit 173900c8dc

View File

@@ -413,32 +413,15 @@ func (p *paymentLifecycle) collectResultAsync(attempt *channeldb.HTLCAttempt) {
defer handleResultErr()
// Block until the result is available.
result, err := p.collectResult(attempt)
_, err := p.collectResult(attempt)
if err != nil {
if err != ErrRouterShuttingDown &&
err != htlcswitch.ErrSwitchExiting &&
err != errShardHandlerExiting {
log.Errorf("Error collecting result for attempt %v "+
"in payment %v: %v", attempt.AttemptID,
p.identifier, err)
log.Errorf("Error collecting result for "+
"shard %v for payment %v: %v",
attempt.AttemptID, p.identifier, err)
}
// Overwrite the param errToSend and return so that the
// defer function will use the param to proceed.
errToSend = err
return
}
// If a non-critical error was encountered handle it and mark
// the payment failed if the failure was terminal.
if result.err != nil {
// 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)
return
}
}()
}
@@ -477,24 +460,12 @@ func (p *paymentLifecycle) collectResult(attempt *channeldb.HTLCAttempt) (
resultChan, err := p.router.cfg.Payer.GetAttemptResult(
attempt.AttemptID, p.identifier, errorDecryptor,
)
switch {
// If this attempt ID is unknown to the Switch, it means it was never
// checkpointed and forwarded by the switch before a restart. In this
// case we can safely send a new payment attempt, and wait for its
// result to be available.
case err == htlcswitch.ErrPaymentIDNotFound:
log.Debugf("Attempt ID %v for payment %v not found in "+
"the Switch, retrying.", attempt.AttemptID,
p.identifier)
return p.failAttempt(attempt.AttemptID, err)
// A critical, unexpected error was encountered.
case err != nil:
// Handle the switch error.
if err != nil {
log.Errorf("Failed getting result for attemptID %d "+
"from switch: %v", attempt.AttemptID, err)
return nil, err
return p.handleSwitchErr(attempt, err)
}
// The switch knows about this payment, we'll wait for a result to be
@@ -517,7 +488,7 @@ func (p *paymentLifecycle) collectResult(attempt *channeldb.HTLCAttempt) (
// In case of a payment failure, fail the attempt with the control
// tower and return.
if result.Error != nil {
return p.failAttempt(attempt.AttemptID, result.Error)
return p.handleSwitchErr(attempt, result.Error)
}
// We successfully got a payment result back from the switch.
@@ -754,6 +725,17 @@ func (p *paymentLifecycle) handleSwitchErr(attempt *channeldb.HTLCAttempt,
return p.failPaymentAndAttempt(attemptID, reason, sendErr)
}
// If this attempt ID is unknown to the Switch, it means it was never
// checkpointed and forwarded by the switch before a restart. In this
// case we can safely send a new payment attempt, and wait for its
// result to be available.
if errors.Is(sendErr, htlcswitch.ErrPaymentIDNotFound) {
log.Debugf("Attempt ID %v for payment %v not found in the "+
"Switch, retrying.", attempt.AttemptID, p.identifier)
return p.failAttempt(attemptID, sendErr)
}
if sendErr == htlcswitch.ErrUnreadableFailureMessage {
log.Warn("Unreadable failure when sending htlc: id=%v, hash=%v",
attempt.AttemptID, attempt.Hash)