123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364 |
- package channeldb
- import (
- "bytes"
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "time"
- "github.com/lightningnetwork/lnd/channeldb/models"
- "github.com/lightningnetwork/lnd/htlcswitch/hop"
- invpkg "github.com/lightningnetwork/lnd/invoices"
- "github.com/lightningnetwork/lnd/kvdb"
- "github.com/lightningnetwork/lnd/lntypes"
- "github.com/lightningnetwork/lnd/lnwire"
- "github.com/lightningnetwork/lnd/record"
- "github.com/lightningnetwork/lnd/tlv"
- )
- 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
- // for looking up incoming HTLCs to determine if we're able to settle
- // them fully.
- //
- // maps: payHash => invoiceKey
- invoiceIndexBucket = []byte("paymenthashes")
- // payAddrIndexBucket is the name of the top-level bucket that maps
- // payment addresses to their invoice number. This can be used
- // to efficiently query or update non-legacy invoices. Note that legacy
- // invoices will not be included in this index since they all have the
- // same, all-zero payment address, however all newly generated invoices
- // will end up in this index.
- //
- // maps: payAddr => invoiceKey
- payAddrIndexBucket = []byte("pay-addr-index")
- // setIDIndexBucket is the name of the top-level bucket that maps set
- // ids to their invoice number. This can be used to efficiently query or
- // update AMP invoice. Note that legacy or MPP invoices will not be
- // included in this index, since their HTLCs do not have a set id.
- //
- // maps: setID => invoiceKey
- setIDIndexBucket = []byte("set-id-index")
- // 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")
- // 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 => invoiceKey
- 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 => invoiceKey
- settleIndexBucket = []byte("invoice-settle-index")
- )
- const (
- // A set of tlv type definitions used to serialize invoice htlcs to the
- // database.
- //
- // NOTE: A migration should be added whenever this list changes. This
- // prevents against the database being rolled back to an older
- // format where the surrounding logic might assume a different set of
- // fields are known.
- chanIDType tlv.Type = 1
- htlcIDType tlv.Type = 3
- amtType tlv.Type = 5
- acceptHeightType tlv.Type = 7
- acceptTimeType tlv.Type = 9
- resolveTimeType tlv.Type = 11
- expiryHeightType tlv.Type = 13
- htlcStateType tlv.Type = 15
- mppTotalAmtType tlv.Type = 17
- htlcAMPType tlv.Type = 19
- htlcHashType tlv.Type = 21
- htlcPreimageType tlv.Type = 23
- // A set of tlv type definitions used to serialize invoice bodiees.
- //
- // NOTE: A migration should be added whenever this list changes. This
- // prevents against the database being rolled back to an older
- // format where the surrounding logic might assume a different set of
- // fields are known.
- 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
- hodlInvoiceType tlv.Type = 14
- invoiceAmpStateType tlv.Type = 15
- // A set of tlv type definitions used to serialize the invoice AMP
- // state along-side the main invoice body.
- ampStateSetIDType tlv.Type = 0
- ampStateHtlcStateType tlv.Type = 1
- ampStateSettleIndexType tlv.Type = 2
- ampStateSettleDateType tlv.Type = 3
- ampStateCircuitKeysType tlv.Type = 4
- ampStateAmtPaidType tlv.Type = 5
- )
- // 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. A side effect of this function is that it sets
- // AddIndex on newInvoice.
- func (d *DB) AddInvoice(_ context.Context, newInvoice *invpkg.Invoice,
- paymentHash lntypes.Hash) (uint64, error) {
- if err := invpkg.ValidateInvoice(newInvoice, paymentHash); err != nil {
- return 0, err
- }
- var invoiceAddIndex uint64
- err := kvdb.Update(d, func(tx kvdb.RwTx) error {
- invoices, err := tx.CreateTopLevelBucket(invoiceBucket)
- if err != nil {
- return err
- }
- invoiceIndex, err := invoices.CreateBucketIfNotExists(
- invoiceIndexBucket,
- )
- if err != nil {
- return err
- }
- addIndex, err := invoices.CreateBucketIfNotExists(
- addIndexBucket,
- )
- if err != nil {
- return err
- }
- // Ensure that an invoice an identical payment hash doesn't
- // already exist within the index.
- if invoiceIndex.Get(paymentHash[:]) != nil {
- return invpkg.ErrDuplicateInvoice
- }
- // Check that we aren't inserting an invoice with a duplicate
- // payment address. The all-zeros payment address is
- // special-cased to support legacy keysend invoices which don't
- // assign one. This is safe since later we also will avoid
- // indexing them and avoid collisions.
- payAddrIndex := tx.ReadWriteBucket(payAddrIndexBucket)
- if newInvoice.Terms.PaymentAddr != invpkg.BlankPayAddr {
- paymentAddr := newInvoice.Terms.PaymentAddr[:]
- if payAddrIndex.Get(paymentAddr) != nil {
- return invpkg.ErrDuplicatePayAddr
- }
- }
- // If the current running payment ID counter hasn't yet been
- // created, then create it now.
- var invoiceNum uint32
- invoiceCounter := invoiceIndex.Get(numInvoicesKey)
- if invoiceCounter == nil {
- var scratch [4]byte
- byteOrder.PutUint32(scratch[:], invoiceNum)
- err := invoiceIndex.Put(numInvoicesKey, scratch[:])
- if err != nil {
- return err
- }
- } else {
- invoiceNum = byteOrder.Uint32(invoiceCounter)
- }
- newIndex, err := putInvoice(
- invoices, invoiceIndex, payAddrIndex, addIndex,
- newInvoice, invoiceNum, paymentHash,
- )
- if err != nil {
- return err
- }
- invoiceAddIndex = newIndex
- return nil
- }, func() {
- invoiceAddIndex = 0
- })
- if err != nil {
- return 0, err
- }
- return invoiceAddIndex, err
- }
- // 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(_ context.Context, sinceAddIndex uint64) (
- []invpkg.Invoice, error) {
- var newInvoices []invpkg.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 := kvdb.View(d, func(tx kvdb.RTx) error {
- invoices := tx.ReadBucket(invoiceBucket)
- if invoices == nil {
- return nil
- }
- addIndex := invoices.NestedReadBucket(addIndexBucket)
- if addIndex == nil {
- return nil
- }
- // 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.ReadCursor()
- // 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
- }, func() {
- newInvoices = nil
- })
- if err != nil {
- return nil, err
- }
- return newInvoices, nil
- }
- // 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
- // passed payment hash isn't found, then an error is returned. Otherwise, the
- // 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.
- func (d *DB) LookupInvoice(_ context.Context, ref invpkg.InvoiceRef) (
- invpkg.Invoice, error) {
- var invoice invpkg.Invoice
- err := kvdb.View(d, func(tx kvdb.RTx) error {
- invoices := tx.ReadBucket(invoiceBucket)
- if invoices == nil {
- return invpkg.ErrNoInvoicesCreated
- }
- invoiceIndex := invoices.NestedReadBucket(invoiceIndexBucket)
- if invoiceIndex == nil {
- return invpkg.ErrNoInvoicesCreated
- }
- payAddrIndex := tx.ReadBucket(payAddrIndexBucket)
- setIDIndex := tx.ReadBucket(setIDIndexBucket)
- // Retrieve the invoice number for this invoice using
- // the provided invoice reference.
- invoiceNum, err := fetchInvoiceNumByRef(
- invoiceIndex, payAddrIndex, setIDIndex, ref,
- )
- if err != nil {
- return err
- }
- var setID *invpkg.SetID
- switch {
- // If this is a payment address ref, and the blank modified was
- // specified, then we'll use the zero set ID to indicate that
- // we won't want any HTLCs returned.
- case ref.PayAddr() != nil &&
- ref.Modifier() == invpkg.HtlcSetBlankModifier:
- var zeroSetID invpkg.SetID
- setID = &zeroSetID
- // If this is a set ID ref, and the htlc set only modified was
- // specified, then we'll pass through the specified setID so
- // only that will be returned.
- case ref.SetID() != nil &&
- ref.Modifier() == invpkg.HtlcSetOnlyModifier:
- setID = (*invpkg.SetID)(ref.SetID())
- }
- // An invoice was found, retrieve the remainder of the invoice
- // body.
- i, err := fetchInvoice(invoiceNum, invoices, setID)
- if err != nil {
- return err
- }
- invoice = i
- return nil
- }, func() {})
- if err != nil {
- return invoice, err
- }
- return invoice, nil
- }
- // fetchInvoiceNumByRef retrieve the invoice number for the provided invoice
- // reference. The payment address will be treated as the primary key, falling
- // back to the payment hash if nothing is found for the payment address. An
- // error is returned if the invoice is not found.
- func fetchInvoiceNumByRef(invoiceIndex, payAddrIndex, setIDIndex kvdb.RBucket,
- ref invpkg.InvoiceRef) ([]byte, error) {
- // If the set id is present, we only consult the set id index for this
- // invoice. This type of query is only used to facilitate user-facing
- // requests to lookup, settle or cancel an AMP invoice.
- setID := ref.SetID()
- if setID != nil {
- invoiceNumBySetID := setIDIndex.Get(setID[:])
- if invoiceNumBySetID == nil {
- return nil, invpkg.ErrInvoiceNotFound
- }
- return invoiceNumBySetID, nil
- }
- payHash := ref.PayHash()
- payAddr := ref.PayAddr()
- getInvoiceNumByHash := func() []byte {
- if payHash != nil {
- return invoiceIndex.Get(payHash[:])
- }
- return nil
- }
- getInvoiceNumByAddr := func() []byte {
- if payAddr != nil {
- // Only allow lookups for payment address if it is not a
- // blank payment address, which is a special-cased value
- // for legacy keysend invoices.
- if *payAddr != invpkg.BlankPayAddr {
- return payAddrIndex.Get(payAddr[:])
- }
- }
- return nil
- }
- invoiceNumByHash := getInvoiceNumByHash()
- invoiceNumByAddr := getInvoiceNumByAddr()
- switch {
- // If payment address and payment hash both reference an existing
- // invoice, ensure they reference the _same_ invoice.
- case invoiceNumByAddr != nil && invoiceNumByHash != nil:
- if !bytes.Equal(invoiceNumByAddr, invoiceNumByHash) {
- return nil, invpkg.ErrInvRefEquivocation
- }
- return invoiceNumByAddr, nil
- // Return invoices by payment addr only.
- //
- // NOTE: We constrain this lookup to only apply if the invoice ref does
- // not contain a payment hash. Legacy and MPP payments depend on the
- // payment hash index to enforce that the HTLCs payment hash matches the
- // payment hash for the invoice, without this check we would
- // inadvertently assume the invoice contains the correct preimage for
- // the HTLC, which we only enforce via the lookup by the invoice index.
- case invoiceNumByAddr != nil && payHash == nil:
- return invoiceNumByAddr, nil
- // If we were only able to reference the invoice by hash, return the
- // corresponding invoice number. This can happen when no payment address
- // was provided, or if it didn't match anything in our records.
- case invoiceNumByHash != nil:
- return invoiceNumByHash, nil
- // Otherwise we don't know of the target invoice.
- default:
- return nil, invpkg.ErrInvoiceNotFound
- }
- }
- // FetchPendingInvoices returns all invoices that have not yet been settled or
- // canceled. The returned map is keyed by the payment hash of each respective
- // invoice.
- func (d *DB) FetchPendingInvoices(_ context.Context) (
- map[lntypes.Hash]invpkg.Invoice, error) {
- result := make(map[lntypes.Hash]invpkg.Invoice)
- err := kvdb.View(d, func(tx kvdb.RTx) error {
- invoices := tx.ReadBucket(invoiceBucket)
- if invoices == nil {
- return nil
- }
- invoiceIndex := invoices.NestedReadBucket(invoiceIndexBucket)
- if invoiceIndex == nil {
- // Mask the error if there's no invoice
- // index as that simply means there are no
- // invoices added yet to the DB. In this case
- // we simply return an empty list.
- return nil
- }
- return invoiceIndex.ForEach(func(k, v []byte) error {
- // Skip the special numInvoicesKey as that does not
- // point to a valid invoice.
- if bytes.Equal(k, numInvoicesKey) {
- return nil
- }
- // Skip sub-buckets.
- if v == nil {
- return nil
- }
- invoice, err := fetchInvoice(v, invoices)
- if err != nil {
- return err
- }
- if invoice.IsPending() {
- var paymentHash lntypes.Hash
- copy(paymentHash[:], k)
- result[paymentHash] = invoice
- }
- return nil
- })
- }, func() {
- result = make(map[lntypes.Hash]invpkg.Invoice)
- })
- if err != nil {
- return nil, err
- }
- return result, nil
- }
- // QueryInvoices allows a caller to query the invoice database for invoices
- // within the specified add index range.
- func (d *DB) QueryInvoices(_ context.Context, q invpkg.InvoiceQuery) (
- invpkg.InvoiceSlice, error) {
- var resp invpkg.InvoiceSlice
- err := kvdb.View(d, func(tx kvdb.RTx) error {
- // If the bucket wasn't found, then there aren't any invoices
- // within the database yet, so we can simply exit.
- invoices := tx.ReadBucket(invoiceBucket)
- if invoices == nil {
- return invpkg.ErrNoInvoicesCreated
- }
- // Get the add index bucket which we will use to iterate through
- // our indexed invoices.
- invoiceAddIndex := invoices.NestedReadBucket(addIndexBucket)
- if invoiceAddIndex == nil {
- return invpkg.ErrNoInvoicesCreated
- }
- // Create a paginator which reads from our add index bucket with
- // the parameters provided by the invoice query.
- paginator := newPaginator(
- invoiceAddIndex.ReadCursor(), q.Reversed, q.IndexOffset,
- q.NumMaxInvoices,
- )
- // accumulateInvoices looks up an invoice based on the index we
- // are given, adds it to our set of invoices if it has the right
- // characteristics for our query and returns the number of items
- // we have added to our set of invoices.
- accumulateInvoices := func(_, indexValue []byte) (bool, error) {
- invoice, err := fetchInvoice(indexValue, invoices)
- if err != nil {
- return false, err
- }
- // Skip any settled or canceled invoices if the caller
- // is only interested in pending ones.
- if q.PendingOnly && !invoice.IsPending() {
- return false, nil
- }
- // Get the creation time in Unix seconds, this always
- // rounds down the nanoseconds to full seconds.
- createTime := invoice.CreationDate.Unix()
- // Skip any invoices that were created before the
- // specified time.
- if createTime < q.CreationDateStart {
- return false, nil
- }
- // Skip any invoices that were created after the
- // specified time.
- if q.CreationDateEnd != 0 &&
- createTime > q.CreationDateEnd {
- return false, nil
- }
- // At this point, we've exhausted the offset, so we'll
- // begin collecting invoices found within the range.
- resp.Invoices = append(resp.Invoices, invoice)
- return true, nil
- }
- // Query our paginator using accumulateInvoices to build up a
- // set of invoices.
- if err := paginator.query(accumulateInvoices); err != nil {
- return err
- }
- // If we iterated through the add index in reverse order, then
- // we'll need to reverse the slice of invoices to return them in
- // forward order.
- if q.Reversed {
- numInvoices := len(resp.Invoices)
- for i := 0; i < numInvoices/2; i++ {
- reverse := numInvoices - i - 1
- resp.Invoices[i], resp.Invoices[reverse] =
- resp.Invoices[reverse], resp.Invoices[i]
- }
- }
- return nil
- }, func() {
- resp = invpkg.InvoiceSlice{
- InvoiceQuery: q,
- }
- })
- if err != nil && !errors.Is(err, invpkg.ErrNoInvoicesCreated) {
- return resp, err
- }
- // Finally, record the indexes of the first and last invoices returned
- // so that the caller can resume from this point later on.
- if len(resp.Invoices) > 0 {
- resp.FirstIndexOffset = resp.Invoices[0].AddIndex
- lastIdx := len(resp.Invoices) - 1
- resp.LastIndexOffset = resp.Invoices[lastIdx].AddIndex
- }
- return resp, nil
- }
- // UpdateInvoice attempts to update an invoice corresponding to the passed
- // payment hash. If an invoice matching the passed payment hash doesn't exist
- // within the database, then the action will fail with a "not found" error.
- //
- // The update is performed inside the same database transaction that fetches the
- // invoice and is therefore atomic. The fields to update are controlled by the
- // supplied callback. When updating an invoice, the update itself happens
- // in-memory on a copy of the invoice. Once it is written successfully to the
- // database, the in-memory copy is returned to the caller.
- func (d *DB) UpdateInvoice(_ context.Context, ref invpkg.InvoiceRef,
- setIDHint *invpkg.SetID, callback invpkg.InvoiceUpdateCallback) (
- *invpkg.Invoice, error) {
- var updatedInvoice *invpkg.Invoice
- err := kvdb.Update(d, func(tx kvdb.RwTx) error {
- invoices, err := tx.CreateTopLevelBucket(invoiceBucket)
- if err != nil {
- return err
- }
- invoiceIndex, err := invoices.CreateBucketIfNotExists(
- invoiceIndexBucket,
- )
- if err != nil {
- return err
- }
- settleIndex, err := invoices.CreateBucketIfNotExists(
- settleIndexBucket,
- )
- if err != nil {
- return err
- }
- payAddrIndex := tx.ReadBucket(payAddrIndexBucket)
- setIDIndex := tx.ReadWriteBucket(setIDIndexBucket)
- // Retrieve the invoice number for this invoice using the
- // provided invoice reference.
- invoiceNum, err := fetchInvoiceNumByRef(
- invoiceIndex, payAddrIndex, setIDIndex, ref,
- )
- if err != nil {
- return err
- }
- // If the set ID hint is non-nil, then we'll use that to filter
- // out the HTLCs for AMP invoice so we don't need to read them
- // all out to satisfy the invoice callback below. If it's nil,
- // then we pass in the zero set ID which means no HTLCs will be
- // read out.
- var invSetID invpkg.SetID
- if setIDHint != nil {
- invSetID = *setIDHint
- }
- invoice, err := fetchInvoice(invoiceNum, invoices, &invSetID)
- if err != nil {
- return err
- }
- now := d.clock.Now()
- updater := &kvInvoiceUpdater{
- db: d,
- invoicesBucket: invoices,
- settleIndexBucket: settleIndex,
- setIDIndexBucket: setIDIndex,
- updateTime: now,
- invoiceNum: invoiceNum,
- invoice: &invoice,
- updatedAmpHtlcs: make(ampHTLCsMap),
- settledSetIDs: make(map[invpkg.SetID]struct{}),
- }
- payHash := ref.PayHash()
- updatedInvoice, err = invpkg.UpdateInvoice(
- payHash, updater.invoice, now, callback, updater,
- )
- return err
- }, func() {
- updatedInvoice = nil
- })
- return updatedInvoice, err
- }
- // ampHTLCsMap is a map of AMP HTLCs affected by an invoice update.
- type ampHTLCsMap map[invpkg.SetID]map[models.CircuitKey]*invpkg.InvoiceHTLC
- // kvInvoiceUpdater is an implementation of the InvoiceUpdater interface that
- // is used with the kv implementation of the invoice database. Note that this
- // updater is not concurrency safe and synchronizaton is expected to be handled
- // on the DB level.
- type kvInvoiceUpdater struct {
- db *DB
- invoicesBucket kvdb.RwBucket
- settleIndexBucket kvdb.RwBucket
- setIDIndexBucket kvdb.RwBucket
- // updateTime is the timestamp for the update.
- updateTime time.Time
- // invoiceNum is a legacy key similar to the add index that is used
- // only in the kv implementation.
- invoiceNum []byte
- // invoice is the invoice that we're updating. As a side effect of the
- // update this invoice will be mutated.
- invoice *invpkg.Invoice
- // updatedAmpHtlcs holds the set of AMP HTLCs that were added or
- // cancelled as part of this update.
- updatedAmpHtlcs ampHTLCsMap
- // settledSetIDs holds the set IDs that are settled with this update.
- settledSetIDs map[invpkg.SetID]struct{}
- }
- // NOTE: this method does nothing in the k/v implementation of InvoiceUpdater.
- func (k *kvInvoiceUpdater) AddHtlc(_ models.CircuitKey,
- _ *invpkg.InvoiceHTLC) error {
- return nil
- }
- // NOTE: this method does nothing in the k/v implementation of InvoiceUpdater.
- func (k *kvInvoiceUpdater) ResolveHtlc(_ models.CircuitKey, _ invpkg.HtlcState,
- _ time.Time) error {
- return nil
- }
- // NOTE: this method does nothing in the k/v implementation of InvoiceUpdater.
- func (k *kvInvoiceUpdater) AddAmpHtlcPreimage(_ [32]byte, _ models.CircuitKey,
- _ lntypes.Preimage) error {
- return nil
- }
- // NOTE: this method does nothing in the k/v implementation of InvoiceUpdater.
- func (k *kvInvoiceUpdater) UpdateInvoiceState(_ invpkg.ContractState,
- _ *lntypes.Preimage) error {
- return nil
- }
- // NOTE: this method does nothing in the k/v implementation of InvoiceUpdater.
- func (k *kvInvoiceUpdater) UpdateInvoiceAmtPaid(_ lnwire.MilliSatoshi) error {
- return nil
- }
- // UpdateAmpState updates the state of the AMP invoice identified by the setID.
- func (k *kvInvoiceUpdater) UpdateAmpState(setID [32]byte,
- state invpkg.InvoiceStateAMP, circuitKey models.CircuitKey) error {
- if _, ok := k.updatedAmpHtlcs[setID]; !ok {
- switch state.State {
- case invpkg.HtlcStateAccepted:
- // If we're just now creating the HTLCs for this set
- // then we'll also pull in the existing HTLCs that are
- // part of this set, so we can write them all to disk
- // together (same value)
- k.updatedAmpHtlcs[setID] = k.invoice.HTLCSet(
- &setID, invpkg.HtlcStateAccepted,
- )
- case invpkg.HtlcStateCanceled:
- // Only HTLCs in the accepted state, can be cancelled,
- // but we also want to merge that with HTLCs that may be
- // canceled as well since it can be cancelled one by
- // one.
- k.updatedAmpHtlcs[setID] = k.invoice.HTLCSet(
- &setID, invpkg.HtlcStateAccepted,
- )
- cancelledHtlcs := k.invoice.HTLCSet(
- &setID, invpkg.HtlcStateCanceled,
- )
- for htlcKey, htlc := range cancelledHtlcs {
- k.updatedAmpHtlcs[setID][htlcKey] = htlc
- }
- case invpkg.HtlcStateSettled:
- k.updatedAmpHtlcs[setID] = make(
- map[models.CircuitKey]*invpkg.InvoiceHTLC,
- )
- }
- }
- if state.State == invpkg.HtlcStateSettled {
- // Add the set ID to the set that was settled in this invoice
- // update. We'll use this later to update the settle index.
- k.settledSetIDs[setID] = struct{}{}
- }
- k.updatedAmpHtlcs[setID][circuitKey] = k.invoice.Htlcs[circuitKey]
- return nil
- }
- // Finalize finalizes the update before it is written to the database.
- func (k *kvInvoiceUpdater) Finalize(updateType invpkg.UpdateType) error {
- switch updateType {
- case invpkg.AddHTLCsUpdate:
- return k.storeAddHtlcsUpdate()
- case invpkg.CancelHTLCsUpdate:
- return k.storeCancelHtlcsUpdate()
- case invpkg.SettleHodlInvoiceUpdate:
- return k.storeSettleHodlInvoiceUpdate()
- case invpkg.CancelInvoiceUpdate:
- return k.serializeAndStoreInvoice()
- }
- return fmt.Errorf("unknown update type: %v", updateType)
- }
- // storeCancelHtlcsUpdate updates the invoice in the database after cancelling a
- // set of HTLCs.
- func (k *kvInvoiceUpdater) storeCancelHtlcsUpdate() error {
- err := k.serializeAndStoreInvoice()
- if err != nil {
- return err
- }
- // If this is an AMP invoice, then we'll actually store the rest
- // of the HTLCs in-line with the invoice, using the invoice ID
- // as a prefix, and the AMP key as a suffix: invoiceNum ||
- // setID.
- if k.invoice.IsAMP() {
- return k.updateAMPInvoices()
- }
- return nil
- }
- // storeAddHtlcsUpdate updates the invoice in the database after adding a set of
- // HTLCs.
- func (k *kvInvoiceUpdater) storeAddHtlcsUpdate() error {
- invoiceIsAMP := k.invoice.IsAMP()
- for htlcSetID := range k.updatedAmpHtlcs {
- // Check if this SetID already exist.
- setIDInvNum := k.setIDIndexBucket.Get(htlcSetID[:])
- if setIDInvNum == nil {
- err := k.setIDIndexBucket.Put(
- htlcSetID[:], k.invoiceNum,
- )
- if err != nil {
- return err
- }
- } else if !bytes.Equal(setIDInvNum, k.invoiceNum) {
- return invpkg.ErrDuplicateSetID{
- SetID: htlcSetID,
- }
- }
- }
- // If this is a non-AMP invoice, then the state can eventually go to
- // ContractSettled, so we pass in nil value as part of
- // setSettleMetaFields.
- if !invoiceIsAMP && k.invoice.State == invpkg.ContractSettled {
- err := k.setSettleMetaFields(nil)
- if err != nil {
- return err
- }
- }
- // As we don't update the settle index above for AMP invoices, we'll do
- // it here for each sub-AMP invoice that was settled.
- for settledSetID := range k.settledSetIDs {
- settledSetID := settledSetID
- err := k.setSettleMetaFields(&settledSetID)
- if err != nil {
- return err
- }
- }
- err := k.serializeAndStoreInvoice()
- if err != nil {
- return err
- }
- // If this is an AMP invoice, then we'll actually store the rest of the
- // HTLCs in-line with the invoice, using the invoice ID as a prefix,
- // and the AMP key as a suffix: invoiceNum || setID.
- if invoiceIsAMP {
- return k.updateAMPInvoices()
- }
- return nil
- }
- // storeSettleHodlInvoiceUpdate updates the invoice in the database after
- // settling a hodl invoice.
- func (k *kvInvoiceUpdater) storeSettleHodlInvoiceUpdate() error {
- err := k.setSettleMetaFields(nil)
- if err != nil {
- return err
- }
- return k.serializeAndStoreInvoice()
- }
- // setSettleMetaFields updates the metadata associated with settlement of an
- // invoice. If a non-nil setID is passed in, then the value will be append to
- // the invoice number as well, in order to allow us to detect repeated payments
- // to the same AMP invoices "across time".
- func (k *kvInvoiceUpdater) setSettleMetaFields(setID *invpkg.SetID) error {
- // 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 := k.settleIndexBucket.NextSequence()
- if err != nil {
- return err
- }
- // Make a new byte array on the stack that can potentially store the 4
- // byte invoice number along w/ the 32 byte set ID. We capture valueLen
- // here which is the number of bytes copied so we can only store the 4
- // bytes if this is a non-AMP invoice.
- var indexKey [invoiceSetIDKeyLen]byte
- valueLen := copy(indexKey[:], k.invoiceNum)
- if setID != nil {
- valueLen += copy(indexKey[valueLen:], setID[:])
- }
- var seqNoBytes [8]byte
- byteOrder.PutUint64(seqNoBytes[:], nextSettleSeqNo)
- err = k.settleIndexBucket.Put(seqNoBytes[:], indexKey[:valueLen])
- if err != nil {
- return err
- }
- // If the setID is nil, then this means that this is a non-AMP settle,
- // so we'll update the invoice settle index directly.
- if setID == nil {
- k.invoice.SettleDate = k.updateTime
- k.invoice.SettleIndex = nextSettleSeqNo
- } else {
- // If the set ID isn't blank, we'll update the AMP state map
- // which tracks when each of the setIDs associated with a given
- // AMP invoice are settled.
- ampState := k.invoice.AMPState[*setID]
- ampState.SettleDate = k.updateTime
- ampState.SettleIndex = nextSettleSeqNo
- k.invoice.AMPState[*setID] = ampState
- }
- return nil
- }
- // updateAMPInvoices updates the set of AMP invoices in-place. For AMP, rather
- // then continually write the invoices to the end of the invoice value, we
- // instead write the invoices into a new key preifx that follows the main
- // invoice number. This ensures that we don't need to continually decode a
- // potentially massive HTLC set, and also allows us to quickly find the HLTCs
- // associated with a particular HTLC set.
- func (k *kvInvoiceUpdater) updateAMPInvoices() error {
- for setID, htlcSet := range k.updatedAmpHtlcs {
- // First write out the set of HTLCs including all the relevant
- // TLV values.
- var b bytes.Buffer
- if err := serializeHtlcs(&b, htlcSet); err != nil {
- return err
- }
- // Next store each HTLC in-line, using a prefix based off the
- // invoice number.
- invoiceSetIDKey := makeInvoiceSetIDKey(k.invoiceNum, setID[:])
- err := k.invoicesBucket.Put(invoiceSetIDKey[:], b.Bytes())
- if err != nil {
- return err
- }
- }
- return nil
- }
- // serializeAndStoreInvoice is a helper function used to store invoices.
- func (k *kvInvoiceUpdater) serializeAndStoreInvoice() error {
- var buf bytes.Buffer
- if err := serializeInvoice(&buf, k.invoice); err != nil {
- return err
- }
- return k.invoicesBucket.Put(k.invoiceNum, buf.Bytes())
- }
- // 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(_ context.Context, sinceSettleIndex uint64) (
- []invpkg.Invoice, error) {
- var settledInvoices []invpkg.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 := kvdb.View(d, func(tx kvdb.RTx) error {
- invoices := tx.ReadBucket(invoiceBucket)
- if invoices == nil {
- return nil
- }
- settleIndex := invoices.NestedReadBucket(settleIndexBucket)
- if settleIndex == nil {
- return nil
- }
- // 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.ReadCursor()
- // 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, indexValue := invoiceCursor.Next()
- for ; seqNo != nil && bytes.Compare(seqNo, startIndex[:]) > 0; seqNo, indexValue = invoiceCursor.Next() {
- // Depending on the length of the index value, this may
- // or may not be an AMP invoice, so we'll extract the
- // invoice value into two components: the invoice num,
- // and the setID (may not be there).
- var (
- invoiceKey [4]byte
- setID *invpkg.SetID
- )
- valueLen := copy(invoiceKey[:], indexValue)
- if len(indexValue) == invoiceSetIDKeyLen {
- setID = new(invpkg.SetID)
- copy(setID[:], indexValue[valueLen:])
- }
- // For each key found, we'll look up the actual
- // invoice, then accumulate it into our return value.
- invoice, err := fetchInvoice(
- invoiceKey[:], invoices, setID,
- )
- if err != nil {
- return err
- }
- settledInvoices = append(settledInvoices, invoice)
- }
- return nil
- }, func() {
- settledInvoices = nil
- })
- if err != nil {
- return nil, err
- }
- return settledInvoices, nil
- }
- func putInvoice(invoices, invoiceIndex, payAddrIndex, addIndex kvdb.RwBucket,
- i *invpkg.Invoice, invoiceNum uint32, paymentHash lntypes.Hash) (
- uint64, error) {
- // 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)
- if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil {
- return 0, err
- }
- // Add the payment hash to the invoice index. This will let us quickly
- // identify if we can settle an incoming payment, and also to possibly
- // allow a single invoice to have multiple payment installations.
- err := invoiceIndex.Put(paymentHash[:], invoiceKey[:])
- if err != nil {
- return 0, err
- }
- // Add the invoice to the payment address index, but only if the invoice
- // has a non-zero payment address. The all-zero payment address is still
- // in use by legacy keysend, so we special-case here to avoid
- // collisions.
- if i.Terms.PaymentAddr != invpkg.BlankPayAddr {
- err = payAddrIndex.Put(i.Terms.PaymentAddr[:], invoiceKey[:])
- if err != nil {
- return 0, err
- }
- }
- // 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 {
- return 0, err
- }
- // 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 {
- return 0, err
- }
- i.AddIndex = nextAddSeqNo
- // Finally, serialize the invoice itself to be written to the disk.
- var buf bytes.Buffer
- if err := serializeInvoice(&buf, i); err != nil {
- return 0, err
- }
- if err := invoices.Put(invoiceKey[:], buf.Bytes()); err != nil {
- return 0, err
- }
- return nextAddSeqNo, nil
- }
- // recordSize returns the amount of bytes this TLV record will occupy when
- // encoded.
- func ampRecordSize(a *invpkg.AMPInvoiceState) func() uint64 {
- var (
- b bytes.Buffer
- buf [8]byte
- )
- // We know that encoding works since the tests pass in the build this
- // file is checked into, so we'll simplify things and simply encode it
- // ourselves then report the total amount of bytes used.
- if err := ampStateEncoder(&b, a, &buf); err != nil {
- // This should never error out, but we log it just in case it
- // does.
- log.Errorf("encoding the amp invoice state failed: %v", err)
- }
- return func() uint64 {
- return uint64(len(b.Bytes()))
- }
- }
- // serializeInvoice serializes an invoice to a writer.
- //
- // Note: this function is in use for a migration. Before making changes that
- // would modify the on disk format, make a copy of the original code and store
- // it with the migration.
- func serializeInvoice(w io.Writer, i *invpkg.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(invpkg.UnknownPreimage)
- if i.Terms.PaymentPreimage != nil {
- preimage = *i.Terms.PaymentPreimage
- if preimage == invpkg.UnknownPreimage {
- return errors.New("cannot use all-zeroes preimage")
- }
- }
- value := uint64(i.Terms.Value)
- cltvDelta := uint32(i.Terms.FinalCltvDelta)
- expiry := uint64(i.Terms.Expiry)
- amtPaid := uint64(i.AmtPaid)
- state := uint8(i.State)
- var hodlInvoice uint8
- if i.HodlInvoice {
- hodlInvoice = 1
- }
- 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),
- tlv.MakePrimitiveRecord(hodlInvoiceType, &hodlInvoice),
- // Invoice AMP state.
- tlv.MakeDynamicRecord(
- invoiceAmpStateType, &i.AMPState,
- ampRecordSize(&i.AMPState),
- ampStateEncoder, ampStateDecoder,
- ),
- )
- 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
- }
- // Only if this is a _non_ AMP invoice do we serialize the HTLCs
- // in-line with the rest of the invoice.
- if i.IsAMP() {
- return nil
- }
- return serializeHtlcs(w, i.Htlcs)
- }
- // serializeHtlcs serializes a map containing circuit keys and invoice htlcs to
- // a writer.
- func serializeHtlcs(w io.Writer,
- htlcs map[models.CircuitKey]*invpkg.InvoiceHTLC) error {
- for key, htlc := range htlcs {
- // Encode the htlc in a tlv stream.
- chanID := key.ChanID.ToUint64()
- amt := uint64(htlc.Amt)
- mppTotalAmt := uint64(htlc.MppTotalAmt)
- acceptTime := putNanoTime(htlc.AcceptTime)
- resolveTime := putNanoTime(htlc.ResolveTime)
- state := uint8(htlc.State)
- var records []tlv.Record
- records = append(records,
- tlv.MakePrimitiveRecord(chanIDType, &chanID),
- tlv.MakePrimitiveRecord(htlcIDType, &key.HtlcID),
- tlv.MakePrimitiveRecord(amtType, &amt),
- tlv.MakePrimitiveRecord(
- acceptHeightType, &htlc.AcceptHeight,
- ),
- tlv.MakePrimitiveRecord(acceptTimeType, &acceptTime),
- tlv.MakePrimitiveRecord(resolveTimeType, &resolveTime),
- tlv.MakePrimitiveRecord(expiryHeightType, &htlc.Expiry),
- tlv.MakePrimitiveRecord(htlcStateType, &state),
- tlv.MakePrimitiveRecord(mppTotalAmtType, &mppTotalAmt),
- )
- if htlc.AMP != nil {
- setIDRecord := tlv.MakeDynamicRecord(
- htlcAMPType, &htlc.AMP.Record,
- htlc.AMP.Record.PayloadSize,
- record.AMPEncoder, record.AMPDecoder,
- )
- records = append(records, setIDRecord)
- hash32 := [32]byte(htlc.AMP.Hash)
- hashRecord := tlv.MakePrimitiveRecord(
- htlcHashType, &hash32,
- )
- records = append(records, hashRecord)
- if htlc.AMP.Preimage != nil {
- preimage32 := [32]byte(*htlc.AMP.Preimage)
- preimageRecord := tlv.MakePrimitiveRecord(
- htlcPreimageType, &preimage32,
- )
- records = append(records, preimageRecord)
- }
- }
- // Convert the custom records to tlv.Record types that are ready
- // for serialization.
- customRecords := tlv.MapToRecords(htlc.CustomRecords)
- // Append the custom records. Their ids are in the experimental
- // range and sorted, so there is no need to sort again.
- records = append(records, customRecords...)
- tlvStream, err := tlv.NewStream(records...)
- if err != nil {
- return err
- }
- var b bytes.Buffer
- if err := tlvStream.Encode(&b); err != nil {
- return err
- }
- // Write the length of the tlv stream followed by the stream
- // bytes.
- err = binary.Write(w, byteOrder, uint64(b.Len()))
- if err != nil {
- return err
- }
- if _, err := w.Write(b.Bytes()); err != nil {
- return err
- }
- }
- return nil
- }
- // putNanoTime returns the unix nano time for the passed timestamp. A zero-value
- // timestamp will be mapped to 0, since calling UnixNano in that case is
- // undefined.
- func putNanoTime(t time.Time) uint64 {
- if t.IsZero() {
- return 0
- }
- return uint64(t.UnixNano())
- }
- // getNanoTime returns a timestamp for the given number of nano seconds. If zero
- // is provided, an zero-value time stamp is returned.
- func getNanoTime(ns uint64) time.Time {
- if ns == 0 {
- return time.Time{}
- }
- return time.Unix(0, int64(ns))
- }
- // fetchFilteredAmpInvoices retrieves only a select set of AMP invoices
- // identified by the setID value.
- func fetchFilteredAmpInvoices(invoiceBucket kvdb.RBucket, invoiceNum []byte,
- setIDs ...*invpkg.SetID) (map[models.CircuitKey]*invpkg.InvoiceHTLC,
- error) {
- htlcs := make(map[models.CircuitKey]*invpkg.InvoiceHTLC)
- for _, setID := range setIDs {
- invoiceSetIDKey := makeInvoiceSetIDKey(invoiceNum, setID[:])
- htlcSetBytes := invoiceBucket.Get(invoiceSetIDKey[:])
- if htlcSetBytes == nil {
- // A set ID was passed in, but we don't have this
- // stored yet, meaning that the setID is being added
- // for the first time.
- return htlcs, invpkg.ErrInvoiceNotFound
- }
- htlcSetReader := bytes.NewReader(htlcSetBytes)
- htlcsBySetID, err := deserializeHtlcs(htlcSetReader)
- if err != nil {
- return nil, err
- }
- for key, htlc := range htlcsBySetID {
- htlcs[key] = htlc
- }
- }
- return htlcs, nil
- }
- // forEachAMPInvoice is a helper function that attempts to iterate over each of
- // the HTLC sets (based on their set ID) for the given AMP invoice identified
- // by its invoiceNum. The callback closure is called for each key within the
- // prefix range.
- func forEachAMPInvoice(invoiceBucket kvdb.RBucket, invoiceNum []byte,
- callback func(key, htlcSet []byte) error) error {
- invoiceCursor := invoiceBucket.ReadCursor()
- // Seek to the first key that includes the invoice data itself.
- invoiceCursor.Seek(invoiceNum)
- // Advance to the very first key _after_ the invoice data, as this is
- // where we'll encounter our first HTLC (if any are present).
- cursorKey, htlcSet := invoiceCursor.Next()
- // If at this point, the cursor key doesn't match the invoice num
- // prefix, then we know that this HTLC doesn't have any set ID HTLCs
- // associated with it.
- if !bytes.HasPrefix(cursorKey, invoiceNum) {
- return nil
- }
- // Otherwise continue to iterate until we no longer match the prefix,
- // executing the call back at each step.
- for ; cursorKey != nil && bytes.HasPrefix(cursorKey, invoiceNum); cursorKey, htlcSet = invoiceCursor.Next() {
- err := callback(cursorKey, htlcSet)
- if err != nil {
- return err
- }
- }
- return nil
- }
- // fetchAmpSubInvoices attempts to use the invoiceNum as a prefix within the
- // AMP bucket to find all the individual HTLCs (by setID) associated with a
- // given invoice. If a list of set IDs are specified, then only HTLCs
- // associated with that setID will be retrieved.
- func fetchAmpSubInvoices(invoiceBucket kvdb.RBucket, invoiceNum []byte,
- setIDs ...*invpkg.SetID) (map[models.CircuitKey]*invpkg.InvoiceHTLC,
- error) {
- // If a set of setIDs was specified, then we can skip the cursor and
- // just read out exactly what we need.
- if len(setIDs) != 0 && setIDs[0] != nil {
- return fetchFilteredAmpInvoices(
- invoiceBucket, invoiceNum, setIDs...,
- )
- }
- // Otherwise, iterate over all the htlc sets that are prefixed beside
- // this invoice in the main invoice bucket.
- htlcs := make(map[models.CircuitKey]*invpkg.InvoiceHTLC)
- err := forEachAMPInvoice(invoiceBucket, invoiceNum,
- func(key, htlcSet []byte) error {
- htlcSetReader := bytes.NewReader(htlcSet)
- htlcsBySetID, err := deserializeHtlcs(htlcSetReader)
- if err != nil {
- return err
- }
- for key, htlc := range htlcsBySetID {
- htlcs[key] = htlc
- }
- return nil
- },
- )
- if err != nil {
- return nil, err
- }
- return htlcs, nil
- }
- // fetchInvoice attempts to read out the relevant state for the invoice as
- // specified by the invoice number. If the setID fields are set, then only the
- // HTLC information pertaining to those set IDs is returned.
- func fetchInvoice(invoiceNum []byte, invoices kvdb.RBucket,
- setIDs ...*invpkg.SetID) (invpkg.Invoice, error) {
- invoiceBytes := invoices.Get(invoiceNum)
- if invoiceBytes == nil {
- return invpkg.Invoice{}, invpkg.ErrInvoiceNotFound
- }
- invoiceReader := bytes.NewReader(invoiceBytes)
- invoice, err := deserializeInvoice(invoiceReader)
- if err != nil {
- return invpkg.Invoice{}, err
- }
- // If this is an AMP invoice we'll also attempt to read out the set of
- // HTLCs that were paid to prior set IDs, if needed.
- if !invoice.IsAMP() {
- return invoice, nil
- }
- if shouldFetchAMPHTLCs(invoice, setIDs) {
- invoice.Htlcs, err = fetchAmpSubInvoices(
- invoices, invoiceNum, setIDs...,
- )
- // TODO(positiveblue): we should fail when we are not able to
- // fetch all the HTLCs for an AMP invoice. Multiple tests in
- // the invoice and channeldb package break if we return this
- // error. We need to update them when we migrate this logic to
- // the sql implementation.
- if err != nil {
- log.Errorf("unable to fetch amp htlcs for inv "+
- "%v and setIDs %v: %w", invoiceNum, setIDs, err)
- }
- }
- return invoice, nil
- }
- // shouldFetchAMPHTLCs returns true if we need to fetch the set of HTLCs that
- // were paid to the relevant set IDs.
- func shouldFetchAMPHTLCs(invoice invpkg.Invoice, setIDs []*invpkg.SetID) bool {
- // For AMP invoice that already have HTLCs populated (created before
- // recurring invoices), then we don't need to read from the prefix
- // keyed section of the bucket.
- if len(invoice.Htlcs) != 0 {
- return false
- }
- // If the "zero" setID was specified, then this means that no HTLC data
- // should be returned alongside of it.
- if len(setIDs) != 0 && setIDs[0] != nil &&
- *setIDs[0] == invpkg.BlankPayAddr {
- return false
- }
- return true
- }
- // fetchInvoiceStateAMP retrieves the state of all the relevant sub-invoice for
- // an AMP invoice. This methods only decode the relevant state vs the entire
- // invoice.
- func fetchInvoiceStateAMP(invoiceNum []byte,
- invoices kvdb.RBucket) (invpkg.AMPInvoiceState, error) {
- // Fetch the raw invoice bytes.
- invoiceBytes := invoices.Get(invoiceNum)
- if invoiceBytes == nil {
- return nil, invpkg.ErrInvoiceNotFound
- }
- r := bytes.NewReader(invoiceBytes)
- var bodyLen int64
- err := binary.Read(r, byteOrder, &bodyLen)
- if err != nil {
- return nil, err
- }
- // Next, we'll make a new TLV stream that only attempts to decode the
- // bytes we actually need.
- ampState := make(invpkg.AMPInvoiceState)
- tlvStream, err := tlv.NewStream(
- // Invoice AMP state.
- tlv.MakeDynamicRecord(
- invoiceAmpStateType, &State, nil,
- ampStateEncoder, ampStateDecoder,
- ),
- )
- if err != nil {
- return nil, err
- }
- invoiceReader := io.LimitReader(r, bodyLen)
- if err = tlvStream.Decode(invoiceReader); err != nil {
- return nil, err
- }
- return ampState, nil
- }
- func deserializeInvoice(r io.Reader) (invpkg.Invoice, error) {
- var (
- preimageBytes [32]byte
- value uint64
- cltvDelta uint32
- expiry uint64
- amtPaid uint64
- state uint8
- hodlInvoice uint8
- creationDateBytes []byte
- settleDateBytes []byte
- featureBytes []byte
- )
- var i invpkg.Invoice
- i.AMPState = make(invpkg.AMPInvoiceState)
- 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, &preimageBytes),
- 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),
- tlv.MakePrimitiveRecord(hodlInvoiceType, &hodlInvoice),
- // Invoice AMP state.
- tlv.MakeDynamicRecord(
- invoiceAmpStateType, &i.AMPState, nil,
- ampStateEncoder, ampStateDecoder,
- ),
- )
- if err != nil {
- return i, err
- }
- var bodyLen int64
- err = binary.Read(r, byteOrder, &bodyLen)
- if err != nil {
- return i, err
- }
- lr := io.LimitReader(r, bodyLen)
- if err = tlvStream.Decode(lr); err != nil {
- return i, err
- }
- preimage := lntypes.Preimage(preimageBytes)
- if preimage != invpkg.UnknownPreimage {
- i.Terms.PaymentPreimage = &preimage
- }
- i.Terms.Value = lnwire.MilliSatoshi(value)
- i.Terms.FinalCltvDelta = int32(cltvDelta)
- i.Terms.Expiry = time.Duration(expiry)
- i.AmtPaid = lnwire.MilliSatoshi(amtPaid)
- i.State = invpkg.ContractState(state)
- if hodlInvoice != 0 {
- i.HodlInvoice = true
- }
- err = i.CreationDate.UnmarshalBinary(creationDateBytes)
- if err != nil {
- return i, err
- }
- err = i.SettleDate.UnmarshalBinary(settleDateBytes)
- if err != nil {
- return i, err
- }
- rawFeatures := lnwire.NewRawFeatureVector()
- err = rawFeatures.DecodeBase256(
- bytes.NewReader(featureBytes), len(featureBytes),
- )
- if err != nil {
- return i, err
- }
- i.Terms.Features = lnwire.NewFeatureVector(
- rawFeatures, lnwire.Features,
- )
- i.Htlcs, err = deserializeHtlcs(r)
- return i, err
- }
- func encodeCircuitKeys(w io.Writer, val interface{}, buf *[8]byte) error {
- if v, ok := val.(*map[models.CircuitKey]struct{}); ok {
- // We encode the set of circuit keys as a varint length prefix.
- // followed by a series of fixed sized uint8 integers.
- numKeys := uint64(len(*v))
- if err := tlv.WriteVarInt(w, numKeys, buf); err != nil {
- return err
- }
- for key := range *v {
- scidInt := key.ChanID.ToUint64()
- if err := tlv.EUint64(w, &scidInt, buf); err != nil {
- return err
- }
- if err := tlv.EUint64(w, &key.HtlcID, buf); err != nil {
- return err
- }
- }
- return nil
- }
- return tlv.NewTypeForEncodingErr(val, "*map[CircuitKey]struct{}")
- }
- func decodeCircuitKeys(r io.Reader, val interface{}, buf *[8]byte,
- l uint64) error {
- if v, ok := val.(*map[models.CircuitKey]struct{}); ok {
- // First, we'll read out the varint that encodes the number of
- // circuit keys encoded.
- numKeys, err := tlv.ReadVarInt(r, buf)
- if err != nil {
- return err
- }
- // Now that we know how many keys to expect, iterate reading
- // each one until we're done.
- for i := uint64(0); i < numKeys; i++ {
- var (
- key models.CircuitKey
- scid uint64
- )
- if err := tlv.DUint64(r, &scid, buf, 8); err != nil {
- return err
- }
- key.ChanID = lnwire.NewShortChanIDFromInt(scid)
- err := tlv.DUint64(r, &key.HtlcID, buf, 8)
- if err != nil {
- return err
- }
- (*v)[key] = struct{}{}
- }
- return nil
- }
- return tlv.NewTypeForDecodingErr(val, "*map[CircuitKey]struct{}", l, l)
- }
- // ampStateEncoder is a custom TLV encoder for the AMPInvoiceState record.
- func ampStateEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
- if v, ok := val.(*invpkg.AMPInvoiceState); ok {
- // We'll encode the AMP state as a series of KV pairs on the
- // wire with a length prefix.
- numRecords := uint64(len(*v))
- // First, we'll write out the number of records as a var int.
- if err := tlv.WriteVarInt(w, numRecords, buf); err != nil {
- return err
- }
- // With that written out, we'll now encode the entries
- // themselves as a sub-TLV record, which includes its _own_
- // inner length prefix.
- for setID, ampState := range *v {
- setID := [32]byte(setID)
- ampState := ampState
- htlcState := uint8(ampState.State)
- settleDate := ampState.SettleDate
- settleDateBytes, err := settleDate.MarshalBinary()
- if err != nil {
- return err
- }
- amtPaid := uint64(ampState.AmtPaid)
- var ampStateTlvBytes bytes.Buffer
- tlvStream, err := tlv.NewStream(
- tlv.MakePrimitiveRecord(
- ampStateSetIDType, &setID,
- ),
- tlv.MakePrimitiveRecord(
- ampStateHtlcStateType, &htlcState,
- ),
- tlv.MakePrimitiveRecord(
- ampStateSettleIndexType,
- &State.SettleIndex,
- ),
- tlv.MakePrimitiveRecord(
- ampStateSettleDateType,
- &settleDateBytes,
- ),
- tlv.MakeDynamicRecord(
- ampStateCircuitKeysType,
- &State.InvoiceKeys,
- func() uint64 {
- // The record takes 8 bytes to
- // encode the set of circuits,
- // 8 bytes for the scid for the
- // key, and 8 bytes for the HTLC
- // index.
- keys := ampState.InvoiceKeys
- numKeys := uint64(len(keys))
- size := tlv.VarIntSize(numKeys)
- dataSize := (numKeys * 16)
- return size + dataSize
- },
- encodeCircuitKeys, decodeCircuitKeys,
- ),
- tlv.MakePrimitiveRecord(
- ampStateAmtPaidType, &amtPaid,
- ),
- )
- if err != nil {
- return err
- }
- err = tlvStream.Encode(&StateTlvBytes)
- if err != nil {
- return err
- }
- // We encode the record with a varint length followed by
- // the _raw_ TLV bytes.
- tlvLen := uint64(len(ampStateTlvBytes.Bytes()))
- if err := tlv.WriteVarInt(w, tlvLen, buf); err != nil {
- return err
- }
- _, err = w.Write(ampStateTlvBytes.Bytes())
- if err != nil {
- return err
- }
- }
- return nil
- }
- return tlv.NewTypeForEncodingErr(val, "channeldb.AMPInvoiceState")
- }
- // ampStateDecoder is a custom TLV decoder for the AMPInvoiceState record.
- func ampStateDecoder(r io.Reader, val interface{}, buf *[8]byte,
- l uint64) error {
- if v, ok := val.(*invpkg.AMPInvoiceState); ok {
- // First, we'll decode the varint that encodes how many set IDs
- // are encoded within the greater map.
- numRecords, err := tlv.ReadVarInt(r, buf)
- if err != nil {
- return err
- }
- // Now that we know how many records we'll need to read, we can
- // iterate and read them all out in series.
- for i := uint64(0); i < numRecords; i++ {
- // Read out the varint that encodes the size of this
- // inner TLV record.
- stateRecordSize, err := tlv.ReadVarInt(r, buf)
- if err != nil {
- return err
- }
- // Using this information, we'll create a new limited
- // reader that'll return an EOF once the end has been
- // reached so the stream stops consuming bytes.
- innerTlvReader := io.LimitedReader{
- R: r,
- N: int64(stateRecordSize),
- }
- var (
- setID [32]byte
- htlcState uint8
- settleIndex uint64
- settleDateBytes []byte
- invoiceKeys = make(
- map[models.CircuitKey]struct{},
- )
- amtPaid uint64
- )
- tlvStream, err := tlv.NewStream(
- tlv.MakePrimitiveRecord(
- ampStateSetIDType, &setID,
- ),
- tlv.MakePrimitiveRecord(
- ampStateHtlcStateType, &htlcState,
- ),
- tlv.MakePrimitiveRecord(
- ampStateSettleIndexType, &settleIndex,
- ),
- tlv.MakePrimitiveRecord(
- ampStateSettleDateType,
- &settleDateBytes,
- ),
- tlv.MakeDynamicRecord(
- ampStateCircuitKeysType,
- &invoiceKeys, nil,
- encodeCircuitKeys, decodeCircuitKeys,
- ),
- tlv.MakePrimitiveRecord(
- ampStateAmtPaidType, &amtPaid,
- ),
- )
- if err != nil {
- return err
- }
- err = tlvStream.Decode(&innerTlvReader)
- if err != nil {
- return err
- }
- var settleDate time.Time
- err = settleDate.UnmarshalBinary(settleDateBytes)
- if err != nil {
- return err
- }
- (*v)[setID] = invpkg.InvoiceStateAMP{
- State: invpkg.HtlcState(htlcState),
- SettleIndex: settleIndex,
- SettleDate: settleDate,
- InvoiceKeys: invoiceKeys,
- AmtPaid: lnwire.MilliSatoshi(amtPaid),
- }
- }
- return nil
- }
- return tlv.NewTypeForDecodingErr(
- val, "channeldb.AMPInvoiceState", l, l,
- )
- }
- // deserializeHtlcs reads a list of invoice htlcs from a reader and returns it
- // as a map.
- func deserializeHtlcs(r io.Reader) (map[models.CircuitKey]*invpkg.InvoiceHTLC,
- error) {
- htlcs := make(map[models.CircuitKey]*invpkg.InvoiceHTLC)
- for {
- // Read the length of the tlv stream for this htlc.
- var streamLen int64
- if err := binary.Read(r, byteOrder, &streamLen); err != nil {
- if err == io.EOF {
- break
- }
- return nil, err
- }
- // Limit the reader so that it stops at the end of this htlc's
- // stream.
- htlcReader := io.LimitReader(r, streamLen)
- // Decode the contents into the htlc fields.
- var (
- htlc invpkg.InvoiceHTLC
- key models.CircuitKey
- chanID uint64
- state uint8
- acceptTime, resolveTime uint64
- amt, mppTotalAmt uint64
- amp = &record.AMP{}
- hash32 = &[32]byte{}
- preimage32 = &[32]byte{}
- )
- tlvStream, err := tlv.NewStream(
- tlv.MakePrimitiveRecord(chanIDType, &chanID),
- tlv.MakePrimitiveRecord(htlcIDType, &key.HtlcID),
- tlv.MakePrimitiveRecord(amtType, &amt),
- tlv.MakePrimitiveRecord(
- acceptHeightType, &htlc.AcceptHeight,
- ),
- tlv.MakePrimitiveRecord(acceptTimeType, &acceptTime),
- tlv.MakePrimitiveRecord(resolveTimeType, &resolveTime),
- tlv.MakePrimitiveRecord(expiryHeightType, &htlc.Expiry),
- tlv.MakePrimitiveRecord(htlcStateType, &state),
- tlv.MakePrimitiveRecord(mppTotalAmtType, &mppTotalAmt),
- tlv.MakeDynamicRecord(
- htlcAMPType, amp, amp.PayloadSize,
- record.AMPEncoder, record.AMPDecoder,
- ),
- tlv.MakePrimitiveRecord(htlcHashType, hash32),
- tlv.MakePrimitiveRecord(htlcPreimageType, preimage32),
- )
- if err != nil {
- return nil, err
- }
- parsedTypes, err := tlvStream.DecodeWithParsedTypes(htlcReader)
- if err != nil {
- return nil, err
- }
- if _, ok := parsedTypes[htlcAMPType]; !ok {
- amp = nil
- }
- var preimage *lntypes.Preimage
- if _, ok := parsedTypes[htlcPreimageType]; ok {
- pimg := lntypes.Preimage(*preimage32)
- preimage = &pimg
- }
- var hash *lntypes.Hash
- if _, ok := parsedTypes[htlcHashType]; ok {
- h := lntypes.Hash(*hash32)
- hash = &h
- }
- key.ChanID = lnwire.NewShortChanIDFromInt(chanID)
- htlc.AcceptTime = getNanoTime(acceptTime)
- htlc.ResolveTime = getNanoTime(resolveTime)
- htlc.State = invpkg.HtlcState(state)
- htlc.Amt = lnwire.MilliSatoshi(amt)
- htlc.MppTotalAmt = lnwire.MilliSatoshi(mppTotalAmt)
- if amp != nil && hash != nil {
- htlc.AMP = &invpkg.InvoiceHtlcAMPData{
- Record: *amp,
- Hash: *hash,
- Preimage: preimage,
- }
- }
- // Reconstruct the custom records fields from the parsed types
- // map return from the tlv parser.
- htlc.CustomRecords = hop.NewCustomRecords(parsedTypes)
- htlcs[key] = &htlc
- }
- return htlcs, nil
- }
- // invoiceSetIDKeyLen is the length of the key that's used to store the
- // individual HTLCs prefixed by their ID along side the main invoice within the
- // invoiceBytes. We use 4 bytes for the invoice number, and 32 bytes for the
- // set ID.
- const invoiceSetIDKeyLen = 4 + 32
- // makeInvoiceSetIDKey returns the prefix key, based on the set ID and invoice
- // number where the HTLCs for this setID will be stored udner.
- func makeInvoiceSetIDKey(invoiceNum, setID []byte) [invoiceSetIDKeyLen]byte {
- // Construct the prefix key we need to obtain the invoice information:
- // invoiceNum || setID.
- var invoiceSetIDKey [invoiceSetIDKeyLen]byte
- copy(invoiceSetIDKey[:], invoiceNum)
- copy(invoiceSetIDKey[len(invoiceNum):], setID)
- return invoiceSetIDKey
- }
- // delAMPInvoices attempts to delete all the "sub" invoices associated with a
- // greater AMP invoices. We do this by deleting the set of keys that share the
- // invoice number as a prefix.
- func delAMPInvoices(invoiceNum []byte, invoiceBucket kvdb.RwBucket) error {
- // Since it isn't safe to delete using an active cursor, we'll use the
- // cursor simply to collect the set of keys we need to delete, _then_
- // delete them in another pass.
- var keysToDel [][]byte
- err := forEachAMPInvoice(
- invoiceBucket, invoiceNum,
- func(cursorKey, v []byte) error {
- keysToDel = append(keysToDel, cursorKey)
- return nil
- },
- )
- if err != nil {
- return err
- }
- // In this next phase, we'll then delete all the relevant invoices.
- for _, keyToDel := range keysToDel {
- if err := invoiceBucket.Delete(keyToDel); err != nil {
- return err
- }
- }
- return nil
- }
- // delAMPSettleIndex removes all the entries in the settle index associated
- // with a given AMP invoice.
- func delAMPSettleIndex(invoiceNum []byte, invoices,
- settleIndex kvdb.RwBucket) error {
- // First, we need to grab the AMP invoice state to see if there's
- // anything that we even need to delete.
- ampState, err := fetchInvoiceStateAMP(invoiceNum, invoices)
- if err != nil {
- return err
- }
- // If there's no AMP state at all (non-AMP invoice), then we can return
- // early.
- if len(ampState) == 0 {
- return nil
- }
- // Otherwise, we'll need to iterate and delete each settle index within
- // the set of returned entries.
- var settleIndexKey [8]byte
- for _, subState := range ampState {
- byteOrder.PutUint64(
- settleIndexKey[:], subState.SettleIndex,
- )
- if err := settleIndex.Delete(settleIndexKey[:]); err != nil {
- return err
- }
- }
- return nil
- }
- // DeleteCanceledInvoices deletes all canceled invoices from the database.
- func (d *DB) DeleteCanceledInvoices(_ context.Context) error {
- return kvdb.Update(d, func(tx kvdb.RwTx) error {
- invoices := tx.ReadWriteBucket(invoiceBucket)
- if invoices == nil {
- return nil
- }
- invoiceIndex := invoices.NestedReadWriteBucket(
- invoiceIndexBucket,
- )
- if invoiceIndex == nil {
- return nil
- }
- invoiceAddIndex := invoices.NestedReadWriteBucket(
- addIndexBucket,
- )
- if invoiceAddIndex == nil {
- return nil
- }
- payAddrIndex := tx.ReadWriteBucket(payAddrIndexBucket)
- return invoiceIndex.ForEach(func(k, v []byte) error {
- // Skip the special numInvoicesKey as that does not
- // point to a valid invoice.
- if bytes.Equal(k, numInvoicesKey) {
- return nil
- }
- // Skip sub-buckets.
- if v == nil {
- return nil
- }
- invoice, err := fetchInvoice(v, invoices)
- if err != nil {
- return err
- }
- if invoice.State != invpkg.ContractCanceled {
- return nil
- }
- // Delete the payment hash from the invoice index.
- err = invoiceIndex.Delete(k)
- if err != nil {
- return err
- }
- // Delete payment address index reference if there's a
- // valid payment address.
- if invoice.Terms.PaymentAddr != invpkg.BlankPayAddr {
- // To ensure consistency check that the already
- // fetched invoice key matches the one in the
- // payment address index.
- key := payAddrIndex.Get(
- invoice.Terms.PaymentAddr[:],
- )
- if bytes.Equal(key, k) {
- // Delete from the payment address
- // index.
- if err := payAddrIndex.Delete(
- invoice.Terms.PaymentAddr[:],
- ); err != nil {
- return err
- }
- }
- }
- // Remove from the add index.
- var addIndexKey [8]byte
- byteOrder.PutUint64(addIndexKey[:], invoice.AddIndex)
- err = invoiceAddIndex.Delete(addIndexKey[:])
- if err != nil {
- return err
- }
- // Note that we don't need to delete the invoice from
- // the settle index as it is not added until the
- // invoice is settled.
- // Now remove all sub invoices.
- err = delAMPInvoices(k, invoices)
- if err != nil {
- return err
- }
- // Finally remove the serialized invoice from the
- // invoice bucket.
- return invoices.Delete(k)
- })
- }, func() {})
- }
- // DeleteInvoice attempts to delete the passed invoices from the database in
- // one transaction. The passed delete references hold all keys required to
- // delete the invoices without also needing to deserialize them.
- func (d *DB) DeleteInvoice(_ context.Context,
- invoicesToDelete []invpkg.InvoiceDeleteRef) error {
- err := kvdb.Update(d, func(tx kvdb.RwTx) error {
- invoices := tx.ReadWriteBucket(invoiceBucket)
- if invoices == nil {
- return invpkg.ErrNoInvoicesCreated
- }
- invoiceIndex := invoices.NestedReadWriteBucket(
- invoiceIndexBucket,
- )
- if invoiceIndex == nil {
- return invpkg.ErrNoInvoicesCreated
- }
- invoiceAddIndex := invoices.NestedReadWriteBucket(
- addIndexBucket,
- )
- if invoiceAddIndex == nil {
- return invpkg.ErrNoInvoicesCreated
- }
- // settleIndex can be nil, as the bucket is created lazily
- // when the first invoice is settled.
- settleIndex := invoices.NestedReadWriteBucket(settleIndexBucket)
- payAddrIndex := tx.ReadWriteBucket(payAddrIndexBucket)
- for _, ref := range invoicesToDelete {
- // Fetch the invoice key for using it to check for
- // consistency and also to delete from the invoice
- // index.
- invoiceKey := invoiceIndex.Get(ref.PayHash[:])
- if invoiceKey == nil {
- return invpkg.ErrInvoiceNotFound
- }
- err := invoiceIndex.Delete(ref.PayHash[:])
- if err != nil {
- return err
- }
- // Delete payment address index reference if there's a
- // valid payment address passed.
- if ref.PayAddr != nil {
- // To ensure consistency check that the already
- // fetched invoice key matches the one in the
- // payment address index.
- key := payAddrIndex.Get(ref.PayAddr[:])
- if bytes.Equal(key, invoiceKey) {
- // Delete from the payment address
- // index. Note that since the payment
- // address index has been introduced
- // with an empty migration it may be
- // possible that the index doesn't have
- // an entry for this invoice.
- // ref: https://github.com/lightningnetwork/lnd/pull/4285/commits/cbf71b5452fa1d3036a43309e490787c5f7f08dc#r426368127
- if err := payAddrIndex.Delete(
- ref.PayAddr[:],
- ); err != nil {
- return err
- }
- }
- }
- var addIndexKey [8]byte
- byteOrder.PutUint64(addIndexKey[:], ref.AddIndex)
- // To ensure consistency check that the key stored in
- // the add index also matches the previously fetched
- // invoice key.
- key := invoiceAddIndex.Get(addIndexKey[:])
- if !bytes.Equal(key, invoiceKey) {
- return fmt.Errorf("unknown invoice " +
- "in add index")
- }
- // Remove from the add index.
- err = invoiceAddIndex.Delete(addIndexKey[:])
- if err != nil {
- return err
- }
- // Remove from the settle index if available and
- // if the invoice is settled.
- if settleIndex != nil && ref.SettleIndex > 0 {
- var settleIndexKey [8]byte
- byteOrder.PutUint64(
- settleIndexKey[:], ref.SettleIndex,
- )
- // To ensure consistency check that the already
- // fetched invoice key matches the one in the
- // settle index
- key := settleIndex.Get(settleIndexKey[:])
- if !bytes.Equal(key, invoiceKey) {
- return fmt.Errorf("unknown invoice " +
- "in settle index")
- }
- err = settleIndex.Delete(settleIndexKey[:])
- if err != nil {
- return err
- }
- }
- // In addition to deleting the main invoice state, if
- // this is an AMP invoice, then we'll also need to
- // delete the set HTLC set stored as a key prefix. For
- // non-AMP invoices, this'll be a noop.
- err = delAMPSettleIndex(
- invoiceKey, invoices, settleIndex,
- )
- if err != nil {
- return err
- }
- err = delAMPInvoices(invoiceKey, invoices)
- if err != nil {
- return err
- }
- // Finally remove the serialized invoice from the
- // invoice bucket.
- err = invoices.Delete(invoiceKey)
- if err != nil {
- return err
- }
- }
- return nil
- }, func() {})
- return err
- }
|