2016-09-17 03:23:37 +03:00
|
|
|
package channeldb
|
|
|
|
|
|
|
|
import (
|
2016-09-19 21:51:37 +03:00
|
|
|
"crypto/rand"
|
2020-06-05 16:02:06 +03:00
|
|
|
"fmt"
|
2020-01-03 18:21:40 +03:00
|
|
|
"math"
|
2016-09-17 03:23:37 +03:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2019-12-03 14:13:10 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
2017-08-22 08:51:19 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2019-12-12 02:01:55 +03:00
|
|
|
"github.com/lightningnetwork/lnd/record"
|
2020-06-05 16:02:06 +03:00
|
|
|
"github.com/stretchr/testify/require"
|
2016-09-17 03:23:37 +03:00
|
|
|
)
|
|
|
|
|
2019-11-22 13:24:28 +03:00
|
|
|
var (
|
|
|
|
emptyFeatures = lnwire.NewFeatureVector(nil, lnwire.Features)
|
2020-01-03 18:19:49 +03:00
|
|
|
testNow = time.Unix(1, 0)
|
2019-11-22 13:24:28 +03:00
|
|
|
)
|
|
|
|
|
2017-08-22 08:51:19 +03:00
|
|
|
func randInvoice(value lnwire.MilliSatoshi) (*Invoice, error) {
|
2020-04-08 14:47:10 +03:00
|
|
|
var (
|
|
|
|
pre lntypes.Preimage
|
|
|
|
payAddr [32]byte
|
|
|
|
)
|
2016-09-19 21:51:37 +03:00
|
|
|
if _, err := rand.Read(pre[:]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-22 01:37:39 +03:00
|
|
|
if _, err := rand.Read(payAddr[:]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-09-19 21:51:37 +03:00
|
|
|
|
|
|
|
i := &Invoice{
|
2020-01-03 18:19:49 +03:00
|
|
|
CreationDate: testNow,
|
2016-09-19 21:51:37 +03:00
|
|
|
Terms: ContractTerm{
|
2019-11-22 13:25:02 +03:00
|
|
|
Expiry: 4000,
|
2020-04-08 14:47:10 +03:00
|
|
|
PaymentPreimage: &pre,
|
2020-05-22 01:37:39 +03:00
|
|
|
PaymentAddr: payAddr,
|
2016-09-19 21:51:37 +03:00
|
|
|
Value: value,
|
2019-11-22 13:24:28 +03:00
|
|
|
Features: emptyFeatures,
|
2016-09-19 21:51:37 +03:00
|
|
|
},
|
2019-11-22 13:25:02 +03:00
|
|
|
Htlcs: map[CircuitKey]*InvoiceHTLC{},
|
2016-09-19 21:51:37 +03:00
|
|
|
}
|
2016-09-24 01:15:22 +03:00
|
|
|
i.Memo = []byte("memo")
|
2016-09-19 21:51:37 +03:00
|
|
|
|
2017-09-05 18:59:52 +03:00
|
|
|
// Create a random byte slice of MaxPaymentRequestSize bytes to be used
|
|
|
|
// as a dummy paymentrequest, and determine if it should be set based
|
|
|
|
// on one of the random bytes.
|
|
|
|
var r [MaxPaymentRequestSize]byte
|
|
|
|
if _, err := rand.Read(r[:]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if r[0]&1 == 0 {
|
|
|
|
i.PaymentRequest = r[:]
|
|
|
|
} else {
|
|
|
|
i.PaymentRequest = []byte("")
|
|
|
|
}
|
|
|
|
|
2016-09-19 21:51:37 +03:00
|
|
|
return i, nil
|
|
|
|
}
|
|
|
|
|
2020-01-03 18:21:40 +03:00
|
|
|
// settleTestInvoice settles a test invoice.
|
|
|
|
func settleTestInvoice(invoice *Invoice, settleIndex uint64) {
|
|
|
|
invoice.SettleDate = testNow
|
|
|
|
invoice.AmtPaid = invoice.Terms.Value
|
|
|
|
invoice.State = ContractSettled
|
|
|
|
invoice.Htlcs[CircuitKey{}] = &InvoiceHTLC{
|
|
|
|
Amt: invoice.Terms.Value,
|
|
|
|
AcceptTime: testNow,
|
|
|
|
ResolveTime: testNow,
|
|
|
|
State: HtlcStateSettled,
|
|
|
|
CustomRecords: make(record.CustomSet),
|
|
|
|
}
|
|
|
|
invoice.SettleIndex = settleIndex
|
|
|
|
}
|
|
|
|
|
2019-12-02 15:24:26 +03:00
|
|
|
// Tests that pending invoices are those which are either in ContractOpen or
|
|
|
|
// in ContractAccepted state.
|
|
|
|
func TestInvoiceIsPending(t *testing.T) {
|
|
|
|
contractStates := []ContractState{
|
|
|
|
ContractOpen, ContractSettled, ContractCanceled, ContractAccepted,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, state := range contractStates {
|
|
|
|
invoice := Invoice{
|
|
|
|
State: state,
|
|
|
|
}
|
|
|
|
|
|
|
|
// We expect that an invoice is pending if it's either in ContractOpen
|
|
|
|
// or ContractAccepted state.
|
|
|
|
pending := (state == ContractOpen || state == ContractAccepted)
|
|
|
|
|
|
|
|
if invoice.IsPending() != pending {
|
|
|
|
t.Fatalf("expected pending: %v, got: %v, invoice: %v",
|
|
|
|
pending, invoice.IsPending(), invoice)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 01:37:39 +03:00
|
|
|
type invWorkflowTest struct {
|
|
|
|
name string
|
|
|
|
queryPayHash bool
|
|
|
|
queryPayAddr bool
|
|
|
|
}
|
|
|
|
|
|
|
|
var invWorkflowTests = []invWorkflowTest{
|
|
|
|
{
|
|
|
|
name: "unknown",
|
|
|
|
queryPayHash: false,
|
|
|
|
queryPayAddr: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "only payhash known",
|
|
|
|
queryPayHash: true,
|
|
|
|
queryPayAddr: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "payaddr and payhash known",
|
|
|
|
queryPayHash: true,
|
|
|
|
queryPayAddr: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestInvoiceWorkflow asserts the basic process of inserting, fetching, and
|
|
|
|
// updating an invoice. We assert that the flow is successful using when
|
|
|
|
// querying with various combinations of payment hash and payment address.
|
2016-09-17 03:23:37 +03:00
|
|
|
func TestInvoiceWorkflow(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2020-05-22 01:37:39 +03:00
|
|
|
for _, test := range invWorkflowTests {
|
|
|
|
test := test
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
testInvoiceWorkflow(t, test)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testInvoiceWorkflow(t *testing.T, test invWorkflowTest) {
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB()
|
2016-12-22 23:04:41 +03:00
|
|
|
defer cleanUp()
|
2016-09-17 03:23:37 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to make test db: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a fake invoice which we'll use several times in the tests
|
|
|
|
// below.
|
2020-05-22 01:37:39 +03:00
|
|
|
fakeInvoice, err := randInvoice(10000)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create invoice: %v", err)
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
2020-05-22 01:37:39 +03:00
|
|
|
invPayHash := fakeInvoice.Terms.PaymentPreimage.Hash()
|
2016-09-17 03:23:37 +03:00
|
|
|
|
2020-05-22 01:37:39 +03:00
|
|
|
// Select the payment hash and payment address we will use to lookup or
|
|
|
|
// update the invoice for the remainder of the test.
|
|
|
|
var (
|
|
|
|
payHash lntypes.Hash
|
|
|
|
payAddr *[32]byte
|
|
|
|
ref InvoiceRef
|
|
|
|
)
|
|
|
|
switch {
|
|
|
|
case test.queryPayHash && test.queryPayAddr:
|
|
|
|
payHash = invPayHash
|
|
|
|
payAddr = &fakeInvoice.Terms.PaymentAddr
|
|
|
|
ref = InvoiceRefByHashAndAddr(payHash, *payAddr)
|
|
|
|
case test.queryPayHash:
|
|
|
|
payHash = invPayHash
|
|
|
|
ref = InvoiceRefByHash(payHash)
|
|
|
|
}
|
2018-10-05 11:14:56 +03:00
|
|
|
|
2018-02-07 06:11:11 +03:00
|
|
|
// Add the invoice to the database, this should succeed as there aren't
|
2016-09-17 03:23:37 +03:00
|
|
|
// any existing invoices within the database with the same payment
|
|
|
|
// hash.
|
2020-05-22 01:37:39 +03:00
|
|
|
if _, err := db.AddInvoice(fakeInvoice, invPayHash); err != nil {
|
2016-09-17 03:23:37 +03:00
|
|
|
t.Fatalf("unable to find invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to retrieve the invoice which was just added to the
|
|
|
|
// database. It should be found, and the invoice returned should be
|
|
|
|
// identical to the one created above.
|
2020-05-22 01:37:10 +03:00
|
|
|
dbInvoice, err := db.LookupInvoice(ref)
|
2020-05-22 01:37:39 +03:00
|
|
|
if !test.queryPayAddr && !test.queryPayHash {
|
|
|
|
if err != ErrInvoiceNotFound {
|
|
|
|
t.Fatalf("invoice should not exist: %v", err)
|
|
|
|
}
|
|
|
|
return
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
2020-06-05 16:02:06 +03:00
|
|
|
|
|
|
|
require.Equal(t,
|
|
|
|
*fakeInvoice, dbInvoice,
|
|
|
|
"invoice fetched from db doesn't match original",
|
|
|
|
)
|
2016-09-17 03:23:37 +03:00
|
|
|
|
2018-04-25 07:01:22 +03:00
|
|
|
// The add index of the invoice retrieved from the database should now
|
|
|
|
// be fully populated. As this is the first index written to the DB,
|
|
|
|
// the addIndex should be 1.
|
|
|
|
if dbInvoice.AddIndex != 1 {
|
|
|
|
t.Fatalf("wrong add index: expected %v, got %v", 1,
|
|
|
|
dbInvoice.AddIndex)
|
|
|
|
}
|
|
|
|
|
2017-12-05 09:07:21 +03:00
|
|
|
// Settle the invoice, the version retrieved from the database should
|
|
|
|
// now have the settled bit toggle to true and a non-default
|
|
|
|
// SettledDate
|
2018-04-25 07:01:22 +03:00
|
|
|
payAmt := fakeInvoice.Terms.Value * 2
|
2020-05-22 01:37:10 +03:00
|
|
|
_, err = db.UpdateInvoice(ref, getUpdateInvoice(payAmt))
|
2019-04-16 13:11:20 +03:00
|
|
|
if err != nil {
|
2016-09-17 03:23:37 +03:00
|
|
|
t.Fatalf("unable to settle invoice: %v", err)
|
|
|
|
}
|
2020-05-22 01:37:10 +03:00
|
|
|
dbInvoice2, err := db.LookupInvoice(ref)
|
2016-09-17 03:23:37 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to fetch invoice: %v", err)
|
|
|
|
}
|
2019-11-22 13:25:02 +03:00
|
|
|
if dbInvoice2.State != ContractSettled {
|
2016-09-17 03:23:37 +03:00
|
|
|
t.Fatalf("invoice should now be settled but isn't")
|
|
|
|
}
|
2017-12-05 09:07:21 +03:00
|
|
|
if dbInvoice2.SettleDate.IsZero() {
|
|
|
|
t.Fatalf("invoice should have non-zero SettledDate but isn't")
|
|
|
|
}
|
|
|
|
|
2018-04-25 07:01:22 +03:00
|
|
|
// Our 2x payment should be reflected, and also the settle index of 1
|
|
|
|
// should also have been committed for this index.
|
|
|
|
if dbInvoice2.AmtPaid != payAmt {
|
|
|
|
t.Fatalf("wrong amt paid: expected %v, got %v", payAmt,
|
|
|
|
dbInvoice2.AmtPaid)
|
|
|
|
}
|
|
|
|
if dbInvoice2.SettleIndex != 1 {
|
|
|
|
t.Fatalf("wrong settle index: expected %v, got %v", 1,
|
|
|
|
dbInvoice2.SettleIndex)
|
|
|
|
}
|
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
// Attempt to insert generated above again, this should fail as
|
|
|
|
// duplicates are rejected by the processing logic.
|
2020-05-22 01:37:39 +03:00
|
|
|
if _, err := db.AddInvoice(fakeInvoice, payHash); err != ErrDuplicateInvoice {
|
2016-09-17 03:23:37 +03:00
|
|
|
t.Fatalf("invoice insertion should fail due to duplication, "+
|
|
|
|
"instead %v", err)
|
|
|
|
}
|
|
|
|
|
2017-09-25 21:25:58 +03:00
|
|
|
// Attempt to look up a non-existent invoice, this should also fail but
|
2016-09-17 03:23:37 +03:00
|
|
|
// with a "not found" error.
|
|
|
|
var fakeHash [32]byte
|
2020-05-22 01:37:10 +03:00
|
|
|
fakeRef := InvoiceRefByHash(fakeHash)
|
|
|
|
_, err = db.LookupInvoice(fakeRef)
|
|
|
|
if err != ErrInvoiceNotFound {
|
2016-09-17 03:23:37 +03:00
|
|
|
t.Fatalf("lookup should have failed, instead %v", err)
|
|
|
|
}
|
2016-09-19 21:51:37 +03:00
|
|
|
|
2018-04-25 07:01:22 +03:00
|
|
|
// Add 10 random invoices.
|
2016-09-19 21:51:37 +03:00
|
|
|
const numInvoices = 10
|
2017-08-22 08:51:19 +03:00
|
|
|
amt := lnwire.NewMSatFromSatoshis(1000)
|
2016-09-19 21:51:37 +03:00
|
|
|
invoices := make([]*Invoice, numInvoices+1)
|
2018-04-25 07:01:22 +03:00
|
|
|
invoices[0] = &dbInvoice2
|
2020-01-03 18:21:40 +03:00
|
|
|
for i := 1; i < len(invoices); i++ {
|
2016-09-19 21:51:37 +03:00
|
|
|
invoice, err := randInvoice(amt)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-05 11:14:56 +03:00
|
|
|
hash := invoice.Terms.PaymentPreimage.Hash()
|
|
|
|
if _, err := db.AddInvoice(invoice, hash); err != nil {
|
2016-09-19 21:51:37 +03:00
|
|
|
t.Fatalf("unable to add invoice %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
invoices[i] = invoice
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform a scan to collect all the active invoices.
|
2020-01-03 18:21:40 +03:00
|
|
|
query := InvoiceQuery{
|
|
|
|
IndexOffset: 0,
|
|
|
|
NumMaxInvoices: math.MaxUint64,
|
|
|
|
PendingOnly: false,
|
|
|
|
}
|
|
|
|
|
|
|
|
response, err := db.QueryInvoices(query)
|
2016-09-19 21:51:37 +03:00
|
|
|
if err != nil {
|
2020-01-03 18:21:40 +03:00
|
|
|
t.Fatalf("invoice query failed: %v", err)
|
2016-09-19 21:51:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// The retrieve list of invoices should be identical as since we're
|
2018-02-07 06:11:11 +03:00
|
|
|
// using big endian, the invoices should be retrieved in ascending
|
2016-09-19 21:51:37 +03:00
|
|
|
// order (and the primary key should be incremented with each
|
|
|
|
// insertion).
|
2020-01-03 18:21:40 +03:00
|
|
|
for i := 0; i < len(invoices); i++ {
|
2020-06-05 16:12:01 +03:00
|
|
|
require.Equal(t,
|
2020-06-05 16:02:06 +03:00
|
|
|
*invoices[i], response.Invoices[i],
|
|
|
|
"retrieved invoice doesn't match",
|
|
|
|
)
|
2016-09-19 21:51:37 +03:00
|
|
|
}
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
2018-04-25 07:01:43 +03:00
|
|
|
|
2020-05-22 01:37:39 +03:00
|
|
|
// TestAddDuplicatePayAddr asserts that the payment addresses of inserted
|
|
|
|
// invoices are unique.
|
|
|
|
func TestAddDuplicatePayAddr(t *testing.T) {
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB()
|
2020-05-22 01:37:39 +03:00
|
|
|
defer cleanUp()
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
// Create two invoices with the same payment addr.
|
|
|
|
invoice1, err := randInvoice(1000)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
invoice2, err := randInvoice(20000)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
invoice2.Terms.PaymentAddr = invoice1.Terms.PaymentAddr
|
|
|
|
|
|
|
|
// First insert should succeed.
|
|
|
|
inv1Hash := invoice1.Terms.PaymentPreimage.Hash()
|
|
|
|
_, err = db.AddInvoice(invoice1, inv1Hash)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
// Second insert should fail with duplicate payment addr.
|
|
|
|
inv2Hash := invoice2.Terms.PaymentPreimage.Hash()
|
|
|
|
_, err = db.AddInvoice(invoice2, inv2Hash)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.Error(t, err, ErrDuplicatePayAddr)
|
2020-05-22 01:37:39 +03:00
|
|
|
}
|
|
|
|
|
2020-06-26 12:00:03 +03:00
|
|
|
// TestAddDuplicateKeysendPayAddr asserts that we permit duplicate payment
|
|
|
|
// addresses to be inserted if they are blank to support JIT legacy keysend
|
|
|
|
// invoices.
|
|
|
|
func TestAddDuplicateKeysendPayAddr(t *testing.T) {
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB()
|
2020-06-26 12:00:03 +03:00
|
|
|
defer cleanUp()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Create two invoices with the same _blank_ payment addr.
|
|
|
|
invoice1, err := randInvoice(1000)
|
|
|
|
require.NoError(t, err)
|
|
|
|
invoice1.Terms.PaymentAddr = BlankPayAddr
|
|
|
|
|
|
|
|
invoice2, err := randInvoice(20000)
|
|
|
|
require.NoError(t, err)
|
|
|
|
invoice2.Terms.PaymentAddr = BlankPayAddr
|
|
|
|
|
|
|
|
// Inserting both should succeed without a duplicate payment address
|
|
|
|
// failure.
|
|
|
|
inv1Hash := invoice1.Terms.PaymentPreimage.Hash()
|
|
|
|
_, err = db.AddInvoice(invoice1, inv1Hash)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
inv2Hash := invoice2.Terms.PaymentPreimage.Hash()
|
|
|
|
_, err = db.AddInvoice(invoice2, inv2Hash)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Querying for each should succeed. Here we use hash+addr refs since
|
|
|
|
// the lookup will fail if the hash and addr point to different
|
|
|
|
// invoices, so if both succeed we can be assured they aren't included
|
|
|
|
// in the payment address index.
|
|
|
|
ref1 := InvoiceRefByHashAndAddr(inv1Hash, BlankPayAddr)
|
|
|
|
dbInv1, err := db.LookupInvoice(ref1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, invoice1, &dbInv1)
|
|
|
|
|
|
|
|
ref2 := InvoiceRefByHashAndAddr(inv2Hash, BlankPayAddr)
|
|
|
|
dbInv2, err := db.LookupInvoice(ref2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, invoice2, &dbInv2)
|
|
|
|
}
|
|
|
|
|
2020-05-22 01:37:39 +03:00
|
|
|
// TestInvRefEquivocation asserts that retrieving or updating an invoice using
|
|
|
|
// an equivocating InvoiceRef results in ErrInvRefEquivocation.
|
|
|
|
func TestInvRefEquivocation(t *testing.T) {
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB()
|
2020-05-22 01:37:39 +03:00
|
|
|
defer cleanUp()
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
// Add two random invoices.
|
|
|
|
invoice1, err := randInvoice(1000)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
inv1Hash := invoice1.Terms.PaymentPreimage.Hash()
|
|
|
|
_, err = db.AddInvoice(invoice1, inv1Hash)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
invoice2, err := randInvoice(2000)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
inv2Hash := invoice2.Terms.PaymentPreimage.Hash()
|
|
|
|
_, err = db.AddInvoice(invoice2, inv2Hash)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
// Now, query using invoice 1's payment address, but invoice 2's payment
|
|
|
|
// hash. We expect an error since the invref points to multiple
|
|
|
|
// invoices.
|
|
|
|
ref := InvoiceRefByHashAndAddr(inv2Hash, invoice1.Terms.PaymentAddr)
|
|
|
|
_, err = db.LookupInvoice(ref)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.Error(t, err, ErrInvRefEquivocation)
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
// The same error should be returned when updating an equivocating
|
|
|
|
// reference.
|
|
|
|
nop := func(_ *Invoice) (*InvoiceUpdateDesc, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
_, err = db.UpdateInvoice(ref, nop)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.Error(t, err, ErrInvRefEquivocation)
|
2020-05-22 01:37:39 +03:00
|
|
|
}
|
|
|
|
|
2019-12-03 14:13:10 +03:00
|
|
|
// TestInvoiceCancelSingleHtlc tests that a single htlc can be canceled on the
|
|
|
|
// invoice.
|
|
|
|
func TestInvoiceCancelSingleHtlc(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB()
|
2019-12-03 14:13:10 +03:00
|
|
|
defer cleanUp()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to make test db: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-04-08 14:47:10 +03:00
|
|
|
preimage := lntypes.Preimage{1}
|
|
|
|
paymentHash := preimage.Hash()
|
|
|
|
|
2019-12-03 14:13:10 +03:00
|
|
|
testInvoice := &Invoice{
|
|
|
|
Htlcs: map[CircuitKey]*InvoiceHTLC{},
|
2020-04-08 14:47:10 +03:00
|
|
|
Terms: ContractTerm{
|
|
|
|
Value: lnwire.NewMSatFromSatoshis(10000),
|
|
|
|
Features: emptyFeatures,
|
|
|
|
PaymentPreimage: &preimage,
|
|
|
|
},
|
2019-12-03 14:13:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := db.AddInvoice(testInvoice, paymentHash); err != nil {
|
|
|
|
t.Fatalf("unable to find invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Accept an htlc on this invoice.
|
|
|
|
key := CircuitKey{ChanID: lnwire.NewShortChanIDFromInt(1), HtlcID: 4}
|
2019-11-19 15:33:05 +03:00
|
|
|
htlc := HtlcAcceptDesc{
|
|
|
|
Amt: 500,
|
2019-12-12 02:01:55 +03:00
|
|
|
CustomRecords: make(record.CustomSet),
|
2019-11-19 15:33:05 +03:00
|
|
|
}
|
2020-05-22 01:37:10 +03:00
|
|
|
|
|
|
|
ref := InvoiceRefByHash(paymentHash)
|
|
|
|
invoice, err := db.UpdateInvoice(ref,
|
2019-12-03 14:13:10 +03:00
|
|
|
func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
|
|
|
return &InvoiceUpdateDesc{
|
|
|
|
AddHtlcs: map[CircuitKey]*HtlcAcceptDesc{
|
2019-11-19 15:33:05 +03:00
|
|
|
key: &htlc,
|
2019-12-03 14:13:10 +03:00
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to add invoice htlc: %v", err)
|
|
|
|
}
|
|
|
|
if len(invoice.Htlcs) != 1 {
|
|
|
|
t.Fatalf("expected the htlc to be added")
|
|
|
|
}
|
|
|
|
if invoice.Htlcs[key].State != HtlcStateAccepted {
|
|
|
|
t.Fatalf("expected htlc in state accepted")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cancel the htlc again.
|
2020-05-22 01:37:10 +03:00
|
|
|
invoice, err = db.UpdateInvoice(ref,
|
|
|
|
func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
|
|
|
return &InvoiceUpdateDesc{
|
|
|
|
CancelHtlcs: map[CircuitKey]struct{}{
|
|
|
|
key: {},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
})
|
2019-12-03 14:13:10 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to cancel htlc: %v", err)
|
|
|
|
}
|
|
|
|
if len(invoice.Htlcs) != 1 {
|
|
|
|
t.Fatalf("expected the htlc to be present")
|
|
|
|
}
|
|
|
|
if invoice.Htlcs[key].State != HtlcStateCanceled {
|
|
|
|
t.Fatalf("expected htlc in state canceled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-25 07:01:43 +03:00
|
|
|
// TestInvoiceTimeSeries tests that newly added invoices invoices, as well as
|
|
|
|
// settled invoices are added to the database are properly placed in the add
|
|
|
|
// add or settle index which serves as an event time series.
|
|
|
|
func TestInvoiceAddTimeSeries(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB(OptionClock(testClock))
|
2018-04-25 07:01:43 +03:00
|
|
|
defer cleanUp()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to make test db: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-05-20 06:31:26 +03:00
|
|
|
_, err = db.InvoicesAddedSince(0)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-20 06:31:26 +03:00
|
|
|
|
2018-04-25 07:01:43 +03:00
|
|
|
// We'll start off by creating 20 random invoices, and inserting them
|
|
|
|
// into the database.
|
|
|
|
const numInvoices = 20
|
|
|
|
amt := lnwire.NewMSatFromSatoshis(1000)
|
|
|
|
invoices := make([]Invoice, numInvoices)
|
|
|
|
for i := 0; i < len(invoices); i++ {
|
|
|
|
invoice, err := randInvoice(amt)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-05 11:14:56 +03:00
|
|
|
paymentHash := invoice.Terms.PaymentPreimage.Hash()
|
|
|
|
|
|
|
|
if _, err := db.AddInvoice(invoice, paymentHash); err != nil {
|
2018-04-25 07:01:43 +03:00
|
|
|
t.Fatalf("unable to add invoice %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
invoices[i] = *invoice
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the invoices constructed, we'll now create a series of queries
|
|
|
|
// that we'll use to assert expected return values of
|
|
|
|
// InvoicesAddedSince.
|
|
|
|
addQueries := []struct {
|
|
|
|
sinceAddIndex uint64
|
|
|
|
|
|
|
|
resp []Invoice
|
|
|
|
}{
|
|
|
|
// If we specify a value of zero, we shouldn't get any invoices
|
|
|
|
// back.
|
|
|
|
{
|
|
|
|
sinceAddIndex: 0,
|
|
|
|
},
|
|
|
|
|
|
|
|
// If we specify a value well beyond the number of inserted
|
|
|
|
// invoices, we shouldn't get any invoices back.
|
|
|
|
{
|
|
|
|
sinceAddIndex: 99999999,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Using an index of 1 should result in all values, but the
|
|
|
|
// first one being returned.
|
|
|
|
{
|
|
|
|
sinceAddIndex: 1,
|
|
|
|
resp: invoices[1:],
|
|
|
|
},
|
|
|
|
|
|
|
|
// If we use an index of 10, then we should retrieve the
|
|
|
|
// reaming 10 invoices.
|
|
|
|
{
|
|
|
|
sinceAddIndex: 10,
|
|
|
|
resp: invoices[10:],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, query := range addQueries {
|
|
|
|
resp, err := db.InvoicesAddedSince(query.sinceAddIndex)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to query: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, len(query.resp), len(resp))
|
|
|
|
|
|
|
|
for j := 0; j < len(query.resp); j++ {
|
|
|
|
require.Equal(t,
|
|
|
|
query.resp[j], resp[j],
|
|
|
|
fmt.Sprintf("test: #%v, item: #%v", i, j),
|
|
|
|
)
|
2018-04-25 07:01:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 06:31:26 +03:00
|
|
|
_, err = db.InvoicesSettledSince(0)
|
2020-06-05 16:12:01 +03:00
|
|
|
require.NoError(t, err)
|
2020-05-20 06:31:26 +03:00
|
|
|
|
2020-01-03 18:21:40 +03:00
|
|
|
var settledInvoices []Invoice
|
|
|
|
var settleIndex uint64 = 1
|
2018-04-25 07:01:43 +03:00
|
|
|
// We'll now only settle the latter half of each of those invoices.
|
|
|
|
for i := 10; i < len(invoices); i++ {
|
|
|
|
invoice := &invoices[i]
|
|
|
|
|
2018-10-05 11:14:56 +03:00
|
|
|
paymentHash := invoice.Terms.PaymentPreimage.Hash()
|
2018-04-25 07:01:43 +03:00
|
|
|
|
2020-05-22 01:37:10 +03:00
|
|
|
ref := InvoiceRefByHash(paymentHash)
|
2019-08-09 14:40:34 +03:00
|
|
|
_, err := db.UpdateInvoice(
|
2020-05-22 01:37:10 +03:00
|
|
|
ref, getUpdateInvoice(invoice.Terms.Value),
|
2019-04-16 13:11:20 +03:00
|
|
|
)
|
2018-04-25 07:01:43 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to settle invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-01-03 18:21:40 +03:00
|
|
|
// Create the settled invoice for the expectation set.
|
|
|
|
settleTestInvoice(invoice, settleIndex)
|
|
|
|
settleIndex++
|
2018-04-25 07:01:43 +03:00
|
|
|
|
2020-01-03 18:21:40 +03:00
|
|
|
settledInvoices = append(settledInvoices, *invoice)
|
|
|
|
}
|
2018-04-25 07:01:43 +03:00
|
|
|
|
|
|
|
// We'll now prepare an additional set of queries to ensure the settle
|
|
|
|
// time series has properly been maintained in the database.
|
|
|
|
settleQueries := []struct {
|
|
|
|
sinceSettleIndex uint64
|
|
|
|
|
|
|
|
resp []Invoice
|
|
|
|
}{
|
|
|
|
// If we specify a value of zero, we shouldn't get any settled
|
|
|
|
// invoices back.
|
|
|
|
{
|
|
|
|
sinceSettleIndex: 0,
|
|
|
|
},
|
|
|
|
|
|
|
|
// If we specify a value well beyond the number of settled
|
|
|
|
// invoices, we shouldn't get any invoices back.
|
|
|
|
{
|
|
|
|
sinceSettleIndex: 99999999,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Using an index of 1 should result in the final 10 invoices
|
|
|
|
// being returned, as we only settled those.
|
|
|
|
{
|
|
|
|
sinceSettleIndex: 1,
|
2020-01-03 18:21:40 +03:00
|
|
|
resp: settledInvoices[1:],
|
2018-04-25 07:01:43 +03:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, query := range settleQueries {
|
|
|
|
resp, err := db.InvoicesSettledSince(query.sinceSettleIndex)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to query: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, len(query.resp), len(resp))
|
|
|
|
|
|
|
|
for j := 0; j < len(query.resp); j++ {
|
|
|
|
require.Equal(t,
|
|
|
|
query.resp[j], resp[j],
|
|
|
|
fmt.Sprintf("test: #%v, item: #%v", i, j),
|
|
|
|
)
|
2018-04-25 07:01:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-18 02:27:04 +03:00
|
|
|
|
2019-12-10 17:45:46 +03:00
|
|
|
// Tests that FetchAllInvoicesWithPaymentHash returns all invoices with their
|
|
|
|
// corresponding payment hashes.
|
|
|
|
func TestFetchAllInvoicesWithPaymentHash(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanup, err := MakeTestDB()
|
2019-12-10 17:45:46 +03:00
|
|
|
defer cleanup()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to make test db: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// With an empty DB we expect to return no error and an empty list.
|
|
|
|
empty, err := db.FetchAllInvoicesWithPaymentHash(false)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to call FetchAllInvoicesWithPaymentHash on empty DB: %v",
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(empty) != 0 {
|
|
|
|
t.Fatalf("expected empty list as a result, got: %v", empty)
|
|
|
|
}
|
|
|
|
|
|
|
|
states := []ContractState{
|
|
|
|
ContractOpen, ContractSettled, ContractCanceled, ContractAccepted,
|
|
|
|
}
|
|
|
|
|
2019-12-19 19:14:39 +03:00
|
|
|
numInvoices := len(states) * 2
|
|
|
|
testPendingInvoices := make(map[lntypes.Hash]*Invoice)
|
|
|
|
testAllInvoices := make(map[lntypes.Hash]*Invoice)
|
|
|
|
|
|
|
|
// Now populate the DB and check if we can get all invoices with their
|
|
|
|
// payment hashes as expected.
|
|
|
|
for i := 1; i <= numInvoices; i++ {
|
|
|
|
invoice, err := randInvoice(lnwire.MilliSatoshi(i))
|
2019-12-10 17:45:46 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-12-19 19:14:39 +03:00
|
|
|
// Set the contract state of the next invoice such that there's an equal
|
|
|
|
// number for all possbile states.
|
|
|
|
invoice.State = states[i%len(states)]
|
2019-12-10 17:45:46 +03:00
|
|
|
paymentHash := invoice.Terms.PaymentPreimage.Hash()
|
|
|
|
|
2019-12-02 15:24:26 +03:00
|
|
|
if invoice.IsPending() {
|
2019-12-10 17:45:46 +03:00
|
|
|
testPendingInvoices[paymentHash] = invoice
|
|
|
|
}
|
|
|
|
|
|
|
|
testAllInvoices[paymentHash] = invoice
|
|
|
|
|
|
|
|
if _, err := db.AddInvoice(invoice, paymentHash); err != nil {
|
|
|
|
t.Fatalf("unable to add invoice: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pendingInvoices, err := db.FetchAllInvoicesWithPaymentHash(true)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("can't fetch invoices with payment hash: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(testPendingInvoices) != len(pendingInvoices) {
|
|
|
|
t.Fatalf("expected %v pending invoices, got: %v",
|
|
|
|
len(testPendingInvoices), len(pendingInvoices))
|
|
|
|
}
|
|
|
|
|
|
|
|
allInvoices, err := db.FetchAllInvoicesWithPaymentHash(false)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("can't fetch invoices with payment hash: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(testAllInvoices) != len(allInvoices) {
|
|
|
|
t.Fatalf("expected %v invoices, got: %v",
|
|
|
|
len(testAllInvoices), len(allInvoices))
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range pendingInvoices {
|
|
|
|
expected, ok := testPendingInvoices[pendingInvoices[i].PaymentHash]
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("coulnd't find invoice with hash: %v",
|
|
|
|
pendingInvoices[i].PaymentHash)
|
|
|
|
}
|
|
|
|
|
2020-06-05 16:02:06 +03:00
|
|
|
// Zero out add index to not confuse require.Equal.
|
2019-12-10 17:45:46 +03:00
|
|
|
pendingInvoices[i].Invoice.AddIndex = 0
|
|
|
|
expected.AddIndex = 0
|
|
|
|
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, *expected, pendingInvoices[i].Invoice)
|
2019-12-10 17:45:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := range allInvoices {
|
|
|
|
expected, ok := testAllInvoices[allInvoices[i].PaymentHash]
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("coulnd't find invoice with hash: %v",
|
|
|
|
allInvoices[i].PaymentHash)
|
|
|
|
}
|
|
|
|
|
2020-06-05 16:02:06 +03:00
|
|
|
// Zero out add index to not confuse require.Equal.
|
2019-12-10 17:45:46 +03:00
|
|
|
allInvoices[i].Invoice.AddIndex = 0
|
|
|
|
expected.AddIndex = 0
|
|
|
|
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, *expected, allInvoices[i].Invoice)
|
2019-12-10 17:45:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-07-18 02:27:04 +03:00
|
|
|
// TestDuplicateSettleInvoice tests that if we add a new invoice and settle it
|
|
|
|
// twice, then the second time we also receive the invoice that we settled as a
|
|
|
|
// return argument.
|
|
|
|
func TestDuplicateSettleInvoice(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB(OptionClock(testClock))
|
2018-07-18 02:27:04 +03:00
|
|
|
defer cleanUp()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to make test db: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll start out by creating an invoice and writing it to the DB.
|
|
|
|
amt := lnwire.NewMSatFromSatoshis(1000)
|
|
|
|
invoice, err := randInvoice(amt)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-05 11:14:56 +03:00
|
|
|
payHash := invoice.Terms.PaymentPreimage.Hash()
|
|
|
|
|
|
|
|
if _, err := db.AddInvoice(invoice, payHash); err != nil {
|
2018-07-18 02:27:04 +03:00
|
|
|
t.Fatalf("unable to add invoice %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the invoice in the DB, we'll now attempt to settle the invoice.
|
2020-05-22 01:37:10 +03:00
|
|
|
ref := InvoiceRefByHash(payHash)
|
|
|
|
dbInvoice, err := db.UpdateInvoice(ref, getUpdateInvoice(amt))
|
2018-07-18 02:27:04 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to settle invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll update what we expect the settle invoice to be so that our
|
|
|
|
// comparison below has the correct assumption.
|
|
|
|
invoice.SettleIndex = 1
|
2019-11-22 13:25:02 +03:00
|
|
|
invoice.State = ContractSettled
|
2018-07-18 02:27:04 +03:00
|
|
|
invoice.AmtPaid = amt
|
|
|
|
invoice.SettleDate = dbInvoice.SettleDate
|
2019-08-09 16:09:57 +03:00
|
|
|
invoice.Htlcs = map[CircuitKey]*InvoiceHTLC{
|
|
|
|
{}: {
|
2019-11-19 15:33:05 +03:00
|
|
|
Amt: amt,
|
|
|
|
AcceptTime: time.Unix(1, 0),
|
|
|
|
ResolveTime: time.Unix(1, 0),
|
|
|
|
State: HtlcStateSettled,
|
2019-12-12 02:01:55 +03:00
|
|
|
CustomRecords: make(record.CustomSet),
|
2019-08-09 16:09:57 +03:00
|
|
|
},
|
|
|
|
}
|
2018-07-18 02:27:04 +03:00
|
|
|
|
|
|
|
// We should get back the exact same invoice that we just inserted.
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, invoice, dbInvoice, "wrong invoice after settle")
|
2018-07-18 02:27:04 +03:00
|
|
|
|
|
|
|
// If we try to settle the invoice again, then we should get the very
|
2019-01-14 14:03:26 +03:00
|
|
|
// same invoice back, but with an error this time.
|
2020-05-22 01:37:10 +03:00
|
|
|
dbInvoice, err = db.UpdateInvoice(ref, getUpdateInvoice(amt))
|
2019-01-14 14:03:26 +03:00
|
|
|
if err != ErrInvoiceAlreadySettled {
|
|
|
|
t.Fatalf("expected ErrInvoiceAlreadySettled")
|
2018-07-18 02:27:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if dbInvoice == nil {
|
|
|
|
t.Fatalf("invoice from db is nil after settle!")
|
|
|
|
}
|
|
|
|
|
|
|
|
invoice.SettleDate = dbInvoice.SettleDate
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, invoice, dbInvoice, "wrong invoice after second settle")
|
2018-07-18 02:27:04 +03:00
|
|
|
}
|
2018-08-11 06:24:04 +03:00
|
|
|
|
|
|
|
// TestQueryInvoices ensures that we can properly query the invoice database for
|
2018-09-11 04:20:38 +03:00
|
|
|
// invoices using different types of queries.
|
2018-08-11 06:24:04 +03:00
|
|
|
func TestQueryInvoices(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB(OptionClock(testClock))
|
2018-08-11 06:24:04 +03:00
|
|
|
defer cleanUp()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to make test db: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-09-11 04:20:38 +03:00
|
|
|
// To begin the test, we'll add 50 invoices to the database. We'll
|
2018-08-11 06:24:04 +03:00
|
|
|
// assume that the index of the invoice within the database is the same
|
|
|
|
// as the amount of the invoice itself.
|
2018-09-11 04:20:38 +03:00
|
|
|
const numInvoices = 50
|
2020-01-03 18:21:40 +03:00
|
|
|
var settleIndex uint64 = 1
|
|
|
|
var invoices []Invoice
|
|
|
|
var pendingInvoices []Invoice
|
|
|
|
|
|
|
|
for i := 1; i <= numInvoices; i++ {
|
|
|
|
amt := lnwire.MilliSatoshi(i)
|
|
|
|
invoice, err := randInvoice(amt)
|
2018-08-11 06:24:04 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-05 11:14:56 +03:00
|
|
|
paymentHash := invoice.Terms.PaymentPreimage.Hash()
|
|
|
|
|
|
|
|
if _, err := db.AddInvoice(invoice, paymentHash); err != nil {
|
2018-08-11 06:24:04 +03:00
|
|
|
t.Fatalf("unable to add invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll only settle half of all invoices created.
|
|
|
|
if i%2 == 0 {
|
2020-05-22 01:37:10 +03:00
|
|
|
ref := InvoiceRefByHash(paymentHash)
|
|
|
|
_, err := db.UpdateInvoice(ref, getUpdateInvoice(amt))
|
2019-04-16 13:11:20 +03:00
|
|
|
if err != nil {
|
2018-08-11 06:24:04 +03:00
|
|
|
t.Fatalf("unable to settle invoice: %v", err)
|
|
|
|
}
|
2020-01-03 18:21:40 +03:00
|
|
|
|
|
|
|
// Create the settled invoice for the expectation set.
|
|
|
|
settleTestInvoice(invoice, settleIndex)
|
|
|
|
settleIndex++
|
|
|
|
} else {
|
|
|
|
pendingInvoices = append(pendingInvoices, *invoice)
|
2018-08-11 06:24:04 +03:00
|
|
|
}
|
|
|
|
|
2020-01-03 18:21:40 +03:00
|
|
|
invoices = append(invoices, *invoice)
|
2018-08-11 06:24:04 +03:00
|
|
|
}
|
|
|
|
|
2018-09-11 04:20:38 +03:00
|
|
|
// The test will consist of several queries along with their respective
|
|
|
|
// expected response. Each query response should match its expected one.
|
|
|
|
testCases := []struct {
|
|
|
|
query InvoiceQuery
|
|
|
|
expected []Invoice
|
|
|
|
}{
|
|
|
|
// Fetch all invoices with a single query.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
expected: invoices,
|
|
|
|
},
|
2018-09-28 13:58:06 +03:00
|
|
|
// Fetch all invoices with a single query, reversed.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
expected: invoices,
|
|
|
|
},
|
2018-09-11 04:20:38 +03:00
|
|
|
// Fetch the first 25 invoices.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
NumMaxInvoices: numInvoices / 2,
|
|
|
|
},
|
|
|
|
expected: invoices[:numInvoices/2],
|
|
|
|
},
|
|
|
|
// Fetch the first 10 invoices, but this time iterating
|
|
|
|
// backwards.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 11,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
expected: invoices[:10],
|
|
|
|
},
|
|
|
|
// Fetch the last 40 invoices.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 10,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
expected: invoices[10:],
|
|
|
|
},
|
2018-09-28 13:58:06 +03:00
|
|
|
// Fetch all but the first invoice.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 1,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
expected: invoices[1:],
|
|
|
|
},
|
|
|
|
// Fetch one invoice, reversed, with index offset 3. This
|
|
|
|
// should give us the second invoice in the array.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 3,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[1:2],
|
|
|
|
},
|
|
|
|
// Same as above, at index 2.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 2,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[0:1],
|
|
|
|
},
|
2018-09-28 13:29:03 +03:00
|
|
|
// Fetch one invoice, at index 1, reversed. Since invoice#1 is
|
|
|
|
// the very first, there won't be any left in a reverse search,
|
|
|
|
// so we expect no invoices to be returned.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 1,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: nil,
|
|
|
|
},
|
|
|
|
// Same as above, but don't restrict the number of invoices to
|
|
|
|
// 1.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 1,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
expected: nil,
|
|
|
|
},
|
2018-09-28 13:58:06 +03:00
|
|
|
// Fetch one invoice, reversed, with no offset set. We expect
|
|
|
|
// the last invoice in the response.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[numInvoices-1:],
|
|
|
|
},
|
|
|
|
// Fetch one invoice, reversed, the offset set at numInvoices+1.
|
|
|
|
// We expect this to return the last invoice.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: numInvoices + 1,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[numInvoices-1:],
|
|
|
|
},
|
|
|
|
// Same as above, at offset numInvoices.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: numInvoices,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[numInvoices-2 : numInvoices-1],
|
|
|
|
},
|
|
|
|
// Fetch one invoice, at no offset (same as offset 0). We
|
|
|
|
// expect the first invoice only in the response.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[:1],
|
|
|
|
},
|
|
|
|
// Same as above, at offset 1.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 1,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[1:2],
|
|
|
|
},
|
|
|
|
// Same as above, at offset 2.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 2,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[2:3],
|
|
|
|
},
|
|
|
|
// Same as above, at offset numInvoices-1. Expect the last
|
|
|
|
// invoice to be returned.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: numInvoices - 1,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: invoices[numInvoices-1:],
|
|
|
|
},
|
|
|
|
// Same as above, at offset numInvoices. No invoices should be
|
|
|
|
// returned, as there are no invoices after this offset.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: numInvoices,
|
|
|
|
NumMaxInvoices: 1,
|
|
|
|
},
|
|
|
|
expected: nil,
|
|
|
|
},
|
2018-09-11 04:20:38 +03:00
|
|
|
// Fetch all pending invoices with a single query.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
PendingOnly: true,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
expected: pendingInvoices,
|
|
|
|
},
|
|
|
|
// Fetch the first 12 pending invoices.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
PendingOnly: true,
|
|
|
|
NumMaxInvoices: numInvoices / 4,
|
|
|
|
},
|
|
|
|
expected: pendingInvoices[:len(pendingInvoices)/2],
|
|
|
|
},
|
|
|
|
// Fetch the first 5 pending invoices, but this time iterating
|
|
|
|
// backwards.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 10,
|
|
|
|
PendingOnly: true,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
// Since we seek to the invoice with index 10 and
|
|
|
|
// iterate backwards, there should only be 5 pending
|
|
|
|
// invoices before it as every other invoice within the
|
|
|
|
// index is settled.
|
|
|
|
expected: pendingInvoices[:5],
|
|
|
|
},
|
|
|
|
// Fetch the last 15 invoices.
|
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: 20,
|
|
|
|
PendingOnly: true,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
|
|
|
// Since we seek to the invoice with index 20, there are
|
|
|
|
// 30 invoices left. From these 30, only 15 of them are
|
|
|
|
// still pending.
|
|
|
|
expected: pendingInvoices[len(pendingInvoices)-15:],
|
|
|
|
},
|
2020-06-10 13:34:27 +03:00
|
|
|
// Fetch all invoices paginating backwards, with an index offset
|
2020-06-10 13:34:27 +03:00
|
|
|
// that is beyond our last offset. We expect all invoices to be
|
|
|
|
// returned.
|
2020-06-10 13:34:27 +03:00
|
|
|
{
|
|
|
|
query: InvoiceQuery{
|
|
|
|
IndexOffset: numInvoices * 2,
|
|
|
|
PendingOnly: false,
|
|
|
|
Reversed: true,
|
|
|
|
NumMaxInvoices: numInvoices,
|
|
|
|
},
|
2020-06-10 13:34:27 +03:00
|
|
|
expected: invoices,
|
2020-06-10 13:34:27 +03:00
|
|
|
},
|
2018-08-11 06:24:04 +03:00
|
|
|
}
|
|
|
|
|
2018-09-11 04:20:38 +03:00
|
|
|
for i, testCase := range testCases {
|
|
|
|
response, err := db.QueryInvoices(testCase.query)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to query invoice database: %v", err)
|
2018-08-11 06:24:04 +03:00
|
|
|
}
|
|
|
|
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, len(testCase.expected), len(response.Invoices))
|
|
|
|
|
|
|
|
for j, expected := range testCase.expected {
|
|
|
|
require.Equal(t,
|
|
|
|
expected, response.Invoices[j],
|
|
|
|
fmt.Sprintf("test: #%v, item: #%v", i, j),
|
|
|
|
)
|
2018-08-11 06:24:04 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-16 13:11:20 +03:00
|
|
|
|
2019-08-09 14:40:34 +03:00
|
|
|
// getUpdateInvoice returns an invoice update callback that, when called,
|
|
|
|
// settles the invoice with the given amount.
|
|
|
|
func getUpdateInvoice(amt lnwire.MilliSatoshi) InvoiceUpdateCallback {
|
|
|
|
return func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
2019-11-22 13:25:02 +03:00
|
|
|
if invoice.State == ContractSettled {
|
2019-08-09 14:40:34 +03:00
|
|
|
return nil, ErrInvoiceAlreadySettled
|
|
|
|
}
|
2019-06-10 13:02:06 +03:00
|
|
|
|
2019-12-12 02:01:55 +03:00
|
|
|
noRecords := make(record.CustomSet)
|
2019-11-19 15:33:05 +03:00
|
|
|
|
2019-08-09 14:40:34 +03:00
|
|
|
update := &InvoiceUpdateDesc{
|
2019-11-27 15:20:14 +03:00
|
|
|
State: &InvoiceStateUpdateDesc{
|
|
|
|
Preimage: invoice.Terms.PaymentPreimage,
|
|
|
|
NewState: ContractSettled,
|
|
|
|
},
|
2019-11-27 16:19:15 +03:00
|
|
|
AddHtlcs: map[CircuitKey]*HtlcAcceptDesc{
|
2019-08-09 16:09:57 +03:00
|
|
|
{}: {
|
2019-11-19 15:33:05 +03:00
|
|
|
Amt: amt,
|
|
|
|
CustomRecords: noRecords,
|
2019-08-09 16:09:57 +03:00
|
|
|
},
|
|
|
|
},
|
2019-08-09 14:40:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return update, nil
|
|
|
|
}
|
2019-04-16 13:11:20 +03:00
|
|
|
}
|
2019-11-19 15:33:05 +03:00
|
|
|
|
|
|
|
// TestCustomRecords tests that custom records are properly recorded in the
|
|
|
|
// invoice database.
|
|
|
|
func TestCustomRecords(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2020-06-24 13:50:11 +03:00
|
|
|
db, cleanUp, err := MakeTestDB()
|
2019-11-19 15:33:05 +03:00
|
|
|
defer cleanUp()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to make test db: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-04-08 14:47:10 +03:00
|
|
|
preimage := lntypes.Preimage{1}
|
|
|
|
paymentHash := preimage.Hash()
|
|
|
|
|
2019-11-19 15:33:05 +03:00
|
|
|
testInvoice := &Invoice{
|
|
|
|
Htlcs: map[CircuitKey]*InvoiceHTLC{},
|
2020-04-08 14:47:10 +03:00
|
|
|
Terms: ContractTerm{
|
|
|
|
Value: lnwire.NewMSatFromSatoshis(10000),
|
|
|
|
Features: emptyFeatures,
|
|
|
|
PaymentPreimage: &preimage,
|
|
|
|
},
|
2019-11-19 15:33:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := db.AddInvoice(testInvoice, paymentHash); err != nil {
|
2020-04-08 14:47:10 +03:00
|
|
|
t.Fatalf("unable to add invoice: %v", err)
|
2019-11-19 15:33:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Accept an htlc with custom records on this invoice.
|
|
|
|
key := CircuitKey{ChanID: lnwire.NewShortChanIDFromInt(1), HtlcID: 4}
|
|
|
|
|
2019-12-12 02:01:55 +03:00
|
|
|
records := record.CustomSet{
|
2019-11-19 15:33:05 +03:00
|
|
|
100000: []byte{},
|
|
|
|
100001: []byte{1, 2},
|
|
|
|
}
|
|
|
|
|
2020-05-22 01:37:10 +03:00
|
|
|
ref := InvoiceRefByHash(paymentHash)
|
|
|
|
_, err = db.UpdateInvoice(ref,
|
2019-11-19 15:33:05 +03:00
|
|
|
func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
|
|
|
return &InvoiceUpdateDesc{
|
|
|
|
AddHtlcs: map[CircuitKey]*HtlcAcceptDesc{
|
|
|
|
key: {
|
|
|
|
Amt: 500,
|
|
|
|
CustomRecords: records,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to add invoice htlc: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve the invoice from that database and verify that the custom
|
|
|
|
// records are present.
|
2020-05-22 01:37:10 +03:00
|
|
|
dbInvoice, err := db.LookupInvoice(ref)
|
2019-11-19 15:33:05 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to lookup invoice: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(dbInvoice.Htlcs) != 1 {
|
|
|
|
t.Fatalf("expected the htlc to be added")
|
|
|
|
}
|
2020-06-05 16:02:06 +03:00
|
|
|
|
|
|
|
require.Equal(t,
|
|
|
|
records, dbInvoice.Htlcs[key].CustomRecords,
|
|
|
|
"invalid custom records",
|
|
|
|
)
|
2019-11-19 15:33:05 +03:00
|
|
|
}
|
2020-05-22 01:37:10 +03:00
|
|
|
|
|
|
|
// TestInvoiceRef asserts that the proper identifiers are returned from an
|
|
|
|
// InvoiceRef depending on the constructor used.
|
|
|
|
func TestInvoiceRef(t *testing.T) {
|
|
|
|
payHash := lntypes.Hash{0x01}
|
2020-05-22 01:37:39 +03:00
|
|
|
payAddr := [32]byte{0x02}
|
2020-05-22 01:37:10 +03:00
|
|
|
|
|
|
|
// An InvoiceRef by hash should return the provided hash and a nil
|
|
|
|
// payment addr.
|
|
|
|
refByHash := InvoiceRefByHash(payHash)
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, payHash, refByHash.PayHash())
|
|
|
|
require.Equal(t, (*[32]byte)(nil), refByHash.PayAddr())
|
2020-05-22 01:37:39 +03:00
|
|
|
|
|
|
|
// An InvoiceRef by hash and addr should return the payment hash and
|
|
|
|
// payment addr passed to the constructor.
|
|
|
|
refByHashAndAddr := InvoiceRefByHashAndAddr(payHash, payAddr)
|
2020-06-05 16:02:06 +03:00
|
|
|
require.Equal(t, payHash, refByHashAndAddr.PayHash())
|
|
|
|
require.Equal(t, &payAddr, refByHashAndAddr.PayAddr())
|
2020-05-22 01:37:10 +03:00
|
|
|
}
|