63
63
ListPeersEmptyRewriter = mid .NewResponseEmptier [
64
64
* lnrpc.ListPeersRequest , * lnrpc.ListPeersResponse ,
65
65
]()
66
+
67
+ emptyHash lntypes.Hash
66
68
)
67
69
68
70
// CheckerMap is a type alias that maps gRPC request URIs to their
@@ -181,7 +183,8 @@ func NewAccountChecker(service Service,
181
183
func (ctx context.Context , r * lnrpc.SendRequest ) error {
182
184
return checkSend (
183
185
ctx , chainParams , service , r .Amt ,
184
- r .AmtMsat , r .PaymentRequest , r .FeeLimit ,
186
+ r .AmtMsat , r .PaymentRequest ,
187
+ r .PaymentHash , r .FeeLimit ,
185
188
)
186
189
}, sendResponseHandler , mid .PassThroughErrorHandler ,
187
190
),
@@ -191,7 +194,8 @@ func NewAccountChecker(service Service,
191
194
func (ctx context.Context , r * lnrpc.SendRequest ) error {
192
195
return checkSend (
193
196
ctx , chainParams , service , r .Amt ,
194
- r .AmtMsat , r .PaymentRequest , r .FeeLimit ,
197
+ r .AmtMsat , r .PaymentRequest ,
198
+ r .PaymentHash , r .FeeLimit ,
195
199
)
196
200
}, sendResponseHandler , mid .PassThroughErrorHandler ,
197
201
),
@@ -210,6 +214,7 @@ func NewAccountChecker(service Service,
210
214
return checkSend (
211
215
ctx , chainParams , service , r .Amt ,
212
216
r .AmtMsat , r .PaymentRequest ,
217
+ r .PaymentHash ,
213
218
& lnrpc.FeeLimit {
214
219
Limit : & lnrpc.FeeLimit_FixedMsat {
215
220
FixedMsat : feeLimitMsat ,
@@ -240,7 +245,9 @@ func NewAccountChecker(service Service,
240
245
func (ctx context.Context ,
241
246
r * lnrpc.SendToRouteRequest ) error {
242
247
243
- return checkSendToRoute (ctx , service , r .Route )
248
+ return checkSendToRoute (
249
+ ctx , service , r .PaymentHash , r .Route ,
250
+ )
244
251
}, sendResponseHandler , mid .PassThroughErrorHandler ,
245
252
),
246
253
"/lnrpc.Lightning/SendToRouteSync" : mid .NewFullChecker (
@@ -249,7 +256,9 @@ func NewAccountChecker(service Service,
249
256
func (ctx context.Context ,
250
257
r * lnrpc.SendToRouteRequest ) error {
251
258
252
- return checkSendToRoute (ctx , service , r .Route )
259
+ return checkSendToRoute (
260
+ ctx , service , r .PaymentHash , r .Route ,
261
+ )
253
262
}, sendResponseHandler , mid .PassThroughErrorHandler ,
254
263
),
255
264
// routerrpc.Router/SendToRoute is deprecated.
@@ -259,7 +268,9 @@ func NewAccountChecker(service Service,
259
268
func (ctx context.Context ,
260
269
r * routerrpc.SendToRouteRequest ) error {
261
270
262
- return checkSendToRoute (ctx , service , r .Route )
271
+ return checkSendToRoute (
272
+ ctx , service , r .PaymentHash , r .Route ,
273
+ )
263
274
},
264
275
// We don't get the payment hash in the response to this
265
276
// call. So we can't optimize things and need to rely on
@@ -509,20 +520,29 @@ func filterPayments(ctx context.Context,
509
520
// the context has enough balance to pay for it.
510
521
func checkSend (ctx context.Context , chainParams * chaincfg.Params ,
511
522
service Service , amt , amtMsat int64 , invoice string ,
512
- feeLimit * lnrpc.FeeLimit ) error {
523
+ paymentHash [] byte , feeLimit * lnrpc.FeeLimit ) error {
513
524
514
525
acct , err := AccountFromContext (ctx )
515
526
if err != nil {
516
527
return err
517
528
}
518
529
530
+ reqID , err := RequestIDFromContext (ctx )
531
+ if err != nil {
532
+ return err
533
+ }
534
+
519
535
sendAmt := lnwire .NewMSatFromSatoshis (btcutil .Amount (amt ))
520
536
if lnwire .MilliSatoshi (amtMsat ) > sendAmt {
521
537
sendAmt = lnwire .MilliSatoshi (amtMsat )
522
538
}
523
539
524
- // The invoice is optional.
525
- var paymentHash lntypes.Hash
540
+ // We require that a payment hash is set, which we either read from the
541
+ // payment hash from the invoice or from the request.
542
+ var pHash lntypes.Hash
543
+
544
+ // Check if an invoice was provided. If so, glean the payment hash from
545
+ // that.
526
546
if len (invoice ) > 0 {
527
547
payReq , err := zpay32 .Decode (invoice , chainParams )
528
548
if err != nil {
@@ -534,10 +554,36 @@ func checkSend(ctx context.Context, chainParams *chaincfg.Params,
534
554
}
535
555
536
556
if payReq .PaymentHash != nil {
537
- paymentHash = * payReq .PaymentHash
557
+ pHash = * payReq .PaymentHash
538
558
}
539
559
}
540
560
561
+ // If a payment hash was separately provided in the request, then glean
562
+ // derive the hash from there. If the invoice was set then the two
563
+ // payment hashes must match.
564
+ if len (paymentHash ) != 0 {
565
+ hash , err := lntypes .MakeHash (paymentHash )
566
+ if err != nil {
567
+ return err
568
+ }
569
+
570
+ if pHash != emptyHash && hash != pHash {
571
+ return fmt .Errorf ("two conflicting hashes provided" )
572
+ }
573
+
574
+ pHash = hash
575
+ }
576
+
577
+ // If at this point we still have no payment hash, then we error out
578
+ // for now.
579
+ // TODO(elle): support key sends by:
580
+ // 1) allowing the user to set a pre-image
581
+ // 2) deriving a pre-image here.
582
+ // Then glean the payment hash from that.
583
+ if pHash == emptyHash {
584
+ return fmt .Errorf ("a payment hash is required" )
585
+ }
586
+
541
587
// We also add the max fee to the amount to check. This might mean that
542
588
// not every single satoshi of an account can be used up. But it
543
589
// prevents an account from going into a negative balance if we only
@@ -554,15 +600,15 @@ func checkSend(ctx context.Context, chainParams *chaincfg.Params,
554
600
return fmt .Errorf ("error validating account balance: %w" , err )
555
601
}
556
602
557
- emptyHash := lntypes.Hash {}
558
- if paymentHash != emptyHash {
559
- err = service .AssociatePayment (acct .ID , paymentHash , sendAmt )
560
- if err != nil {
561
- return fmt .Errorf ("error associating payment: %w" , err )
562
- }
603
+ err = service .AssociatePayment (acct .ID , pHash , sendAmt )
604
+ if err != nil {
605
+ return fmt .Errorf ("error associating payment: %w" , err )
563
606
}
564
607
565
- return nil
608
+ return service .RegisterValues (reqID , & RequestValues {
609
+ PaymentHash : pHash ,
610
+ PaymentAmount : sendAmt ,
611
+ })
566
612
}
567
613
568
614
// checkSendResponse makes sure that a payment that is in flight is tracked
@@ -593,14 +639,24 @@ func checkSendResponse(ctx context.Context, service Service,
593
639
594
640
// checkSendToRoute checks if a payment can be sent to the route by making sure
595
641
// the account in the context has enough balance to pay for it.
596
- func checkSendToRoute (ctx context.Context , service Service ,
642
+ func checkSendToRoute (ctx context.Context , service Service , paymentHash [] byte ,
597
643
route * lnrpc.Route ) error {
598
644
599
645
acct , err := AccountFromContext (ctx )
600
646
if err != nil {
601
647
return err
602
648
}
603
649
650
+ hash , err := lntypes .MakeHash (paymentHash )
651
+ if err != nil {
652
+ return err
653
+ }
654
+
655
+ reqID , err := RequestIDFromContext (ctx )
656
+ if err != nil {
657
+ return err
658
+ }
659
+
604
660
if route == nil {
605
661
return fmt .Errorf ("invalid route" )
606
662
}
@@ -625,5 +681,14 @@ func checkSendToRoute(ctx context.Context, service Service,
625
681
return fmt .Errorf ("error validating account balance: %w" , err )
626
682
}
627
683
628
- return nil
684
+ err = service .AssociatePayment (acct .ID , hash , sendAmt )
685
+ if err != nil {
686
+ return fmt .Errorf ("error associating payment with hash %s: %w" ,
687
+ hash , err )
688
+ }
689
+
690
+ return service .RegisterValues (reqID , & RequestValues {
691
+ PaymentHash : hash ,
692
+ PaymentAmount : sendAmt ,
693
+ })
629
694
}
0 commit comments