routing+routerrpc: notify full payment structures

This commit fixes the inconsistency between the payment state as
reported by routerrpc.SendPayment/routerrpc.TrackPayment and the main
rpc ListPayments call.

In addition to that, payment state changes are now sent out for every
state change. This opens the door to user interfaces giving more
feedback to the user about the payment process. This is especially
interesting for multi-part payments.
This commit is contained in:
Joost Jager committed 2020-04-08 09:26:33 +02:00
1 parent 5bebda8c5d
commit af4abe7d58
8 files changed
+392 -617

No files matched your search

+24 -82
View File
@@ -441,7 +441,7 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash,
router := s.cfg.RouterBackend
// Subscribe to the outcome of this payment.
inFlight, resultChan, err := router.Tower.SubscribePayment(
subscription, err := router.Tower.SubscribePayment(
paymentHash,
)
switch {
@@ -450,95 +450,37 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash,
case err != nil:
return err
}
defer subscription.Close()
// If it is in flight, send a state update to the client. Payment status
// update streams are expected to always send the current payment state
// immediately.
if inFlight {
err = stream.Send(&PaymentStatus{
State: PaymentState_IN_FLIGHT,
})
if err != nil {
return err
}
}
// Wait for the outcome of the payment. For payments that have
// completed, the result should already be waiting on the channel.
select {
case result := <-resultChan:
// Marshall result to rpc type.
var status PaymentStatus
if result.Success {
log.Debugf("Payment %v successfully completed",
paymentHash)
status.State = PaymentState_SUCCEEDED
status.Preimage = result.Preimage[:]
} else {
state, err := marshallFailureReason(
result.FailureReason,
)
if err != nil {
return err
// Stream updates back to the client. The first update is always the
// current state of the payment.
for {
select {
case item, ok := <-subscription.Updates:
if !ok {
// No more payment updates.
return nil
}
status.State = state
}
// Marshal our list of HTLCs that have been tried for this
// payment.
htlcs := make([]*lnrpc.HTLCAttempt, 0, len(result.HTLCs))
for _, dbHtlc := range result.HTLCs {
htlc, err := router.MarshalHTLCAttempt(dbHtlc)
result := item.(*channeldb.MPPayment)
rpcPayment, err := router.MarshallPayment(result)
if err != nil {
return err
}
htlcs = append(htlcs, htlc)
// Send event to the client.
err = stream.Send(rpcPayment)
if err != nil {
return err
}
case <-s.quit:
return errServerShuttingDown
case <-stream.Context().Done():
log.Debugf("Payment status stream %v canceled", paymentHash)
return stream.Context().Err()
}
status.Htlcs = htlcs
// Send event to the client.
err = stream.Send(&status)
if err != nil {
return err
}
case <-s.quit:
return errServerShuttingDown
case <-stream.Context().Done():
log.Debugf("Payment status stream %v canceled", paymentHash)
return stream.Context().Err()
}
return nil
}
// marshallFailureReason marshalls the failure reason to the corresponding rpc
// type.
func marshallFailureReason(reason channeldb.FailureReason) (
PaymentState, error) {
switch reason {
case channeldb.FailureReasonTimeout:
return PaymentState_FAILED_TIMEOUT, nil
case channeldb.FailureReasonNoRoute:
return PaymentState_FAILED_NO_ROUTE, nil
case channeldb.FailureReasonError:
return PaymentState_FAILED_ERROR, nil
case channeldb.FailureReasonPaymentDetails:
return PaymentState_FAILED_INCORRECT_PAYMENT_DETAILS, nil
case channeldb.FailureReasonInsufficientBalance:
return PaymentState_FAILED_INSUFFICIENT_BALANCE, nil
}
return 0, errors.New("unknown failure reason")
}
// BuildRoute builds a route from a list of hop addresses.