Merge pull request #7174 from yyforyongyu/prepare-refactor-payment

Prepare for refactoring payment lifecycle
This commit is contained in:
Oliver Gugger authored and GitHub committed 2022-12-07 12:56:06 +01:00
commit 33a5c30671
15 files changed
+164 -137

No files matched your search

+4 -2
View File
@@ -43,7 +43,8 @@ var (
// ErrPaymentTerminal is returned if we attempt to alter a payment that // ErrPaymentTerminal is returned if we attempt to alter a payment that
// already has reached a terminal condition. // already has reached a terminal condition.
ErrPaymentTerminal = errors.New("payment has reached terminal condition") ErrPaymentTerminal = errors.New("payment has reached terminal " +
"condition")
// ErrAttemptAlreadySettled is returned if we try to alter an already // ErrAttemptAlreadySettled is returned if we try to alter an already
// settled HTLC attempt. // settled HTLC attempt.
@@ -77,7 +78,8 @@ var (
// ErrMPPTotalAmountMismatch is returned if we try to register an MPP // ErrMPPTotalAmountMismatch is returned if we try to register an MPP
// shard where the total amount doesn't match existing shards. // shard where the total amount doesn't match existing shards.
ErrMPPTotalAmountMismatch = errors.New("mp payment total amount mismatch") ErrMPPTotalAmountMismatch = errors.New("mp payment total amount " +
"mismatch")
// errNoAttemptInfo is returned when no attempt info is stored yet. // errNoAttemptInfo is returned when no attempt info is stored yet.
errNoAttemptInfo = errors.New("unable to find attempt info for " + errNoAttemptInfo = errors.New("unable to find attempt info for " +
+42
View File
@@ -0,0 +1,42 @@
package channeldb
// PaymentStatus represent current status of payment.
type PaymentStatus byte
const (
// StatusUnknown is the status where a payment has never been initiated
// and hence is unknown.
StatusUnknown PaymentStatus = 0
// StatusInFlight is the status where a payment has been initiated, but
// a response has not been received.
StatusInFlight PaymentStatus = 1
// StatusSucceeded is the status where a payment has been initiated and
// the payment was completed successfully.
StatusSucceeded PaymentStatus = 2
// StatusFailed is the status where a payment has been initiated and a
// failure result has come back.
StatusFailed PaymentStatus = 3
)
// String returns readable representation of payment status.
func (ps PaymentStatus) String() string {
switch ps {
case StatusUnknown:
return "Unknown"
case StatusInFlight:
return "In Flight"
case StatusSucceeded:
return "Succeeded"
case StatusFailed:
return "Failed"
default:
return "Unknown"
}
}
-37
View File
@@ -175,43 +175,6 @@ func (r FailureReason) String() string {
return "unknown" return "unknown"
} }
// PaymentStatus represent current status of payment
type PaymentStatus byte
const (
// StatusUnknown is the status where a payment has never been initiated
// and hence is unknown.
StatusUnknown PaymentStatus = 0
// StatusInFlight is the status where a payment has been initiated, but
// a response has not been received.
StatusInFlight PaymentStatus = 1
// StatusSucceeded is the status where a payment has been initiated and
// the payment was completed successfully.
StatusSucceeded PaymentStatus = 2
// StatusFailed is the status where a payment has been initiated and a
// failure result has come back.
StatusFailed PaymentStatus = 3
)
// String returns readable representation of payment status.
func (ps PaymentStatus) String() string {
switch ps {
case StatusUnknown:
return "Unknown"
case StatusInFlight:
return "In Flight"
case StatusSucceeded:
return "Succeeded"
case StatusFailed:
return "Failed"
default:
return "Unknown"
}
}
// PaymentCreationInfo is the information necessary to have ready when // PaymentCreationInfo is the information necessary to have ready when
// initiating a payment, moving it into state InFlight. // initiating a payment, moving it into state InFlight.
type PaymentCreationInfo struct { type PaymentCreationInfo struct {
@@ -225,6 +225,10 @@ certain large transactions](https://github.com/lightningnetwork/lnd/pull/7100).
* [CI: update test coverage library * [CI: update test coverage library
go-acc](https://github.com/lightningnetwork/lnd/pull/7221) v0.2.6 -> v0.2.8 go-acc](https://github.com/lightningnetwork/lnd/pull/7221) v0.2.6 -> v0.2.8
* Payment related code [has been
refactored](https://github.com/lightningnetwork/lnd/pull/7174) to allow the
usage of new payment statuses.
## Watchtowers ## Watchtowers
+2 -2
View File
@@ -1306,7 +1306,7 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) {
) )
require.NoError(t, err, "unable to get send payment") require.NoError(t, err, "unable to get send payment")
resultChan, err := n.aliceServer.htlcSwitch.GetPaymentResult( resultChan, err := n.aliceServer.htlcSwitch.GetAttemptResult(
pid, htlc.PaymentHash, newMockDeobfuscator(), pid, htlc.PaymentHash, newMockDeobfuscator(),
) )
require.NoError(t, err, "unable to get payment result") require.NoError(t, err, "unable to get payment result")
@@ -3979,7 +3979,7 @@ func TestChannelLinkAcceptDuplicatePayment(t *testing.T) {
) )
require.NoError(t, err, "unable to send payment to carol") require.NoError(t, err, "unable to send payment to carol")
resultChan, err := n.aliceServer.htlcSwitch.GetPaymentResult( resultChan, err := n.aliceServer.htlcSwitch.GetAttemptResult(
pid, htlc.PaymentHash, newMockDeobfuscator(), pid, htlc.PaymentHash, newMockDeobfuscator(),
) )
require.NoError(t, err, "unable to get payment result") require.NoError(t, err, "unable to get payment result")
+4 -4
View File
@@ -427,16 +427,16 @@ func (s *Switch) ProcessContractResolution(msg contractcourt.ResolutionMsg) erro
} }
} }
// GetPaymentResult returns the the result of the payment attempt with the // GetAttemptResult returns the result of the payment attempt with the given
// given attemptID. The paymentHash should be set to the payment's overall // attemptID. The paymentHash should be set to the payment's overall hash, or
// hash, or in case of AMP payments the payment's unique identifier. // in case of AMP payments the payment's unique identifier.
// //
// The method returns a channel where the payment result will be sent when // The method returns a channel where the payment result will be sent when
// available, or an error is encountered during forwarding. When a result is // available, or an error is encountered during forwarding. When a result is
// received on the channel, the HTLC is guaranteed to no longer be in flight. // received on the channel, the HTLC is guaranteed to no longer be in flight.
// The switch shutting down is signaled by closing the channel. If the // The switch shutting down is signaled by closing the channel. If the
// attemptID is unknown, ErrPaymentIDNotFound will be returned. // attemptID is unknown, ErrPaymentIDNotFound will be returned.
func (s *Switch) GetPaymentResult(attemptID uint64, paymentHash lntypes.Hash, func (s *Switch) GetAttemptResult(attemptID uint64, paymentHash lntypes.Hash,
deobfuscator ErrorDecrypter) (<-chan *PaymentResult, error) { deobfuscator ErrorDecrypter) (<-chan *PaymentResult, error) {
var ( var (
+10 -10
View File
@@ -2699,7 +2699,7 @@ func TestSwitchSendPayment(t *testing.T) {
// First check that the switch will correctly respond that this payment // First check that the switch will correctly respond that this payment
// ID is unknown. // ID is unknown.
_, err = s.GetPaymentResult( _, err = s.GetAttemptResult(
paymentID, rhash, newMockDeobfuscator(), paymentID, rhash, newMockDeobfuscator(),
) )
if err != ErrPaymentIDNotFound { if err != ErrPaymentIDNotFound {
@@ -2717,7 +2717,7 @@ func TestSwitchSendPayment(t *testing.T) {
return return
} }
resultChan, err := s.GetPaymentResult( resultChan, err := s.GetAttemptResult(
paymentID, rhash, newMockDeobfuscator(), paymentID, rhash, newMockDeobfuscator(),
) )
if err != nil { if err != nil {
@@ -3072,11 +3072,11 @@ func TestUpdateFailMalformedHTLCErrorConversion(t *testing.T) {
}) })
} }
// TestSwitchGetPaymentResult tests that the switch interacts as expected with // TestSwitchGetAttemptResult tests that the switch interacts as expected with
// the circuit map and network result store when looking up the result of a // the circuit map and network result store when looking up the result of a
// payment ID. This is important for not to lose results under concurrent // payment ID. This is important for not to lose results under concurrent
// lookup and receiving results. // lookup and receiving results.
func TestSwitchGetPaymentResult(t *testing.T) { func TestSwitchGetAttemptResult(t *testing.T) {
t.Parallel() t.Parallel()
const paymentID = 123 const paymentID = 123
@@ -3100,7 +3100,7 @@ func TestSwitchGetPaymentResult(t *testing.T) {
// added anything to the store yet, ErrPaymentIDNotFound should be // added anything to the store yet, ErrPaymentIDNotFound should be
// returned. // returned.
lookup <- nil lookup <- nil
_, err = s.GetPaymentResult( _, err = s.GetAttemptResult(
paymentID, lntypes.Hash{}, newMockDeobfuscator(), paymentID, lntypes.Hash{}, newMockDeobfuscator(),
) )
if err != ErrPaymentIDNotFound { if err != ErrPaymentIDNotFound {
@@ -3110,7 +3110,7 @@ func TestSwitchGetPaymentResult(t *testing.T) {
// Next let the lookup find the circuit in the circuit map. It should // Next let the lookup find the circuit in the circuit map. It should
// subscribe to payment results, and return the result when available. // subscribe to payment results, and return the result when available.
lookup <- &PaymentCircuit{} lookup <- &PaymentCircuit{}
resultChan, err := s.GetPaymentResult( resultChan, err := s.GetAttemptResult(
paymentID, lntypes.Hash{}, newMockDeobfuscator(), paymentID, lntypes.Hash{}, newMockDeobfuscator(),
) )
require.NoError(t, err, "unable to get payment result") require.NoError(t, err, "unable to get payment result")
@@ -3151,7 +3151,7 @@ func TestSwitchGetPaymentResult(t *testing.T) {
// in the circuit map, it should be immediately available from the // in the circuit map, it should be immediately available from the
// store. // store.
lookup <- nil lookup <- nil
resultChan, err = s.GetPaymentResult( resultChan, err = s.GetAttemptResult(
paymentID, lntypes.Hash{}, newMockDeobfuscator(), paymentID, lntypes.Hash{}, newMockDeobfuscator(),
) )
require.NoError(t, err, "unable to get payment result") require.NoError(t, err, "unable to get payment result")
@@ -3257,7 +3257,7 @@ func TestInvalidFailure(t *testing.T) {
}, },
} }
resultChan, err := s.GetPaymentResult( resultChan, err := s.GetAttemptResult(
paymentID, rhash, &deobfuscator, paymentID, rhash, &deobfuscator,
) )
if err != nil { if err != nil {
@@ -3283,7 +3283,7 @@ func TestInvalidFailure(t *testing.T) {
}, },
} }
resultChan, err = s.GetPaymentResult( resultChan, err = s.GetAttemptResult(
paymentID, rhash, &deobfuscator, paymentID, rhash, &deobfuscator,
) )
if err != nil { if err != nil {
@@ -4415,7 +4415,7 @@ func TestSwitchDustForwarding(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
carolAttemptID++ carolAttemptID++
carolResultChan, err := n.carolServer.htlcSwitch.GetPaymentResult( carolResultChan, err := n.carolServer.htlcSwitch.GetAttemptResult(
uint64(carolAttemptID-1), carolHash, newMockDeobfuscator(), uint64(carolAttemptID-1), carolHash, newMockDeobfuscator(),
) )
require.NoError(t, err) require.NoError(t, err)
+2 -2
View File
@@ -787,7 +787,7 @@ func preparePayment(sendingPeer, receivingPeer lnpeer.Peer,
if err != nil { if err != nil {
return err return err
} }
resultChan, err := sender.htlcSwitch.GetPaymentResult( resultChan, err := sender.htlcSwitch.GetAttemptResult(
pid, hash, newMockDeobfuscator(), pid, hash, newMockDeobfuscator(),
) )
if err != nil { if err != nil {
@@ -1345,7 +1345,7 @@ func (n *twoHopNetwork) makeHoldPayment(sendingPeer, receivingPeer lnpeer.Peer,
} }
go func() { go func() {
resultChan, err := sender.htlcSwitch.GetPaymentResult( resultChan, err := sender.htlcSwitch.GetAttemptResult(
pid, rhash, newMockDeobfuscator(), pid, rhash, newMockDeobfuscator(),
) )
if err != nil { if err != nil {
+9 -9
View File
@@ -46,13 +46,13 @@ type ControlTower interface {
// hash. // hash.
FetchPayment(paymentHash lntypes.Hash) (*channeldb.MPPayment, error) FetchPayment(paymentHash lntypes.Hash) (*channeldb.MPPayment, error)
// Fail transitions a payment into the Failed state, and records the // FailPayment transitions a payment into the Failed state, and records
// ultimate reason the payment failed. Note that this should only be // the ultimate reason the payment failed. Note that this should only
// called when all active active attempts are already failed. After // be called when all active attempts are already failed. After
// invoking this method, InitPayment should return nil on its next call // invoking this method, InitPayment should return nil on its next call
// for this payment hash, allowing the user to make a subsequent // for this payment hash, allowing the user to make a subsequent
// payment. // payment.
Fail(lntypes.Hash, channeldb.FailureReason) error FailPayment(lntypes.Hash, channeldb.FailureReason) error
// FetchInFlightPayments returns all payments with status InFlight. // FetchInFlightPayments returns all payments with status InFlight.
FetchInFlightPayments() ([]*channeldb.MPPayment, error) FetchInFlightPayments() ([]*channeldb.MPPayment, error)
@@ -229,11 +229,11 @@ func (p *controlTower) FetchPayment(paymentHash lntypes.Hash) (
return p.db.FetchPayment(paymentHash) return p.db.FetchPayment(paymentHash)
} }
// Fail transitions a payment into the Failed state, and records the reason the // FailPayment transitions a payment into the Failed state, and records the
// payment failed. After invoking this method, InitPayment should return nil on // reason the payment failed. After invoking this method, InitPayment should
// its next call for this payment hash, allowing the switch to make a // return nil on its next call for this payment hash, allowing the switch to
// subsequent payment. // make a subsequent payment.
func (p *controlTower) Fail(paymentHash lntypes.Hash, func (p *controlTower) FailPayment(paymentHash lntypes.Hash,
reason channeldb.FailureReason) error { reason channeldb.FailureReason) error {
p.paymentsMtx.Lock(paymentHash) p.paymentsMtx.Lock(paymentHash)
+4 -1
View File
@@ -457,7 +457,10 @@ func testPaymentControlSubscribeFail(t *testing.T, registerAttempt,
} }
// Mark the payment as failed. // Mark the payment as failed.
if err := pControl.Fail(info.PaymentIdentifier, channeldb.FailureReasonTimeout); err != nil { err = pControl.FailPayment(
info.PaymentIdentifier, channeldb.FailureReasonTimeout,
)
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+6 -5
View File
@@ -56,7 +56,7 @@ func (m *mockPaymentAttemptDispatcherOld) SendHTLC(
return nil return nil
} }
func (m *mockPaymentAttemptDispatcherOld) GetPaymentResult(paymentID uint64, func (m *mockPaymentAttemptDispatcherOld) GetAttemptResult(paymentID uint64,
_ lntypes.Hash, _ htlcswitch.ErrorDecrypter) ( _ lntypes.Hash, _ htlcswitch.ErrorDecrypter) (
<-chan *htlcswitch.PaymentResult, error) { <-chan *htlcswitch.PaymentResult, error) {
@@ -205,7 +205,7 @@ func (m *mockPayerOld) SendHTLC(_ lnwire.ShortChannelID,
} }
func (m *mockPayerOld) GetPaymentResult(paymentID uint64, _ lntypes.Hash, func (m *mockPayerOld) GetAttemptResult(paymentID uint64, _ lntypes.Hash,
_ htlcswitch.ErrorDecrypter) (<-chan *htlcswitch.PaymentResult, error) { _ htlcswitch.ErrorDecrypter) (<-chan *htlcswitch.PaymentResult, error) {
select { select {
@@ -469,7 +469,7 @@ func (m *mockControlTowerOld) FailAttempt(phash lntypes.Hash, pid uint64,
return nil, fmt.Errorf("pid not found") return nil, fmt.Errorf("pid not found")
} }
func (m *mockControlTowerOld) Fail(phash lntypes.Hash, func (m *mockControlTowerOld) FailPayment(phash lntypes.Hash,
reason channeldb.FailureReason) error { reason channeldb.FailureReason) error {
m.Lock() m.Lock()
@@ -578,7 +578,7 @@ func (m *mockPaymentAttemptDispatcher) SendHTLC(firstHop lnwire.ShortChannelID,
return args.Error(0) return args.Error(0)
} }
func (m *mockPaymentAttemptDispatcher) GetPaymentResult(attemptID uint64, func (m *mockPaymentAttemptDispatcher) GetAttemptResult(attemptID uint64,
paymentHash lntypes.Hash, deobfuscator htlcswitch.ErrorDecrypter) ( paymentHash lntypes.Hash, deobfuscator htlcswitch.ErrorDecrypter) (
<-chan *htlcswitch.PaymentResult, error) { <-chan *htlcswitch.PaymentResult, error) {
@@ -733,7 +733,7 @@ func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64,
return args.Get(0).(*channeldb.HTLCAttempt), args.Error(1) return args.Get(0).(*channeldb.HTLCAttempt), args.Error(1)
} }
func (m *mockControlTower) Fail(phash lntypes.Hash, func (m *mockControlTower) FailPayment(phash lntypes.Hash,
reason channeldb.FailureReason) error { reason channeldb.FailureReason) error {
m.Lock() m.Lock()
@@ -758,6 +758,7 @@ func (m *mockControlTower) FetchPayment(phash lntypes.Hash) (
// Make a copy of the payment here to avoid data race. // Make a copy of the payment here to avoid data race.
p := args.Get(0).(*channeldb.MPPayment) p := args.Get(0).(*channeldb.MPPayment)
payment := &channeldb.MPPayment{ payment := &channeldb.MPPayment{
Info: p.Info,
FailureReason: p.FailureReason, FailureReason: p.FailureReason,
} }
payment.HTLCs = make([]channeldb.HTLCAttempt, len(p.HTLCs)) payment.HTLCs = make([]channeldb.HTLCAttempt, len(p.HTLCs))
+8 -9
View File
@@ -23,7 +23,6 @@ var errShardHandlerExiting = fmt.Errorf("shard handler exiting")
// needed to resume if from any point. // needed to resume if from any point.
type paymentLifecycle struct { type paymentLifecycle struct {
router *ChannelRouter router *ChannelRouter
totalAmount lnwire.MilliSatoshi
feeLimit lnwire.MilliSatoshi feeLimit lnwire.MilliSatoshi
identifier lntypes.Hash identifier lntypes.Hash
paySession PaymentSession paySession PaymentSession
@@ -83,9 +82,10 @@ func (p *paymentLifecycle) fetchPaymentState() (*channeldb.MPPayment,
sentAmt, fees := payment.SentAmt() sentAmt, fees := payment.SentAmt()
// Sanity check we haven't sent a value larger than the payment amount. // Sanity check we haven't sent a value larger than the payment amount.
if sentAmt > p.totalAmount { totalAmt := payment.Info.Value
if sentAmt > totalAmt {
return nil, nil, fmt.Errorf("amount sent %v exceeds "+ return nil, nil, fmt.Errorf("amount sent %v exceeds "+
"total amount %v", sentAmt, p.totalAmount) "total amount %v", sentAmt, totalAmt)
} }
// We'll subtract the used fee from our fee budget, but allow the fees // We'll subtract the used fee from our fee budget, but allow the fees
@@ -109,7 +109,7 @@ func (p *paymentLifecycle) fetchPaymentState() (*channeldb.MPPayment,
// Update the payment state. // Update the payment state.
state := &paymentState{ state := &paymentState{
numShardsInFlight: len(payment.InFlightHTLCs()), numShardsInFlight: len(payment.InFlightHTLCs()),
remainingAmt: p.totalAmount - sentAmt, remainingAmt: totalAmt - sentAmt,
remainingFees: feeBudget, remainingFees: feeBudget,
terminate: terminate, terminate: terminate,
} }
@@ -233,7 +233,7 @@ lifecycle:
// tower, no further shards will be launched and we'll // tower, no further shards will be launched and we'll
// return with an error the moment all active shards // return with an error the moment all active shards
// have finished. // have finished.
saveErr := p.router.cfg.Control.Fail( saveErr := p.router.cfg.Control.FailPayment(
p.identifier, channeldb.FailureReasonTimeout, p.identifier, channeldb.FailureReasonTimeout,
) )
if saveErr != nil { if saveErr != nil {
@@ -273,7 +273,7 @@ lifecycle:
"failed with no route: %v", "failed with no route: %v",
p.identifier, failureCode) p.identifier, failureCode)
saveErr := p.router.cfg.Control.Fail( saveErr := p.router.cfg.Control.FailPayment(
p.identifier, failureCode, p.identifier, failureCode,
) )
if saveErr != nil { if saveErr != nil {
@@ -337,7 +337,6 @@ lifecycle:
// Now that the shard was successfully sent, launch a go // Now that the shard was successfully sent, launch a go
// routine that will handle its result when its back. // routine that will handle its result when its back.
shardHandler.collectResultAsync(attempt) shardHandler.collectResultAsync(attempt)
} }
} }
@@ -563,7 +562,7 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
// 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.
resultChan, err := p.router.cfg.Payer.GetPaymentResult( resultChan, err := p.router.cfg.Payer.GetAttemptResult(
attempt.AttemptID, p.identifier, errorDecryptor, attempt.AttemptID, p.identifier, errorDecryptor,
) )
switch { switch {
@@ -785,7 +784,7 @@ func (p *shardHandler) handleSendError(attempt *channeldb.HTLCAttemptInfo,
p.identifier, *reason, sendErr) p.identifier, *reason, sendErr)
// Fail the payment via control tower. // Fail the payment via control tower.
if err := p.router.cfg.Control.Fail( if err := p.router.cfg.Control.FailPayment(
p.identifier, *reason, p.identifier, *reason,
); err != nil { ); err != nil {
log.Errorf("unable to report failure to control "+ log.Errorf("unable to report failure to control "+
+18 -13
View File
@@ -107,20 +107,20 @@ const (
sendToSwitchResultFailure = "SendToSwitch:failure" sendToSwitchResultFailure = "SendToSwitch:failure"
// getPaymentResultSuccess is a test step where we expect the // getPaymentResultSuccess is a test step where we expect the
// router to call the GetPaymentResult method, and we will // router to call the GetAttemptResult method, and we will
// respond with a successful payment result. // respond with a successful payment result.
getPaymentResultSuccess = "GetPaymentResult:success" getPaymentResultSuccess = "GetAttemptResult:success"
// getPaymentResultTempFailure is a test step where we expect the // getPaymentResultTempFailure is a test step where we expect the
// router to call the GetPaymentResult method, and we will // router to call the GetAttemptResult method, and we will
// respond with a forwarding error, expecting the router to retry. // respond with a forwarding error, expecting the router to retry.
getPaymentResultTempFailure = "GetPaymentResult:temp-failure" getPaymentResultTempFailure = "GetAttemptResult:temp-failure"
// getPaymentResultTerminalFailure is a test step where we // getPaymentResultTerminalFailure is a test step where we
// expect the router to call the GetPaymentResult method, and // expect the router to call the GetAttemptResult method, and
// we will respond with a terminal error, expecting the router // we will respond with a terminal error, expecting the router
// to stop making payment attempts. // to stop making payment attempts.
getPaymentResultTerminalFailure = "GetPaymentResult:terminal-failure" getPaymentResultTerminalFailure = "GetAttemptResult:terminal-failure"
// resendPayment is a test step where we manually try to resend // resendPayment is a test step where we manually try to resend
// the same payment, making sure the router responds with an // the same payment, making sure the router responds with an
@@ -652,7 +652,7 @@ func testPaymentLifecycle(t *testing.T, test paymentLifecycleTestCase,
fatal("unable to send result") fatal("unable to send result")
} }
// In this step we expect the GetPaymentResult method // In this step we expect the GetAttemptResult method
// to be called, and we respond with the preimage to // to be called, and we respond with the preimage to
// complete the payment. // complete the payment.
case getPaymentResultSuccess: case getPaymentResultSuccess:
@@ -664,7 +664,7 @@ func testPaymentLifecycle(t *testing.T, test paymentLifecycleTestCase,
fatal("unable to send result") fatal("unable to send result")
} }
// In this state we expect the GetPaymentResult method // In this state we expect the GetAttemptResult method
// to be called, and we respond with a forwarding // to be called, and we respond with a forwarding
// error, indicating that the router should retry. // error, indicating that the router should retry.
case getPaymentResultTempFailure: case getPaymentResultTempFailure:
@@ -682,7 +682,7 @@ func testPaymentLifecycle(t *testing.T, test paymentLifecycleTestCase,
} }
// In this state we expect the router to call the // In this state we expect the router to call the
// GetPaymentResult method, and we will respond with a // GetAttemptResult method, and we will respond with a
// terminal error, indicating the router should stop // terminal error, indicating the router should stop
// making payment attempts. // making payment attempts.
case getPaymentResultTerminalFailure: case getPaymentResultTerminalFailure:
@@ -1052,10 +1052,9 @@ func TestUpdatePaymentState(t *testing.T) {
ct := &mockControlTower{} ct := &mockControlTower{}
rt := &ChannelRouter{cfg: &Config{Control: ct}} rt := &ChannelRouter{cfg: &Config{Control: ct}}
pl := &paymentLifecycle{ pl := &paymentLifecycle{
router: rt, router: rt,
identifier: paymentHash, identifier: paymentHash,
totalAmount: lnwire.MilliSatoshi(tc.totalAmt), feeLimit: lnwire.MilliSatoshi(tc.feeLimit),
feeLimit: lnwire.MilliSatoshi(tc.feeLimit),
} }
if tc.payment == nil { if tc.payment == nil {
@@ -1066,6 +1065,12 @@ func TestUpdatePaymentState(t *testing.T) {
nil, dummyErr, nil, dummyErr,
) )
} else { } else {
// Attach the payment info.
info := &channeldb.PaymentCreationInfo{
Value: lnwire.MilliSatoshi(tc.totalAmt),
}
tc.payment.Info = info
// Otherwise we will return the payment. // Otherwise we will return the payment.
ct.On("FetchPayment", paymentHash).Return( ct.On("FetchPayment", paymentHash).Return(
tc.payment, nil, tc.payment, nil,
+14 -14
View File
@@ -169,7 +169,7 @@ type PaymentAttemptDispatcher interface {
attemptID uint64, attemptID uint64,
htlcAdd *lnwire.UpdateAddHTLC) error htlcAdd *lnwire.UpdateAddHTLC) error
// GetPaymentResult returns the result of the payment attempt with // GetAttemptResult returns the result of the payment attempt with
// the given attemptID. The paymentHash should be set to the payment's // the given attemptID. The paymentHash should be set to the payment's
// overall hash, or in case of AMP payments the payment's unique // overall hash, or in case of AMP payments the payment's unique
// identifier. // identifier.
@@ -180,7 +180,7 @@ type PaymentAttemptDispatcher interface {
// longer be in flight. The switch shutting down is signaled by // longer be in flight. The switch shutting down is signaled by
// closing the channel. If the attemptID is unknown, // closing the channel. If the attemptID is unknown,
// ErrPaymentIDNotFound will be returned. // ErrPaymentIDNotFound will be returned.
GetPaymentResult(attemptID uint64, paymentHash lntypes.Hash, GetAttemptResult(attemptID uint64, paymentHash lntypes.Hash,
deobfuscator htlcswitch.ErrorDecrypter) ( deobfuscator htlcswitch.ErrorDecrypter) (
<-chan *htlcswitch.PaymentResult, error) <-chan *htlcswitch.PaymentResult, error)
@@ -667,9 +667,8 @@ func (r *ChannelRouter) Start() error {
// also set a zero fee limit, as no more routes should // also set a zero fee limit, as no more routes should
// be tried. // be tried.
_, _, err := r.sendPayment( _, _, err := r.sendPayment(
payment.Info.Value, 0, 0, payment.Info.PaymentIdentifier, 0,
payment.Info.PaymentIdentifier, 0, paySession, paySession, shardTracker,
shardTracker,
) )
if err != nil { if err != nil {
log.Errorf("Resuming payment %v failed: %v.", log.Errorf("Resuming payment %v failed: %v.",
@@ -2048,7 +2047,7 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte,
// Since this is the first time this payment is being made, we pass nil // Since this is the first time this payment is being made, we pass nil
// for the existing attempt. // for the existing attempt.
return r.sendPayment( return r.sendPayment(
payment.Amount, payment.FeeLimit, payment.Identifier(), payment.FeeLimit, payment.Identifier(),
payment.PayAttemptTimeout, paySession, shardTracker, payment.PayAttemptTimeout, paySession, shardTracker,
) )
} }
@@ -2071,7 +2070,7 @@ func (r *ChannelRouter) SendPaymentAsync(payment *LightningPayment) error {
spewPayment(payment)) spewPayment(payment))
_, _, err := r.sendPayment( _, _, err := r.sendPayment(
payment.Amount, payment.FeeLimit, payment.Identifier(), payment.FeeLimit, payment.Identifier(),
payment.PayAttemptTimeout, paySession, shardTracker, payment.PayAttemptTimeout, paySession, shardTracker,
) )
if err != nil { if err != nil {
@@ -2253,7 +2252,7 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
log.Debugf("Invalid route provided for payment %x: %v", log.Debugf("Invalid route provided for payment %x: %v",
paymentIdentifier, err) paymentIdentifier, err)
controlErr := r.cfg.Control.Fail( controlErr := r.cfg.Control.FailPayment(
paymentIdentifier, channeldb.FailureReasonError, paymentIdentifier, channeldb.FailureReasonError,
) )
if controlErr != nil { if controlErr != nil {
@@ -2301,14 +2300,16 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
// If a non-terminal error is returned and `skipTempErr` is false, then // If a non-terminal error is returned and `skipTempErr` is false, then
// we'll use the normal no route error. // we'll use the normal no route error.
case err == nil && !skipTempErr: case err == nil && !skipTempErr:
err = r.cfg.Control.Fail( err = r.cfg.Control.FailPayment(
paymentIdentifier, channeldb.FailureReasonNoRoute, paymentIdentifier, channeldb.FailureReasonNoRoute,
) )
// If this is a failure reason, then we'll apply the failure directly // If this is a failure reason, then we'll apply the failure directly
// to the control tower, and return the normal response to the caller. // to the control tower, and return the normal response to the caller.
case goErrors.As(err, &failureReason): case goErrors.As(err, &failureReason):
err = r.cfg.Control.Fail(paymentIdentifier, *failureReason) err = r.cfg.Control.FailPayment(
paymentIdentifier, *failureReason,
)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@@ -2333,9 +2334,9 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
// carry out its execution. After restarts it is safe, and assumed, that the // carry out its execution. After restarts it is safe, and assumed, that the
// router will call this method for every payment still in-flight according to // router will call this method for every payment still in-flight according to
// the ControlTower. // the ControlTower.
func (r *ChannelRouter) sendPayment( func (r *ChannelRouter) sendPayment(feeLimit lnwire.MilliSatoshi,
totalAmt, feeLimit lnwire.MilliSatoshi, identifier lntypes.Hash, identifier lntypes.Hash, timeout time.Duration,
timeout time.Duration, paySession PaymentSession, paySession PaymentSession,
shardTracker shards.ShardTracker) ([32]byte, *route.Route, error) { shardTracker shards.ShardTracker) ([32]byte, *route.Route, error) {
// We'll also fetch the current block height so we can properly // We'll also fetch the current block height so we can properly
@@ -2349,7 +2350,6 @@ func (r *ChannelRouter) sendPayment(
// can resume the payment from the current state. // can resume the payment from the current state.
p := &paymentLifecycle{ p := &paymentLifecycle{
router: r, router: r,
totalAmount: totalAmt,
feeLimit: feeLimit, feeLimit: feeLimit,
identifier: identifier, identifier: identifier,
paySession: paySession, paySession: paySession,
+37 -29
View File
@@ -3421,7 +3421,9 @@ func TestSendMPPaymentSucceed(t *testing.T) {
// The following mocked methods are called inside resumePayment. Note // The following mocked methods are called inside resumePayment. Note
// that the payment object below will determine the state of the // that the payment object below will determine the state of the
// paymentLifecycle. // paymentLifecycle.
payment := &channeldb.MPPayment{} payment := &channeldb.MPPayment{
Info: &channeldb.PaymentCreationInfo{Value: paymentAmt},
}
controlTower.On("FetchPayment", identifier).Return(payment, nil) controlTower.On("FetchPayment", identifier).Return(payment, nil)
// Create a route that can send 1/4 of the total amount. This value // Create a route that can send 1/4 of the total amount. This value
@@ -3441,9 +3443,9 @@ func TestSendMPPaymentSucceed(t *testing.T) {
payment.HTLCs = append(payment.HTLCs, activeAttempt) payment.HTLCs = append(payment.HTLCs, activeAttempt)
}) })
// Create a buffered chan and it will be returned by GetPaymentResult. // Create a buffered chan and it will be returned by GetAttemptResult.
payer.resultChan = make(chan *htlcswitch.PaymentResult, 10) payer.resultChan = make(chan *htlcswitch.PaymentResult, 10)
payer.On("GetPaymentResult", payer.On("GetAttemptResult",
mock.Anything, identifier, mock.Anything, mock.Anything, identifier, mock.Anything,
).Run(func(args mock.Arguments) { ).Run(func(args mock.Arguments) {
// Before the mock method is returned, we send the result to // Before the mock method is returned, we send the result to
@@ -3588,7 +3590,9 @@ func TestSendMPPaymentSucceedOnExtraShards(t *testing.T) {
// The following mocked methods are called inside resumePayment. Note // The following mocked methods are called inside resumePayment. Note
// that the payment object below will determine the state of the // that the payment object below will determine the state of the
// paymentLifecycle. // paymentLifecycle.
payment := &channeldb.MPPayment{} payment := &channeldb.MPPayment{
Info: &channeldb.PaymentCreationInfo{Value: paymentAmt},
}
controlTower.On("FetchPayment", identifier).Return(payment, nil) controlTower.On("FetchPayment", identifier).Return(payment, nil)
// Create a route that can send 1/4 of the total amount. This value // Create a route that can send 1/4 of the total amount. This value
@@ -3608,14 +3612,14 @@ func TestSendMPPaymentSucceedOnExtraShards(t *testing.T) {
payment.HTLCs = append(payment.HTLCs, activeAttempt) payment.HTLCs = append(payment.HTLCs, activeAttempt)
}) })
// Create a buffered chan and it will be returned by GetPaymentResult. // Create a buffered chan and it will be returned by GetAttemptResult.
payer.resultChan = make(chan *htlcswitch.PaymentResult, 10) payer.resultChan = make(chan *htlcswitch.PaymentResult, 10)
// We use the failAttemptCount to track how many attempts we want to // We use the failAttemptCount to track how many attempts we want to
// fail. Each time the following mock method is called, the count gets // fail. Each time the following mock method is called, the count gets
// updated. // updated.
failAttemptCount := 0 failAttemptCount := 0
payer.On("GetPaymentResult", payer.On("GetAttemptResult",
mock.Anything, identifier, mock.Anything, mock.Anything, identifier, mock.Anything,
).Run(func(args mock.Arguments) { ).Run(func(args mock.Arguments) {
// Before the mock method is returned, we send the result to // Before the mock method is returned, we send the result to
@@ -3800,7 +3804,9 @@ func TestSendMPPaymentFailed(t *testing.T) {
// The following mocked methods are called inside resumePayment. Note // The following mocked methods are called inside resumePayment. Note
// that the payment object below will determine the state of the // that the payment object below will determine the state of the
// paymentLifecycle. // paymentLifecycle.
payment := &channeldb.MPPayment{} payment := &channeldb.MPPayment{
Info: &channeldb.PaymentCreationInfo{Value: paymentAmt},
}
controlTower.On("FetchPayment", identifier).Return(payment, nil) controlTower.On("FetchPayment", identifier).Return(payment, nil)
// Create a route that can send 1/4 of the total amount. This value // Create a route that can send 1/4 of the total amount. This value
@@ -3820,14 +3826,14 @@ func TestSendMPPaymentFailed(t *testing.T) {
payment.HTLCs = append(payment.HTLCs, activeAttempt) payment.HTLCs = append(payment.HTLCs, activeAttempt)
}) })
// Create a buffered chan and it will be returned by GetPaymentResult. // Create a buffered chan and it will be returned by GetAttemptResult.
payer.resultChan = make(chan *htlcswitch.PaymentResult, 10) payer.resultChan = make(chan *htlcswitch.PaymentResult, 10)
// We use the failAttemptCount to track how many attempts we want to // We use the failAttemptCount to track how many attempts we want to
// fail. Each time the following mock method is called, the count gets // fail. Each time the following mock method is called, the count gets
// updated. // updated.
failAttemptCount := 0 failAttemptCount := 0
payer.On("GetPaymentResult", payer.On("GetAttemptResult",
mock.Anything, identifier, mock.Anything, mock.Anything, identifier, mock.Anything,
).Run(func(args mock.Arguments) { ).Run(func(args mock.Arguments) {
// Before the mock method is returned, we send the result to // Before the mock method is returned, we send the result to
@@ -3896,7 +3902,7 @@ func TestSendMPPaymentFailed(t *testing.T) {
}) })
// Simple mocking the rest. // Simple mocking the rest.
controlTower.On("Fail", identifier, failureReason).Return(nil) controlTower.On("FailPayment", identifier, failureReason).Return(nil)
payer.On("SendHTLC", payer.On("SendHTLC",
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Return(nil) ).Return(nil)
@@ -4004,7 +4010,9 @@ func TestSendMPPaymentFailedWithShardsInFlight(t *testing.T) {
// The following mocked methods are called inside resumePayment. Note // The following mocked methods are called inside resumePayment. Note
// that the payment object below will determine the state of the // that the payment object below will determine the state of the
// paymentLifecycle. // paymentLifecycle.
payment := &channeldb.MPPayment{} payment := &channeldb.MPPayment{
Info: &channeldb.PaymentCreationInfo{Value: paymentAmt},
}
controlTower.On("FetchPayment", identifier).Return(payment, nil) controlTower.On("FetchPayment", identifier).Return(payment, nil)
// Create a route that can send 1/4 of the total amount. This value // Create a route that can send 1/4 of the total amount. This value
@@ -4024,18 +4032,18 @@ func TestSendMPPaymentFailedWithShardsInFlight(t *testing.T) {
payment.HTLCs = append(payment.HTLCs, activeAttempt) payment.HTLCs = append(payment.HTLCs, activeAttempt)
}) })
// Create a buffered chan and it will be returned by GetPaymentResult. // Create a buffered chan and it will be returned by GetAttemptResult.
payer.resultChan = make(chan *htlcswitch.PaymentResult, 10) payer.resultChan = make(chan *htlcswitch.PaymentResult, 10)
// We use the getPaymentResultCnt to track how many times we called // We use the getPaymentResultCnt to track how many times we called
// GetPaymentResult. As shard launch is sequential, and we fail the // GetAttemptResult. As shard launch is sequential, and we fail the
// first shard that calls GetPaymentResult, we may end up with different // first shard that calls GetAttemptResult, we may end up with different
// counts since the lifecycle itself is asynchronous. To avoid flakes // counts since the lifecycle itself is asynchronous. To avoid flakes
// due to this undeterminsitic behavior, we'll compare the final // due to this undeterminsitic behavior, we'll compare the final
// getPaymentResultCnt with other counters to create a final test // getPaymentResultCnt with other counters to create a final test
// expectation. // expectation.
getPaymentResultCnt := 0 getPaymentResultCnt := 0
payer.On("GetPaymentResult", payer.On("GetAttemptResult",
mock.Anything, identifier, mock.Anything, mock.Anything, identifier, mock.Anything,
).Run(func(args mock.Arguments) { ).Run(func(args mock.Arguments) {
// Before the mock method is returned, we send the result to // Before the mock method is returned, we send the result to
@@ -4091,7 +4099,7 @@ func TestSendMPPaymentFailedWithShardsInFlight(t *testing.T) {
// Simple mocking the rest. // Simple mocking the rest.
cntFail := 0 cntFail := 0
controlTower.On("Fail", identifier, failureReason).Return(nil) controlTower.On("FailPayment", identifier, failureReason).Return(nil)
payer.On("SendHTLC", payer.On("SendHTLC",
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Return(nil).Run(func(args mock.Arguments) { ).Return(nil).Run(func(args mock.Arguments) {
@@ -4254,9 +4262,9 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) {
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Return(nil) ).Return(nil)
// Create a buffered chan and it will be returned by GetPaymentResult. // Create a buffered chan and it will be returned by GetAttemptResult.
payer.resultChan = make(chan *htlcswitch.PaymentResult, 1) payer.resultChan = make(chan *htlcswitch.PaymentResult, 1)
payer.On("GetPaymentResult", payer.On("GetAttemptResult",
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Run(func(_ mock.Arguments) { ).Run(func(_ mock.Arguments) {
// Send a successful payment result. // Send a successful payment result.
@@ -4329,7 +4337,7 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) {
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Return(nil) ).Return(nil)
// Create a buffered chan and it will be returned by GetPaymentResult. // Create a buffered chan and it will be returned by GetAttemptResult.
payer.resultChan = make(chan *htlcswitch.PaymentResult, 1) payer.resultChan = make(chan *htlcswitch.PaymentResult, 1)
// Create the error to be returned. // Create the error to be returned.
@@ -4338,8 +4346,8 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) {
1, 1,
) )
// Mock GetPaymentResult to return a failure. // Mock GetAttemptResult to return a failure.
payer.On("GetPaymentResult", payer.On("GetAttemptResult",
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Run(func(_ mock.Arguments) { ).Run(func(_ mock.Arguments) {
// Send an attempt failure. // Send an attempt failure.
@@ -4412,13 +4420,13 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) {
).Return(testAttempt, nil) ).Return(testAttempt, nil)
// Expect the payment to be failed. // Expect the payment to be failed.
controlTower.On("Fail", payHash, mock.Anything).Return(nil) controlTower.On("FailPayment", payHash, mock.Anything).Return(nil)
payer.On("SendHTLC", payer.On("SendHTLC",
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Return(nil) ).Return(nil)
// Create a buffered chan and it will be returned by GetPaymentResult. // Create a buffered chan and it will be returned by GetAttemptResult.
payer.resultChan = make(chan *htlcswitch.PaymentResult, 1) payer.resultChan = make(chan *htlcswitch.PaymentResult, 1)
// Create the error to be returned. // Create the error to be returned.
@@ -4426,8 +4434,8 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) {
&lnwire.FailIncorrectDetails{}, 1, &lnwire.FailIncorrectDetails{}, 1,
) )
// Mock GetPaymentResult to return a failure. // Mock GetAttemptResult to return a failure.
payer.On("GetPaymentResult", payer.On("GetAttemptResult",
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Run(func(_ mock.Arguments) { ).Run(func(_ mock.Arguments) {
// Send a permanent failure. // Send a permanent failure.
@@ -4501,13 +4509,13 @@ func TestSendToRouteTempFailure(t *testing.T) {
).Return(testAttempt, nil) ).Return(testAttempt, nil)
// Expect the payment to be failed. // Expect the payment to be failed.
controlTower.On("Fail", payHash, mock.Anything).Return(nil) controlTower.On("FailPayment", payHash, mock.Anything).Return(nil)
payer.On("SendHTLC", payer.On("SendHTLC",
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Return(nil) ).Return(nil)
// Create a buffered chan and it will be returned by GetPaymentResult. // Create a buffered chan and it will be returned by GetAttemptResult.
payer.resultChan = make(chan *htlcswitch.PaymentResult, 1) payer.resultChan = make(chan *htlcswitch.PaymentResult, 1)
// Create the error to be returned. // Create the error to be returned.
@@ -4516,8 +4524,8 @@ func TestSendToRouteTempFailure(t *testing.T) {
1, 1,
) )
// Mock GetPaymentResult to return a failure. // Mock GetAttemptResult to return a failure.
payer.On("GetPaymentResult", payer.On("GetAttemptResult",
mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything,
).Run(func(_ mock.Arguments) { ).Run(func(_ mock.Arguments) {
// Send an attempt failure. // Send an attempt failure.