channeldb+routing: store all payment htlcs

This commit converts the database structure of a payment so that it can
not just store the last htlc attempt, but all attempts that have been
made. This is a preparation for mpp sending.

In addition to that, we now also persist the fail time of an htlc. In a
later commit, the full failure reason will be added as well.

A key change is made to the control tower interface. Previously the
control tower wasn't aware of individual htlc outcomes. The payment
remained in-flight with the latest attempt recorded, but an outcome was
only set when the payment finished. With this commit, the outcome of
every htlc is expected by the control tower and recorded in the
database.

Co-authored-by: Johan T. Halseth <johanth@gmail.com>
This commit is contained in:
Joost Jager
2020-02-20 18:08:01 +01:00
parent 8558534417
commit 48c0e42c26
10 changed files with 550 additions and 274 deletions

View File

@@ -85,7 +85,7 @@ func TestPaymentControlSwitchFail(t *testing.T) {
assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, nil, lntypes.Preimage{},
t, pControl, info.PaymentHash, info, 0, nil, lntypes.Preimage{},
nil,
)
@@ -99,7 +99,7 @@ func TestPaymentControlSwitchFail(t *testing.T) {
// Verify the status is indeed Failed.
assertPaymentStatus(t, pControl, info.PaymentHash, StatusFailed)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, nil, lntypes.Preimage{},
t, pControl, info.PaymentHash, info, 0, nil, lntypes.Preimage{},
&failReason,
)
@@ -112,7 +112,7 @@ func TestPaymentControlSwitchFail(t *testing.T) {
assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, nil, lntypes.Preimage{},
t, pControl, info.PaymentHash, info, 0, nil, lntypes.Preimage{},
nil,
)
@@ -125,6 +125,13 @@ func TestPaymentControlSwitchFail(t *testing.T) {
t.Fatalf("unable to register attempt: %v", err)
}
err = pControl.FailAttempt(
info.PaymentHash, 2, &HTLCFailInfo{},
)
if err != nil {
t.Fatal(err)
}
// Record another attempt.
attempt.AttemptID = 3
err = pControl.RegisterAttempt(info.PaymentHash, attempt)
@@ -133,19 +140,24 @@ func TestPaymentControlSwitchFail(t *testing.T) {
}
assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, attempt, lntypes.Preimage{},
t, pControl, info.PaymentHash, info, 0, attempt, lntypes.Preimage{},
nil,
)
// Settle the attempt and verify that status was changed to StatusSucceeded.
var payment *MPPayment
payment, err = pControl.Success(info.PaymentHash, preimg)
payment, err = pControl.SettleAttempt(
info.PaymentHash, 3,
&HTLCSettleInfo{
Preimage: preimg,
},
)
if err != nil {
t.Fatalf("error shouldn't have been received, got: %v", err)
}
if len(payment.HTLCs) != 1 {
t.Fatalf("payment should have one htlc, got: %d",
if len(payment.HTLCs) != 2 {
t.Fatalf("payment should have two htlcs, got: %d",
len(payment.HTLCs))
}
@@ -157,7 +169,7 @@ func TestPaymentControlSwitchFail(t *testing.T) {
}
assertPaymentStatus(t, pControl, info.PaymentHash, StatusSucceeded)
assertPaymentInfo(t, pControl, info.PaymentHash, info, attempt, preimg, nil)
assertPaymentInfo(t, pControl, info.PaymentHash, info, 1, attempt, preimg, nil)
// Attempt a final payment, which should now fail since the prior
// payment succeed.
@@ -193,7 +205,7 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) {
assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, nil, lntypes.Preimage{},
t, pControl, info.PaymentHash, info, 0, nil, lntypes.Preimage{},
nil,
)
@@ -213,7 +225,7 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) {
}
assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, attempt, lntypes.Preimage{},
t, pControl, info.PaymentHash, info, 0, attempt, lntypes.Preimage{},
nil,
)
@@ -225,11 +237,17 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) {
}
// After settling, the error should be ErrAlreadyPaid.
if _, err := pControl.Success(info.PaymentHash, preimg); err != nil {
_, err = pControl.SettleAttempt(
info.PaymentHash, 1,
&HTLCSettleInfo{
Preimage: preimg,
},
)
if err != nil {
t.Fatalf("error shouldn't have been received, got: %v", err)
}
assertPaymentStatus(t, pControl, info.PaymentHash, StatusSucceeded)
assertPaymentInfo(t, pControl, info.PaymentHash, info, attempt, preimg, nil)
assertPaymentInfo(t, pControl, info.PaymentHash, info, 0, attempt, preimg, nil)
err = pControl.InitPayment(info.PaymentHash, info)
if err != ErrAlreadyPaid {
@@ -255,7 +273,12 @@ func TestPaymentControlSuccessesWithoutInFlight(t *testing.T) {
}
// Attempt to complete the payment should fail.
_, err = pControl.Success(info.PaymentHash, preimg)
_, err = pControl.SettleAttempt(
info.PaymentHash, 0,
&HTLCSettleInfo{
Preimage: preimg,
},
)
if err != ErrPaymentNotInitiated {
t.Fatalf("expected ErrPaymentNotInitiated, got %v", err)
}
@@ -336,6 +359,15 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) {
}
if p.failed {
// Fail the payment attempt.
err := pControl.FailAttempt(
info.PaymentHash, attempt.AttemptID,
&HTLCFailInfo{},
)
if err != nil {
t.Fatalf("unable to fail htlc: %v", err)
}
// Fail the payment, which should moved it to Failed.
failReason := FailureReasonNoRoute
_, err = pControl.Fail(info.PaymentHash, failReason)
@@ -346,24 +378,29 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) {
// Verify the status is indeed Failed.
assertPaymentStatus(t, pControl, info.PaymentHash, StatusFailed)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, attempt,
t, pControl, info.PaymentHash, info, 0, attempt,
lntypes.Preimage{}, &failReason,
)
} else if p.success {
// Verifies that status was changed to StatusSucceeded.
_, err := pControl.Success(info.PaymentHash, preimg)
_, err := pControl.SettleAttempt(
info.PaymentHash, 1,
&HTLCSettleInfo{
Preimage: preimg,
},
)
if err != nil {
t.Fatalf("error shouldn't have been received, got: %v", err)
}
assertPaymentStatus(t, pControl, info.PaymentHash, StatusSucceeded)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, attempt, preimg, nil,
t, pControl, info.PaymentHash, info, 0, attempt, preimg, nil,
)
} else {
assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight)
assertPaymentInfo(
t, pControl, info.PaymentHash, info, attempt,
t, pControl, info.PaymentHash, info, 0, attempt,
lntypes.Preimage{}, nil,
)
}
@@ -414,7 +451,7 @@ func assertPaymentStatus(t *testing.T, p *PaymentControl,
// assertPaymentInfo retrieves the payment referred to by hash and verifies the
// expected values.
func assertPaymentInfo(t *testing.T, p *PaymentControl, hash lntypes.Hash,
c *PaymentCreationInfo, a *HTLCAttemptInfo, s lntypes.Preimage,
c *PaymentCreationInfo, aIdx int, a *HTLCAttemptInfo, s lntypes.Preimage,
f *FailureReason) {
t.Helper()
@@ -446,7 +483,7 @@ func assertPaymentInfo(t *testing.T, p *PaymentControl, hash lntypes.Hash,
return
}
htlc := payment.HTLCs[0]
htlc := payment.HTLCs[aIdx]
if err := assertRouteEqual(&htlc.Route, &a.Route); err != nil {
t.Fatal("routes do not match")
}