mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-12-08 20:03:10 +01:00
routing: extract payment flow into method on paymentLifecycle
This encapsulates all state needed to resume a payment from any point of the payment flow, and that must be shared between the different stages of the execution. This is done to prepare for breaking the send loop into smaller parts, and being able to resume the payment from any point from persistent state.
This commit is contained in:
@@ -1618,166 +1618,21 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
||||
|
||||
timeoutChan := time.After(payAttemptTimeout)
|
||||
|
||||
paymentHash := payment.PaymentHash
|
||||
|
||||
// We'll continue until either our payment succeeds, or we encounter a
|
||||
// critical error during path finding.
|
||||
var lastError error
|
||||
for {
|
||||
// Before we attempt this next payment, we'll check to see if
|
||||
// either we've gone past the payment attempt timeout, or the
|
||||
// router is exiting. In either case, we'll stop this payment
|
||||
// attempt short.
|
||||
select {
|
||||
case <-timeoutChan:
|
||||
errStr := fmt.Sprintf("payment attempt not completed "+
|
||||
"before timeout of %v", payAttemptTimeout)
|
||||
|
||||
return [32]byte{}, nil, newErr(
|
||||
ErrPaymentAttemptTimeout, errStr,
|
||||
)
|
||||
|
||||
case <-r.quit:
|
||||
return [32]byte{}, nil, ErrRouterShuttingDown
|
||||
|
||||
default:
|
||||
// Fall through if we haven't hit our time limit, or
|
||||
// are expiring.
|
||||
}
|
||||
|
||||
route, err := paySession.RequestRoute(
|
||||
payment, uint32(currentHeight), finalCLTVDelta,
|
||||
)
|
||||
if err != nil {
|
||||
// If we're unable to successfully make a payment using
|
||||
// any of the routes we've found, then return an error.
|
||||
if lastError != nil {
|
||||
return [32]byte{}, nil, fmt.Errorf("unable to "+
|
||||
"route payment to destination: %v",
|
||||
lastError)
|
||||
}
|
||||
|
||||
return [32]byte{}, nil, err
|
||||
}
|
||||
|
||||
log.Tracef("Attempting to send payment %x, using route: %v",
|
||||
paymentHash, newLogClosure(func() string {
|
||||
return spew.Sdump(route)
|
||||
}),
|
||||
)
|
||||
|
||||
// Generate a new key to be used for this attempt.
|
||||
sessionKey, err := generateNewSessionKey()
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, err
|
||||
}
|
||||
|
||||
// Generate the raw encoded sphinx packet to be included along
|
||||
// with the htlcAdd message that we send directly to the
|
||||
// switch.
|
||||
onionBlob, circuit, err := generateSphinxPacket(
|
||||
route, paymentHash[:], sessionKey,
|
||||
)
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, err
|
||||
}
|
||||
|
||||
// Craft an HTLC packet to send to the layer 2 switch. The
|
||||
// metadata within this packet will be used to route the
|
||||
// payment through the network, starting with the first-hop.
|
||||
htlcAdd := &lnwire.UpdateAddHTLC{
|
||||
Amount: route.TotalAmount,
|
||||
Expiry: route.TotalTimeLock,
|
||||
PaymentHash: paymentHash,
|
||||
}
|
||||
copy(htlcAdd.OnionBlob[:], onionBlob)
|
||||
|
||||
// Attempt to send this payment through the network to complete
|
||||
// the payment. If this attempt fails, then we'll continue on
|
||||
// to the next available route.
|
||||
firstHop := lnwire.NewShortChanIDFromInt(
|
||||
route.Hops[0].ChannelID,
|
||||
)
|
||||
|
||||
// We generate a new, unique payment ID that we will use for
|
||||
// this HTLC.
|
||||
paymentID, err := r.cfg.NextPaymentID()
|
||||
if err != nil {
|
||||
return [32]byte{}, nil, err
|
||||
}
|
||||
|
||||
err = r.cfg.Payer.SendHTLC(
|
||||
firstHop, paymentID, htlcAdd,
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("Failed sending attempt %d for payment "+
|
||||
"%x to switch: %v", paymentID, paymentHash, err)
|
||||
|
||||
// We must inspect the error to know whether it was
|
||||
// critical or not, to decide whether we should
|
||||
// continue trying.
|
||||
finalOutcome := r.processSendError(
|
||||
paySession, route, err,
|
||||
)
|
||||
if finalOutcome {
|
||||
return [32]byte{}, nil, err
|
||||
}
|
||||
|
||||
lastError = err
|
||||
continue
|
||||
}
|
||||
|
||||
// Using the created circuit, initialize the error decrypter so we can
|
||||
// parse+decode any failures incurred by this payment within the
|
||||
// switch.
|
||||
errorDecryptor := &htlcswitch.SphinxErrorDecrypter{
|
||||
OnionErrorDecrypter: sphinx.NewOnionErrorDecrypter(circuit),
|
||||
}
|
||||
|
||||
// Now ask the switch to return the result of the payment when
|
||||
// available.
|
||||
resultChan, err := r.cfg.Payer.GetPaymentResult(
|
||||
paymentID, errorDecryptor,
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("Failed getting result for paymentID %d "+
|
||||
"from switch: %v", paymentID, err)
|
||||
return [32]byte{}, nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
result *htlcswitch.PaymentResult
|
||||
ok bool
|
||||
)
|
||||
select {
|
||||
case result, ok = <-resultChan:
|
||||
if !ok {
|
||||
return [32]byte{}, nil, htlcswitch.ErrSwitchExiting
|
||||
}
|
||||
|
||||
case <-r.quit:
|
||||
return [32]byte{}, nil, ErrRouterShuttingDown
|
||||
}
|
||||
|
||||
if result.Error != nil {
|
||||
log.Errorf("Attempt to send payment %x failed: %v",
|
||||
paymentHash, result.Error)
|
||||
|
||||
finalOutcome := r.processSendError(
|
||||
paySession, route, result.Error,
|
||||
)
|
||||
|
||||
if finalOutcome {
|
||||
return [32]byte{}, nil, result.Error
|
||||
}
|
||||
|
||||
lastError = result.Error
|
||||
continue
|
||||
}
|
||||
|
||||
return result.Preimage, route, nil
|
||||
// Now set up a paymentLifecycle struct with these params, such that we
|
||||
// can resume the payment from the current state.
|
||||
p := &paymentLifecycle{
|
||||
router: r,
|
||||
payment: payment,
|
||||
paySession: paySession,
|
||||
timeoutChan: timeoutChan,
|
||||
currentHeight: currentHeight,
|
||||
finalCLTVDelta: finalCLTVDelta,
|
||||
circuit: nil,
|
||||
lastError: nil,
|
||||
}
|
||||
|
||||
return p.resumePayment()
|
||||
|
||||
}
|
||||
|
||||
// processSendError analyzes the error for the payment attempt received from the
|
||||
|
||||
Reference in New Issue
Block a user