@@ -262,7 +262,7 @@ func NewAccountChecker(service Service,
262
262
}, sendResponseHandler , mid .PassThroughErrorHandler ,
263
263
),
264
264
// routerrpc.Router/SendToRoute is deprecated.
265
- "/routerrpc.Router/SendToRouteV2" : mid .NewRequestChecker (
265
+ "/routerrpc.Router/SendToRouteV2" : mid .NewFullChecker (
266
266
& routerrpc.SendToRouteRequest {},
267
267
& lnrpc.HTLCAttempt {},
268
268
func (ctx context.Context ,
@@ -272,10 +272,8 @@ func NewAccountChecker(service Service,
272
272
ctx , service , r .PaymentHash , r .Route ,
273
273
)
274
274
},
275
- // We don't get the payment hash in the response to this
276
- // call. So we can't optimize things and need to rely on
277
- // the payment being tracked by the hash sent in the
278
- // request.
275
+ sendToRouteHTLCResponseHandler (service ),
276
+ mid .PassThroughErrorHandler ,
279
277
),
280
278
"/lnrpc.Lightning/DecodePayReq" : DecodePayReqPassThrough ,
281
279
"/lnrpc.Lightning/ListPayments" : mid .NewResponseRewriter (
@@ -616,23 +614,42 @@ func checkSendResponse(ctx context.Context, service Service,
616
614
status lnrpc.Payment_PaymentStatus , hash lntypes.Hash ,
617
615
fullAmt int64 ) (proto.Message , error ) {
618
616
619
- acct , err := AccountFromContext (ctx )
617
+ log , acct , reqID , err := requestScopedValuesFromCtx (ctx )
620
618
if err != nil {
621
619
return nil , err
622
620
}
623
621
622
+ reqVals , ok := service .GetValues (reqID )
623
+ if ! ok {
624
+ return nil , nil
625
+ }
626
+
627
+ if hash != reqVals .PaymentHash {
628
+ return nil , fmt .Errorf ("response hash did not match request " +
629
+ "hash" )
630
+ }
631
+
632
+ log .Tracef ("Handling response payment with hash: %s" , hash )
633
+
624
634
// If we directly observe a failure, make sure
625
635
// we stop tracking the payment and then exit
626
636
// early.
627
637
if status == lnrpc .Payment_FAILED {
638
+ service .DeleteValues (reqID )
639
+
628
640
return nil , service .RemovePayment (hash )
629
641
}
630
642
631
- // If there is no immediate failure, make sure
632
- // we track the payment.
633
- return nil , service .TrackPayment (
634
- acct .ID , hash , lnwire .MilliSatoshi (fullAmt ),
635
- )
643
+ // If there is no immediate failure, make sure we track the payment.
644
+ err = service .TrackPayment (acct .ID , hash , lnwire .MilliSatoshi (fullAmt ))
645
+ if err != nil {
646
+ return nil , err
647
+ }
648
+
649
+ // We can now delete the request values for this request ID.
650
+ service .DeleteValues (reqID )
651
+
652
+ return nil , nil
636
653
}
637
654
638
655
// checkSendToRoute checks if a payment can be sent to the route by making sure
@@ -688,3 +705,69 @@ func checkSendToRoute(ctx context.Context, service Service, paymentHash []byte,
688
705
PaymentAmount : sendAmt ,
689
706
})
690
707
}
708
+
709
+ // sendToRouteHTLCResponseHandler creates a response handler for the
710
+ // SendToRouteV2 endpoint which is a streaming endpoint that may return multiple
711
+ // HTLCAttempt updates.
712
+ func sendToRouteHTLCResponseHandler (service Service ) func (ctx context.Context ,
713
+ r * lnrpc.HTLCAttempt ) (proto.Message , error ) {
714
+
715
+ return func (ctx context.Context , r * lnrpc.HTLCAttempt ) (proto.Message ,
716
+ error ) {
717
+
718
+ log , acct , reqID , err := requestScopedValuesFromCtx (ctx )
719
+ if err != nil {
720
+ return nil , err
721
+ }
722
+
723
+ // Since SendToRouteV2 is a streaming endpoint, we may get
724
+ // multiple responses for it. If we have already handled it then
725
+ // we would have deleted the request ID to hash mapping, and so
726
+ // we can exit early here.
727
+ reqValues , ok := service .GetValues (reqID )
728
+ if ! ok {
729
+ return nil , nil
730
+ }
731
+
732
+ log .Tracef ("Handling response for payment with hash: %s and " +
733
+ "amount: %d" , reqValues .PaymentHash ,
734
+ reqValues .PaymentAmount )
735
+
736
+ // If the pre-image is provided, do a sanity check to make sure
737
+ // that this does actually match the hash we stored for this
738
+ // payment.
739
+ if len (r .Preimage ) != 0 {
740
+ preimage , err := lntypes .MakePreimage (r .Preimage )
741
+ if err != nil {
742
+ return nil , err
743
+ }
744
+
745
+ if ! preimage .Matches (reqValues .PaymentHash ) {
746
+ return nil , fmt .Errorf ("the preimage (%s) " +
747
+ "returned by the response does not " +
748
+ "match the payment hash (%s) of the " +
749
+ "payment" , preimage ,
750
+ reqValues .PaymentHash )
751
+ }
752
+ }
753
+
754
+ route := r .Route
755
+ totalAmount := int64 (0 )
756
+ if route != nil {
757
+ totalAmount = route .TotalAmtMsat + route .TotalFeesMsat
758
+ }
759
+
760
+ err = service .TrackPayment (
761
+ acct .ID , reqValues .PaymentHash ,
762
+ lnwire .MilliSatoshi (totalAmount ),
763
+ )
764
+ if err != nil {
765
+ return nil , err
766
+ }
767
+
768
+ // We can now delete the request values for this ID.
769
+ service .DeleteValues (reqID )
770
+
771
+ return nil , nil
772
+ }
773
+ }
0 commit comments