2016-09-17 03:23:37 +03:00
|
|
|
package channeldb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-03-16 04:56:25 +03:00
|
|
|
"crypto/sha256"
|
2017-09-21 03:37:26 +03:00
|
|
|
"encoding/binary"
|
2016-09-24 01:15:22 +03:00
|
|
|
"fmt"
|
2016-09-17 03:23:37 +03:00
|
|
|
"io"
|
|
|
|
"time"
|
|
|
|
|
2018-07-18 02:28:08 +03:00
|
|
|
"github.com/btcsuite/btcd/wire"
|
2018-03-11 06:00:57 +03:00
|
|
|
"github.com/coreos/bbolt"
|
2017-08-22 08:51:19 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2016-09-17 03:23:37 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// invoiceBucket is the name of the bucket within the database that
|
|
|
|
// stores all data related to invoices no matter their final state.
|
|
|
|
// Within the invoice bucket, each invoice is keyed by its invoice ID
|
|
|
|
// which is a monotonically increasing uint32.
|
|
|
|
invoiceBucket = []byte("invoices")
|
|
|
|
|
|
|
|
// paymentHashIndexBucket is the name of the sub-bucket within the
|
|
|
|
// invoiceBucket which indexes all invoices by their payment hash. The
|
|
|
|
// payment hash is the sha256 of the invoice's payment preimage. This
|
|
|
|
// index is used to detect duplicates, and also to provide a fast path
|
2017-01-13 08:01:50 +03:00
|
|
|
// for looking up incoming HTLCs to determine if we're able to settle
|
2016-09-17 03:23:37 +03:00
|
|
|
// them fully.
|
2018-04-25 06:50:56 +03:00
|
|
|
//
|
|
|
|
// maps: payHash => invoiceIndex
|
2016-09-19 21:46:48 +03:00
|
|
|
invoiceIndexBucket = []byte("paymenthashes")
|
|
|
|
|
|
|
|
// numInvoicesKey is the name of key which houses the auto-incrementing
|
|
|
|
// invoice ID which is essentially used as a primary key. With each
|
|
|
|
// invoice inserted, the primary key is incremented by one. This key is
|
|
|
|
// stored within the invoiceIndexBucket. Within the invoiceBucket
|
|
|
|
// invoices are uniquely identified by the invoice ID.
|
|
|
|
numInvoicesKey = []byte("nik")
|
2018-04-25 06:57:30 +03:00
|
|
|
|
|
|
|
// addIndexBucket is an index bucket that we'll use to create a
|
|
|
|
// monotonically increasing set of add indexes. Each time we add a new
|
|
|
|
// invoice, this sequence number will be incremented and then populated
|
|
|
|
// within the new invoice.
|
|
|
|
//
|
|
|
|
// In addition to this sequence number, we map:
|
|
|
|
//
|
|
|
|
// addIndexNo => invoiceIndex
|
|
|
|
addIndexBucket = []byte("invoice-add-index")
|
|
|
|
|
|
|
|
// settleIndexBucket is an index bucket that we'll use to create a
|
|
|
|
// monotonically increasing integer for tracking a "settle index". Each
|
|
|
|
// time an invoice is settled, this sequence number will be incremented
|
|
|
|
// as populate within the newly settled invoice.
|
|
|
|
//
|
|
|
|
// In addition to this sequence number, we map:
|
|
|
|
//
|
|
|
|
// settleIndexNo => invoiceIndex
|
|
|
|
settleIndexBucket = []byte("invoice-settle-index")
|
2016-09-17 03:23:37 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// MaxMemoSize is maximum size of the memo field within invoices stored
|
|
|
|
// in the database.
|
|
|
|
MaxMemoSize = 1024
|
|
|
|
|
|
|
|
// MaxReceiptSize is the maximum size of the payment receipt stored
|
|
|
|
// within the database along side incoming/outgoing invoices.
|
|
|
|
MaxReceiptSize = 1024
|
2017-09-05 18:59:52 +03:00
|
|
|
|
2018-04-18 05:02:04 +03:00
|
|
|
// MaxPaymentRequestSize is the max size of a payment request for
|
2017-09-05 18:59:52 +03:00
|
|
|
// this invoice.
|
|
|
|
// TODO(halseth): determine the max length payment request when field
|
|
|
|
// lengths are final.
|
|
|
|
MaxPaymentRequestSize = 4096
|
2016-09-17 03:23:37 +03:00
|
|
|
)
|
|
|
|
|
2016-09-19 21:46:48 +03:00
|
|
|
// 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.
|
|
|
|
type ContractTerm struct {
|
|
|
|
// PaymentPreimage is the preimage which is to be revealed in the
|
|
|
|
// occasion that an HTLC paying to the hash of this preimage is
|
|
|
|
// extended.
|
|
|
|
PaymentPreimage [32]byte
|
|
|
|
|
2018-04-25 06:50:56 +03:00
|
|
|
// Value is the expected amount of milli-satoshis to be paid to an HTLC
|
|
|
|
// which can be satisfied by the above preimage.
|
2017-08-22 08:51:19 +03:00
|
|
|
Value lnwire.MilliSatoshi
|
2016-09-19 21:46:48 +03:00
|
|
|
|
|
|
|
// Settled indicates if this particular contract term has been fully
|
|
|
|
// settled by the payer.
|
|
|
|
Settled bool
|
|
|
|
}
|
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
// Invoice is a payment invoice generated by a payee in order to request
|
|
|
|
// payment for some good or service. The inclusion of invoices within Lightning
|
|
|
|
// creates a payment work flow for merchants very similar to that of the
|
|
|
|
// existing financial system within PayPal, etc. Invoices are added to the
|
|
|
|
// database when a payment is requested, then can be settled manually once the
|
|
|
|
// payment is received at the upper layer. For record keeping purposes,
|
|
|
|
// 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
|
2018-04-25 06:50:56 +03:00
|
|
|
// sha256 of the payment preimage.
|
2016-09-17 03:23:37 +03:00
|
|
|
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,
|
|
|
|
// or any other message which fits within the size constraints.
|
2016-09-24 01:15:22 +03:00
|
|
|
Memo []byte
|
2016-09-17 03:23:37 +03:00
|
|
|
|
|
|
|
// Receipt is an optional field dedicated for storing a
|
|
|
|
// cryptographically binding receipt of payment.
|
|
|
|
//
|
|
|
|
// TODO(roasbeef): document scheme.
|
2016-09-24 01:15:22 +03:00
|
|
|
Receipt []byte
|
2016-09-17 03:23:37 +03:00
|
|
|
|
2017-09-05 18:59:52 +03:00
|
|
|
// PaymentRequest is an optional field where a payment request created
|
|
|
|
// for this invoice can be stored.
|
|
|
|
PaymentRequest []byte
|
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
// CreationDate is the exact time the invoice was created.
|
|
|
|
CreationDate time.Time
|
|
|
|
|
2017-12-05 09:07:21 +03:00
|
|
|
// SettleDate is the exact time the invoice was settled.
|
|
|
|
SettleDate time.Time
|
|
|
|
|
2018-04-25 06:50:56 +03:00
|
|
|
// 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.
|
2016-09-17 03:23:37 +03:00
|
|
|
//
|
|
|
|
// TODO(roasbeef): later allow for multiple terms to fulfill the final
|
|
|
|
// invoice: payment fragmentation, etc.
|
|
|
|
Terms ContractTerm
|
2018-04-25 06:57:30 +03:00
|
|
|
|
|
|
|
// AddIndex is an auto-incrementing integer that acts as a
|
|
|
|
// monotonically increasing sequence number for all invoices created.
|
|
|
|
// Clients can then use this field as a "checkpoint" of sorts when
|
|
|
|
// implementing a streaming RPC to notify consumers of instances where
|
|
|
|
// an invoice has been added before they re-connected.
|
|
|
|
//
|
|
|
|
// NOTE: This index starts at 1.
|
|
|
|
AddIndex uint64
|
|
|
|
|
|
|
|
// SettleIndex is an auto-incrementing integer that acts as a
|
|
|
|
// monotonically increasing sequence number for all settled invoices.
|
|
|
|
// Clients can then use this field as a "checkpoint" of sorts when
|
|
|
|
// implementing a streaming RPC to notify consumers of instances where
|
|
|
|
// an invoice has been settled before they re-connected.
|
|
|
|
//
|
|
|
|
// NOTE: This index starts at 1.
|
|
|
|
SettleIndex uint64
|
|
|
|
|
2018-04-25 06:50:56 +03:00
|
|
|
// 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
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2016-12-05 14:59:36 +03:00
|
|
|
func validateInvoice(i *Invoice) error {
|
2016-09-24 01:15:22 +03:00
|
|
|
if len(i.Memo) > MaxMemoSize {
|
|
|
|
return fmt.Errorf("max length a memo is %v, and invoice "+
|
|
|
|
"of length %v was provided", MaxMemoSize, len(i.Memo))
|
|
|
|
}
|
|
|
|
if len(i.Receipt) > MaxReceiptSize {
|
|
|
|
return fmt.Errorf("max length a receipt is %v, and invoice "+
|
|
|
|
"of length %v was provided", MaxReceiptSize,
|
|
|
|
len(i.Receipt))
|
|
|
|
}
|
2017-09-05 18:59:52 +03:00
|
|
|
if len(i.PaymentRequest) > MaxPaymentRequestSize {
|
|
|
|
return fmt.Errorf("max length of payment request is %v, length "+
|
|
|
|
"provided was %v", MaxPaymentRequestSize,
|
|
|
|
len(i.PaymentRequest))
|
|
|
|
}
|
2016-12-05 14:59:36 +03:00
|
|
|
return nil
|
|
|
|
}
|
2016-09-24 01:15:22 +03:00
|
|
|
|
2016-12-05 14:59:36 +03:00
|
|
|
// AddInvoice inserts the targeted invoice into the database. If the invoice
|
|
|
|
// has *any* payment hashes which already exists within the database, then the
|
|
|
|
// insertion will be aborted and rejected due to the strict policy banning any
|
|
|
|
// duplicate payment hashes.
|
2018-06-30 04:05:51 +03:00
|
|
|
func (d *DB) AddInvoice(newInvoice *Invoice) (uint64, error) {
|
2018-04-25 06:57:30 +03:00
|
|
|
if err := validateInvoice(newInvoice); err != nil {
|
2018-06-30 04:05:51 +03:00
|
|
|
return 0, err
|
2016-12-05 14:59:36 +03:00
|
|
|
}
|
2018-04-25 06:57:30 +03:00
|
|
|
|
2018-06-30 04:05:51 +03:00
|
|
|
var invoiceAddIndex uint64
|
2018-04-25 06:57:30 +03:00
|
|
|
err := d.Update(func(tx *bolt.Tx) error {
|
2016-09-17 03:23:37 +03:00
|
|
|
invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-25 06:57:30 +03:00
|
|
|
invoiceIndex, err := invoices.CreateBucketIfNotExists(
|
|
|
|
invoiceIndexBucket,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
addIndex, err := invoices.CreateBucketIfNotExists(
|
|
|
|
addIndexBucket,
|
|
|
|
)
|
2016-09-17 03:23:37 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that an invoice an identical payment hash doesn't
|
|
|
|
// already exist within the index.
|
2018-04-25 06:57:30 +03:00
|
|
|
paymentHash := sha256.Sum256(
|
|
|
|
newInvoice.Terms.PaymentPreimage[:],
|
|
|
|
)
|
2016-09-17 03:23:37 +03:00
|
|
|
if invoiceIndex.Get(paymentHash[:]) != nil {
|
|
|
|
return ErrDuplicateInvoice
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the current running payment ID counter hasn't yet been
|
|
|
|
// created, then create it now.
|
|
|
|
var invoiceNum uint32
|
2016-09-19 21:46:48 +03:00
|
|
|
invoiceCounter := invoiceIndex.Get(numInvoicesKey)
|
2016-09-17 03:23:37 +03:00
|
|
|
if invoiceCounter == nil {
|
|
|
|
var scratch [4]byte
|
|
|
|
byteOrder.PutUint32(scratch[:], invoiceNum)
|
2018-04-25 06:57:30 +03:00
|
|
|
err := invoiceIndex.Put(numInvoicesKey, scratch[:])
|
|
|
|
if err != nil {
|
2016-09-17 03:23:37 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
invoiceNum = byteOrder.Uint32(invoiceCounter)
|
|
|
|
}
|
|
|
|
|
2018-06-30 04:05:51 +03:00
|
|
|
newIndex, err := putInvoice(
|
2018-04-25 06:57:30 +03:00
|
|
|
invoices, invoiceIndex, addIndex, newInvoice, invoiceNum,
|
|
|
|
)
|
2018-06-30 04:05:51 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
invoiceAddIndex = newIndex
|
|
|
|
return nil
|
2018-04-25 06:57:30 +03:00
|
|
|
})
|
|
|
|
if err != nil {
|
2018-06-30 04:05:51 +03:00
|
|
|
return 0, err
|
2018-04-25 06:57:30 +03:00
|
|
|
}
|
|
|
|
|
2018-06-30 04:05:51 +03:00
|
|
|
return invoiceAddIndex, err
|
2018-04-25 06:57:30 +03:00
|
|
|
}
|
2018-04-25 06:59:53 +03:00
|
|
|
|
|
|
|
// InvoicesAddedSince can be used by callers to seek into the event time series
|
|
|
|
// of all the invoices added in the database. The specified sinceAddIndex
|
|
|
|
// should be the highest add index that the caller knows of. This method will
|
|
|
|
// return all invoices with an add index greater than the specified
|
|
|
|
// sinceAddIndex.
|
|
|
|
//
|
|
|
|
// NOTE: The index starts from 1, as a result. We enforce that specifying a
|
|
|
|
// value below the starting index value is a noop.
|
|
|
|
func (d *DB) InvoicesAddedSince(sinceAddIndex uint64) ([]Invoice, error) {
|
|
|
|
var newInvoices []Invoice
|
|
|
|
|
|
|
|
// If an index of zero was specified, then in order to maintain
|
|
|
|
// backwards compat, we won't send out any new invoices.
|
|
|
|
if sinceAddIndex == 0 {
|
|
|
|
return newInvoices, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var startIndex [8]byte
|
|
|
|
byteOrder.PutUint64(startIndex[:], sinceAddIndex)
|
|
|
|
|
|
|
|
err := d.DB.View(func(tx *bolt.Tx) error {
|
|
|
|
invoices := tx.Bucket(invoiceBucket)
|
|
|
|
if invoices == nil {
|
|
|
|
return ErrNoInvoicesCreated
|
|
|
|
}
|
|
|
|
|
|
|
|
addIndex := invoices.Bucket(addIndexBucket)
|
|
|
|
if addIndex == nil {
|
|
|
|
return ErrNoInvoicesCreated
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll now run through each entry in the add index starting
|
|
|
|
// at our starting index. We'll continue until we reach the
|
|
|
|
// very end of the current key space.
|
|
|
|
invoiceCursor := addIndex.Cursor()
|
|
|
|
|
|
|
|
// We'll seek to the starting index, then manually advance the
|
|
|
|
// cursor in order to skip the entry with the since add index.
|
|
|
|
invoiceCursor.Seek(startIndex[:])
|
|
|
|
addSeqNo, invoiceKey := invoiceCursor.Next()
|
|
|
|
|
|
|
|
for ; addSeqNo != nil && bytes.Compare(addSeqNo, startIndex[:]) > 0; addSeqNo, invoiceKey = invoiceCursor.Next() {
|
|
|
|
|
|
|
|
// For each key found, we'll look up the actual
|
|
|
|
// invoice, then accumulate it into our return value.
|
|
|
|
invoice, err := fetchInvoice(invoiceKey, invoices)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
newInvoices = append(newInvoices, invoice)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2016-09-17 03:23:37 +03:00
|
|
|
})
|
2018-04-25 06:59:53 +03:00
|
|
|
switch {
|
|
|
|
// If no invoices have been created, then we'll return the empty set of
|
|
|
|
// invoices.
|
|
|
|
case err == ErrNoInvoicesCreated:
|
|
|
|
|
|
|
|
case err != nil:
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return newInvoices, nil
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2018-02-07 06:13:07 +03:00
|
|
|
// LookupInvoice attempts to look up an invoice according to its 32 byte
|
|
|
|
// payment hash. If an invoice which can settle the HTLC identified by the
|
2017-01-08 07:09:08 +03:00
|
|
|
// passed payment hash isn't found, then an error is returned. Otherwise, the
|
2016-09-17 03:23:37 +03:00
|
|
|
// full invoice is returned. Before setting the incoming HTLC, the values
|
|
|
|
// SHOULD be checked to ensure the payer meets the agreed upon contractual
|
|
|
|
// terms of the payment.
|
2018-04-25 06:57:30 +03:00
|
|
|
func (d *DB) LookupInvoice(paymentHash [32]byte) (Invoice, error) {
|
|
|
|
var invoice Invoice
|
2016-11-28 05:32:45 +03:00
|
|
|
err := d.View(func(tx *bolt.Tx) error {
|
2016-09-17 03:23:37 +03:00
|
|
|
invoices := tx.Bucket(invoiceBucket)
|
|
|
|
if invoices == nil {
|
2017-02-24 16:32:33 +03:00
|
|
|
return ErrNoInvoicesCreated
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
2016-09-19 21:46:48 +03:00
|
|
|
invoiceIndex := invoices.Bucket(invoiceIndexBucket)
|
2016-09-17 03:23:37 +03:00
|
|
|
if invoiceIndex == nil {
|
2017-02-24 16:32:33 +03:00
|
|
|
return ErrNoInvoicesCreated
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check the invoice index to see if an invoice paying to this
|
|
|
|
// hash exists within the DB.
|
|
|
|
invoiceNum := invoiceIndex.Get(paymentHash[:])
|
|
|
|
if invoiceNum == nil {
|
|
|
|
return ErrInvoiceNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// An invoice matching the payment hash has been found, so
|
|
|
|
// retrieve the record of the invoice itself.
|
|
|
|
i, err := fetchInvoice(invoiceNum, invoices)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
invoice = i
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return invoice, nil
|
|
|
|
}
|
|
|
|
|
2016-09-19 21:51:37 +03:00
|
|
|
// FetchAllInvoices returns all invoices currently stored within the database.
|
|
|
|
// If the pendingOnly param is true, then only unsettled invoices will be
|
|
|
|
// returned, skipping all invoices that are fully settled.
|
2018-04-25 06:57:30 +03:00
|
|
|
func (d *DB) FetchAllInvoices(pendingOnly bool) ([]Invoice, error) {
|
|
|
|
var invoices []Invoice
|
2016-09-19 21:51:37 +03:00
|
|
|
|
2016-11-28 05:32:45 +03:00
|
|
|
err := d.View(func(tx *bolt.Tx) error {
|
2016-09-19 21:51:37 +03:00
|
|
|
invoiceB := tx.Bucket(invoiceBucket)
|
|
|
|
if invoiceB == nil {
|
|
|
|
return ErrNoInvoicesCreated
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate through the entire key space of the top-level
|
|
|
|
// invoice bucket. If key with a non-nil value stores the next
|
|
|
|
// invoice ID which maps to the corresponding invoice.
|
|
|
|
return invoiceB.ForEach(func(k, v []byte) error {
|
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
invoiceReader := bytes.NewReader(v)
|
|
|
|
invoice, err := deserializeInvoice(invoiceReader)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if pendingOnly && invoice.Terms.Settled {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
invoices = append(invoices, invoice)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return invoices, nil
|
|
|
|
}
|
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
// SettleInvoice attempts to mark an invoice corresponding to the passed
|
|
|
|
// 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.
|
2018-06-29 07:43:18 +03:00
|
|
|
func (d *DB) SettleInvoice(paymentHash [32]byte,
|
|
|
|
amtPaid lnwire.MilliSatoshi) (*Invoice, error) {
|
2018-04-25 06:50:56 +03:00
|
|
|
|
2018-06-29 07:43:18 +03:00
|
|
|
var settledInvoice *Invoice
|
|
|
|
err := d.Update(func(tx *bolt.Tx) error {
|
2016-09-17 03:23:37 +03:00
|
|
|
invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-25 06:57:30 +03:00
|
|
|
invoiceIndex, err := invoices.CreateBucketIfNotExists(
|
|
|
|
invoiceIndexBucket,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
settleIndex, err := invoices.CreateBucketIfNotExists(
|
|
|
|
settleIndexBucket,
|
|
|
|
)
|
2016-09-17 03:23:37 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the invoice index to see if an invoice paying to this
|
|
|
|
// hash exists within the DB.
|
|
|
|
invoiceNum := invoiceIndex.Get(paymentHash[:])
|
|
|
|
if invoiceNum == nil {
|
|
|
|
return ErrInvoiceNotFound
|
|
|
|
}
|
|
|
|
|
2018-06-29 07:43:18 +03:00
|
|
|
invoice, err := settleInvoice(
|
2018-04-25 06:50:56 +03:00
|
|
|
invoices, settleIndex, invoiceNum, amtPaid,
|
|
|
|
)
|
2018-06-29 07:43:18 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
settledInvoice = invoice
|
|
|
|
return nil
|
2016-09-17 03:23:37 +03:00
|
|
|
})
|
2018-06-29 07:43:18 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return settledInvoice, nil
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2018-04-25 06:59:53 +03:00
|
|
|
// InvoicesSettledSince can be used by callers to catch up any settled invoices
|
|
|
|
// they missed within the settled invoice time series. We'll return all known
|
|
|
|
// settled invoice that have a settle index higher than the passed
|
|
|
|
// sinceSettleIndex.
|
|
|
|
//
|
|
|
|
// NOTE: The index starts from 1, as a result. We enforce that specifying a
|
|
|
|
// value below the starting index value is a noop.
|
|
|
|
func (d *DB) InvoicesSettledSince(sinceSettleIndex uint64) ([]Invoice, error) {
|
|
|
|
var settledInvoices []Invoice
|
|
|
|
|
|
|
|
// If an index of zero was specified, then in order to maintain
|
|
|
|
// backwards compat, we won't send out any new invoices.
|
|
|
|
if sinceSettleIndex == 0 {
|
|
|
|
return settledInvoices, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var startIndex [8]byte
|
|
|
|
byteOrder.PutUint64(startIndex[:], sinceSettleIndex)
|
|
|
|
|
|
|
|
err := d.DB.View(func(tx *bolt.Tx) error {
|
|
|
|
invoices := tx.Bucket(invoiceBucket)
|
|
|
|
if invoices == nil {
|
|
|
|
return ErrNoInvoicesCreated
|
|
|
|
}
|
|
|
|
|
|
|
|
settleIndex := invoices.Bucket(settleIndexBucket)
|
|
|
|
if settleIndex == nil {
|
|
|
|
return ErrNoInvoicesCreated
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll now run through each entry in the add index starting
|
|
|
|
// at our starting index. We'll continue until we reach the
|
|
|
|
// very end of the current key space.
|
|
|
|
invoiceCursor := settleIndex.Cursor()
|
|
|
|
|
|
|
|
// We'll seek to the starting index, then manually advance the
|
|
|
|
// cursor in order to skip the entry with the since add index.
|
|
|
|
invoiceCursor.Seek(startIndex[:])
|
|
|
|
seqNo, invoiceKey := invoiceCursor.Next()
|
|
|
|
|
|
|
|
for ; seqNo != nil && bytes.Compare(seqNo, startIndex[:]) > 0; seqNo, invoiceKey = invoiceCursor.Next() {
|
|
|
|
|
|
|
|
// For each key found, we'll look up the actual
|
|
|
|
// invoice, then accumulate it into our return value.
|
|
|
|
invoice, err := fetchInvoice(invoiceKey, invoices)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
settledInvoices = append(settledInvoices, invoice)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return settledInvoices, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func putInvoice(invoices, invoiceIndex, addIndex *bolt.Bucket,
|
2018-06-30 04:05:51 +03:00
|
|
|
i *Invoice, invoiceNum uint32) (uint64, error) {
|
2016-09-17 03:23:37 +03:00
|
|
|
|
|
|
|
// Create the invoice key which is just the big-endian representation
|
|
|
|
// of the invoice number.
|
|
|
|
var invoiceKey [4]byte
|
|
|
|
byteOrder.PutUint32(invoiceKey[:], invoiceNum)
|
|
|
|
|
|
|
|
// Increment the num invoice counter index so the next invoice bares
|
|
|
|
// the proper ID.
|
|
|
|
var scratch [4]byte
|
|
|
|
invoiceCounter := invoiceNum + 1
|
|
|
|
byteOrder.PutUint32(scratch[:], invoiceCounter)
|
2016-09-19 21:46:48 +03:00
|
|
|
if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil {
|
2018-06-30 04:05:51 +03:00
|
|
|
return 0, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2018-02-07 06:11:11 +03:00
|
|
|
// Add the payment hash to the invoice index. This will let us quickly
|
2016-09-17 03:23:37 +03:00
|
|
|
// identify if we can settle an incoming payment, and also to possibly
|
|
|
|
// allow a single invoice to have multiple payment installations.
|
2017-03-16 04:56:25 +03:00
|
|
|
paymentHash := sha256.Sum256(i.Terms.PaymentPreimage[:])
|
2018-04-25 06:57:30 +03:00
|
|
|
err := invoiceIndex.Put(paymentHash[:], invoiceKey[:])
|
|
|
|
if err != nil {
|
2018-06-30 04:05:51 +03:00
|
|
|
return 0, err
|
2018-04-25 06:57:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we'll obtain the next add invoice index (sequence
|
|
|
|
// number), so we can properly place this invoice within this
|
|
|
|
// event stream.
|
|
|
|
nextAddSeqNo, err := addIndex.NextSequence()
|
|
|
|
if err != nil {
|
2018-06-30 04:05:51 +03:00
|
|
|
return 0, err
|
2018-04-25 06:57:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// With the next sequence obtained, we'll updating the event series in
|
|
|
|
// the add index bucket to map this current add counter to the index of
|
|
|
|
// this new invoice.
|
|
|
|
var seqNoBytes [8]byte
|
|
|
|
byteOrder.PutUint64(seqNoBytes[:], nextAddSeqNo)
|
|
|
|
if err := addIndex.Put(seqNoBytes[:], invoiceKey[:]); err != nil {
|
2018-06-30 04:05:51 +03:00
|
|
|
return 0, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2018-04-25 06:57:30 +03:00
|
|
|
i.AddIndex = nextAddSeqNo
|
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
// Finally, serialize the invoice itself to be written to the disk.
|
|
|
|
var buf bytes.Buffer
|
|
|
|
if err := serializeInvoice(&buf, i); err != nil {
|
2018-06-30 04:05:51 +03:00
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := invoices.Put(invoiceKey[:], buf.Bytes()); err != nil {
|
|
|
|
return 0, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2018-06-30 04:05:51 +03:00
|
|
|
return nextAddSeqNo, nil
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func serializeInvoice(w io.Writer, i *Invoice) error {
|
2016-09-24 01:15:22 +03:00
|
|
|
if err := wire.WriteVarBytes(w, 0, i.Memo[:]); err != nil {
|
2016-09-17 03:23:37 +03:00
|
|
|
return err
|
|
|
|
}
|
2016-09-24 01:15:22 +03:00
|
|
|
if err := wire.WriteVarBytes(w, 0, i.Receipt[:]); err != nil {
|
2016-09-17 03:23:37 +03:00
|
|
|
return err
|
|
|
|
}
|
2017-09-05 18:59:52 +03:00
|
|
|
if err := wire.WriteVarBytes(w, 0, i.PaymentRequest[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-09-17 03:23:37 +03:00
|
|
|
|
|
|
|
birthBytes, err := i.CreationDate.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-05 09:07:21 +03:00
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
if err := wire.WriteVarBytes(w, 0, birthBytes); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-12-05 09:07:21 +03:00
|
|
|
settleBytes, err := i.SettleDate.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := wire.WriteVarBytes(w, 0, settleBytes); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
if _, err := w.Write(i.Terms.PaymentPreimage[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var scratch [8]byte
|
|
|
|
byteOrder.PutUint64(scratch[:], uint64(i.Terms.Value))
|
|
|
|
if _, err := w.Write(scratch[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-21 03:37:26 +03:00
|
|
|
if err := binary.Write(w, byteOrder, i.Terms.Settled); err != nil {
|
2016-09-17 03:23:37 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-25 06:57:30 +03:00
|
|
|
if err := binary.Write(w, byteOrder, i.AddIndex); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := binary.Write(w, byteOrder, i.SettleIndex); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-25 06:50:56 +03:00
|
|
|
if err := binary.Write(w, byteOrder, int64(i.AmtPaid)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-25 06:57:30 +03:00
|
|
|
|
2018-06-29 22:40:42 +03:00
|
|
|
return nil
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2018-04-25 06:57:30 +03:00
|
|
|
func fetchInvoice(invoiceNum []byte, invoices *bolt.Bucket) (Invoice, error) {
|
2016-09-17 03:23:37 +03:00
|
|
|
invoiceBytes := invoices.Get(invoiceNum)
|
|
|
|
if invoiceBytes == nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return Invoice{}, ErrInvoiceNotFound
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
invoiceReader := bytes.NewReader(invoiceBytes)
|
|
|
|
|
|
|
|
return deserializeInvoice(invoiceReader)
|
|
|
|
}
|
|
|
|
|
2018-04-25 06:57:30 +03:00
|
|
|
func deserializeInvoice(r io.Reader) (Invoice, error) {
|
2016-09-24 01:15:22 +03:00
|
|
|
var err error
|
2018-04-25 06:57:30 +03:00
|
|
|
invoice := Invoice{}
|
2016-09-17 03:23:37 +03:00
|
|
|
|
|
|
|
// TODO(roasbeef): use read full everywhere
|
2016-09-24 01:15:22 +03:00
|
|
|
invoice.Memo, err = wire.ReadVarBytes(r, 0, MaxMemoSize, "")
|
|
|
|
if err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
2016-09-24 01:15:22 +03:00
|
|
|
invoice.Receipt, err = wire.ReadVarBytes(r, 0, MaxReceiptSize, "")
|
|
|
|
if err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2017-09-05 18:59:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
invoice.PaymentRequest, err = wire.ReadVarBytes(r, 0, MaxPaymentRequestSize, "")
|
|
|
|
if err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
birthBytes, err := wire.ReadVarBytes(r, 0, 300, "birth")
|
|
|
|
if err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
if err := invoice.CreationDate.UnmarshalBinary(birthBytes); err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2017-12-05 09:07:21 +03:00
|
|
|
settledBytes, err := wire.ReadVarBytes(r, 0, 300, "settled")
|
|
|
|
if err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2017-12-05 09:07:21 +03:00
|
|
|
}
|
|
|
|
if err := invoice.SettleDate.UnmarshalBinary(settledBytes); err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2017-12-05 09:07:21 +03:00
|
|
|
}
|
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
if _, err := io.ReadFull(r, invoice.Terms.PaymentPreimage[:]); err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
var scratch [8]byte
|
|
|
|
if _, err := io.ReadFull(r, scratch[:]); err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
2017-08-22 08:51:19 +03:00
|
|
|
invoice.Terms.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
|
2016-09-17 03:23:37 +03:00
|
|
|
|
2017-09-21 03:37:26 +03:00
|
|
|
if err := binary.Read(r, byteOrder, &invoice.Terms.Settled); err != nil {
|
2018-04-25 06:57:30 +03:00
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := binary.Read(r, byteOrder, &invoice.AddIndex); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
if err := binary.Read(r, byteOrder, &invoice.SettleIndex); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
2018-04-25 06:50:56 +03:00
|
|
|
if err := binary.Read(r, byteOrder, &invoice.AmtPaid); err != nil {
|
|
|
|
return invoice, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return invoice, nil
|
|
|
|
}
|
|
|
|
|
2018-04-25 06:50:56 +03:00
|
|
|
func settleInvoice(invoices, settleIndex *bolt.Bucket, invoiceNum []byte,
|
2018-06-29 07:43:18 +03:00
|
|
|
amtPaid lnwire.MilliSatoshi) (*Invoice, error) {
|
2018-04-25 06:50:56 +03:00
|
|
|
|
2016-09-17 03:23:37 +03:00
|
|
|
invoice, err := fetchInvoice(invoiceNum, invoices)
|
|
|
|
if err != nil {
|
2018-06-29 07:43:18 +03:00
|
|
|
return nil, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2018-02-06 06:46:19 +03:00
|
|
|
// Add idempotency to duplicate settles, return here to avoid
|
|
|
|
// overwriting the previous info.
|
|
|
|
if invoice.Terms.Settled {
|
2018-07-18 02:28:08 +03:00
|
|
|
return &invoice, nil
|
2018-02-06 06:46:19 +03:00
|
|
|
}
|
|
|
|
|
2018-04-25 06:57:30 +03:00
|
|
|
// Now that we know the invoice hasn't already been settled, we'll
|
|
|
|
// update the settle index so we can place this settle event in the
|
|
|
|
// proper location within our time series.
|
|
|
|
nextSettleSeqNo, err := settleIndex.NextSequence()
|
|
|
|
if err != nil {
|
2018-06-29 07:43:18 +03:00
|
|
|
return nil, err
|
2018-04-25 06:57:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var seqNoBytes [8]byte
|
|
|
|
byteOrder.PutUint64(seqNoBytes[:], nextSettleSeqNo)
|
|
|
|
if err := settleIndex.Put(seqNoBytes[:], invoiceNum); err != nil {
|
2018-06-29 07:43:18 +03:00
|
|
|
return nil, err
|
2018-04-25 06:57:30 +03:00
|
|
|
}
|
|
|
|
|
2018-04-25 06:50:56 +03:00
|
|
|
invoice.AmtPaid = amtPaid
|
2016-09-17 03:23:37 +03:00
|
|
|
invoice.Terms.Settled = true
|
2017-12-05 09:07:21 +03:00
|
|
|
invoice.SettleDate = time.Now()
|
2018-04-25 06:57:30 +03:00
|
|
|
invoice.SettleIndex = nextSettleSeqNo
|
2016-09-17 03:23:37 +03:00
|
|
|
|
|
|
|
var buf bytes.Buffer
|
2018-04-25 06:57:30 +03:00
|
|
|
if err := serializeInvoice(&buf, &invoice); err != nil {
|
2018-06-29 07:43:18 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := invoices.Put(invoiceNum[:], buf.Bytes()); err != nil {
|
|
|
|
return nil, err
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|
|
|
|
|
2018-06-29 07:43:18 +03:00
|
|
|
return &invoice, nil
|
2016-09-17 03:23:37 +03:00
|
|
|
}
|