routing: fail attempt when no shard is found or circuit generation fails

This commit is contained in:
yyforyongyu 2023-03-07 18:58:48 +08:00
parent eda24ec871
commit 09a5d235ec
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
2 changed files with 25 additions and 2 deletions

View File

@ -1000,6 +1000,8 @@ func (s *Switch) extractResult(deobfuscator ErrorDecrypter, n *networkResult,
// We've received a fail update which means we can finalize the // We've received a fail update which means we can finalize the
// user payment and return fail response. // user payment and return fail response.
case *lnwire.UpdateFailHTLC: case *lnwire.UpdateFailHTLC:
// TODO(yy): construct deobfuscator here to avoid creating it
// in paymentLifecycle even for settled HTLCs.
paymentErr := s.parseFailedPayment( paymentErr := s.parseFailedPayment(
deobfuscator, attemptID, paymentHash, n.unencrypted, deobfuscator, attemptID, paymentHash, n.unencrypted,
n.isResolution, htlc, n.isResolution, htlc,

View File

@ -456,15 +456,27 @@ func (p *paymentLifecycle) collectResult(attempt *channeldb.HTLCAttempt) (
// below. // below.
hash, err := p.shardTracker.GetHash(attempt.AttemptID) hash, err := p.shardTracker.GetHash(attempt.AttemptID)
if err != nil { if err != nil {
return nil, err return p.failAttempt(attempt.AttemptID, err)
} }
// Regenerate the circuit for this attempt. // Regenerate the circuit for this attempt.
_, circuit, err := generateSphinxPacket( _, circuit, err := generateSphinxPacket(
&attempt.Route, hash[:], attempt.SessionKey(), &attempt.Route, hash[:], attempt.SessionKey(),
) )
// TODO(yy): We generate this circuit to create the error decryptor,
// which is then used in htlcswitch as the deobfuscator to decode the
// error from `UpdateFailHTLC`. However, suppose it's an
// `UpdateFulfillHTLC` message yet for some reason the sphinx packet is
// failed to be generated, we'd miss settling it. This means we should
// give it a second chance to try the settlement path in case
// `GetAttemptResult` gives us back the preimage. And move the circuit
// creation into htlcswitch so it's only constructed when there's a
// failure message we need to decode.
if err != nil { if err != nil {
return nil, err log.Debugf("Unable to generate circuit for attempt %v: %v",
attempt.AttemptID, err)
return p.failAttempt(attempt.AttemptID, err)
} }
// Using the created circuit, initialize the error decrypter so we can // Using the created circuit, initialize the error decrypter so we can
@ -476,6 +488,12 @@ func (p *paymentLifecycle) collectResult(attempt *channeldb.HTLCAttempt) (
// Now ask the switch to return the result of the payment when // Now ask the switch to return the result of the payment when
// available. // available.
//
// TODO(yy): consider using htlcswitch to create the `errorDecryptor`
// since the htlc is already in db. This will also make the interface
// `PaymentAttemptDispatcher` deeper and easier to use. Moreover, we'd
// only create the decryptor when received a failure, further saving us
// a few CPU cycles.
resultChan, err := p.router.cfg.Payer.GetAttemptResult( resultChan, err := p.router.cfg.Payer.GetAttemptResult(
attempt.AttemptID, p.identifier, errorDecryptor, attempt.AttemptID, p.identifier, errorDecryptor,
) )
@ -536,6 +554,9 @@ func (p *paymentLifecycle) collectResult(attempt *channeldb.HTLCAttempt) (
) )
if err != nil { if err != nil {
log.Errorf("Unable to settle payment attempt: %v", err) log.Errorf("Unable to settle payment attempt: %v", err)
// We won't mark the attempt as failed since we already have
// the preimage.
return nil, err return nil, err
} }