Merge pull request #3499 from cfromknecht/mpp-payments-rpc
channeldb+rpcserver: expose legacy payments as multi-path payments
This commit is contained in:
16 files changed
+1377
-831
No files matched your search
+29
-34
@@ -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,17 +51,17 @@ 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
|
||||
|
||||
// PaymentPreimage is the preimage of a successful payment. This serves
|
||||
// as a proof of payment. It is only set for successful payments.
|
||||
// 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
|
||||
|
||||
// Failure is a failure reason code indicating the reason the payment
|
||||
// failed. It is only set for failed payments.
|
||||
// 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.FailureReasonIncorrectPaymentDetails {
|
||||
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.PaymentPreimage,
|
||||
)
|
||||
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:
|
||||
|
||||
@@ -144,10 +144,13 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
|
||||
if result.Preimage != preimg {
|
||||
t.Fatal("unexpected preimage")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(result.Route, &attempt.Route) {
|
||||
t.Fatalf("unexpected route: %v vs %v",
|
||||
spew.Sdump(result.Route),
|
||||
if len(result.HTLCs) != 1 {
|
||||
t.Fatalf("expected one htlc, got %d", len(result.HTLCs))
|
||||
}
|
||||
htlc := result.HTLCs[0]
|
||||
if !reflect.DeepEqual(htlc.Route, attempt.Route) {
|
||||
t.Fatalf("unexpected htlc route: %v vs %v",
|
||||
spew.Sdump(htlc.Route),
|
||||
spew.Sdump(attempt.Route))
|
||||
}
|
||||
|
||||
@@ -168,6 +171,15 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
|
||||
func TestPaymentControlSubscribeFail(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("register attempt", func(t *testing.T) {
|
||||
testPaymentControlSubscribeFail(t, true)
|
||||
})
|
||||
t.Run("no register attempt", func(t *testing.T) {
|
||||
testPaymentControlSubscribeFail(t, false)
|
||||
})
|
||||
}
|
||||
|
||||
func testPaymentControlSubscribeFail(t *testing.T, registerAttempt bool) {
|
||||
db, err := initDB()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to init db: %v", err)
|
||||
@@ -176,7 +188,7 @@ func TestPaymentControlSubscribeFail(t *testing.T) {
|
||||
pControl := NewControlTower(channeldb.NewPaymentControl(db))
|
||||
|
||||
// Initiate a payment.
|
||||
info, _, _, err := genInfo()
|
||||
info, attempt, _, err := genInfo()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -192,6 +204,17 @@ func TestPaymentControlSubscribeFail(t *testing.T) {
|
||||
t.Fatalf("expected subscribe to succeed, but got: %v", err)
|
||||
}
|
||||
|
||||
// Conditionally register the attempt based on the test type. This
|
||||
// allows us to simulate failing after attempting with an htlc or before
|
||||
// making any attempts at all.
|
||||
if registerAttempt {
|
||||
// Register an attempt.
|
||||
err = pControl.RegisterAttempt(info.PaymentHash, attempt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the payment as failed.
|
||||
if err := pControl.Fail(info.PaymentHash, channeldb.FailureReasonTimeout); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -223,9 +246,28 @@ func TestPaymentControlSubscribeFail(t *testing.T) {
|
||||
if result.Success {
|
||||
t.Fatal("unexpected payment state")
|
||||
}
|
||||
if result.Route != nil {
|
||||
t.Fatal("expected no route")
|
||||
|
||||
// There will either be one or zero htlcs depending on whether
|
||||
// or not the attempt was registered. Assert the correct number
|
||||
// is present, and the route taken if the attempt was
|
||||
// registered.
|
||||
if registerAttempt {
|
||||
if len(result.HTLCs) != 1 {
|
||||
t.Fatalf("expected 1 htlc, got: %d",
|
||||
len(result.HTLCs))
|
||||
}
|
||||
|
||||
htlc := result.HTLCs[0]
|
||||
if !reflect.DeepEqual(htlc.Route, testRoute) {
|
||||
t.Fatalf("unexpected htlc route: %v vs %v",
|
||||
spew.Sdump(htlc.Route),
|
||||
spew.Sdump(testRoute))
|
||||
}
|
||||
} else if len(result.HTLCs) != 0 {
|
||||
t.Fatalf("expected 0 htlcs, got: %d",
|
||||
len(result.HTLCs))
|
||||
}
|
||||
|
||||
if result.FailureReason != channeldb.FailureReasonTimeout {
|
||||
t.Fatal("unexpected failure reason")
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// Instantiate variables to allow taking a reference from the failure reason.
|
||||
var (
|
||||
reasonError = channeldb.FailureReasonError
|
||||
reasonIncorrectDetails = channeldb.FailureReasonIncorrectPaymentDetails
|
||||
reasonIncorrectDetails = channeldb.FailureReasonPaymentDetails
|
||||
)
|
||||
|
||||
// pairResult contains the result of the interpretation of a payment attempt for
|
||||
|
||||
Reference in new issue
Block a user