diff --git a/channeldb/invoices.go b/channeldb/invoices.go index 8d2c585f9..9d4a71721 100644 --- a/channeldb/invoices.go +++ b/channeldb/invoices.go @@ -26,6 +26,8 @@ var ( // index is used to detect duplicates, and also to provide a fast path // for looking up incoming HTLCs to determine if we're able to settle // them fully. + // + // maps: payHash => invoiceIndex invoiceIndexBucket = []byte("paymenthashes") // numInvoicesKey is the name of key which houses the auto-incrementing @@ -61,8 +63,8 @@ type ContractTerm struct { // extended. PaymentPreimage [32]byte - // Value is the expected amount of milli-satoshis to be paid to an - // HTLC which can be satisfied by the above preimage. + // Value is the expected amount of milli-satoshis to be paid to an HTLC + // which can be satisfied by the above preimage. Value lnwire.MilliSatoshi // Settled indicates if this particular contract term has been fully @@ -79,8 +81,7 @@ type ContractTerm struct { // invoices are never deleted from the database, instead a bit is toggled // denoting the invoice has been fully settled. Within the database, all // invoices must have a unique payment hash which is generated by taking the -// sha256 of the payment -// preimage. +// sha256 of the payment preimage. type Invoice struct { // Memo is an optional memo to be stored along side an invoice. The // memo may contain further details pertaining to the invoice itself, @@ -103,13 +104,18 @@ type Invoice struct { // SettleDate is the exact time the invoice was settled. SettleDate time.Time - // Terms are the contractual payment terms of the invoice. Once - // all the terms have been satisfied by the payer, then the invoice can - // be considered fully fulfilled. + // Terms are the contractual payment terms of the invoice. Once all the + // terms have been satisfied by the payer, then the invoice can be + // considered fully fulfilled. // // TODO(roasbeef): later allow for multiple terms to fulfill the final // invoice: payment fragmentation, etc. Terms ContractTerm + // AmtPaid is the final amount that we ultimately accepted for pay for + // this invoice. We specify this value independently as it's possible + // that the invoice originally didn't specify an amount, or the sender + // overpaid. + AmtPaid lnwire.MilliSatoshi } func validateInvoice(i *Invoice) error { @@ -262,7 +268,8 @@ func (d *DB) FetchAllInvoices(pendingOnly bool) ([]*Invoice, error) { // payment hash as fully settled. If an invoice matching the passed payment // hash doesn't existing within the database, then the action will fail with a // "not found" error. -func (d *DB) SettleInvoice(paymentHash [32]byte) error { +func (d *DB) SettleInvoice(paymentHash [32]byte, amtPaid lnwire.MilliSatoshi) error { + return d.Update(func(tx *bolt.Tx) error { invoices, err := tx.CreateBucketIfNotExists(invoiceBucket) if err != nil { @@ -280,7 +287,9 @@ func (d *DB) SettleInvoice(paymentHash [32]byte) error { return ErrInvoiceNotFound } - return settleInvoice(invoices, invoiceNum) + return settleInvoice( + invoices, settleIndex, invoiceNum, amtPaid, + ) }) } @@ -361,6 +370,9 @@ func serializeInvoice(w io.Writer, i *Invoice) error { return err } + if err := binary.Write(w, byteOrder, int64(i.AmtPaid)); err != nil { + return err + } return nil } @@ -421,12 +433,16 @@ func deserializeInvoice(r io.Reader) (*Invoice, error) { if err := binary.Read(r, byteOrder, &invoice.Terms.Settled); err != nil { return nil, err + if err := binary.Read(r, byteOrder, &invoice.AmtPaid); err != nil { + return invoice, err } return invoice, nil } -func settleInvoice(invoices *bolt.Bucket, invoiceNum []byte) error { +func settleInvoice(invoices, settleIndex *bolt.Bucket, invoiceNum []byte, + amtPaid lnwire.MilliSatoshi) error { + invoice, err := fetchInvoice(invoiceNum, invoices) if err != nil { return err @@ -438,6 +454,7 @@ func settleInvoice(invoices *bolt.Bucket, invoiceNum []byte) error { return nil } + invoice.AmtPaid = amtPaid invoice.Terms.Settled = true invoice.SettleDate = time.Now()