2019-11-22 11:23:47 +01:00
|
|
|
package migration12
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
|
|
"github.com/lightningnetwork/lnd/tlv"
|
|
|
|
)
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
// MaxPaymentRequestSize is the max size of a payment request for
|
|
|
|
// this invoice.
|
|
|
|
// TODO(halseth): determine the max length payment request when field
|
|
|
|
// lengths are final.
|
|
|
|
MaxPaymentRequestSize = 4096
|
|
|
|
|
|
|
|
memoType tlv.Type = 0
|
|
|
|
payReqType tlv.Type = 1
|
|
|
|
createTimeType tlv.Type = 2
|
|
|
|
settleTimeType tlv.Type = 3
|
|
|
|
addIndexType tlv.Type = 4
|
|
|
|
settleIndexType tlv.Type = 5
|
|
|
|
preimageType tlv.Type = 6
|
|
|
|
valueType tlv.Type = 7
|
|
|
|
cltvDeltaType tlv.Type = 8
|
|
|
|
expiryType tlv.Type = 9
|
|
|
|
paymentAddrType tlv.Type = 10
|
|
|
|
featuresType tlv.Type = 11
|
|
|
|
invStateType tlv.Type = 12
|
|
|
|
amtPaidType tlv.Type = 13
|
|
|
|
)
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
// Big endian is the preferred byte order, due to cursor scans over
|
|
|
|
// integer keys iterating in order.
|
|
|
|
byteOrder = binary.BigEndian
|
|
|
|
)
|
|
|
|
|
|
|
|
// ContractState describes the state the invoice is in.
|
|
|
|
type ContractState uint8
|
|
|
|
|
|
|
|
// 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 lntypes.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
|
|
|
|
|
|
|
|
// State describes the state the invoice is in.
|
|
|
|
State ContractState
|
2019-11-22 11:23:59 +01:00
|
|
|
|
|
|
|
// PaymentAddr is a randomly generated value include in the MPP record
|
|
|
|
// by the sender to prevent probing of the receiver.
|
|
|
|
PaymentAddr [32]byte
|
|
|
|
|
|
|
|
// Features is the feature vectors advertised on the payment request.
|
|
|
|
Features *lnwire.FeatureVector
|
2019-11-22 11:23:47 +01: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
|
|
|
|
// 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,
|
|
|
|
// or any other message which fits within the size constraints.
|
|
|
|
Memo []byte
|
|
|
|
|
|
|
|
// PaymentRequest is an optional field where a payment request created
|
|
|
|
// for this invoice can be stored.
|
|
|
|
PaymentRequest []byte
|
|
|
|
|
|
|
|
// FinalCltvDelta is the minimum required number of blocks before htlc
|
|
|
|
// expiry when the invoice is accepted.
|
|
|
|
FinalCltvDelta int32
|
|
|
|
|
|
|
|
// Expiry defines how long after creation this invoice should expire.
|
|
|
|
Expiry time.Duration
|
|
|
|
|
|
|
|
// CreationDate is the exact time the invoice was created.
|
|
|
|
CreationDate time.Time
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
// TODO(roasbeef): later allow for multiple terms to fulfill the final
|
|
|
|
// invoice: payment fragmentation, etc.
|
|
|
|
Terms ContractTerm
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// Htlcs records all htlcs that paid to this invoice. Some of these
|
|
|
|
// htlcs may have been marked as canceled.
|
|
|
|
Htlcs []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// LegacyDeserializeInvoice decodes an invoice from the passed io.Reader using
|
|
|
|
// the pre-TLV serialization.
|
|
|
|
func LegacyDeserializeInvoice(r io.Reader) (Invoice, error) {
|
|
|
|
var err error
|
|
|
|
invoice := Invoice{}
|
|
|
|
|
|
|
|
// TODO(roasbeef): use read full everywhere
|
|
|
|
invoice.Memo, err = wire.ReadVarBytes(r, 0, MaxMemoSize, "")
|
|
|
|
if err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
_, err = wire.ReadVarBytes(r, 0, maxReceiptSize, "")
|
|
|
|
if err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
|
|
|
|
invoice.PaymentRequest, err = wire.ReadVarBytes(r, 0, MaxPaymentRequestSize, "")
|
|
|
|
if err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := binary.Read(r, byteOrder, &invoice.FinalCltvDelta); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var expiry int64
|
|
|
|
if err := binary.Read(r, byteOrder, &expiry); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
invoice.Expiry = time.Duration(expiry)
|
|
|
|
|
|
|
|
birthBytes, err := wire.ReadVarBytes(r, 0, 300, "birth")
|
|
|
|
if err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
if err := invoice.CreationDate.UnmarshalBinary(birthBytes); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
|
|
|
|
settledBytes, err := wire.ReadVarBytes(r, 0, 300, "settled")
|
|
|
|
if err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
if err := invoice.SettleDate.UnmarshalBinary(settledBytes); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := io.ReadFull(r, invoice.Terms.PaymentPreimage[:]); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
var scratch [8]byte
|
|
|
|
if _, err := io.ReadFull(r, scratch[:]); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
invoice.Terms.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
|
|
|
|
|
|
|
|
if err := binary.Read(r, byteOrder, &invoice.Terms.State); err != nil {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
if err := binary.Read(r, byteOrder, &invoice.AmtPaid); err != nil {
|
|
|
|
return invoice, err
|
|
|
|
}
|
|
|
|
|
|
|
|
invoice.Htlcs, err = deserializeHtlcs(r)
|
|
|
|
if err != nil {
|
|
|
|
return Invoice{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return invoice, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// deserializeHtlcs reads a list of invoice htlcs from a reader and returns it
|
|
|
|
// as a flattened byte slice.
|
|
|
|
func deserializeHtlcs(r io.Reader) ([]byte, error) {
|
|
|
|
var b bytes.Buffer
|
|
|
|
_, err := io.Copy(&b, r)
|
|
|
|
return b.Bytes(), err
|
|
|
|
}
|
2019-11-22 11:23:59 +01:00
|
|
|
|
|
|
|
// SerializeInvoice serializes an invoice to a writer.
|
2019-11-22 11:24:28 +01:00
|
|
|
//
|
|
|
|
// nolint: dupl
|
2019-11-22 11:23:59 +01:00
|
|
|
func SerializeInvoice(w io.Writer, i *Invoice) error {
|
|
|
|
creationDateBytes, err := i.CreationDate.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
settleDateBytes, err := i.SettleDate.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var fb bytes.Buffer
|
|
|
|
err = i.Terms.Features.EncodeBase256(&fb)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
featureBytes := fb.Bytes()
|
|
|
|
|
|
|
|
preimage := [32]byte(i.Terms.PaymentPreimage)
|
|
|
|
value := uint64(i.Terms.Value)
|
|
|
|
cltvDelta := uint32(i.FinalCltvDelta)
|
|
|
|
expiry := uint64(i.Expiry)
|
|
|
|
|
|
|
|
amtPaid := uint64(i.AmtPaid)
|
|
|
|
state := uint8(i.Terms.State)
|
|
|
|
|
|
|
|
tlvStream, err := tlv.NewStream(
|
|
|
|
// Memo and payreq.
|
|
|
|
tlv.MakePrimitiveRecord(memoType, &i.Memo),
|
|
|
|
tlv.MakePrimitiveRecord(payReqType, &i.PaymentRequest),
|
|
|
|
|
|
|
|
// Add/settle metadata.
|
|
|
|
tlv.MakePrimitiveRecord(createTimeType, &creationDateBytes),
|
|
|
|
tlv.MakePrimitiveRecord(settleTimeType, &settleDateBytes),
|
|
|
|
tlv.MakePrimitiveRecord(addIndexType, &i.AddIndex),
|
|
|
|
tlv.MakePrimitiveRecord(settleIndexType, &i.SettleIndex),
|
|
|
|
|
|
|
|
// Terms.
|
|
|
|
tlv.MakePrimitiveRecord(preimageType, &preimage),
|
|
|
|
tlv.MakePrimitiveRecord(valueType, &value),
|
|
|
|
tlv.MakePrimitiveRecord(cltvDeltaType, &cltvDelta),
|
|
|
|
tlv.MakePrimitiveRecord(expiryType, &expiry),
|
|
|
|
tlv.MakePrimitiveRecord(paymentAddrType, &i.Terms.PaymentAddr),
|
|
|
|
tlv.MakePrimitiveRecord(featuresType, &featureBytes),
|
|
|
|
|
|
|
|
// Invoice state.
|
|
|
|
tlv.MakePrimitiveRecord(invStateType, &state),
|
|
|
|
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
if err = tlvStream.Encode(&b); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = binary.Write(w, byteOrder, uint64(b.Len()))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = w.Write(b.Bytes()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return serializeHtlcs(w, i.Htlcs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// serializeHtlcs writes a serialized list of invoice htlcs into a writer.
|
|
|
|
func serializeHtlcs(w io.Writer, htlcs []byte) error {
|
|
|
|
_, err := w.Write(htlcs)
|
|
|
|
return err
|
|
|
|
}
|