7569cca19b
To avoid code changing underneath the static migrations, we copy the lnwire dependency in its current form into the migration directory. Ideally the migrations doesn't depend on any code that might change, this takes us a step closer.
319 lines
9.5 KiB
Go
319 lines
9.5 KiB
Go
package migration12
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
"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
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// SerializeInvoice serializes an invoice to a writer.
|
|
//
|
|
// nolint: dupl
|
|
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
|
|
}
|