htlcswitch+router: define PaymentResult, GetPaymentResult
This lets us distinguish an critical error from a actual payment result (success or failure). This is important since we know that we can only attempt another payment when a final result from the previous payment attempt is received.
This commit is contained in:
7 files changed
+251
-68
No files matched your search
+40
-7
@@ -1,28 +1,61 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
type mockPaymentAttemptDispatcher struct {
|
||||
onPayment func(firstHop lnwire.ShortChannelID) ([32]byte, error)
|
||||
results map[uint64]*htlcswitch.PaymentResult
|
||||
}
|
||||
|
||||
var _ PaymentAttemptDispatcher = (*mockPaymentAttemptDispatcher)(nil)
|
||||
|
||||
func (m *mockPaymentAttemptDispatcher) SendHTLC(firstHop lnwire.ShortChannelID,
|
||||
_ uint64,
|
||||
pid uint64,
|
||||
_ *lnwire.UpdateAddHTLC,
|
||||
_ htlcswitch.ErrorDecrypter) ([sha256.Size]byte, error) {
|
||||
_ htlcswitch.ErrorDecrypter) error {
|
||||
|
||||
if m.onPayment != nil {
|
||||
return m.onPayment(firstHop)
|
||||
if m.onPayment == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return [sha256.Size]byte{}, nil
|
||||
if m.results == nil {
|
||||
m.results = make(map[uint64]*htlcswitch.PaymentResult)
|
||||
}
|
||||
|
||||
var result *htlcswitch.PaymentResult
|
||||
preimage, err := m.onPayment(firstHop)
|
||||
if err != nil {
|
||||
fwdErr, ok := err.(*htlcswitch.ForwardingError)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
result = &htlcswitch.PaymentResult{
|
||||
Error: fwdErr,
|
||||
}
|
||||
} else {
|
||||
result = &htlcswitch.PaymentResult{Preimage: preimage}
|
||||
}
|
||||
|
||||
m.results[pid] = result
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockPaymentAttemptDispatcher) GetPaymentResult(paymentID uint64) (
|
||||
<-chan *htlcswitch.PaymentResult, error) {
|
||||
|
||||
c := make(chan *htlcswitch.PaymentResult, 1)
|
||||
res, ok := m.results[paymentID]
|
||||
if !ok {
|
||||
return nil, htlcswitch.ErrPaymentIDNotFound
|
||||
}
|
||||
c <- res
|
||||
|
||||
return c, nil
|
||||
|
||||
}
|
||||
|
||||
func (m *mockPaymentAttemptDispatcher) setPaymentResult(
|
||||
|
||||
+47
-9
@@ -2,7 +2,6 @@ package routing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
@@ -136,7 +135,15 @@ type PaymentAttemptDispatcher interface {
|
||||
SendHTLC(firstHop lnwire.ShortChannelID,
|
||||
paymentID uint64,
|
||||
htlcAdd *lnwire.UpdateAddHTLC,
|
||||
deobfuscator htlcswitch.ErrorDecrypter) ([sha256.Size]byte, error)
|
||||
deobfuscator htlcswitch.ErrorDecrypter) error
|
||||
|
||||
// GetPaymentResult returns the the result of the payment attempt with
|
||||
// the given paymentID. The method returns a channel where the payment
|
||||
// result will be sent when available, or an error is encountered. If
|
||||
// the paymentID is unknown, htlcswitch.ErrPaymentIDNotFound will be
|
||||
// returned.
|
||||
GetPaymentResult(paymentID uint64) (
|
||||
<-chan *htlcswitch.PaymentResult, error)
|
||||
}
|
||||
|
||||
// FeeSchema is the set fee configuration for a Lightning Node on the network.
|
||||
@@ -1711,19 +1718,50 @@ func (r *ChannelRouter) sendPaymentAttempt(paySession *paymentSession,
|
||||
return [32]byte{}, true, err
|
||||
}
|
||||
|
||||
preimage, err := r.cfg.Payer.SendHTLC(
|
||||
err = r.cfg.Payer.SendHTLC(
|
||||
firstHop, paymentID, htlcAdd, errorDecryptor,
|
||||
)
|
||||
if err == nil {
|
||||
return preimage, true, nil
|
||||
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,
|
||||
)
|
||||
|
||||
return [32]byte{}, finalOutcome, err
|
||||
}
|
||||
|
||||
log.Errorf("Attempt to send payment %x failed: %v",
|
||||
paymentHash, err)
|
||||
// Now ask the switch to return the result of the payment when
|
||||
// available.
|
||||
resultChan, err := r.cfg.Payer.GetPaymentResult(paymentID)
|
||||
if err != nil {
|
||||
log.Errorf("Failed getting result for paymentID %d "+
|
||||
"from switch: %v", paymentID, err)
|
||||
return [32]byte{}, true, err
|
||||
}
|
||||
|
||||
finalOutcome := r.processSendError(paySession, route, err)
|
||||
var result *htlcswitch.PaymentResult
|
||||
select {
|
||||
case result = <-resultChan:
|
||||
case <-r.quit:
|
||||
return [32]byte{}, true, ErrRouterShuttingDown
|
||||
}
|
||||
|
||||
return [32]byte{}, finalOutcome, err
|
||||
if result.Error != nil {
|
||||
log.Errorf("Attempt to send payment %x failed: %v",
|
||||
paymentHash, result.Error)
|
||||
|
||||
finalOutcome := r.processSendError(
|
||||
paySession, route, result.Error,
|
||||
)
|
||||
|
||||
return [32]byte{}, finalOutcome, result.Error
|
||||
}
|
||||
|
||||
return result.Preimage, true, nil
|
||||
}
|
||||
|
||||
// processSendError analyzes the error for the payment attempt received from the
|
||||
|
||||
Reference in new issue
Block a user