channeldb: convert settled boolean to state

This commit is a preparation for the addition of new invoice
states. A database migration is not needed because we keep
the same field length and values.
This commit is contained in:
Joost Jager 2018-12-19 15:56:26 +01:00
parent 552a9b7190
commit 5515713b88
No known key found for this signature in database
GPG Key ID: AE6B0D042C8E38D9
8 changed files with 88 additions and 46 deletions

@ -106,7 +106,7 @@ func TestInvoiceWorkflow(t *testing.T) {
if err != nil {
t.Fatalf("unable to fetch invoice: %v", err)
}
if !dbInvoice2.Terms.Settled {
if dbInvoice2.Terms.State != ContractSettled {
t.Fatalf("invoice should now be settled but isn't")
}
if dbInvoice2.SettleDate.IsZero() {
@ -348,7 +348,7 @@ func TestDuplicateSettleInvoice(t *testing.T) {
// We'll update what we expect the settle invoice to be so that our
// comparison below has the correct assumption.
invoice.SettleIndex = 1
invoice.Terms.Settled = true
invoice.Terms.State = ContractSettled
invoice.AmtPaid = amt
invoice.SettleDate = dbInvoice.SettleDate

@ -74,6 +74,30 @@ const (
MaxPaymentRequestSize = 4096
)
// ContractState describes the state the invoice is in.
type ContractState uint8
const (
// ContractOpen means the invoice has only been created.
ContractOpen ContractState = 0
// ContractSettled means the htlc is settled and the invoice has been
// paid.
ContractSettled ContractState = 1
)
// String returns a human readable identifier for the ContractState type.
func (c ContractState) String() string {
switch c {
case ContractOpen:
return "Open"
case ContractSettled:
return "Settled"
}
return "Unknown"
}
// ContractTerm is a companion struct to the Invoice struct. This struct houses
// the necessary conditions required before the invoice can be considered fully
// settled by the payee.
@ -87,9 +111,8 @@ type ContractTerm struct {
// which can be satisfied by the above preimage.
Value lnwire.MilliSatoshi
// Settled indicates if this particular contract term has been fully
// settled by the payer.
Settled bool
// State describes the state the invoice is in.
State ContractState
}
// Invoice is a payment invoice generated by a payee in order to request
@ -380,7 +403,9 @@ func (d *DB) FetchAllInvoices(pendingOnly bool) ([]Invoice, error) {
return err
}
if pendingOnly && invoice.Terms.Settled {
if pendingOnly &&
invoice.Terms.State == ContractSettled {
return nil
}
@ -528,7 +553,9 @@ func (d *DB) QueryInvoices(q InvoiceQuery) (InvoiceSlice, error) {
// Skip any settled invoices if the caller is only
// interested in unsettled.
if q.PendingOnly && invoice.Terms.Settled {
if q.PendingOnly &&
invoice.Terms.State == ContractSettled {
continue
}
@ -773,7 +800,7 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
return err
}
if err := binary.Write(w, byteOrder, i.Terms.Settled); err != nil {
if err := binary.Write(w, byteOrder, i.Terms.State); err != nil {
return err
}
@ -845,7 +872,7 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
}
invoice.Terms.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
if err := binary.Read(r, byteOrder, &invoice.Terms.Settled); err != nil {
if err := binary.Read(r, byteOrder, &invoice.Terms.State); err != nil {
return invoice, err
}
@ -872,7 +899,7 @@ func settleInvoice(invoices, settleIndex *bbolt.Bucket, invoiceNum []byte,
// Add idempotency to duplicate settles, return here to avoid
// overwriting the previous info.
if invoice.Terms.Settled {
if invoice.Terms.State == ContractSettled {
return &invoice, nil
}
@ -891,7 +918,7 @@ func settleInvoice(invoices, settleIndex *bbolt.Bucket, invoiceNum []byte,
}
invoice.AmtPaid = amtPaid
invoice.Terms.Settled = true
invoice.Terms.State = ContractSettled
invoice.SettleDate = time.Now()
invoice.SettleIndex = nextSettleSeqNo

@ -189,7 +189,7 @@ func migrateInvoiceTimeSeries(tx *bbolt.Tx) error {
// Next, we'll check if the invoice has been settled or not. If
// so, then we'll also add it to the settle index.
var nextSettleSeqNo uint64
if invoice.Terms.Settled {
if invoice.Terms.State == ContractSettled {
nextSettleSeqNo, err = settleIndex.NextSequence()
if err != nil {
return err

@ -2334,7 +2334,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
// TODO(conner): track ownership of settlements to
// properly recover from failures? or add batch invoice
// settlement
if invoice.Terms.Settled {
if invoice.Terms.State != channeldb.ContractOpen {
log.Warnf("Accepting duplicate payment for "+
"hash=%x", pd.RHash[:])
}

@ -236,7 +236,7 @@ func TestChannelLinkSingleHopPayment(t *testing.T) {
if err != nil {
t.Fatalf("unable to get invoice: %v", err)
}
if !invoice.Terms.Settled {
if invoice.Terms.State != channeldb.ContractSettled {
t.Fatal("alice invoice wasn't settled")
}
@ -467,7 +467,7 @@ func TestChannelLinkMultiHopPayment(t *testing.T) {
if err != nil {
t.Fatalf("unable to get invoice: %v", err)
}
if !invoice.Terms.Settled {
if invoice.Terms.State != channeldb.ContractSettled {
t.Fatal("carol invoice haven't been settled")
}
@ -818,7 +818,7 @@ func TestUpdateForwardingPolicy(t *testing.T) {
if err != nil {
t.Fatalf("unable to get invoice: %v", err)
}
if !invoice.Terms.Settled {
if invoice.Terms.State != channeldb.ContractSettled {
t.Fatal("carol invoice haven't been settled")
}
@ -937,7 +937,7 @@ func TestChannelLinkMultiHopInsufficientPayment(t *testing.T) {
if err != nil {
t.Fatalf("unable to get invoice: %v", err)
}
if invoice.Terms.Settled {
if invoice.Terms.State == channeldb.ContractSettled {
t.Fatal("carol invoice have been settled")
}
@ -1026,7 +1026,7 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) {
// Check that alice invoice wasn't settled and bandwidth of htlc
// links hasn't been changed.
if invoice.Terms.Settled {
if invoice.Terms.State == channeldb.ContractSettled {
t.Fatal("alice invoice was settled")
}
@ -1112,7 +1112,7 @@ func TestChannelLinkMultiHopUnknownNextHop(t *testing.T) {
if err != nil {
t.Fatalf("unable to get invoice: %v", err)
}
if invoice.Terms.Settled {
if invoice.Terms.State == channeldb.ContractSettled {
t.Fatal("carol invoice have been settled")
}
@ -1227,7 +1227,7 @@ func TestChannelLinkMultiHopDecodeError(t *testing.T) {
if err != nil {
t.Fatalf("unable to get invoice: %v", err)
}
if invoice.Terms.Settled {
if invoice.Terms.State == channeldb.ContractSettled {
t.Fatal("carol invoice have been settled")
}
@ -3332,7 +3332,7 @@ func TestChannelRetransmission(t *testing.T) {
err = errors.Errorf("unable to get invoice: %v", err)
continue
}
if !invoice.Terms.Settled {
if invoice.Terms.State != channeldb.ContractSettled {
err = errors.Errorf("alice invoice haven't been settled")
continue
}
@ -3828,7 +3828,7 @@ func TestChannelLinkAcceptOverpay(t *testing.T) {
if err != nil {
t.Fatalf("unable to get invoice: %v", err)
}
if !invoice.Terms.Settled {
if invoice.Terms.State != channeldb.ContractSettled {
t.Fatal("carol invoice haven't been settled")
}

@ -720,11 +720,11 @@ func (i *mockInvoiceRegistry) SettleInvoice(rhash chainhash.Hash,
return fmt.Errorf("can't find mock invoice: %x", rhash[:])
}
if invoice.Terms.Settled {
if invoice.Terms.State == channeldb.ContractSettled {
return nil
}
invoice.Terms.Settled = true
invoice.Terms.State = channeldb.ContractSettled
invoice.AmtPaid = amt
i.invoices[rhash] = invoice

@ -89,8 +89,7 @@ func (i *invoiceRegistry) Stop() {
// Only two event types are currently supported: newly created invoices, and
// instance where invoices are settled.
type invoiceEvent struct {
isSettle bool
state channeldb.ContractState
invoice *channeldb.Invoice
}
@ -143,27 +142,27 @@ func (i *invoiceRegistry) invoiceEventNotifier() {
switch {
// If we've already sent this settle event to
// the client, then we can skip this.
case event.isSettle &&
case event.state == channeldb.ContractSettled &&
client.settleIndex >= invoice.SettleIndex:
continue
// Similarly, if we've already sent this add to
// the client then we can skip this one.
case !event.isSettle &&
case event.state == channeldb.ContractOpen &&
client.addIndex >= invoice.AddIndex:
continue
// These two states should never happen, but we
// log them just in case so we can detect this
// instance.
case !event.isSettle &&
case event.state == channeldb.ContractOpen &&
client.addIndex+1 != invoice.AddIndex:
ltndLog.Warnf("client=%v for invoice "+
"notifications missed an update, "+
"add_index=%v, new add event index=%v",
clientID, client.addIndex,
invoice.AddIndex)
case event.isSettle &&
case event.state == channeldb.ContractSettled &&
client.settleIndex+1 != invoice.SettleIndex:
ltndLog.Warnf("client=%v for invoice "+
"notifications missed an update, "+
@ -174,8 +173,8 @@ func (i *invoiceRegistry) invoiceEventNotifier() {
select {
case client.ntfnQueue.ChanIn() <- &invoiceEvent{
isSettle: event.isSettle,
invoice: invoice,
state: event.state,
invoice: invoice,
}:
case <-i.quit:
return
@ -187,10 +186,14 @@ func (i *invoiceRegistry) invoiceEventNotifier() {
// don't send a notification twice, which can
// happen if a new event is added while we're
// catching up a new client.
if event.isSettle {
switch event.state {
case channeldb.ContractSettled:
client.settleIndex = invoice.SettleIndex
} else {
case channeldb.ContractOpen:
client.addIndex = invoice.AddIndex
default:
ltndLog.Errorf("unknown invoice "+
"state: %v", event.state)
}
}
@ -225,8 +228,8 @@ func (i *invoiceRegistry) deliverBacklogEvents(client *invoiceSubscription) erro
select {
case client.ntfnQueue.ChanIn() <- &invoiceEvent{
isSettle: false,
invoice: &addEvent,
state: channeldb.ContractOpen,
invoice: &addEvent,
}:
case <-i.quit:
return fmt.Errorf("registry shutting down")
@ -239,8 +242,8 @@ func (i *invoiceRegistry) deliverBacklogEvents(client *invoiceSubscription) erro
select {
case client.ntfnQueue.ChanIn() <- &invoiceEvent{
isSettle: true,
invoice: &settleEvent,
state: channeldb.ContractSettled,
invoice: &settleEvent,
}:
case <-i.quit:
return fmt.Errorf("registry shutting down")
@ -296,7 +299,7 @@ func (i *invoiceRegistry) AddInvoice(invoice *channeldb.Invoice) (uint64, error)
// Now that we've added the invoice, we'll send dispatch a message to
// notify the clients of this new invoice.
i.notifyClients(invoice, false)
i.notifyClients(invoice, channeldb.ContractOpen)
return addIndex, nil
}
@ -365,17 +368,19 @@ func (i *invoiceRegistry) SettleInvoice(rHash chainhash.Hash,
ltndLog.Infof("Payment received: %v", spew.Sdump(invoice))
i.notifyClients(invoice, true)
i.notifyClients(invoice, channeldb.ContractSettled)
return nil
}
// notifyClients notifies all currently registered invoice notification clients
// of a newly added/settled invoice.
func (i *invoiceRegistry) notifyClients(invoice *channeldb.Invoice, settle bool) {
func (i *invoiceRegistry) notifyClients(invoice *channeldb.Invoice,
state channeldb.ContractState) {
event := &invoiceEvent{
isSettle: settle,
invoice: invoice,
state: state,
invoice: invoice,
}
select {
@ -483,9 +488,17 @@ func (i *invoiceRegistry) SubscribeNotifications(addIndex, settleIndex uint64) *
case ntfn := <-client.ntfnQueue.ChanOut():
invoiceEvent := ntfn.(*invoiceEvent)
targetChan := client.NewInvoices
if invoiceEvent.isSettle {
var targetChan chan *channeldb.Invoice
switch invoiceEvent.state {
case channeldb.ContractOpen:
targetChan = client.NewInvoices
case channeldb.ContractSettled:
targetChan = client.SettledInvoices
default:
ltndLog.Errorf("unknown invoice "+
"state: %v", invoiceEvent.state)
continue
}
select {

@ -3303,6 +3303,8 @@ func createRPCInvoice(invoice *channeldb.Invoice) (*lnrpc.Invoice, error) {
satAmt := invoice.Terms.Value.ToSatoshis()
satAmtPaid := invoice.AmtPaid.ToSatoshis()
isSettled := invoice.Terms.State == channeldb.ContractSettled
return &lnrpc.Invoice{
Memo: string(invoice.Memo[:]),
Receipt: invoice.Receipt[:],
@ -3311,7 +3313,7 @@ func createRPCInvoice(invoice *channeldb.Invoice) (*lnrpc.Invoice, error) {
Value: int64(satAmt),
CreationDate: invoice.CreationDate.Unix(),
SettleDate: settleDate,
Settled: invoice.Terms.Settled,
Settled: isSettled,
PaymentRequest: paymentRequest,
DescriptionHash: descHash,
Expiry: expiry,