From 674c19904795691dabee397e9bb71cefd5e99b4e Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 6 May 2020 15:36:51 +0200 Subject: [PATCH] routing: return full htlc attempt from router --- lnrpc/routerrpc/router_server.go | 37 ++++++++++++++++++-------------- routing/router.go | 25 ++++++++++++--------- routing/router_test.go | 4 ++-- rpcserver.go | 7 +++++- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index e38c648b..4fbba0b4 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -318,25 +318,30 @@ func (s *Server) SendToRoute(ctx context.Context, return nil, err } - preimage, err := s.cfg.Router.SendToRoute(hash, route) + // Pass route to the router. This call returns the full htlc attempt + // information as it is stored in the database. It is possible that both + // the attempt return value and err are non-nil. This can happen when + // the attempt was already initiated before the error happened. In that + // case, we give precedence to the attempt information as stored in the + // db. + attempt, err := s.cfg.Router.SendToRoute(hash, route) + if attempt != nil { + rpcAttempt, err := s.cfg.RouterBackend.MarshalHTLCAttempt( + *attempt, + ) + if err != nil { + return nil, err + } - // In the success case, return the preimage. - if err == nil { - return &SendToRouteResponse{ - Preimage: preimage[:], - }, nil + resp := &SendToRouteResponse{ + Preimage: rpcAttempt.Preimage, + Failure: rpcAttempt.Failure, + } + + return resp, nil } - // In the failure case, marshall the failure message to the rpc format - // before returning it to the caller. - rpcErr, err := marshallError(err) - if err != nil { - return nil, err - } - - return &SendToRouteResponse{ - Failure: rpcErr, - }, nil + return nil, err } // ResetMissionControl clears all mission control state and starts with a clean diff --git a/routing/router.go b/routing/router.go index 1777a23c..8398ec5b 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1741,10 +1741,12 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) ( } // SendToRoute attempts to send a payment with the given hash through the -// provided route. This function is blocking and will return the obtained -// preimage if the payment is successful or the full error in case of a failure. +// provided route. This function is blocking and will return the attempt +// information as it is stored in the database. For a successful htlc, this +// information will contain the preimage. If an error occurs after the attempt +// was initiated, both return values will be non-nil. func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) ( - lntypes.Preimage, error) { + *channeldb.HTLCAttempt, error) { // Calculate amount paid to receiver. amt := rt.ReceiverAmt() @@ -1774,7 +1776,7 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) ( // Any other error is not tolerated. case err != nil: - return [32]byte{}, err + return nil, err } log.Tracef("Dispatching SendToRoute for hash %v: %v", @@ -1804,34 +1806,37 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) ( hash, channeldb.FailureReasonError, ) if controlErr != nil { - return [32]byte{}, controlErr + return nil, controlErr } } // In any case, don't continue if there is an error. if err != nil { - return lntypes.Preimage{}, err + return nil, err } + var htlcAttempt *channeldb.HTLCAttempt switch { // Failed to launch shard. case outcome.err != nil: shardError = outcome.err + htlcAttempt = outcome.attempt // Shard successfully launched, wait for the result to be available. default: result, err := sh.collectResult(attempt) if err != nil { - return lntypes.Preimage{}, err + return nil, err } // We got a successful result. if result.err == nil { - return result.attempt.Settle.Preimage, nil + return result.attempt, nil } // The shard failed, break switch to handle it. shardError = result.err + htlcAttempt = result.attempt } // Since for SendToRoute we won't retry in case the shard fails, we'll @@ -1848,10 +1853,10 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) ( err = r.cfg.Control.Fail(hash, *reason) if err != nil { - return lntypes.Preimage{}, err + return nil, err } - return lntypes.Preimage{}, shardError + return htlcAttempt, shardError } // sendPayment attempts to send a payment to the passed payment hash. This diff --git a/routing/router_test.go b/routing/router_test.go index d8b0950f..3f31225b 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -2809,13 +2809,13 @@ func TestSendToRouteMultiShardSend(t *testing.T) { for i := 0; i < numShards; i++ { go func() { - preimg, err := ctx.router.SendToRoute(payment, rt) + attempt, err := ctx.router.SendToRoute(payment, rt) if err != nil { errChan <- err return } - successes <- preimg + successes <- attempt.Settle.Preimage }() } diff --git a/rpcserver.go b/rpcserver.go index 189d9d38..de434747 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3978,10 +3978,15 @@ func (r *rpcServer) dispatchPaymentIntent( payment, ) } else { - preImage, routerErr = r.server.chanRouter.SendToRoute( + var attempt *channeldb.HTLCAttempt + attempt, routerErr = r.server.chanRouter.SendToRoute( payIntent.rHash, payIntent.route, ) + if routerErr == nil { + preImage = attempt.Settle.Preimage + } + route = payIntent.route }