channeldb+routing: expose HTLCs in payment subscriptions

This commit modifies the FetchPayment method to return MPPayment structs
converted from the legacy on-disk format. This allows us to attach the
HTLCs to the events given to clients subscribing to the outcome of an
HTLC.

This commit also bubbles up to the routerrpc/router_server, by
populating HTLCAttempts in the response and extracting the legacy route
field from the HTLCAttempts.
This commit is contained in:
Conner Fromknecht
2019-11-08 03:39:51 -08:00
parent 68916eb4b7
commit 063f24f2ed
6 changed files with 143 additions and 86 deletions

View File

@@ -6,7 +6,6 @@ import (
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/routing/route"
)
// ControlTower tracks all outgoing payments made, whose primary purpose is to
@@ -52,10 +51,6 @@ type PaymentResult struct {
// Success indicates whether the payment was successful.
Success bool
// Route is the (last) route attempted to send the HTLC. It is only set
// for successful payments.
Route *route.Route
// Preimage is the preimage of a successful payment. This serves as a
// proof of payment. It is only set for successful payments.
Preimage lntypes.Preimage
@@ -63,6 +58,10 @@ type PaymentResult struct {
// FailureReason is a failure reason code indicating the reason the
// payment failed. It is only set for failed payments.
FailureReason channeldb.FailureReason
// HTLCs is a list of HTLCs that have been attempted in order to settle
// the payment.
HTLCs []channeldb.HTLCAttempt
}
// controlTower is persistent implementation of ControlTower to restrict
@@ -107,46 +106,46 @@ func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash,
func (p *controlTower) Success(paymentHash lntypes.Hash,
preimage lntypes.Preimage) error {
route, err := p.db.Success(paymentHash, preimage)
payment, err := p.db.Success(paymentHash, preimage)
if err != nil {
return err
}
// Notify subscribers of success event.
p.notifyFinalEvent(
paymentHash, createSuccessResult(route, preimage),
paymentHash, createSuccessResult(payment.HTLCs),
)
return nil
}
// createSuccessResult creates a success result to send to subscribers.
func createSuccessResult(rt *route.Route,
preimage lntypes.Preimage) *PaymentResult {
func createSuccessResult(htlcs []channeldb.HTLCAttempt) *PaymentResult {
// Extract any preimage from the list of HTLCs.
var preimage lntypes.Preimage
for _, htlc := range htlcs {
if htlc.Settle != nil {
preimage = htlc.Settle.Preimage
break
}
}
return &PaymentResult{
Success: true,
Preimage: preimage,
Route: rt,
HTLCs: htlcs,
}
}
// createFailResult creates a failed result to send to subscribers.
func createFailedResult(rt *route.Route,
func createFailedResult(htlcs []channeldb.HTLCAttempt,
reason channeldb.FailureReason) *PaymentResult {
result := &PaymentResult{
return &PaymentResult{
Success: false,
FailureReason: reason,
HTLCs: htlcs,
}
// In case of incorrect payment details, set the route. This can be used
// for probing and to extract a fee estimate from the route.
if reason == channeldb.FailureReasonPaymentDetails {
result.Route = rt
}
return result
}
// Fail transitions a payment into the Failed state, and records the reason the
@@ -156,14 +155,16 @@ func createFailedResult(rt *route.Route,
func (p *controlTower) Fail(paymentHash lntypes.Hash,
reason channeldb.FailureReason) error {
route, err := p.db.Fail(paymentHash, reason)
payment, err := p.db.Fail(paymentHash, reason)
if err != nil {
return err
}
// Notify subscribers of fail event.
p.notifyFinalEvent(
paymentHash, createFailedResult(route, reason),
paymentHash, createFailedResult(
payment.HTLCs, reason,
),
)
return nil
@@ -213,20 +214,14 @@ func (p *controlTower) SubscribePayment(paymentHash lntypes.Hash) (
// a subscriber, because we can send the result on the channel
// immediately.
case channeldb.StatusSucceeded:
event = *createSuccessResult(
&payment.Attempt.Route, *payment.Preimage,
)
event = *createSuccessResult(payment.HTLCs)
// Payment already failed. It is not necessary to register as a
// subscriber, because we can send the result on the channel
// immediately.
case channeldb.StatusFailed:
var route *route.Route
if payment.Attempt != nil {
route = &payment.Attempt.Route
}
event = *createFailedResult(
route, *payment.Failure,
payment.HTLCs, *payment.FailureReason,
)
default: