invoices: export and rename update result
This commit exports UpdateResult so that calling functions can interpret the outcome of an invoice update. This is useful for determining the wire failure required (fail invalid details or mpp_timeout once implemented) and for notifying specific htlc failure details. The enum is renamed to ResolutionResult.
This commit is contained in:
parent
fb6cf21948
commit
db85c51b77
@ -706,7 +706,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
|||||||
// We'll attempt to settle an invoice matching this rHash on disk (if
|
// We'll attempt to settle an invoice matching this rHash on disk (if
|
||||||
// one exists). The callback will update the invoice state and/or htlcs.
|
// one exists). The callback will update the invoice state and/or htlcs.
|
||||||
var (
|
var (
|
||||||
result updateResult
|
result ResolutionResult
|
||||||
updateSubscribers bool
|
updateSubscribers bool
|
||||||
)
|
)
|
||||||
invoice, err := i.cdb.UpdateInvoice(
|
invoice, err := i.cdb.UpdateInvoice(
|
||||||
|
@ -8,86 +8,129 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
)
|
)
|
||||||
|
|
||||||
// updateResult is the result of the invoice update call.
|
// ResolutionResult provides metadata which about an invoice update which can
|
||||||
type updateResult uint8
|
// be used to take custom actions on resolution of the htlc. Only results which
|
||||||
|
// are actionable by the link are exported.
|
||||||
|
type ResolutionResult uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
resultInvalid updateResult = iota
|
resultInvalid ResolutionResult = iota
|
||||||
resultReplayToCanceled
|
|
||||||
resultReplayToAccepted
|
// ResultReplayToCanceled is returned when we replay a canceled invoice.
|
||||||
resultReplayToSettled
|
ResultReplayToCanceled
|
||||||
resultInvoiceAlreadyCanceled
|
|
||||||
resultAmountTooLow
|
// ResultReplayToAccepted is returned when we replay an accepted invoice.
|
||||||
resultExpiryTooSoon
|
ResultReplayToAccepted
|
||||||
resultDuplicateToAccepted
|
|
||||||
resultDuplicateToSettled
|
// ResultReplayToSettled is returned when we replay a settled invoice.
|
||||||
resultAccepted
|
ResultReplayToSettled
|
||||||
resultSettled
|
|
||||||
resultInvoiceNotOpen
|
// ResultInvoiceAlreadyCanceled is returned when trying to pay an invoice
|
||||||
resultPartialAccepted
|
// that is already canceled.
|
||||||
resultMppInProgress
|
ResultInvoiceAlreadyCanceled
|
||||||
resultAddressMismatch
|
|
||||||
resultHtlcSetTotalMismatch
|
// ResultAmountTooLow is returned when an invoice is underpaid.
|
||||||
resultHtlcSetTotalTooLow
|
ResultAmountTooLow
|
||||||
resultHtlcSetOverpayment
|
|
||||||
|
// ResultExpiryTooSoon is returned when we do not accept an invoice payment
|
||||||
|
// because it expires too soon.
|
||||||
|
ResultExpiryTooSoon
|
||||||
|
|
||||||
|
// ResultDuplicateToAccepted is returned when we accept a duplicate htlc.
|
||||||
|
ResultDuplicateToAccepted
|
||||||
|
|
||||||
|
// ResultDuplicateToSettled is returned when we settle an invoice which has
|
||||||
|
// already been settled at least once.
|
||||||
|
ResultDuplicateToSettled
|
||||||
|
|
||||||
|
// ResultAccepted is returned when we accept a hodl invoice.
|
||||||
|
ResultAccepted
|
||||||
|
|
||||||
|
// ResultSettled is returned when we settle an invoice.
|
||||||
|
ResultSettled
|
||||||
|
|
||||||
|
// ResultInvoiceNotOpen is returned when a mpp invoice is not open.
|
||||||
|
ResultInvoiceNotOpen
|
||||||
|
|
||||||
|
// ResultPartialAccepted is returned when we have partially received
|
||||||
|
// payment.
|
||||||
|
ResultPartialAccepted
|
||||||
|
|
||||||
|
// ResultMppInProgress is returned when we are busy receiving a mpp payment.
|
||||||
|
ResultMppInProgress
|
||||||
|
|
||||||
|
// ResultAddressMismatch is returned when the payment address for a mpp
|
||||||
|
// invoice does not match.
|
||||||
|
ResultAddressMismatch
|
||||||
|
|
||||||
|
// ResultHtlcSetTotalMismatch is returned when the amount paid by a htlc
|
||||||
|
// does not match its set total.
|
||||||
|
ResultHtlcSetTotalMismatch
|
||||||
|
|
||||||
|
// ResultHtlcSetTotalTooLow is returned when a mpp set total is too low for
|
||||||
|
// an invoice.
|
||||||
|
ResultHtlcSetTotalTooLow
|
||||||
|
|
||||||
|
// ResultHtlcSetOverpayment is returned when a mpp set is overpaid.
|
||||||
|
ResultHtlcSetOverpayment
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns a human-readable representation of the invoice update result.
|
// String returns a human-readable representation of the invoice update result.
|
||||||
func (u updateResult) String() string {
|
func (u ResolutionResult) String() string {
|
||||||
switch u {
|
switch u {
|
||||||
|
|
||||||
case resultInvalid:
|
case resultInvalid:
|
||||||
return "invalid"
|
return "invalid"
|
||||||
|
|
||||||
case resultReplayToCanceled:
|
case ResultReplayToCanceled:
|
||||||
return "replayed htlc to canceled invoice"
|
return "replayed htlc to canceled invoice"
|
||||||
|
|
||||||
case resultReplayToAccepted:
|
case ResultReplayToAccepted:
|
||||||
return "replayed htlc to accepted invoice"
|
return "replayed htlc to accepted invoice"
|
||||||
|
|
||||||
case resultReplayToSettled:
|
case ResultReplayToSettled:
|
||||||
return "replayed htlc to settled invoice"
|
return "replayed htlc to settled invoice"
|
||||||
|
|
||||||
case resultInvoiceAlreadyCanceled:
|
case ResultInvoiceAlreadyCanceled:
|
||||||
return "invoice already canceled"
|
return "invoice already canceled"
|
||||||
|
|
||||||
case resultAmountTooLow:
|
case ResultAmountTooLow:
|
||||||
return "amount too low"
|
return "amount too low"
|
||||||
|
|
||||||
case resultExpiryTooSoon:
|
case ResultExpiryTooSoon:
|
||||||
return "expiry too soon"
|
return "expiry too soon"
|
||||||
|
|
||||||
case resultDuplicateToAccepted:
|
case ResultDuplicateToAccepted:
|
||||||
return "accepting duplicate payment to accepted invoice"
|
return "accepting duplicate payment to accepted invoice"
|
||||||
|
|
||||||
case resultDuplicateToSettled:
|
case ResultDuplicateToSettled:
|
||||||
return "accepting duplicate payment to settled invoice"
|
return "accepting duplicate payment to settled invoice"
|
||||||
|
|
||||||
case resultAccepted:
|
case ResultAccepted:
|
||||||
return "accepted"
|
return "accepted"
|
||||||
|
|
||||||
case resultSettled:
|
case ResultSettled:
|
||||||
return "settled"
|
return "settled"
|
||||||
|
|
||||||
case resultInvoiceNotOpen:
|
case ResultInvoiceNotOpen:
|
||||||
return "invoice no longer open"
|
return "invoice no longer open"
|
||||||
|
|
||||||
case resultPartialAccepted:
|
case ResultPartialAccepted:
|
||||||
return "partial payment accepted"
|
return "partial payment accepted"
|
||||||
|
|
||||||
case resultMppInProgress:
|
case ResultMppInProgress:
|
||||||
return "mpp reception in progress"
|
return "mpp reception in progress"
|
||||||
|
|
||||||
case resultAddressMismatch:
|
case ResultAddressMismatch:
|
||||||
return "payment address mismatch"
|
return "payment address mismatch"
|
||||||
|
|
||||||
case resultHtlcSetTotalMismatch:
|
case ResultHtlcSetTotalMismatch:
|
||||||
return "htlc total amt doesn't match set total"
|
return "htlc total amt doesn't match set total"
|
||||||
|
|
||||||
case resultHtlcSetTotalTooLow:
|
case ResultHtlcSetTotalTooLow:
|
||||||
return "set total too low for invoice"
|
return "set total too low for invoice"
|
||||||
|
|
||||||
case resultHtlcSetOverpayment:
|
case ResultHtlcSetOverpayment:
|
||||||
return "mpp is overpaying set total"
|
return "mpp is overpaying set total"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -110,20 +153,20 @@ type invoiceUpdateCtx struct {
|
|||||||
// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice
|
// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice
|
||||||
// settlement logic.
|
// settlement logic.
|
||||||
func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||||
*channeldb.InvoiceUpdateDesc, updateResult, error) {
|
*channeldb.InvoiceUpdateDesc, ResolutionResult, error) {
|
||||||
|
|
||||||
// Don't update the invoice when this is a replayed htlc.
|
// Don't update the invoice when this is a replayed htlc.
|
||||||
htlc, ok := inv.Htlcs[ctx.circuitKey]
|
htlc, ok := inv.Htlcs[ctx.circuitKey]
|
||||||
if ok {
|
if ok {
|
||||||
switch htlc.State {
|
switch htlc.State {
|
||||||
case channeldb.HtlcStateCanceled:
|
case channeldb.HtlcStateCanceled:
|
||||||
return nil, resultReplayToCanceled, nil
|
return nil, ResultReplayToCanceled, nil
|
||||||
|
|
||||||
case channeldb.HtlcStateAccepted:
|
case channeldb.HtlcStateAccepted:
|
||||||
return nil, resultReplayToAccepted, nil
|
return nil, ResultReplayToAccepted, nil
|
||||||
|
|
||||||
case channeldb.HtlcStateSettled:
|
case channeldb.HtlcStateSettled:
|
||||||
return nil, resultReplayToSettled, nil
|
return nil, ResultReplayToSettled, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, 0, errors.New("unknown htlc state")
|
return nil, 0, errors.New("unknown htlc state")
|
||||||
@ -140,7 +183,7 @@ func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
// updateMpp is a callback for DB.UpdateInvoice that contains the invoice
|
// updateMpp is a callback for DB.UpdateInvoice that contains the invoice
|
||||||
// settlement logic for mpp payments.
|
// settlement logic for mpp payments.
|
||||||
func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||||
*channeldb.InvoiceUpdateDesc, updateResult, error) {
|
*channeldb.InvoiceUpdateDesc, ResolutionResult, error) {
|
||||||
|
|
||||||
// Start building the accept descriptor.
|
// Start building the accept descriptor.
|
||||||
acceptDesc := &channeldb.HtlcAcceptDesc{
|
acceptDesc := &channeldb.HtlcAcceptDesc{
|
||||||
@ -156,23 +199,23 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
// Because non-mpp payments don't have a payment address, this is needed
|
// Because non-mpp payments don't have a payment address, this is needed
|
||||||
// to thwart probing.
|
// to thwart probing.
|
||||||
if inv.State != channeldb.ContractOpen {
|
if inv.State != channeldb.ContractOpen {
|
||||||
return nil, resultInvoiceNotOpen, nil
|
return nil, ResultInvoiceNotOpen, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the payment address that authorizes the payment.
|
// Check the payment address that authorizes the payment.
|
||||||
if ctx.mpp.PaymentAddr() != inv.Terms.PaymentAddr {
|
if ctx.mpp.PaymentAddr() != inv.Terms.PaymentAddr {
|
||||||
return nil, resultAddressMismatch, nil
|
return nil, ResultAddressMismatch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't accept zero-valued sets.
|
// Don't accept zero-valued sets.
|
||||||
if ctx.mpp.TotalMsat() == 0 {
|
if ctx.mpp.TotalMsat() == 0 {
|
||||||
return nil, resultHtlcSetTotalTooLow, nil
|
return nil, ResultHtlcSetTotalTooLow, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the total amt of the htlc set is high enough. In case this
|
// Check that the total amt of the htlc set is high enough. In case this
|
||||||
// is a zero-valued invoice, it will always be enough.
|
// is a zero-valued invoice, it will always be enough.
|
||||||
if ctx.mpp.TotalMsat() < inv.Terms.Value {
|
if ctx.mpp.TotalMsat() < inv.Terms.Value {
|
||||||
return nil, resultHtlcSetTotalTooLow, nil
|
return nil, ResultHtlcSetTotalTooLow, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether total amt matches other htlcs in the set.
|
// Check whether total amt matches other htlcs in the set.
|
||||||
@ -186,7 +229,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ctx.mpp.TotalMsat() != htlc.MppTotalAmt {
|
if ctx.mpp.TotalMsat() != htlc.MppTotalAmt {
|
||||||
return nil, resultHtlcSetTotalMismatch, nil
|
return nil, ResultHtlcSetTotalMismatch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newSetTotal += htlc.Amt
|
newSetTotal += htlc.Amt
|
||||||
@ -197,16 +240,16 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
|
|
||||||
// Make sure the communicated set total isn't overpaid.
|
// Make sure the communicated set total isn't overpaid.
|
||||||
if newSetTotal > ctx.mpp.TotalMsat() {
|
if newSetTotal > ctx.mpp.TotalMsat() {
|
||||||
return nil, resultHtlcSetOverpayment, nil
|
return nil, ResultHtlcSetOverpayment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The invoice is still open. Check the expiry.
|
// The invoice is still open. Check the expiry.
|
||||||
if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
|
if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
|
||||||
return nil, resultExpiryTooSoon, nil
|
return nil, ResultExpiryTooSoon, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
|
if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
|
||||||
return nil, resultExpiryTooSoon, nil
|
return nil, ResultExpiryTooSoon, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record HTLC in the invoice database.
|
// Record HTLC in the invoice database.
|
||||||
@ -221,7 +264,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
// If the invoice cannot be settled yet, only record the htlc.
|
// If the invoice cannot be settled yet, only record the htlc.
|
||||||
setComplete := newSetTotal == ctx.mpp.TotalMsat()
|
setComplete := newSetTotal == ctx.mpp.TotalMsat()
|
||||||
if !setComplete {
|
if !setComplete {
|
||||||
return &update, resultPartialAccepted, nil
|
return &update, ResultPartialAccepted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if we can settle or this is an hold invoice and
|
// Check to see if we can settle or this is an hold invoice and
|
||||||
@ -231,7 +274,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractAccepted,
|
NewState: channeldb.ContractAccepted,
|
||||||
}
|
}
|
||||||
return &update, resultAccepted, nil
|
return &update, ResultAccepted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||||
@ -239,18 +282,18 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
Preimage: inv.Terms.PaymentPreimage,
|
Preimage: inv.Terms.PaymentPreimage,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &update, resultSettled, nil
|
return &update, ResultSettled, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateLegacy is a callback for DB.UpdateInvoice that contains the invoice
|
// updateLegacy is a callback for DB.UpdateInvoice that contains the invoice
|
||||||
// settlement logic for legacy payments.
|
// settlement logic for legacy payments.
|
||||||
func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||||
*channeldb.InvoiceUpdateDesc, updateResult, error) {
|
*channeldb.InvoiceUpdateDesc, ResolutionResult, error) {
|
||||||
|
|
||||||
// If the invoice is already canceled, there is no further
|
// If the invoice is already canceled, there is no further
|
||||||
// checking to do.
|
// checking to do.
|
||||||
if inv.State == channeldb.ContractCanceled {
|
if inv.State == channeldb.ContractCanceled {
|
||||||
return nil, resultInvoiceAlreadyCanceled, nil
|
return nil, ResultInvoiceAlreadyCanceled, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an invoice amount is specified, check that enough is paid. Also
|
// If an invoice amount is specified, check that enough is paid. Also
|
||||||
@ -258,7 +301,7 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
// or accepted. In case this is a zero-valued invoice, it will always be
|
// or accepted. In case this is a zero-valued invoice, it will always be
|
||||||
// enough.
|
// enough.
|
||||||
if ctx.amtPaid < inv.Terms.Value {
|
if ctx.amtPaid < inv.Terms.Value {
|
||||||
return nil, resultAmountTooLow, nil
|
return nil, ResultAmountTooLow, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(joostjager): Check invoice mpp required feature
|
// TODO(joostjager): Check invoice mpp required feature
|
||||||
@ -271,17 +314,17 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
if htlc.State == channeldb.HtlcStateAccepted &&
|
if htlc.State == channeldb.HtlcStateAccepted &&
|
||||||
htlc.MppTotalAmt > 0 {
|
htlc.MppTotalAmt > 0 {
|
||||||
|
|
||||||
return nil, resultMppInProgress, nil
|
return nil, ResultMppInProgress, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The invoice is still open. Check the expiry.
|
// The invoice is still open. Check the expiry.
|
||||||
if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
|
if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
|
||||||
return nil, resultExpiryTooSoon, nil
|
return nil, ResultExpiryTooSoon, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
|
if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
|
||||||
return nil, resultExpiryTooSoon, nil
|
return nil, ResultExpiryTooSoon, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record HTLC in the invoice database.
|
// Record HTLC in the invoice database.
|
||||||
@ -302,10 +345,10 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
// We do accept or settle the HTLC.
|
// We do accept or settle the HTLC.
|
||||||
switch inv.State {
|
switch inv.State {
|
||||||
case channeldb.ContractAccepted:
|
case channeldb.ContractAccepted:
|
||||||
return &update, resultDuplicateToAccepted, nil
|
return &update, ResultDuplicateToAccepted, nil
|
||||||
|
|
||||||
case channeldb.ContractSettled:
|
case channeldb.ContractSettled:
|
||||||
return &update, resultDuplicateToSettled, nil
|
return &update, ResultDuplicateToSettled, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if we can settle or this is an hold invoice and we need
|
// Check to see if we can settle or this is an hold invoice and we need
|
||||||
@ -315,7 +358,7 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||||
NewState: channeldb.ContractAccepted,
|
NewState: channeldb.ContractAccepted,
|
||||||
}
|
}
|
||||||
return &update, resultAccepted, nil
|
return &update, ResultAccepted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||||
@ -323,5 +366,5 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
|||||||
Preimage: inv.Terms.PaymentPreimage,
|
Preimage: inv.Terms.PaymentPreimage,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &update, resultSettled, nil
|
return &update, ResultSettled, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user