diff --git a/channeldb/payment_control.go b/channeldb/payment_control.go index 7bd00e4d4..3328f086d 100644 --- a/channeldb/payment_control.go +++ b/channeldb/payment_control.go @@ -43,7 +43,8 @@ var ( // ErrPaymentTerminal is returned if we attempt to alter a payment that // 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 // settled HTLC attempt. @@ -77,7 +78,8 @@ var ( // ErrMPPTotalAmountMismatch is returned if we try to register an MPP // 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 = errors.New("unable to find attempt info for " + diff --git a/channeldb/payment_status.go b/channeldb/payment_status.go new file mode 100644 index 000000000..fedf99d2d --- /dev/null +++ b/channeldb/payment_status.go @@ -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" + } +} diff --git a/channeldb/payments.go b/channeldb/payments.go index aef5ddfa2..93abe4fb1 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -175,43 +175,6 @@ func (r FailureReason) String() string { 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 // initiating a payment, moving it into state InFlight. type PaymentCreationInfo struct { diff --git a/docs/release-notes/release-notes-0.16.0.md b/docs/release-notes/release-notes-0.16.0.md index fa5b657ec..e05966fb8 100644 --- a/docs/release-notes/release-notes-0.16.0.md +++ b/docs/release-notes/release-notes-0.16.0.md @@ -225,6 +225,10 @@ certain large transactions](https://github.com/lightningnetwork/lnd/pull/7100). * [CI: update test coverage library 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 diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index cd3890d17..bf3916651 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -1306,7 +1306,7 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) { ) 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(), ) 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") - resultChan, err := n.aliceServer.htlcSwitch.GetPaymentResult( + resultChan, err := n.aliceServer.htlcSwitch.GetAttemptResult( pid, htlc.PaymentHash, newMockDeobfuscator(), ) require.NoError(t, err, "unable to get payment result") diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 34997ac82..ee1712c77 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -427,16 +427,16 @@ func (s *Switch) ProcessContractResolution(msg contractcourt.ResolutionMsg) erro } } -// GetPaymentResult returns the the result of the payment attempt with the -// given attemptID. The paymentHash should be set to the payment's overall -// hash, or in case of AMP payments the payment's unique identifier. +// GetAttemptResult returns the result of the payment attempt with the given +// attemptID. The paymentHash should be set to the payment's overall hash, or +// in case of AMP payments the payment's unique identifier. // // 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 // 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 // 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) { var ( diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go index 4d666b760..a8c7aa4ab 100644 --- a/htlcswitch/switch_test.go +++ b/htlcswitch/switch_test.go @@ -2699,7 +2699,7 @@ func TestSwitchSendPayment(t *testing.T) { // First check that the switch will correctly respond that this payment // ID is unknown. - _, err = s.GetPaymentResult( + _, err = s.GetAttemptResult( paymentID, rhash, newMockDeobfuscator(), ) if err != ErrPaymentIDNotFound { @@ -2717,7 +2717,7 @@ func TestSwitchSendPayment(t *testing.T) { return } - resultChan, err := s.GetPaymentResult( + resultChan, err := s.GetAttemptResult( paymentID, rhash, newMockDeobfuscator(), ) 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 // payment ID. This is important for not to lose results under concurrent // lookup and receiving results. -func TestSwitchGetPaymentResult(t *testing.T) { +func TestSwitchGetAttemptResult(t *testing.T) { t.Parallel() const paymentID = 123 @@ -3100,7 +3100,7 @@ func TestSwitchGetPaymentResult(t *testing.T) { // added anything to the store yet, ErrPaymentIDNotFound should be // returned. lookup <- nil - _, err = s.GetPaymentResult( + _, err = s.GetAttemptResult( paymentID, lntypes.Hash{}, newMockDeobfuscator(), ) 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 // subscribe to payment results, and return the result when available. lookup <- &PaymentCircuit{} - resultChan, err := s.GetPaymentResult( + resultChan, err := s.GetAttemptResult( paymentID, lntypes.Hash{}, newMockDeobfuscator(), ) 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 // store. lookup <- nil - resultChan, err = s.GetPaymentResult( + resultChan, err = s.GetAttemptResult( paymentID, lntypes.Hash{}, newMockDeobfuscator(), ) 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, ) if err != nil { @@ -3283,7 +3283,7 @@ func TestInvalidFailure(t *testing.T) { }, } - resultChan, err = s.GetPaymentResult( + resultChan, err = s.GetAttemptResult( paymentID, rhash, &deobfuscator, ) if err != nil { @@ -4415,7 +4415,7 @@ func TestSwitchDustForwarding(t *testing.T) { require.NoError(t, err) carolAttemptID++ - carolResultChan, err := n.carolServer.htlcSwitch.GetPaymentResult( + carolResultChan, err := n.carolServer.htlcSwitch.GetAttemptResult( uint64(carolAttemptID-1), carolHash, newMockDeobfuscator(), ) require.NoError(t, err) diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index e4568fc0e..b9ba325ab 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -787,7 +787,7 @@ func preparePayment(sendingPeer, receivingPeer lnpeer.Peer, if err != nil { return err } - resultChan, err := sender.htlcSwitch.GetPaymentResult( + resultChan, err := sender.htlcSwitch.GetAttemptResult( pid, hash, newMockDeobfuscator(), ) if err != nil { @@ -1345,7 +1345,7 @@ func (n *twoHopNetwork) makeHoldPayment(sendingPeer, receivingPeer lnpeer.Peer, } go func() { - resultChan, err := sender.htlcSwitch.GetPaymentResult( + resultChan, err := sender.htlcSwitch.GetAttemptResult( pid, rhash, newMockDeobfuscator(), ) if err != nil { diff --git a/routing/control_tower.go b/routing/control_tower.go index 9f95bb252..a0c5b1df7 100644 --- a/routing/control_tower.go +++ b/routing/control_tower.go @@ -46,13 +46,13 @@ type ControlTower interface { // hash. FetchPayment(paymentHash lntypes.Hash) (*channeldb.MPPayment, error) - // Fail transitions a payment into the Failed state, and records the - // ultimate reason the payment failed. Note that this should only be - // called when all active active attempts are already failed. After + // FailPayment transitions a payment into the Failed state, and records + // the ultimate reason the payment failed. Note that this should only + // be called when all active attempts are already failed. After // invoking this method, InitPayment should return nil on its next call // for this payment hash, allowing the user to make a subsequent // payment. - Fail(lntypes.Hash, channeldb.FailureReason) error + FailPayment(lntypes.Hash, channeldb.FailureReason) error // FetchInFlightPayments returns all payments with status InFlight. FetchInFlightPayments() ([]*channeldb.MPPayment, error) @@ -229,11 +229,11 @@ func (p *controlTower) FetchPayment(paymentHash lntypes.Hash) ( return p.db.FetchPayment(paymentHash) } -// Fail transitions a payment into the Failed state, and records the reason the -// payment failed. After invoking this method, InitPayment should return nil on -// its next call for this payment hash, allowing the switch to make a -// subsequent payment. -func (p *controlTower) Fail(paymentHash lntypes.Hash, +// FailPayment transitions a payment into the Failed state, and records the +// reason the payment failed. After invoking this method, InitPayment should +// return nil on its next call for this payment hash, allowing the switch to +// make a subsequent payment. +func (p *controlTower) FailPayment(paymentHash lntypes.Hash, reason channeldb.FailureReason) error { p.paymentsMtx.Lock(paymentHash) diff --git a/routing/control_tower_test.go b/routing/control_tower_test.go index 3dd2c5234..05f049fa2 100644 --- a/routing/control_tower_test.go +++ b/routing/control_tower_test.go @@ -457,7 +457,10 @@ func testPaymentControlSubscribeFail(t *testing.T, registerAttempt, } // 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) } diff --git a/routing/mock_test.go b/routing/mock_test.go index b0a9ba4ff..afd0126dd 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -56,7 +56,7 @@ func (m *mockPaymentAttemptDispatcherOld) SendHTLC( return nil } -func (m *mockPaymentAttemptDispatcherOld) GetPaymentResult(paymentID uint64, +func (m *mockPaymentAttemptDispatcherOld) GetAttemptResult(paymentID uint64, _ lntypes.Hash, _ htlcswitch.ErrorDecrypter) ( <-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) { select { @@ -469,7 +469,7 @@ func (m *mockControlTowerOld) FailAttempt(phash lntypes.Hash, pid uint64, 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 { m.Lock() @@ -578,7 +578,7 @@ func (m *mockPaymentAttemptDispatcher) SendHTLC(firstHop lnwire.ShortChannelID, return args.Error(0) } -func (m *mockPaymentAttemptDispatcher) GetPaymentResult(attemptID uint64, +func (m *mockPaymentAttemptDispatcher) GetAttemptResult(attemptID uint64, paymentHash lntypes.Hash, deobfuscator htlcswitch.ErrorDecrypter) ( <-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) } -func (m *mockControlTower) Fail(phash lntypes.Hash, +func (m *mockControlTower) FailPayment(phash lntypes.Hash, reason channeldb.FailureReason) error { m.Lock() @@ -758,6 +758,7 @@ func (m *mockControlTower) FetchPayment(phash lntypes.Hash) ( // Make a copy of the payment here to avoid data race. p := args.Get(0).(*channeldb.MPPayment) payment := &channeldb.MPPayment{ + Info: p.Info, FailureReason: p.FailureReason, } payment.HTLCs = make([]channeldb.HTLCAttempt, len(p.HTLCs)) diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index b87dddef1..32f0d811d 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -23,7 +23,6 @@ var errShardHandlerExiting = fmt.Errorf("shard handler exiting") // needed to resume if from any point. type paymentLifecycle struct { router *ChannelRouter - totalAmount lnwire.MilliSatoshi feeLimit lnwire.MilliSatoshi identifier lntypes.Hash paySession PaymentSession @@ -83,9 +82,10 @@ func (p *paymentLifecycle) fetchPaymentState() (*channeldb.MPPayment, sentAmt, fees := payment.SentAmt() // 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 "+ - "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 @@ -109,7 +109,7 @@ func (p *paymentLifecycle) fetchPaymentState() (*channeldb.MPPayment, // Update the payment state. state := &paymentState{ numShardsInFlight: len(payment.InFlightHTLCs()), - remainingAmt: p.totalAmount - sentAmt, + remainingAmt: totalAmt - sentAmt, remainingFees: feeBudget, terminate: terminate, } @@ -233,7 +233,7 @@ lifecycle: // tower, no further shards will be launched and we'll // return with an error the moment all active shards // have finished. - saveErr := p.router.cfg.Control.Fail( + saveErr := p.router.cfg.Control.FailPayment( p.identifier, channeldb.FailureReasonTimeout, ) if saveErr != nil { @@ -273,7 +273,7 @@ lifecycle: "failed with no route: %v", p.identifier, failureCode) - saveErr := p.router.cfg.Control.Fail( + saveErr := p.router.cfg.Control.FailPayment( p.identifier, failureCode, ) if saveErr != nil { @@ -337,7 +337,6 @@ lifecycle: // Now that the shard was successfully sent, launch a go // routine that will handle its result when its back. 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 // available. - resultChan, err := p.router.cfg.Payer.GetPaymentResult( + resultChan, err := p.router.cfg.Payer.GetAttemptResult( attempt.AttemptID, p.identifier, errorDecryptor, ) switch { @@ -785,7 +784,7 @@ func (p *shardHandler) handleSendError(attempt *channeldb.HTLCAttemptInfo, p.identifier, *reason, sendErr) // Fail the payment via control tower. - if err := p.router.cfg.Control.Fail( + if err := p.router.cfg.Control.FailPayment( p.identifier, *reason, ); err != nil { log.Errorf("unable to report failure to control "+ diff --git a/routing/payment_lifecycle_test.go b/routing/payment_lifecycle_test.go index b69df3e79..102aa4c01 100644 --- a/routing/payment_lifecycle_test.go +++ b/routing/payment_lifecycle_test.go @@ -107,20 +107,20 @@ const ( sendToSwitchResultFailure = "SendToSwitch:failure" // 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. - getPaymentResultSuccess = "GetPaymentResult:success" + getPaymentResultSuccess = "GetAttemptResult:success" // 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. - getPaymentResultTempFailure = "GetPaymentResult:temp-failure" + getPaymentResultTempFailure = "GetAttemptResult:temp-failure" // 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 // to stop making payment attempts. - getPaymentResultTerminalFailure = "GetPaymentResult:terminal-failure" + getPaymentResultTerminalFailure = "GetAttemptResult:terminal-failure" // resendPayment is a test step where we manually try to resend // 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") } - // 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 // complete the payment. case getPaymentResultSuccess: @@ -664,7 +664,7 @@ func testPaymentLifecycle(t *testing.T, test paymentLifecycleTestCase, 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 // error, indicating that the router should retry. case getPaymentResultTempFailure: @@ -682,7 +682,7 @@ func testPaymentLifecycle(t *testing.T, test paymentLifecycleTestCase, } // 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 // making payment attempts. case getPaymentResultTerminalFailure: @@ -1052,10 +1052,9 @@ func TestUpdatePaymentState(t *testing.T) { ct := &mockControlTower{} rt := &ChannelRouter{cfg: &Config{Control: ct}} pl := &paymentLifecycle{ - router: rt, - identifier: paymentHash, - totalAmount: lnwire.MilliSatoshi(tc.totalAmt), - feeLimit: lnwire.MilliSatoshi(tc.feeLimit), + router: rt, + identifier: paymentHash, + feeLimit: lnwire.MilliSatoshi(tc.feeLimit), } if tc.payment == nil { @@ -1066,6 +1065,12 @@ func TestUpdatePaymentState(t *testing.T) { nil, dummyErr, ) } else { + // Attach the payment info. + info := &channeldb.PaymentCreationInfo{ + Value: lnwire.MilliSatoshi(tc.totalAmt), + } + tc.payment.Info = info + // Otherwise we will return the payment. ct.On("FetchPayment", paymentHash).Return( tc.payment, nil, diff --git a/routing/router.go b/routing/router.go index cf6357c68..01d4b5022 100644 --- a/routing/router.go +++ b/routing/router.go @@ -169,7 +169,7 @@ type PaymentAttemptDispatcher interface { attemptID uint64, 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 // overall hash, or in case of AMP payments the payment's unique // identifier. @@ -180,7 +180,7 @@ type PaymentAttemptDispatcher interface { // longer be in flight. The switch shutting down is signaled by // closing the channel. If the attemptID is unknown, // ErrPaymentIDNotFound will be returned. - GetPaymentResult(attemptID uint64, paymentHash lntypes.Hash, + GetAttemptResult(attemptID uint64, paymentHash lntypes.Hash, deobfuscator htlcswitch.ErrorDecrypter) ( <-chan *htlcswitch.PaymentResult, error) @@ -667,9 +667,8 @@ func (r *ChannelRouter) Start() error { // also set a zero fee limit, as no more routes should // be tried. _, _, err := r.sendPayment( - payment.Info.Value, 0, - payment.Info.PaymentIdentifier, 0, paySession, - shardTracker, + 0, payment.Info.PaymentIdentifier, 0, + paySession, shardTracker, ) if err != nil { 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 // for the existing attempt. return r.sendPayment( - payment.Amount, payment.FeeLimit, payment.Identifier(), + payment.FeeLimit, payment.Identifier(), payment.PayAttemptTimeout, paySession, shardTracker, ) } @@ -2071,7 +2070,7 @@ func (r *ChannelRouter) SendPaymentAsync(payment *LightningPayment) error { spewPayment(payment)) _, _, err := r.sendPayment( - payment.Amount, payment.FeeLimit, payment.Identifier(), + payment.FeeLimit, payment.Identifier(), payment.PayAttemptTimeout, paySession, shardTracker, ) 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", paymentIdentifier, err) - controlErr := r.cfg.Control.Fail( + controlErr := r.cfg.Control.FailPayment( paymentIdentifier, channeldb.FailureReasonError, ) 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 // we'll use the normal no route error. case err == nil && !skipTempErr: - err = r.cfg.Control.Fail( + err = r.cfg.Control.FailPayment( paymentIdentifier, channeldb.FailureReasonNoRoute, ) // 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. case goErrors.As(err, &failureReason): - err = r.cfg.Control.Fail(paymentIdentifier, *failureReason) + err = r.cfg.Control.FailPayment( + paymentIdentifier, *failureReason, + ) } if err != nil { 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 // router will call this method for every payment still in-flight according to // the ControlTower. -func (r *ChannelRouter) sendPayment( - totalAmt, feeLimit lnwire.MilliSatoshi, identifier lntypes.Hash, - timeout time.Duration, paySession PaymentSession, +func (r *ChannelRouter) sendPayment(feeLimit lnwire.MilliSatoshi, + identifier lntypes.Hash, timeout time.Duration, + paySession PaymentSession, shardTracker shards.ShardTracker) ([32]byte, *route.Route, error) { // 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. p := &paymentLifecycle{ router: r, - totalAmount: totalAmt, feeLimit: feeLimit, identifier: identifier, paySession: paySession, diff --git a/routing/router_test.go b/routing/router_test.go index a3f832b82..ea76140a2 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -3421,7 +3421,9 @@ func TestSendMPPaymentSucceed(t *testing.T) { // The following mocked methods are called inside resumePayment. Note // that the payment object below will determine the state of the // paymentLifecycle. - payment := &channeldb.MPPayment{} + payment := &channeldb.MPPayment{ + Info: &channeldb.PaymentCreationInfo{Value: paymentAmt}, + } controlTower.On("FetchPayment", identifier).Return(payment, nil) // 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) }) - // 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.On("GetPaymentResult", + payer.On("GetAttemptResult", mock.Anything, identifier, mock.Anything, ).Run(func(args mock.Arguments) { // 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 // that the payment object below will determine the state of the // paymentLifecycle. - payment := &channeldb.MPPayment{} + payment := &channeldb.MPPayment{ + Info: &channeldb.PaymentCreationInfo{Value: paymentAmt}, + } controlTower.On("FetchPayment", identifier).Return(payment, nil) // 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) }) - // 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) // We use the failAttemptCount to track how many attempts we want to // fail. Each time the following mock method is called, the count gets // updated. failAttemptCount := 0 - payer.On("GetPaymentResult", + payer.On("GetAttemptResult", mock.Anything, identifier, mock.Anything, ).Run(func(args mock.Arguments) { // 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 // that the payment object below will determine the state of the // paymentLifecycle. - payment := &channeldb.MPPayment{} + payment := &channeldb.MPPayment{ + Info: &channeldb.PaymentCreationInfo{Value: paymentAmt}, + } controlTower.On("FetchPayment", identifier).Return(payment, nil) // 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) }) - // 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) // We use the failAttemptCount to track how many attempts we want to // fail. Each time the following mock method is called, the count gets // updated. failAttemptCount := 0 - payer.On("GetPaymentResult", + payer.On("GetAttemptResult", mock.Anything, identifier, mock.Anything, ).Run(func(args mock.Arguments) { // Before the mock method is returned, we send the result to @@ -3896,7 +3902,7 @@ func TestSendMPPaymentFailed(t *testing.T) { }) // Simple mocking the rest. - controlTower.On("Fail", identifier, failureReason).Return(nil) + controlTower.On("FailPayment", identifier, failureReason).Return(nil) payer.On("SendHTLC", mock.Anything, mock.Anything, mock.Anything, ).Return(nil) @@ -4004,7 +4010,9 @@ func TestSendMPPaymentFailedWithShardsInFlight(t *testing.T) { // The following mocked methods are called inside resumePayment. Note // that the payment object below will determine the state of the // paymentLifecycle. - payment := &channeldb.MPPayment{} + payment := &channeldb.MPPayment{ + Info: &channeldb.PaymentCreationInfo{Value: paymentAmt}, + } controlTower.On("FetchPayment", identifier).Return(payment, nil) // 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) }) - // 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) // We use the getPaymentResultCnt to track how many times we called - // GetPaymentResult. As shard launch is sequential, and we fail the - // first shard that calls GetPaymentResult, we may end up with different + // GetAttemptResult. As shard launch is sequential, and we fail the + // first shard that calls GetAttemptResult, we may end up with different // counts since the lifecycle itself is asynchronous. To avoid flakes // due to this undeterminsitic behavior, we'll compare the final // getPaymentResultCnt with other counters to create a final test // expectation. getPaymentResultCnt := 0 - payer.On("GetPaymentResult", + payer.On("GetAttemptResult", mock.Anything, identifier, mock.Anything, ).Run(func(args mock.Arguments) { // Before the mock method is returned, we send the result to @@ -4091,7 +4099,7 @@ func TestSendMPPaymentFailedWithShardsInFlight(t *testing.T) { // Simple mocking the rest. cntFail := 0 - controlTower.On("Fail", identifier, failureReason).Return(nil) + controlTower.On("FailPayment", identifier, failureReason).Return(nil) payer.On("SendHTLC", mock.Anything, mock.Anything, mock.Anything, ).Return(nil).Run(func(args mock.Arguments) { @@ -4254,9 +4262,9 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) { mock.Anything, mock.Anything, mock.Anything, ).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.On("GetPaymentResult", + payer.On("GetAttemptResult", mock.Anything, mock.Anything, mock.Anything, ).Run(func(_ mock.Arguments) { // Send a successful payment result. @@ -4329,7 +4337,7 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) { mock.Anything, mock.Anything, mock.Anything, ).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) // Create the error to be returned. @@ -4338,8 +4346,8 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) { 1, ) - // Mock GetPaymentResult to return a failure. - payer.On("GetPaymentResult", + // Mock GetAttemptResult to return a failure. + payer.On("GetAttemptResult", mock.Anything, mock.Anything, mock.Anything, ).Run(func(_ mock.Arguments) { // Send an attempt failure. @@ -4412,13 +4420,13 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) { ).Return(testAttempt, nil) // 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", mock.Anything, mock.Anything, mock.Anything, ).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) // Create the error to be returned. @@ -4426,8 +4434,8 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) { &lnwire.FailIncorrectDetails{}, 1, ) - // Mock GetPaymentResult to return a failure. - payer.On("GetPaymentResult", + // Mock GetAttemptResult to return a failure. + payer.On("GetAttemptResult", mock.Anything, mock.Anything, mock.Anything, ).Run(func(_ mock.Arguments) { // Send a permanent failure. @@ -4501,13 +4509,13 @@ func TestSendToRouteTempFailure(t *testing.T) { ).Return(testAttempt, nil) // 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", mock.Anything, mock.Anything, mock.Anything, ).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) // Create the error to be returned. @@ -4516,8 +4524,8 @@ func TestSendToRouteTempFailure(t *testing.T) { 1, ) - // Mock GetPaymentResult to return a failure. - payer.On("GetPaymentResult", + // Mock GetAttemptResult to return a failure. + payer.On("GetAttemptResult", mock.Anything, mock.Anything, mock.Anything, ).Run(func(_ mock.Arguments) { // Send an attempt failure.