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 }