routing/router: persist payment state machine

This commit makes the router use the ControlTower to drive the payment
life cycle state machine, to keep track of active payments across
restarts.  This lets the router resume payments on startup, such that
their final results can be handled and stored when ready.
This commit is contained in:
Johan T. Halseth
2019-05-23 20:05:29 +02:00
parent dd73c51a34
commit de1bf8a518
6 changed files with 198 additions and 56 deletions

View File

@@ -2,7 +2,6 @@ package lnd
import (
"bytes"
"crypto/sha256"
"crypto/tls"
"encoding/hex"
"errors"
@@ -2735,33 +2734,6 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
}
}
// savePayment saves a successfully completed payment to the database for
// historical record keeping.
func (r *rpcServer) savePayment(route *route.Route,
amount lnwire.MilliSatoshi, preImage []byte) error {
paymentPath := make([][33]byte, len(route.Hops))
for i, hop := range route.Hops {
hopPub := hop.PubKeyBytes
copy(paymentPath[i][:], hopPub[:])
}
payment := &channeldb.OutgoingPayment{
Invoice: channeldb.Invoice{
Terms: channeldb.ContractTerm{
Value: amount,
},
CreationDate: time.Now(),
},
Path: paymentPath,
Fee: route.TotalFees(),
TimeLockLength: route.TotalTimeLock,
}
copy(payment.PaymentPreimage[:], preImage)
return r.server.chanDB.AddPayment(payment)
}
// validatePayReqExpiry checks if the passed payment request has expired. In
// the case it has expired, an error will be returned.
func validatePayReqExpiry(payReq *zpay32.Invoice) error {
@@ -3123,18 +3095,6 @@ func (r *rpcServer) dispatchPaymentIntent(
}, nil
}
// Calculate amount paid to receiver.
amt := route.TotalAmount - route.TotalFees()
// Save the completed payment to the database for record keeping
// purposes.
err := r.savePayment(route, amt, preImage[:])
if err != nil {
// We weren't able to save the payment, so we return the save
// err, but a nil routing err.
return nil, err
}
return &paymentIntentResponse{
Route: route,
Preimage: preImage,
@@ -4146,8 +4106,8 @@ func (r *rpcServer) ListPayments(ctx context.Context,
rpcsLog.Debugf("[ListPayments]")
payments, err := r.server.chanDB.FetchAllPayments()
if err != nil && err != channeldb.ErrNoPaymentsCreated {
payments, err := r.server.chanDB.FetchPayments()
if err != nil {
return nil, err
}
@@ -4155,24 +4115,37 @@ func (r *rpcServer) ListPayments(ctx context.Context,
Payments: make([]*lnrpc.Payment, len(payments)),
}
for i, payment := range payments {
path := make([]string, len(payment.Path))
for i, hop := range payment.Path {
path[i] = hex.EncodeToString(hop[:])
// If a payment attempt has been made we can fetch the route.
// Otherwise we'll just populate the RPC response with an empty
// one.
var route route.Route
if payment.Attempt != nil {
route = payment.Attempt.Route
}
path := make([]string, len(route.Hops))
for i, hop := range route.Hops {
path[i] = hex.EncodeToString(hop.PubKeyBytes[:])
}
msatValue := int64(payment.Terms.Value)
satValue := int64(payment.Terms.Value.ToSatoshis())
// If this payment is settled, the preimage will be available.
var preimage lntypes.Preimage
if payment.PaymentPreimage != nil {
preimage = *payment.PaymentPreimage
}
paymentHash := sha256.Sum256(payment.PaymentPreimage[:])
msatValue := int64(payment.Info.Value)
satValue := int64(payment.Info.Value.ToSatoshis())
paymentHash := payment.Info.PaymentHash
paymentsResp.Payments[i] = &lnrpc.Payment{
PaymentHash: hex.EncodeToString(paymentHash[:]),
Value: satValue,
ValueMsat: msatValue,
ValueSat: satValue,
CreationDate: payment.CreationDate.Unix(),
CreationDate: payment.Info.CreationDate.Unix(),
Path: path,
Fee: int64(payment.Fee.ToSatoshis()),
PaymentPreimage: hex.EncodeToString(payment.PaymentPreimage[:]),
Fee: int64(route.TotalFees().ToSatoshis()),
PaymentPreimage: hex.EncodeToString(preimage[:]),
}
}
@@ -4185,7 +4158,7 @@ func (r *rpcServer) DeleteAllPayments(ctx context.Context,
rpcsLog.Debugf("[DeleteAllPayments]")
if err := r.server.chanDB.DeleteAllPayments(); err != nil {
if err := r.server.chanDB.DeletePayments(); err != nil {
return nil, err
}