multi: thread context through payment lifecyle functions

This commit is contained in:
ziggie committed 2026-02-25 08:02:25 +01:00
1 parent dfe0c43c7d
commit 8038dc0054
6 files changed
+91 -54

No files matched your search

+2 -2
View File
@@ -1088,11 +1088,11 @@ func (s *Server) SendToRouteV2(ctx context.Context,
// db. // db.
if req.SkipTempErr { if req.SkipTempErr {
attempt, err = s.cfg.Router.SendToRouteSkipTempErr( attempt, err = s.cfg.Router.SendToRouteSkipTempErr(
hash, route, firstHopRecords, ctx, hash, route, firstHopRecords,
) )
} else { } else {
attempt, err = s.cfg.Router.SendToRoute( attempt, err = s.cfg.Router.SendToRoute(
hash, route, firstHopRecords, ctx, hash, route, firstHopRecords,
) )
} }
if attempt != nil { if attempt != nil {
+18 -12
View File
@@ -128,7 +128,7 @@ const (
// results is sent back. then process its result here. When there's no need to // results is sent back. then process its result here. When there's no need to
// wait for results, the method will exit with `stepExit` such that the payment // wait for results, the method will exit with `stepExit` such that the payment
// lifecycle loop will terminate. // lifecycle loop will terminate.
func (p *paymentLifecycle) decideNextStep( func (p *paymentLifecycle) decideNextStep(ctx context.Context,
payment paymentsdb.DBMPPayment) (stateStep, error) { payment paymentsdb.DBMPPayment) (stateStep, error) {
// Check whether we could make new HTLC attempts. // Check whether we could make new HTLC attempts.
@@ -168,7 +168,7 @@ func (p *paymentLifecycle) decideNextStep(
// stepSkip and move to the next lifecycle iteration, which will // stepSkip and move to the next lifecycle iteration, which will
// refresh the payment and wait for the next attempt result, if // refresh the payment and wait for the next attempt result, if
// any. // any.
_, err := p.handleAttemptResult(r.attempt, r.result) _, err := p.handleAttemptResult(ctx, r.attempt, r.result)
// We would only get a DB-related error here, which will cause // We would only get a DB-related error here, which will cause
// us to abort the payment flow. // us to abort the payment flow.
@@ -192,6 +192,13 @@ func (p *paymentLifecycle) resumePayment(ctx context.Context) ([32]byte,
// We need to make sure we can still do db operations after the context // We need to make sure we can still do db operations after the context
// is cancelled. // is cancelled.
//
// TODO(ziggie): This is a workaround to avoid a greater refactor of the
// payment lifecycle. We can currently not rely on the parent context
// because this method is also collecting the results of inflight HTLCs
// after the context is cancelled. So we need to make sure we only use
// the current context to stop creating new attempts but use this
// cleanupCtx to do all the db operations.
cleanupCtx := context.WithoutCancel(ctx) cleanupCtx := context.WithoutCancel(ctx)
// When the payment lifecycle loop exits, we make sure to signal any // When the payment lifecycle loop exits, we make sure to signal any
@@ -264,7 +271,7 @@ lifecycle:
// //
// Now decide the next step of the current lifecycle. // Now decide the next step of the current lifecycle.
step, err := p.decideNextStep(payment) step, err := p.decideNextStep(cleanupCtx, payment)
if err != nil { if err != nil {
return exitWithErr(err) return exitWithErr(err)
} }
@@ -307,7 +314,9 @@ lifecycle:
log.Tracef("Found route: %s", lnutils.SpewLogClosure(rt.Hops)) log.Tracef("Found route: %s", lnutils.SpewLogClosure(rt.Hops))
// We found a route to try, create a new HTLC attempt to try. // We found a route to try, create a new HTLC attempt to try.
attempt, err := p.registerAttempt(rt, ps.RemainingAmt) attempt, err := p.registerAttempt(
cleanupCtx, rt, ps.RemainingAmt,
)
if err != nil { if err != nil {
return exitWithErr(err) return exitWithErr(err)
} }
@@ -596,11 +605,9 @@ func (p *paymentLifecycle) collectResult(
// registerAttempt is responsible for creating and saving an HTLC attempt in db // registerAttempt is responsible for creating and saving an HTLC attempt in db
// by using the route info provided. The `remainingAmt` is used to decide // by using the route info provided. The `remainingAmt` is used to decide
// whether this is the last attempt. // whether this is the last attempt.
func (p *paymentLifecycle) registerAttempt(rt *route.Route, func (p *paymentLifecycle) registerAttempt(ctx context.Context, rt *route.Route,
remainingAmt lnwire.MilliSatoshi) (*paymentsdb.HTLCAttempt, error) { remainingAmt lnwire.MilliSatoshi) (*paymentsdb.HTLCAttempt, error) {
ctx := context.TODO()
// If this route will consume the last remaining amount to send // If this route will consume the last remaining amount to send
// to the receiver, this will be our last shard (for now). // to the receiver, this will be our last shard (for now).
isLastAttempt := rt.ReceiverAmt() == remainingAmt isLastAttempt := rt.ReceiverAmt() == remainingAmt
@@ -1184,11 +1191,10 @@ func (p *paymentLifecycle) reloadPayment() (paymentsdb.DBMPPayment,
// handleAttemptResult processes the result of an HTLC attempt returned from // handleAttemptResult processes the result of an HTLC attempt returned from
// the htlcswitch. // the htlcswitch.
func (p *paymentLifecycle) handleAttemptResult(attempt *paymentsdb.HTLCAttempt, func (p *paymentLifecycle) handleAttemptResult(ctx context.Context,
attempt *paymentsdb.HTLCAttempt,
result *htlcswitch.PaymentResult) (*attemptResult, error) { result *htlcswitch.PaymentResult) (*attemptResult, error) {
ctx := context.TODO()
// If the result has an error, we need to further process it by failing // If the result has an error, we need to further process it by failing
// the attempt and maybe fail the payment. // the attempt and maybe fail the payment.
if result.Error != nil { if result.Error != nil {
@@ -1235,7 +1241,7 @@ func (p *paymentLifecycle) handleAttemptResult(attempt *paymentsdb.HTLCAttempt,
// available from the Switch, then records the attempt outcome with the control // available from the Switch, then records the attempt outcome with the control
// tower. An attemptResult is returned, indicating the final outcome of this // tower. An attemptResult is returned, indicating the final outcome of this
// HTLC attempt. // HTLC attempt.
func (p *paymentLifecycle) collectAndHandleResult( func (p *paymentLifecycle) collectAndHandleResult(ctx context.Context,
attempt *paymentsdb.HTLCAttempt) (*attemptResult, error) { attempt *paymentsdb.HTLCAttempt) (*attemptResult, error) {
result, err := p.collectResult(attempt) result, err := p.collectResult(attempt)
@@ -1243,5 +1249,5 @@ func (p *paymentLifecycle) collectAndHandleResult(
return nil, err return nil, err
} }
return p.handleAttemptResult(attempt, result) return p.handleAttemptResult(ctx, attempt, result)
} }
+18 -14
View File
@@ -599,7 +599,7 @@ func TestDecideNextStep(t *testing.T) {
// Once the setup is finished, run the test cases. // Once the setup is finished, run the test cases.
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
step, err := p.decideNextStep(payment) step, err := p.decideNextStep(t.Context(), payment)
require.Equal(t, tc.expectedStep, step) require.Equal(t, tc.expectedStep, step)
require.ErrorIs(t, tc.expectedErr, err) require.ErrorIs(t, tc.expectedErr, err)
}) })
@@ -628,7 +628,7 @@ func TestDecideNextStepOnRouterQuit(t *testing.T) {
close(p.router.quit) close(p.router.quit)
// Call the method under test. // Call the method under test.
step, err := p.decideNextStep(payment) step, err := p.decideNextStep(t.Context(), payment)
// We expect stepExit and an error to be returned. // We expect stepExit and an error to be returned.
require.Equal(t, stepExit, step) require.Equal(t, stepExit, step)
@@ -657,7 +657,7 @@ func TestDecideNextStepOnLifecycleQuit(t *testing.T) {
close(p.quit) close(p.quit)
// Call the method under test. // Call the method under test.
step, err := p.decideNextStep(payment) step, err := p.decideNextStep(t.Context(), payment)
// We expect stepExit and an error to be returned. // We expect stepExit and an error to be returned.
require.Equal(t, stepExit, step) require.Equal(t, stepExit, step)
@@ -716,7 +716,7 @@ func TestDecideNextStepHandleAttemptResultSucceed(t *testing.T) {
mock.Anything).Return(attempt, nil).Once() mock.Anything).Return(attempt, nil).Once()
// Call the method under test. // Call the method under test.
step, err := p.decideNextStep(payment) step, err := p.decideNextStep(t.Context(), payment)
// We expect stepSkip and no error to be returned. // We expect stepSkip and no error to be returned.
require.Equal(t, stepSkip, step) require.Equal(t, stepSkip, step)
@@ -774,7 +774,7 @@ func TestDecideNextStepHandleAttemptResultFail(t *testing.T) {
mock.Anything).Return(attempt, errDummy).Once() mock.Anything).Return(attempt, errDummy).Once()
// Call the method under test. // Call the method under test.
step, err := p.decideNextStep(payment) step, err := p.decideNextStep(t.Context(), payment)
// We expect stepExit and the above error to be returned. // We expect stepExit and the above error to be returned.
require.Equal(t, stepExit, step) require.Equal(t, stepExit, step)
@@ -1467,7 +1467,7 @@ func TestCollectResultExitOnErr(t *testing.T) {
m.clock.On("Now").Return(time.Now()) m.clock.On("Now").Return(time.Now())
// Now call the method under test. // Now call the method under test.
result, err := p.collectAndHandleResult(attempt) result, err := p.collectAndHandleResult(t.Context(), attempt)
require.ErrorIs(t, err, errDummy, "expected dummy error") require.ErrorIs(t, err, errDummy, "expected dummy error")
require.Nil(t, result, "expected nil attempt") require.Nil(t, result, "expected nil attempt")
} }
@@ -1513,7 +1513,7 @@ func TestCollectResultExitOnResultErr(t *testing.T) {
m.clock.On("Now").Return(time.Now()) m.clock.On("Now").Return(time.Now())
// Now call the method under test. // Now call the method under test.
result, err := p.collectAndHandleResult(attempt) result, err := p.collectAndHandleResult(t.Context(), attempt)
require.ErrorIs(t, err, errDummy, "expected dummy error") require.ErrorIs(t, err, errDummy, "expected dummy error")
require.Nil(t, result, "expected nil attempt") require.Nil(t, result, "expected nil attempt")
} }
@@ -1539,7 +1539,7 @@ func TestCollectResultExitOnSwitchQuit(t *testing.T) {
}) })
// Now call the method under test. // Now call the method under test.
result, err := p.collectAndHandleResult(attempt) result, err := p.collectAndHandleResult(t.Context(), attempt)
require.ErrorIs(t, err, htlcswitch.ErrSwitchExiting, require.ErrorIs(t, err, htlcswitch.ErrSwitchExiting,
"expected switch exit") "expected switch exit")
require.Nil(t, result, "expected nil attempt") require.Nil(t, result, "expected nil attempt")
@@ -1566,7 +1566,7 @@ func TestCollectResultExitOnRouterQuit(t *testing.T) {
}) })
// Now call the method under test. // Now call the method under test.
result, err := p.collectAndHandleResult(attempt) result, err := p.collectAndHandleResult(t.Context(), attempt)
require.ErrorIs(t, err, ErrRouterShuttingDown, "expected router exit") require.ErrorIs(t, err, ErrRouterShuttingDown, "expected router exit")
require.Nil(t, result, "expected nil attempt") require.Nil(t, result, "expected nil attempt")
} }
@@ -1592,7 +1592,7 @@ func TestCollectResultExitOnLifecycleQuit(t *testing.T) {
}) })
// Now call the method under test. // Now call the method under test.
result, err := p.collectAndHandleResult(attempt) result, err := p.collectAndHandleResult(t.Context(), attempt)
require.ErrorIs(t, err, ErrPaymentLifecycleExiting, require.ErrorIs(t, err, ErrPaymentLifecycleExiting,
"expected lifecycle exit") "expected lifecycle exit")
require.Nil(t, result, "expected nil attempt") require.Nil(t, result, "expected nil attempt")
@@ -1636,7 +1636,7 @@ func TestCollectResultExitOnSettleErr(t *testing.T) {
m.clock.On("Now").Return(time.Now()) m.clock.On("Now").Return(time.Now())
// Now call the method under test. // Now call the method under test.
result, err := p.collectAndHandleResult(attempt) result, err := p.collectAndHandleResult(t.Context(), attempt)
require.ErrorIs(t, err, errDummy, "expected settle error") require.ErrorIs(t, err, errDummy, "expected settle error")
require.Nil(t, result, "expected nil attempt") require.Nil(t, result, "expected nil attempt")
} }
@@ -1678,7 +1678,7 @@ func TestCollectResultSuccess(t *testing.T) {
m.clock.On("Now").Return(time.Now()) m.clock.On("Now").Return(time.Now())
// Now call the method under test. // Now call the method under test.
result, err := p.collectAndHandleResult(attempt) result, err := p.collectAndHandleResult(t.Context(), attempt)
require.NoError(t, err, "expected no error") require.NoError(t, err, "expected no error")
require.Equal(t, preimage, result.attempt.Settle.Preimage, require.Equal(t, preimage, result.attempt.Settle.Preimage,
"preimage mismatch") "preimage mismatch")
@@ -1762,7 +1762,9 @@ func TestHandleAttemptResultWithError(t *testing.T) {
// Call the method under test and expect the dummy error to be // Call the method under test and expect the dummy error to be
// returned. // returned.
attemptResult, err := p.handleAttemptResult(attempt, result) attemptResult, err := p.handleAttemptResult(
t.Context(), attempt, result,
)
require.ErrorIs(t, err, errDummy, "expected fail error") require.ErrorIs(t, err, errDummy, "expected fail error")
require.Nil(t, attemptResult, "expected nil attempt result") require.Nil(t, attemptResult, "expected nil attempt result")
} }
@@ -1800,7 +1802,9 @@ func TestHandleAttemptResultSuccess(t *testing.T) {
// Call the method under test and expect the dummy error to be // Call the method under test and expect the dummy error to be
// returned. // returned.
attemptResult, err := p.handleAttemptResult(attempt, result) attemptResult, err := p.handleAttemptResult(
t.Context(), attempt, result,
)
require.NoError(t, err, "expected no error") require.NoError(t, err, "expected no error")
require.Equal(t, attempt, attemptResult.attempt) require.Equal(t, attempt, attemptResult.attempt)
} }
+11 -5
View File
@@ -1038,7 +1038,8 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) (
// SendToRoute sends a payment using the provided route and fails the payment // SendToRoute sends a payment using the provided route and fails the payment
// when an error is returned from the attempt. // when an error is returned from the attempt.
func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, rt *route.Route, func (r *ChannelRouter) SendToRoute(_ context.Context, htlcHash lntypes.Hash,
rt *route.Route,
firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt, firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt,
error) { error) {
@@ -1047,8 +1048,8 @@ func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, rt *route.Route,
// SendToRouteSkipTempErr sends a payment using the provided route and fails // SendToRouteSkipTempErr sends a payment using the provided route and fails
// the payment ONLY when a terminal error is returned from the attempt. // the payment ONLY when a terminal error is returned from the attempt.
func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash, func (r *ChannelRouter) SendToRouteSkipTempErr(_ context.Context,
rt *route.Route, htlcHash lntypes.Hash, rt *route.Route,
firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt, firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt,
error) { error) {
@@ -1066,6 +1067,11 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt, firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt,
error) { error) {
// TODO(ziggie): We cannot easily thread the context from the caller
// of this method because the payment lifecycle depends on the context
// to update the db. The Sending and Receiving of results is currently
// not cleanly separated which is the reason that we cannot easily
// cancel the context and therefore cancel the ongoing payment.
ctx := context.TODO() ctx := context.TODO()
// Helper function to fail a payment. It makes sure the payment is only // Helper function to fail a payment. It makes sure the payment is only
@@ -1179,7 +1185,7 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
// NOTE: we use zero `remainingAmt` here to simulate the same effect of // NOTE: we use zero `remainingAmt` here to simulate the same effect of
// setting the lastShard to be false, which is used by previous // setting the lastShard to be false, which is used by previous
// implementation. // implementation.
attempt, err := p.registerAttempt(rt, 0) attempt, err := p.registerAttempt(ctx, rt, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1216,7 +1222,7 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
// The attempt was successfully sent, wait for the result to be // The attempt was successfully sent, wait for the result to be
// available. // available.
result, err = p.collectAndHandleResult(attempt) result, err = p.collectAndHandleResult(ctx, attempt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+19 -9
View File
@@ -522,7 +522,7 @@ func TestChannelUpdateValidation(t *testing.T) {
// Send off the payment request to the router. The specified route // Send off the payment request to the router. The specified route
// should be attempted and the channel update should be received by // should be attempted and the channel update should be received by
// graph and ignored because it is missing a valid signature. // graph and ignored because it is missing a valid signature.
_, err = ctx.router.SendToRoute(payment, rt, nil) _, err = ctx.router.SendToRoute(t.Context(), payment, rt, nil)
require.Error(t, err, "expected route to fail with channel update") require.Error(t, err, "expected route to fail with channel update")
_, e1, e2, err = ctx.graph.FetchChannelEdgesByID( _, e1, e2, err = ctx.graph.FetchChannelEdgesByID(
@@ -542,7 +542,7 @@ func TestChannelUpdateValidation(t *testing.T) {
ctx.graphBuilder.setNextReject(false) ctx.graphBuilder.setNextReject(false)
// Retry the payment using the same route as before. // Retry the payment using the same route as before.
_, err = ctx.router.SendToRoute(payment, rt, nil) _, err = ctx.router.SendToRoute(t.Context(), payment, rt, nil)
require.Error(t, err, "expected route to fail with channel update") require.Error(t, err, "expected route to fail with channel update")
// This time a valid signature was supplied and the policy change should // This time a valid signature was supplied and the policy change should
@@ -1427,7 +1427,9 @@ func TestSendToRouteStructuredError(t *testing.T) {
// update should be received by router and ignored // update should be received by router and ignored
// because it is missing a valid // because it is missing a valid
// signature. // signature.
_, err = ctx.router.SendToRoute(payment, rt, nil) _, err = ctx.router.SendToRoute(
t.Context(), payment, rt, nil,
)
fErr, ok := err.(*htlcswitch.ForwardingError) fErr, ok := err.(*htlcswitch.ForwardingError)
require.True( require.True(
@@ -1506,7 +1508,7 @@ func TestSendToRouteMaxHops(t *testing.T) {
// Send off the payment request to the router. We expect an error back // Send off the payment request to the router. We expect an error back
// indicating that the route is too long. // indicating that the route is too long.
var payHash lntypes.Hash var payHash lntypes.Hash
_, err = ctx.router.SendToRoute(payHash, rt, nil) _, err = ctx.router.SendToRoute(t.Context(), payHash, rt, nil)
if err != route.ErrMaxRouteHopsExceeded { if err != route.ErrMaxRouteHopsExceeded {
t.Fatalf("expected ErrMaxRouteHopsExceeded, but got %v", err) t.Fatalf("expected ErrMaxRouteHopsExceeded, but got %v", err)
} }
@@ -2221,7 +2223,9 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) {
).Return(nil) ).Return(nil)
// Expect a successful send to route. // Expect a successful send to route.
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil) attempt, err := router.SendToRouteSkipTempErr(
t.Context(), payHash, rt, nil,
)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, testAttempt, attempt) require.Equal(t, testAttempt, attempt)
@@ -2276,7 +2280,9 @@ func TestSendToRouteSkipTempErrNonMPP(t *testing.T) {
}} }}
// Expect an error to be returned. // Expect an error to be returned.
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil) attempt, err := router.SendToRouteSkipTempErr(
t.Context(), payHash, rt, nil,
)
require.ErrorIs(t, ErrSkipTempErr, err) require.ErrorIs(t, ErrSkipTempErr, err)
require.Nil(t, attempt) require.Nil(t, attempt)
@@ -2356,7 +2362,9 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) {
).Return(nil, nil) ).Return(nil, nil)
// Expect a failed send to route. // Expect a failed send to route.
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil) attempt, err := router.SendToRouteSkipTempErr(
t.Context(), payHash, rt, nil,
)
require.Equal(t, tempErr, err) require.Equal(t, tempErr, err)
require.Equal(t, testAttempt, attempt) require.Equal(t, testAttempt, attempt)
@@ -2440,7 +2448,9 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) {
).Return(&failureReason, nil) ).Return(&failureReason, nil)
// Expect a failed send to route. // Expect a failed send to route.
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil) attempt, err := router.SendToRouteSkipTempErr(
t.Context(), payHash, rt, nil,
)
require.Equal(t, permErr, err) require.Equal(t, permErr, err)
require.Equal(t, testAttempt, attempt) require.Equal(t, testAttempt, attempt)
@@ -2529,7 +2539,7 @@ func TestSendToRouteTempFailure(t *testing.T) {
).Return(nil, nil) ).Return(nil, nil)
// Expect a failed send to route. // Expect a failed send to route.
attempt, err := router.SendToRoute(payHash, rt, nil) attempt, err := router.SendToRoute(t.Context(), payHash, rt, nil)
require.Equal(t, tempErr, err) require.Equal(t, tempErr, err)
require.Equal(t, testAttempt, attempt) require.Equal(t, testAttempt, attempt)
+23 -12
View File
@@ -5581,8 +5581,9 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
// execute sendPayment. We use this struct as a sort of bridge to enable code // execute sendPayment. We use this struct as a sort of bridge to enable code
// re-use between SendPayment and SendToRoute. // re-use between SendPayment and SendToRoute.
type paymentStream struct { type paymentStream struct {
recv func() (*rpcPaymentRequest, error) getCtx func() context.Context
send func(*lnrpc.SendResponse) error recv func() (*rpcPaymentRequest, error)
send func(*lnrpc.SendResponse) error
} }
// rpcPaymentRequest wraps lnrpc.SendRequest so that routes from // rpcPaymentRequest wraps lnrpc.SendRequest so that routes from
@@ -5596,10 +5597,13 @@ type rpcPaymentRequest struct {
// through the Lightning Network. A single RPC invocation creates a persistent // through the Lightning Network. A single RPC invocation creates a persistent
// bi-directional stream allowing clients to rapidly send payments through the // bi-directional stream allowing clients to rapidly send payments through the
// Lightning Network with a single persistent connection. // Lightning Network with a single persistent connection.
func (r *rpcServer) SendPayment(stream lnrpc.Lightning_SendPaymentServer) error { func (r *rpcServer) SendPayment(
stream lnrpc.Lightning_SendPaymentServer) error {
var lock sync.Mutex var lock sync.Mutex
return r.sendPayment(&paymentStream{ return r.sendPayment(&paymentStream{
getCtx: stream.Context,
recv: func() (*rpcPaymentRequest, error) { recv: func() (*rpcPaymentRequest, error) {
req, err := stream.Recv() req, err := stream.Recv()
if err != nil { if err != nil {
@@ -5624,10 +5628,13 @@ func (r *rpcServer) SendPayment(stream lnrpc.Lightning_SendPaymentServer) error
// invocation creates a persistent bi-directional stream allowing clients to // invocation creates a persistent bi-directional stream allowing clients to
// rapidly send payments through the Lightning Network with a single persistent // rapidly send payments through the Lightning Network with a single persistent
// connection. // connection.
func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error { func (r *rpcServer) SendToRoute(
stream lnrpc.Lightning_SendToRouteServer) error {
var lock sync.Mutex var lock sync.Mutex
return r.sendPayment(&paymentStream{ return r.sendPayment(&paymentStream{
getCtx: stream.Context,
recv: func() (*rpcPaymentRequest, error) { recv: func() (*rpcPaymentRequest, error) {
req, err := stream.Recv() req, err := stream.Recv()
if err != nil { if err != nil {
@@ -5697,7 +5704,11 @@ type rpcPaymentIntent struct {
// dispatch a client from the information presented by an RPC client. There are // dispatch a client from the information presented by an RPC client. There are
// three ways a client can specify their payment details: a payment request, // three ways a client can specify their payment details: a payment request,
// via manual details, or via a complete route. // via manual details, or via a complete route.
func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error) { //
//nolint:funlen
func (r *rpcServer) extractPaymentIntent(
rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error) {
payIntent := rpcPaymentIntent{} payIntent := rpcPaymentIntent{}
// If a route was specified, then we can use that directly. // If a route was specified, then we can use that directly.
@@ -5969,7 +5980,7 @@ type paymentIntentResponse struct {
// pre-built route. The first error this method returns denotes if we were // pre-built route. The first error this method returns denotes if we were
// unable to save the payment. The second error returned denotes if the payment // unable to save the payment. The second error returned denotes if the payment
// didn't succeed. // didn't succeed.
func (r *rpcServer) dispatchPaymentIntent( func (r *rpcServer) dispatchPaymentIntent(ctx context.Context,
payIntent *rpcPaymentIntent) (*paymentIntentResponse, error) { payIntent *rpcPaymentIntent) (*paymentIntentResponse, error) {
// Construct a payment request to send to the channel router. If the // Construct a payment request to send to the channel router. If the
@@ -6016,7 +6027,7 @@ func (r *rpcServer) dispatchPaymentIntent(
} else { } else {
var attempt *paymentsdb.HTLCAttempt var attempt *paymentsdb.HTLCAttempt
attempt, routerErr = r.server.chanRouter.SendToRoute( attempt, routerErr = r.server.chanRouter.SendToRoute(
payIntent.rHash, payIntent.route, nil, ctx, payIntent.rHash, payIntent.route, nil,
) )
if routerErr == nil { if routerErr == nil {
@@ -6189,7 +6200,7 @@ sendLoop:
}() }()
resp, saveErr := r.dispatchPaymentIntent( resp, saveErr := r.dispatchPaymentIntent(
payIntent, stream.getCtx(), payIntent,
) )
switch { switch {
@@ -6267,7 +6278,7 @@ sendLoop:
func (r *rpcServer) SendPaymentSync(ctx context.Context, func (r *rpcServer) SendPaymentSync(ctx context.Context,
nextPayment *lnrpc.SendRequest) (*lnrpc.SendResponse, error) { nextPayment *lnrpc.SendRequest) (*lnrpc.SendResponse, error) {
return r.sendPaymentSync(&rpcPaymentRequest{ return r.sendPaymentSync(ctx, &rpcPaymentRequest{
SendRequest: nextPayment, SendRequest: nextPayment,
}) })
} }
@@ -6288,12 +6299,12 @@ func (r *rpcServer) SendToRouteSync(ctx context.Context,
return nil, err return nil, err
} }
return r.sendPaymentSync(paymentRequest) return r.sendPaymentSync(ctx, paymentRequest)
} }
// sendPaymentSync is the synchronous variant of sendPayment. It will block and // sendPaymentSync is the synchronous variant of sendPayment. It will block and
// wait until the payment has been fully completed. // wait until the payment has been fully completed.
func (r *rpcServer) sendPaymentSync( func (r *rpcServer) sendPaymentSync(ctx context.Context,
nextPayment *rpcPaymentRequest) (*lnrpc.SendResponse, error) { nextPayment *rpcPaymentRequest) (*lnrpc.SendResponse, error) {
// We don't allow payments to be sent while the daemon itself is still // We don't allow payments to be sent while the daemon itself is still
@@ -6312,7 +6323,7 @@ func (r *rpcServer) sendPaymentSync(
// With the payment validated, we'll now attempt to dispatch the // With the payment validated, we'll now attempt to dispatch the
// payment. // payment.
resp, saveErr := r.dispatchPaymentIntent(&payIntent) resp, saveErr := r.dispatchPaymentIntent(ctx, &payIntent)
switch { switch {
case saveErr != nil: case saveErr != nil:
return nil, saveErr return nil, saveErr