You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
9.5 KiB
318 lines
9.5 KiB
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 |
|
}
|
|
|