Merge pull request #3838 from bhandras/i1404
channeldb+invoices: fix what pending invoice means in ChannelDB
This commit is contained in:
commit
5ea63158e0
@ -2,7 +2,6 @@ package channeldb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
mrand "math/rand"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -53,6 +52,29 @@ func randInvoice(value lnwire.MilliSatoshi) (*Invoice, error) {
|
|||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestInvoiceWorkflow(t *testing.T) {
|
func TestInvoiceWorkflow(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -416,26 +438,28 @@ func TestFetchAllInvoicesWithPaymentHash(t *testing.T) {
|
|||||||
t.Fatalf("expected empty list as a result, got: %v", empty)
|
t.Fatalf("expected empty list as a result, got: %v", empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now populate the DB and check if we can get all invoices with their
|
|
||||||
// payment hashes as expected.
|
|
||||||
const numInvoices = 20
|
|
||||||
testPendingInvoices := make(map[lntypes.Hash]*Invoice)
|
|
||||||
testAllInvoices := make(map[lntypes.Hash]*Invoice)
|
|
||||||
|
|
||||||
states := []ContractState{
|
states := []ContractState{
|
||||||
ContractOpen, ContractSettled, ContractCanceled, ContractAccepted,
|
ContractOpen, ContractSettled, ContractCanceled, ContractAccepted,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := lnwire.MilliSatoshi(1); i <= numInvoices; i++ {
|
numInvoices := len(states) * 2
|
||||||
invoice, err := randInvoice(i)
|
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))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create invoice: %v", err)
|
t.Fatalf("unable to create invoice: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
invoice.State = states[mrand.Intn(len(states))]
|
// 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)]
|
||||||
paymentHash := invoice.Terms.PaymentPreimage.Hash()
|
paymentHash := invoice.Terms.PaymentPreimage.Hash()
|
||||||
|
|
||||||
if invoice.State != ContractSettled && invoice.State != ContractCanceled {
|
if invoice.IsPending() {
|
||||||
testPendingInvoices[paymentHash] = invoice
|
testPendingInvoices[paymentHash] = invoice
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,6 +602,69 @@ func TestDuplicateSettleInvoice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFetchAllInvoices tests that FetchAllInvoices works as expected.
|
||||||
|
func TestFetchAllInvoices(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, cleanUp, err := makeTestDB()
|
||||||
|
defer cleanUp()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to make test db: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
contractStates := []ContractState{
|
||||||
|
ContractOpen, ContractSettled, ContractCanceled, ContractAccepted,
|
||||||
|
}
|
||||||
|
|
||||||
|
numInvoices := len(contractStates) * 2
|
||||||
|
|
||||||
|
var expectedPendingInvoices []Invoice
|
||||||
|
var expectedAllInvoices []Invoice
|
||||||
|
|
||||||
|
for i := 1; i <= numInvoices; i++ {
|
||||||
|
invoice, err := randInvoice(lnwire.MilliSatoshi(i))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create invoice: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice.AddIndex = uint64(i)
|
||||||
|
// Set the contract state of the next invoice such that there's an equal
|
||||||
|
// number for all possbile states.
|
||||||
|
invoice.State = contractStates[i%len(contractStates)]
|
||||||
|
|
||||||
|
paymentHash := invoice.Terms.PaymentPreimage.Hash()
|
||||||
|
if invoice.IsPending() {
|
||||||
|
expectedPendingInvoices = append(expectedPendingInvoices, *invoice)
|
||||||
|
}
|
||||||
|
expectedAllInvoices = append(expectedAllInvoices, *invoice)
|
||||||
|
|
||||||
|
if _, err := db.AddInvoice(invoice, paymentHash); err != nil {
|
||||||
|
t.Fatalf("unable to add invoice: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingInvoices, err := db.FetchAllInvoices(true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to fetch all pending invoices: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allInvoices, err := db.FetchAllInvoices(false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to fetch all non pending invoices: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(pendingInvoices, expectedPendingInvoices) {
|
||||||
|
t.Fatalf("pending invoices: %v\n != \n expected einvoices: %v",
|
||||||
|
spew.Sdump(pendingInvoices), spew.Sdump(expectedPendingInvoices))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(allInvoices, expectedAllInvoices) {
|
||||||
|
t.Fatalf("pending + non pending: %v\n != \n expected: %v",
|
||||||
|
spew.Sdump(allInvoices), spew.Sdump(expectedAllInvoices))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestQueryInvoices ensures that we can properly query the invoice database for
|
// TestQueryInvoices ensures that we can properly query the invoice database for
|
||||||
// invoices using different types of queries.
|
// invoices using different types of queries.
|
||||||
func TestQueryInvoices(t *testing.T) {
|
func TestQueryInvoices(t *testing.T) {
|
||||||
|
@ -149,15 +149,13 @@ const (
|
|||||||
// ContractOpen means the invoice has only been created.
|
// ContractOpen means the invoice has only been created.
|
||||||
ContractOpen ContractState = 0
|
ContractOpen ContractState = 0
|
||||||
|
|
||||||
// ContractSettled means the htlc is settled and the invoice has been
|
// ContractSettled means the htlc is settled and the invoice has been paid.
|
||||||
// paid.
|
|
||||||
ContractSettled ContractState = 1
|
ContractSettled ContractState = 1
|
||||||
|
|
||||||
// ContractCanceled means the invoice has been canceled.
|
// ContractCanceled means the invoice has been canceled.
|
||||||
ContractCanceled ContractState = 2
|
ContractCanceled ContractState = 2
|
||||||
|
|
||||||
// ContractAccepted means the HTLC has been accepted but not settled
|
// ContractAccepted means the HTLC has been accepted but not settled yet.
|
||||||
// yet.
|
|
||||||
ContractAccepted ContractState = 3
|
ContractAccepted ContractState = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -385,6 +383,11 @@ func validateInvoice(i *Invoice) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPending returns ture if the invoice is in ContractOpen state.
|
||||||
|
func (i *Invoice) IsPending() bool {
|
||||||
|
return i.State == ContractOpen || i.State == ContractAccepted
|
||||||
|
}
|
||||||
|
|
||||||
// AddInvoice inserts the targeted invoice into the database. If the invoice has
|
// AddInvoice inserts the targeted invoice into the database. If the invoice has
|
||||||
// *any* payment hashes which already exists within the database, then the
|
// *any* payment hashes which already exists within the database, then the
|
||||||
// insertion will be aborted and rejected due to the strict policy banning any
|
// insertion will be aborted and rejected due to the strict policy banning any
|
||||||
@ -578,9 +581,9 @@ type InvoiceWithPaymentHash struct {
|
|||||||
|
|
||||||
// FetchAllInvoicesWithPaymentHash returns all invoices and their payment hashes
|
// FetchAllInvoicesWithPaymentHash returns all invoices and their payment hashes
|
||||||
// currently stored within the database. If the pendingOnly param is true, then
|
// currently stored within the database. If the pendingOnly param is true, then
|
||||||
// only unsettled invoices and their payment hashes will be returned, skipping
|
// only open or accepted invoices and their payment hashes will be returned,
|
||||||
// all invoices that are fully settled or canceled. Note that the returned
|
// skipping all invoices that are fully settled or canceled. Note that the
|
||||||
// array is not ordered by add index.
|
// returned array is not ordered by add index.
|
||||||
func (d *DB) FetchAllInvoicesWithPaymentHash(pendingOnly bool) (
|
func (d *DB) FetchAllInvoicesWithPaymentHash(pendingOnly bool) (
|
||||||
[]InvoiceWithPaymentHash, error) {
|
[]InvoiceWithPaymentHash, error) {
|
||||||
|
|
||||||
@ -617,10 +620,7 @@ func (d *DB) FetchAllInvoicesWithPaymentHash(pendingOnly bool) (
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if pendingOnly &&
|
if pendingOnly && !invoice.IsPending() {
|
||||||
(invoice.State == ContractSettled ||
|
|
||||||
invoice.State == ContractCanceled) {
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,8 +643,9 @@ func (d *DB) FetchAllInvoicesWithPaymentHash(pendingOnly bool) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FetchAllInvoices returns all invoices currently stored within the database.
|
// FetchAllInvoices returns all invoices currently stored within the database.
|
||||||
// If the pendingOnly param is true, then only unsettled invoices will be
|
// If the pendingOnly param is set to true, then only invoices in open or
|
||||||
// returned, skipping all invoices that are fully settled.
|
// accepted state will be returned, skipping all invoices that are fully
|
||||||
|
// settled or canceled.
|
||||||
func (d *DB) FetchAllInvoices(pendingOnly bool) ([]Invoice, error) {
|
func (d *DB) FetchAllInvoices(pendingOnly bool) ([]Invoice, error) {
|
||||||
var invoices []Invoice
|
var invoices []Invoice
|
||||||
|
|
||||||
@ -668,9 +669,7 @@ func (d *DB) FetchAllInvoices(pendingOnly bool) ([]Invoice, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if pendingOnly &&
|
if pendingOnly && !invoice.IsPending() {
|
||||||
invoice.State == ContractSettled {
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,11 +815,9 @@ func (d *DB) QueryInvoices(q InvoiceQuery) (InvoiceSlice, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip any settled invoices if the caller is only
|
// Skip any settled or canceled invoices if the caller is
|
||||||
// interested in unsettled.
|
// only interested in pending ones.
|
||||||
if q.PendingOnly &&
|
if q.PendingOnly && !invoice.IsPending() {
|
||||||
invoice.State == ContractSettled {
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7806,7 +7806,9 @@ func (m *PaymentHash) GetRHash() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ListInvoiceRequest struct {
|
type ListInvoiceRequest struct {
|
||||||
/// If set, only unsettled invoices will be returned in the response.
|
//*
|
||||||
|
//If set, only invoices that are not settled and not canceled will be returned
|
||||||
|
//in the response.
|
||||||
PendingOnly bool `protobuf:"varint,1,opt,name=pending_only,proto3" json:"pending_only,omitempty"`
|
PendingOnly bool `protobuf:"varint,1,opt,name=pending_only,proto3" json:"pending_only,omitempty"`
|
||||||
//*
|
//*
|
||||||
//The index of an invoice that will be used as either the start or end of a
|
//The index of an invoice that will be used as either the start or end of a
|
||||||
|
@ -2516,7 +2516,10 @@ message PaymentHash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message ListInvoiceRequest {
|
message ListInvoiceRequest {
|
||||||
/// If set, only unsettled invoices will be returned in the response.
|
/**
|
||||||
|
If set, only invoices that are not settled and not canceled will be returned
|
||||||
|
in the response.
|
||||||
|
*/
|
||||||
bool pending_only = 1 [json_name = "pending_only"];
|
bool pending_only = 1 [json_name = "pending_only"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -896,7 +896,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"name": "pending_only",
|
"name": "pending_only",
|
||||||
"description": "/ If set, only unsettled invoices will be returned in the response.",
|
"description": "*\nIf set, only invoices that are not settled and not canceled will be returned\nin the response.",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"required": false,
|
"required": false,
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
Loading…
Reference in New Issue
Block a user