diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index 3a57eaf8..62998b74 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -977,154 +977,3 @@ func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) { delete(i.hodlReverseSubscriptions, subscriber) } - -// updateResult is the result of the invoice update call. -type updateResult uint8 - -const ( - resultInvalid updateResult = iota - resultReplayToCanceled - resultReplayToAccepted - resultReplayToSettled - resultInvoiceAlreadyCanceled - resultAmountTooLow - resultExpiryTooSoon - resultDuplicateToAccepted - resultDuplicateToSettled - resultAccepted - resultSettled -) - -// String returns a human-readable representation of the invoice update result. -func (u updateResult) String() string { - switch u { - - case resultInvalid: - return "invalid" - - case resultReplayToCanceled: - return "replayed htlc to canceled invoice" - - case resultReplayToAccepted: - return "replayed htlc to accepted invoice" - - case resultReplayToSettled: - return "replayed htlc to settled invoice" - - case resultInvoiceAlreadyCanceled: - return "invoice already canceled" - - case resultAmountTooLow: - return "amount too low" - - case resultExpiryTooSoon: - return "expiry too soon" - - case resultDuplicateToAccepted: - return "accepting duplicate payment to accepted invoice" - - case resultDuplicateToSettled: - return "accepting duplicate payment to settled invoice" - - case resultAccepted: - return "accepted" - - case resultSettled: - return "settled" - - default: - return "unknown" - } -} - -// invoiceUpdateCtx is an object that describes the context for the invoice -// update to be carried out. -type invoiceUpdateCtx struct { - circuitKey channeldb.CircuitKey - amtPaid lnwire.MilliSatoshi - expiry uint32 - currentHeight int32 - finalCltvRejectDelta int32 -} - -// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice -// settlement logic. -func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) ( - *channeldb.InvoiceUpdateDesc, updateResult, error) { - - // Don't update the invoice when this is a replayed htlc. - htlc, ok := inv.Htlcs[ctx.circuitKey] - if ok { - switch htlc.State { - case channeldb.HtlcStateCanceled: - return nil, resultReplayToCanceled, nil - - case channeldb.HtlcStateAccepted: - return nil, resultReplayToAccepted, nil - - case channeldb.HtlcStateSettled: - return nil, resultReplayToSettled, nil - - default: - return nil, 0, errors.New("unknown htlc state") - } - } - - // If the invoice is already canceled, there is no further checking to - // do. - if inv.State == channeldb.ContractCanceled { - return nil, resultInvoiceAlreadyCanceled, nil - } - - // If an invoice amount is specified, check that enough is paid. Also - // check this for duplicate payments if the invoice is already settled - // or accepted. - if inv.Terms.Value > 0 && ctx.amtPaid < inv.Terms.Value { - return nil, resultAmountTooLow, nil - } - - // The invoice is still open. Check the expiry. - if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) { - return nil, resultExpiryTooSoon, nil - } - - if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) { - return nil, resultExpiryTooSoon, nil - } - - // Record HTLC in the invoice database. - newHtlcs := map[channeldb.CircuitKey]*channeldb.HtlcAcceptDesc{ - ctx.circuitKey: { - Amt: ctx.amtPaid, - Expiry: ctx.expiry, - AcceptHeight: ctx.currentHeight, - }, - } - - update := channeldb.InvoiceUpdateDesc{Htlcs: newHtlcs} - - // Don't update invoice state if we are accepting a duplicate payment. - // We do accept or settle the HTLC. - switch inv.State { - case channeldb.ContractAccepted: - update.State = channeldb.ContractAccepted - return &update, resultDuplicateToAccepted, nil - - case channeldb.ContractSettled: - update.State = channeldb.ContractSettled - return &update, resultDuplicateToSettled, nil - } - - // Check to see if we can settle or this is an hold invoice and we need - // to wait for the preimage. - holdInvoice := inv.Terms.PaymentPreimage == channeldb.UnknownPreimage - if holdInvoice { - update.State = channeldb.ContractAccepted - return &update, resultAccepted, nil - } - - update.Preimage = inv.Terms.PaymentPreimage - update.State = channeldb.ContractSettled - - return &update, resultSettled, nil -} diff --git a/invoices/update.go b/invoices/update.go new file mode 100644 index 00000000..1b015ecf --- /dev/null +++ b/invoices/update.go @@ -0,0 +1,159 @@ +package invoices + +import ( + "errors" + + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/lnwire" +) + +// updateResult is the result of the invoice update call. +type updateResult uint8 + +const ( + resultInvalid updateResult = iota + resultReplayToCanceled + resultReplayToAccepted + resultReplayToSettled + resultInvoiceAlreadyCanceled + resultAmountTooLow + resultExpiryTooSoon + resultDuplicateToAccepted + resultDuplicateToSettled + resultAccepted + resultSettled +) + +// String returns a human-readable representation of the invoice update result. +func (u updateResult) String() string { + switch u { + + case resultInvalid: + return "invalid" + + case resultReplayToCanceled: + return "replayed htlc to canceled invoice" + + case resultReplayToAccepted: + return "replayed htlc to accepted invoice" + + case resultReplayToSettled: + return "replayed htlc to settled invoice" + + case resultInvoiceAlreadyCanceled: + return "invoice already canceled" + + case resultAmountTooLow: + return "amount too low" + + case resultExpiryTooSoon: + return "expiry too soon" + + case resultDuplicateToAccepted: + return "accepting duplicate payment to accepted invoice" + + case resultDuplicateToSettled: + return "accepting duplicate payment to settled invoice" + + case resultAccepted: + return "accepted" + + case resultSettled: + return "settled" + + default: + return "unknown" + } +} + +// invoiceUpdateCtx is an object that describes the context for the invoice +// update to be carried out. +type invoiceUpdateCtx struct { + circuitKey channeldb.CircuitKey + amtPaid lnwire.MilliSatoshi + expiry uint32 + currentHeight int32 + finalCltvRejectDelta int32 +} + +// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice +// settlement logic. +func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) ( + *channeldb.InvoiceUpdateDesc, updateResult, error) { + + // Don't update the invoice when this is a replayed htlc. + htlc, ok := inv.Htlcs[ctx.circuitKey] + if ok { + switch htlc.State { + case channeldb.HtlcStateCanceled: + return nil, resultReplayToCanceled, nil + + case channeldb.HtlcStateAccepted: + return nil, resultReplayToAccepted, nil + + case channeldb.HtlcStateSettled: + return nil, resultReplayToSettled, nil + + default: + return nil, 0, errors.New("unknown htlc state") + } + } + + // If the invoice is already canceled, there is no further checking to + // do. + if inv.State == channeldb.ContractCanceled { + return nil, resultInvoiceAlreadyCanceled, nil + } + + // If an invoice amount is specified, check that enough is paid. Also + // check this for duplicate payments if the invoice is already settled + // or accepted. + if inv.Terms.Value > 0 && ctx.amtPaid < inv.Terms.Value { + return nil, resultAmountTooLow, nil + } + + // The invoice is still open. Check the expiry. + if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) { + return nil, resultExpiryTooSoon, nil + } + + if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) { + return nil, resultExpiryTooSoon, nil + } + + // Record HTLC in the invoice database. + newHtlcs := map[channeldb.CircuitKey]*channeldb.HtlcAcceptDesc{ + ctx.circuitKey: { + Amt: ctx.amtPaid, + Expiry: ctx.expiry, + AcceptHeight: ctx.currentHeight, + }, + } + + update := channeldb.InvoiceUpdateDesc{Htlcs: newHtlcs} + + // Don't update invoice state if we are accepting a duplicate payment. + // We do accept or settle the HTLC. + switch inv.State { + case channeldb.ContractAccepted: + update.State = channeldb.ContractAccepted + return &update, resultDuplicateToAccepted, nil + + case channeldb.ContractSettled: + update.State = channeldb.ContractSettled + return &update, resultDuplicateToSettled, nil + } + + // Check to see if we can settle or this is an hold invoice and we need + // to wait for the preimage. + holdInvoice := inv.Terms.PaymentPreimage == channeldb.UnknownPreimage + if holdInvoice { + update.State = channeldb.ContractAccepted + return &update, resultAccepted, nil + } + + update.Preimage = inv.Terms.PaymentPreimage + update.State = channeldb.ContractSettled + + return &update, resultSettled, nil +}