routing/router: move sending and receiving payment result into loop
This commit is contained in:
parent
59c2557cc9
commit
ae7bf2cb7b
@ -1513,6 +1513,7 @@ type LightningPayment struct {
|
|||||||
// when we should should abandon the payment attempt after consecutive
|
// when we should should abandon the payment attempt after consecutive
|
||||||
// payment failure. This prevents us from attempting to send a payment
|
// payment failure. This prevents us from attempting to send a payment
|
||||||
// indefinitely.
|
// indefinitely.
|
||||||
|
// TODO(halseth): make wallclock time to allow resume after startup.
|
||||||
PayAttemptTimeout time.Duration
|
PayAttemptTimeout time.Duration
|
||||||
|
|
||||||
// RouteHints represents the different routing hints that can be used to
|
// RouteHints represents the different routing hints that can be used to
|
||||||
@ -1617,6 +1618,8 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
|
|
||||||
timeoutChan := time.After(payAttemptTimeout)
|
timeoutChan := time.After(payAttemptTimeout)
|
||||||
|
|
||||||
|
paymentHash := payment.PaymentHash
|
||||||
|
|
||||||
// We'll continue until either our payment succeeds, or we encounter a
|
// We'll continue until either our payment succeeds, or we encounter a
|
||||||
// critical error during path finding.
|
// critical error during path finding.
|
||||||
var lastError error
|
var lastError error
|
||||||
@ -1657,131 +1660,124 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
|
|||||||
return [32]byte{}, nil, err
|
return [32]byte{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send payment attempt. It will return a final boolean
|
log.Tracef("Attempting to send payment %x, using route: %v",
|
||||||
// indicating if more attempts are needed.
|
paymentHash, newLogClosure(func() string {
|
||||||
preimage, final, err := r.sendPaymentAttempt(
|
return spew.Sdump(route)
|
||||||
paySession, route, payment.PaymentHash,
|
}),
|
||||||
)
|
)
|
||||||
if final {
|
|
||||||
return preimage, route, err
|
// Generate a new key to be used for this attempt.
|
||||||
|
sessionKey, err := generateNewSessionKey()
|
||||||
|
if err != nil {
|
||||||
|
return [32]byte{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lastError = 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(
|
||||||
// sendPaymentAttempt tries to send the payment via the specified route. If
|
route, paymentHash[:], sessionKey,
|
||||||
// successful, it returns the obtained preimage. If an error occurs, the last
|
|
||||||
// bool parameter indicates whether this is a final outcome or more attempts
|
|
||||||
// should be made.
|
|
||||||
func (r *ChannelRouter) sendPaymentAttempt(paySession *paymentSession,
|
|
||||||
route *route.Route, paymentHash [32]byte) ([32]byte, bool, error) {
|
|
||||||
|
|
||||||
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{}, true, 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{}, true, 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{}, true, 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 err != nil {
|
||||||
return [32]byte{}, finalOutcome, err
|
return [32]byte{}, nil, err
|
||||||
}
|
|
||||||
|
|
||||||
// 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{}, true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
result *htlcswitch.PaymentResult
|
|
||||||
ok bool
|
|
||||||
)
|
|
||||||
select {
|
|
||||||
case result, ok = <-resultChan:
|
|
||||||
if !ok {
|
|
||||||
return [32]byte{}, true, htlcswitch.ErrSwitchExiting
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-r.quit:
|
// Craft an HTLC packet to send to the layer 2 switch. The
|
||||||
return [32]byte{}, true, ErrRouterShuttingDown
|
// 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)
|
||||||
|
|
||||||
if result.Error != nil {
|
// Attempt to send this payment through the network to complete
|
||||||
log.Errorf("Attempt to send payment %x failed: %v",
|
// the payment. If this attempt fails, then we'll continue on
|
||||||
paymentHash, result.Error)
|
// to the next available route.
|
||||||
|
firstHop := lnwire.NewShortChanIDFromInt(
|
||||||
finalOutcome := r.processSendError(
|
route.Hops[0].ChannelID,
|
||||||
paySession, route, result.Error,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return [32]byte{}, finalOutcome, result.Error
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.Preimage, true, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// processSendError analyzes the error for the payment attempt received from the
|
// processSendError analyzes the error for the payment attempt received from the
|
||||||
|
Loading…
Reference in New Issue
Block a user