channeldb/payments: add StatusFailed
This commit is contained in:
parent
bb4aadd16c
commit
6d80661bbb
@ -24,6 +24,10 @@ var (
|
|||||||
// recomplete a completed payment.
|
// recomplete a completed payment.
|
||||||
ErrPaymentAlreadyCompleted = errors.New("payment is already completed")
|
ErrPaymentAlreadyCompleted = errors.New("payment is already completed")
|
||||||
|
|
||||||
|
// ErrPaymentAlreadyFailed is returned in the event we attempt to
|
||||||
|
// re-fail a failed payment.
|
||||||
|
ErrPaymentAlreadyFailed = errors.New("payment has already failed")
|
||||||
|
|
||||||
// ErrUnknownPaymentStatus is returned when we do not recognize the
|
// ErrUnknownPaymentStatus is returned when we do not recognize the
|
||||||
// existing state of a payment.
|
// existing state of a payment.
|
||||||
ErrUnknownPaymentStatus = errors.New("unknown payment status")
|
ErrUnknownPaymentStatus = errors.New("unknown payment status")
|
||||||
@ -86,6 +90,10 @@ func (p *paymentControl) ClearForTakeoff(htlc *lnwire.UpdateAddHTLC) error {
|
|||||||
|
|
||||||
switch paymentStatus {
|
switch paymentStatus {
|
||||||
|
|
||||||
|
// We allow retrying failed payments.
|
||||||
|
case StatusFailed:
|
||||||
|
fallthrough
|
||||||
|
|
||||||
// It is safe to reattempt a payment if we know that we haven't
|
// It is safe to reattempt a payment if we know that we haven't
|
||||||
// left one in flight. Since this one is grounded or failed,
|
// left one in flight. Since this one is grounded or failed,
|
||||||
// transition the payment status to InFlight to prevent others.
|
// transition the payment status to InFlight to prevent others.
|
||||||
@ -121,41 +129,22 @@ func (p *paymentControl) ClearForTakeoff(htlc *lnwire.UpdateAddHTLC) error {
|
|||||||
func (p *paymentControl) Success(paymentHash [32]byte) error {
|
func (p *paymentControl) Success(paymentHash [32]byte) error {
|
||||||
var updateErr error
|
var updateErr error
|
||||||
err := p.db.Batch(func(tx *bbolt.Tx) error {
|
err := p.db.Batch(func(tx *bbolt.Tx) error {
|
||||||
|
// Reset the update error, to avoid carrying over an error
|
||||||
|
// from a previous execution of the batched db transaction.
|
||||||
|
updateErr = nil
|
||||||
|
|
||||||
bucket, err := fetchPaymentBucket(tx, paymentHash)
|
bucket, err := fetchPaymentBucket(tx, paymentHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the existing status, if any.
|
// We can only mark in-flight payments as succeeded.
|
||||||
paymentStatus := fetchPaymentStatus(bucket)
|
if err := ensureInFlight(bucket); err != nil {
|
||||||
|
updateErr = err
|
||||||
// Reset the update error, to avoid carrying over an error
|
return nil
|
||||||
// from a previous execution of the batched db transaction.
|
|
||||||
updateErr = nil
|
|
||||||
|
|
||||||
switch {
|
|
||||||
|
|
||||||
// Our records show the payment as still being grounded,
|
|
||||||
// meaning it never should have left the switch.
|
|
||||||
case paymentStatus == StatusGrounded:
|
|
||||||
updateErr = ErrPaymentNotInitiated
|
|
||||||
|
|
||||||
// A successful response was received for an InFlight payment,
|
|
||||||
// mark it as completed to prevent sending to this payment hash
|
|
||||||
// again.
|
|
||||||
case paymentStatus == StatusInFlight:
|
|
||||||
return bucket.Put(paymentStatusKey, StatusCompleted.Bytes())
|
|
||||||
|
|
||||||
// The payment was completed previously, alert the caller that
|
|
||||||
// this may be a duplicate call.
|
|
||||||
case paymentStatus == StatusCompleted:
|
|
||||||
updateErr = ErrPaymentAlreadyCompleted
|
|
||||||
|
|
||||||
default:
|
|
||||||
updateErr = ErrUnknownPaymentStatus
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return bucket.Put(paymentStatusKey, StatusCompleted.Bytes())
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -170,40 +159,22 @@ func (p *paymentControl) Success(paymentHash [32]byte) error {
|
|||||||
func (p *paymentControl) Fail(paymentHash [32]byte) error {
|
func (p *paymentControl) Fail(paymentHash [32]byte) error {
|
||||||
var updateErr error
|
var updateErr error
|
||||||
err := p.db.Batch(func(tx *bbolt.Tx) error {
|
err := p.db.Batch(func(tx *bbolt.Tx) error {
|
||||||
|
// Reset the update error, to avoid carrying over an error
|
||||||
|
// from a previous execution of the batched db transaction.
|
||||||
|
updateErr = nil
|
||||||
|
|
||||||
bucket, err := fetchPaymentBucket(tx, paymentHash)
|
bucket, err := fetchPaymentBucket(tx, paymentHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentStatus := fetchPaymentStatus(bucket)
|
// We can only mark in-flight payments as failed.
|
||||||
|
if err := ensureInFlight(bucket); err != nil {
|
||||||
// Reset the update error, to avoid carrying over an error
|
updateErr = err
|
||||||
// from a previous execution of the batched db transaction.
|
return nil
|
||||||
updateErr = nil
|
|
||||||
|
|
||||||
switch {
|
|
||||||
|
|
||||||
// Our records show the payment as still being grounded,
|
|
||||||
// meaning it never should have left the switch.
|
|
||||||
case paymentStatus == StatusGrounded:
|
|
||||||
updateErr = ErrPaymentNotInitiated
|
|
||||||
|
|
||||||
// A failed response was received for an InFlight payment, mark
|
|
||||||
// it as Failed to allow subsequent attempts.
|
|
||||||
case paymentStatus == StatusInFlight:
|
|
||||||
return bucket.Put(paymentStatusKey, StatusGrounded.Bytes())
|
|
||||||
|
|
||||||
// The payment was completed previously, and we are now
|
|
||||||
// reporting that it has failed. Leave the status as completed,
|
|
||||||
// but alert the user that something is wrong.
|
|
||||||
case paymentStatus == StatusCompleted:
|
|
||||||
updateErr = ErrPaymentAlreadyCompleted
|
|
||||||
|
|
||||||
default:
|
|
||||||
updateErr = ErrUnknownPaymentStatus
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return bucket.Put(paymentStatusKey, StatusGrounded.Bytes())
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -239,3 +210,33 @@ func fetchPaymentStatus(bucket *bbolt.Bucket) PaymentStatus {
|
|||||||
|
|
||||||
return paymentStatus
|
return paymentStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensureInFlight checks whether the payment found in the given bucket has
|
||||||
|
// status InFlight, and returns an error otherwise. This should be used to
|
||||||
|
// ensure we only mark in-flight payments as succeeded or failed.
|
||||||
|
func ensureInFlight(bucket *bbolt.Bucket) error {
|
||||||
|
paymentStatus := fetchPaymentStatus(bucket)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
|
||||||
|
// The payment was indeed InFlight, return.
|
||||||
|
case paymentStatus == StatusInFlight:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Our records show the payment as unknown, meaning it never
|
||||||
|
// should have left the switch.
|
||||||
|
case paymentStatus == StatusGrounded:
|
||||||
|
return ErrPaymentNotInitiated
|
||||||
|
|
||||||
|
// The payment succeeded previously.
|
||||||
|
case paymentStatus == StatusCompleted:
|
||||||
|
return ErrPaymentAlreadyCompleted
|
||||||
|
|
||||||
|
// The payment was already failed.
|
||||||
|
case paymentStatus == StatusFailed:
|
||||||
|
return ErrPaymentAlreadyFailed
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ErrUnknownPaymentStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -99,8 +99,7 @@ type PaymentStatus byte
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// StatusGrounded is the status where a payment has never been
|
// StatusGrounded is the status where a payment has never been
|
||||||
// initiated, or has been initiated and received an intermittent
|
// initiated.
|
||||||
// failure.
|
|
||||||
StatusGrounded PaymentStatus = 0
|
StatusGrounded PaymentStatus = 0
|
||||||
|
|
||||||
// StatusInFlight is the status where a payment has been initiated, but
|
// StatusInFlight is the status where a payment has been initiated, but
|
||||||
@ -110,6 +109,12 @@ const (
|
|||||||
// StatusCompleted is the status where a payment has been initiated and
|
// StatusCompleted is the status where a payment has been initiated and
|
||||||
// the payment was completed successfully.
|
// the payment was completed successfully.
|
||||||
StatusCompleted PaymentStatus = 2
|
StatusCompleted PaymentStatus = 2
|
||||||
|
|
||||||
|
// StatusFailed is the status where a payment has been initiated and a
|
||||||
|
// failure result has come back.
|
||||||
|
StatusFailed PaymentStatus = 3
|
||||||
|
|
||||||
|
// TODO(halseth): timeout/cancel state?
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bytes returns status as slice of bytes.
|
// Bytes returns status as slice of bytes.
|
||||||
@ -124,7 +129,7 @@ func (ps *PaymentStatus) FromBytes(status []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch PaymentStatus(status[0]) {
|
switch PaymentStatus(status[0]) {
|
||||||
case StatusGrounded, StatusInFlight, StatusCompleted:
|
case StatusGrounded, StatusInFlight, StatusCompleted, StatusFailed:
|
||||||
*ps = PaymentStatus(status[0])
|
*ps = PaymentStatus(status[0])
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown payment status")
|
return errors.New("unknown payment status")
|
||||||
@ -142,6 +147,8 @@ func (ps PaymentStatus) String() string {
|
|||||||
return "In Flight"
|
return "In Flight"
|
||||||
case StatusCompleted:
|
case StatusCompleted:
|
||||||
return "Completed"
|
return "Completed"
|
||||||
|
case StatusFailed:
|
||||||
|
return "Failed"
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user