github.com/decred/dcrlnd@v0.7.6/channeldb/payment_control.go (about) 1 package channeldb 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "io" 9 "sync" 10 11 "github.com/decred/dcrlnd/kvdb" 12 "github.com/decred/dcrlnd/lntypes" 13 ) 14 15 const ( 16 // paymentSeqBlockSize is the block size used when we batch allocate 17 // payment sequences for future payments. 18 paymentSeqBlockSize = 1000 19 ) 20 21 var ( 22 // ErrAlreadyPaid signals we have already paid this payment hash. 23 ErrAlreadyPaid = errors.New("invoice is already paid") 24 25 // ErrPaymentInFlight signals that payment for this payment hash is 26 // already "in flight" on the network. 27 ErrPaymentInFlight = errors.New("payment is in transition") 28 29 // ErrPaymentNotInitiated is returned if the payment wasn't initiated. 30 ErrPaymentNotInitiated = errors.New("payment isn't initiated") 31 32 // ErrPaymentAlreadySucceeded is returned in the event we attempt to 33 // change the status of a payment already succeeded. 34 ErrPaymentAlreadySucceeded = errors.New("payment is already succeeded") 35 36 // ErrPaymentAlreadyFailed is returned in the event we attempt to alter 37 // a failed payment. 38 ErrPaymentAlreadyFailed = errors.New("payment has already failed") 39 40 // ErrUnknownPaymentStatus is returned when we do not recognize the 41 // existing state of a payment. 42 ErrUnknownPaymentStatus = errors.New("unknown payment status") 43 44 // ErrPaymentTerminal is returned if we attempt to alter a payment that 45 // already has reached a terminal condition. 46 ErrPaymentTerminal = errors.New("payment has reached terminal condition") 47 48 // ErrAttemptAlreadySettled is returned if we try to alter an already 49 // settled HTLC attempt. 50 ErrAttemptAlreadySettled = errors.New("attempt already settled") 51 52 // ErrAttemptAlreadyFailed is returned if we try to alter an already 53 // failed HTLC attempt. 54 ErrAttemptAlreadyFailed = errors.New("attempt already failed") 55 56 // ErrValueMismatch is returned if we try to register a non-MPP attempt 57 // with an amount that doesn't match the payment amount. 58 ErrValueMismatch = errors.New("attempted value doesn't match payment" + 59 "amount") 60 61 // ErrValueExceedsAmt is returned if we try to register an attempt that 62 // would take the total sent amount above the payment amount. 63 ErrValueExceedsAmt = errors.New("attempted value exceeds payment" + 64 "amount") 65 66 // ErrNonMPPayment is returned if we try to register an MPP attempt for 67 // a payment that already has a non-MPP attempt regitered. 68 ErrNonMPPayment = errors.New("payment has non-MPP attempts") 69 70 // ErrMPPayment is returned if we try to register a non-MPP attempt for 71 // a payment that already has an MPP attempt regitered. 72 ErrMPPayment = errors.New("payment has MPP attempts") 73 74 // ErrMPPPaymentAddrMismatch is returned if we try to register an MPP 75 // shard where the payment address doesn't match existing shards. 76 ErrMPPPaymentAddrMismatch = errors.New("payment address mismatch") 77 78 // ErrMPPTotalAmountMismatch is returned if we try to register an MPP 79 // shard where the total amount doesn't match existing shards. 80 ErrMPPTotalAmountMismatch = errors.New("mp payment total amount mismatch") 81 82 // errNoAttemptInfo is returned when no attempt info is stored yet. 83 errNoAttemptInfo = errors.New("unable to find attempt info for " + 84 "inflight payment") 85 86 // errNoSequenceNrIndex is returned when an attempt to lookup a payment 87 // index is made for a sequence number that is not indexed. 88 errNoSequenceNrIndex = errors.New("payment sequence number index " + 89 "does not exist") 90 ) 91 92 // PaymentControl implements persistence for payments and payment attempts. 93 type PaymentControl struct { 94 paymentSeqMx sync.Mutex 95 currPaymentSeq uint64 96 storedPaymentSeq uint64 97 db *DB 98 } 99 100 // NewPaymentControl creates a new instance of the PaymentControl. 101 func NewPaymentControl(db *DB) *PaymentControl { 102 return &PaymentControl{ 103 db: db, 104 } 105 } 106 107 // InitPayment checks or records the given PaymentCreationInfo with the DB, 108 // making sure it does not already exist as an in-flight payment. When this 109 // method returns successfully, the payment is guranteeed to be in the InFlight 110 // state. 111 func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, 112 info *PaymentCreationInfo) error { 113 114 // Obtain a new sequence number for this payment. This is used 115 // to sort the payments in order of creation, and also acts as 116 // a unique identifier for each payment. 117 sequenceNum, err := p.nextPaymentSequence() 118 if err != nil { 119 return err 120 } 121 122 var b bytes.Buffer 123 if err := serializePaymentCreationInfo(&b, info); err != nil { 124 return err 125 } 126 infoBytes := b.Bytes() 127 128 var updateErr error 129 err = kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error { 130 // Reset the update error, to avoid carrying over an error 131 // from a previous execution of the batched db transaction. 132 updateErr = nil 133 134 prefetchPayment(tx, paymentHash) 135 bucket, err := createPaymentBucket(tx, paymentHash) 136 if err != nil { 137 return err 138 } 139 140 // Get the existing status of this payment, if any. 141 paymentStatus, err := fetchPaymentStatus(bucket) 142 if err != nil { 143 return err 144 } 145 146 switch paymentStatus { 147 148 // We allow retrying failed payments. 149 case StatusFailed: 150 151 // This is a new payment that is being initialized for the 152 // first time. 153 case StatusUnknown: 154 155 // We already have an InFlight payment on the network. We will 156 // disallow any new payments. 157 case StatusInFlight: 158 updateErr = ErrPaymentInFlight 159 return nil 160 161 // We've already succeeded a payment to this payment hash, 162 // forbid the switch from sending another. 163 case StatusSucceeded: 164 updateErr = ErrAlreadyPaid 165 return nil 166 167 default: 168 updateErr = ErrUnknownPaymentStatus 169 return nil 170 } 171 172 // Before we set our new sequence number, we check whether this 173 // payment has a previously set sequence number and remove its 174 // index entry if it exists. This happens in the case where we 175 // have a previously attempted payment which was left in a state 176 // where we can retry. 177 seqBytes := bucket.Get(paymentSequenceKey) 178 if seqBytes != nil { 179 indexBucket := tx.ReadWriteBucket(paymentsIndexBucket) 180 if err := indexBucket.Delete(seqBytes); err != nil { 181 return err 182 } 183 } 184 185 // Once we have obtained a sequence number, we add an entry 186 // to our index bucket which will map the sequence number to 187 // our payment identifier. 188 err = createPaymentIndexEntry( 189 tx, sequenceNum, info.PaymentIdentifier, 190 ) 191 if err != nil { 192 return err 193 } 194 195 err = bucket.Put(paymentSequenceKey, sequenceNum) 196 if err != nil { 197 return err 198 } 199 200 // Add the payment info to the bucket, which contains the 201 // static information for this payment 202 err = bucket.Put(paymentCreationInfoKey, infoBytes) 203 if err != nil { 204 return err 205 } 206 207 // We'll delete any lingering HTLCs to start with, in case we 208 // are initializing a payment that was attempted earlier, but 209 // left in a state where we could retry. 210 err = bucket.DeleteNestedBucket(paymentHtlcsBucket) 211 if err != nil && err != kvdb.ErrBucketNotFound { 212 return err 213 } 214 215 // Mark payment as inflight in the inflight payments index. 216 err = createPaymentInflightIndexEntry(tx, sequenceNum) 217 if err != nil { 218 return err 219 } 220 221 // Also delete any lingering failure info now that we are 222 // re-attempting. 223 return bucket.Delete(paymentFailInfoKey) 224 }) 225 if err != nil { 226 return err 227 } 228 229 return updateErr 230 } 231 232 // paymentIndexTypeHash is a payment index type which indicates that we have 233 // created an index of payment sequence number to payment hash. 234 type paymentIndexType uint8 235 236 // paymentIndexTypeHash is a payment index type which indicates that we have 237 // created an index of payment sequence number to payment hash. 238 const paymentIndexTypeHash paymentIndexType = 0 239 240 // createPaymentIndexEntry creates a payment hash typed index for a payment. The 241 // index produced contains a payment index type (which can be used in future to 242 // signal different payment index types) and the payment identifier. 243 func createPaymentIndexEntry(tx kvdb.RwTx, sequenceNumber []byte, 244 id lntypes.Hash) error { 245 246 var b bytes.Buffer 247 if err := WriteElements(&b, paymentIndexTypeHash, id[:]); err != nil { 248 return err 249 } 250 251 indexes := tx.ReadWriteBucket(paymentsIndexBucket) 252 return indexes.Put(sequenceNumber, b.Bytes()) 253 } 254 255 // deserializePaymentIndex deserializes a payment index entry. This function 256 // currently only supports deserialization of payment hash indexes, and will 257 // fail for other types. 258 func deserializePaymentIndex(r io.Reader) (lntypes.Hash, error) { 259 var ( 260 indexType paymentIndexType 261 paymentHash []byte 262 ) 263 264 if err := ReadElements(r, &indexType, &paymentHash); err != nil { 265 return lntypes.Hash{}, err 266 } 267 268 // While we only have on payment index type, we do not need to use our 269 // index type to deserialize the index. However, we sanity check that 270 // this type is as expected, since we had to read it out anyway. 271 if indexType != paymentIndexTypeHash { 272 return lntypes.Hash{}, fmt.Errorf("unknown payment index "+ 273 "type: %v", indexType) 274 } 275 276 hash, err := lntypes.MakeHash(paymentHash) 277 if err != nil { 278 return lntypes.Hash{}, err 279 } 280 281 return hash, nil 282 } 283 284 // RegisterAttempt atomically records the provided HTLCAttemptInfo to the 285 // DB. 286 func (p *PaymentControl) RegisterAttempt(paymentHash lntypes.Hash, 287 attempt *HTLCAttemptInfo) (*MPPayment, error) { 288 289 // Serialize the information before opening the db transaction. 290 var a bytes.Buffer 291 err := serializeHTLCAttemptInfo(&a, attempt) 292 if err != nil { 293 return nil, err 294 } 295 htlcInfoBytes := a.Bytes() 296 297 htlcIDBytes := make([]byte, 8) 298 binary.BigEndian.PutUint64(htlcIDBytes, attempt.AttemptID) 299 300 var payment *MPPayment 301 err = kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error { 302 prefetchPayment(tx, paymentHash) 303 bucket, err := fetchPaymentBucketUpdate(tx, paymentHash) 304 if err != nil { 305 return err 306 } 307 308 p, err := fetchPayment(bucket) 309 if err != nil { 310 return err 311 } 312 313 // We cannot register a new attempt if the payment already has 314 // reached a terminal condition. We check this before 315 // ensureInFlight because it is a more general check. 316 settle, fail := p.TerminalInfo() 317 if settle != nil || fail != nil { 318 return ErrPaymentTerminal 319 } 320 321 // Ensure the payment is in-flight. 322 if err := ensureInFlight(p); err != nil { 323 return err 324 } 325 326 // Make sure any existing shards match the new one with regards 327 // to MPP options. 328 mpp := attempt.Route.FinalHop().MPP 329 for _, h := range p.InFlightHTLCs() { 330 hMpp := h.Route.FinalHop().MPP 331 332 switch { 333 334 // We tried to register a non-MPP attempt for a MPP 335 // payment. 336 case mpp == nil && hMpp != nil: 337 return ErrMPPayment 338 339 // We tried to register a MPP shard for a non-MPP 340 // payment. 341 case mpp != nil && hMpp == nil: 342 return ErrNonMPPayment 343 344 // Non-MPP payment, nothing more to validate. 345 case mpp == nil: 346 continue 347 } 348 349 // Check that MPP options match. 350 if mpp.PaymentAddr() != hMpp.PaymentAddr() { 351 return ErrMPPPaymentAddrMismatch 352 } 353 354 if mpp.TotalMAtoms() != hMpp.TotalMAtoms() { 355 return ErrMPPTotalAmountMismatch 356 } 357 } 358 359 // If this is a non-MPP attempt, it must match the total amount 360 // exactly. 361 amt := attempt.Route.ReceiverAmt() 362 if mpp == nil && amt != p.Info.Value { 363 return ErrValueMismatch 364 } 365 366 // Ensure we aren't sending more than the total payment amount. 367 sentAmt, _ := p.SentAmt() 368 if sentAmt+amt > p.Info.Value { 369 return ErrValueExceedsAmt 370 } 371 372 htlcsBucket, err := bucket.CreateBucketIfNotExists( 373 paymentHtlcsBucket, 374 ) 375 if err != nil { 376 return err 377 } 378 379 err = htlcsBucket.Put( 380 htlcBucketKey(htlcAttemptInfoKey, htlcIDBytes), 381 htlcInfoBytes, 382 ) 383 if err != nil { 384 return err 385 } 386 387 // Retrieve attempt info for the notification. 388 payment, err = fetchPayment(bucket) 389 if err != nil { 390 return err 391 } 392 393 err = updatePaymentInflightIndexEntry(tx, payment) 394 return err 395 }) 396 if err != nil { 397 return nil, err 398 } 399 400 return payment, err 401 } 402 403 // SettleAttempt marks the given attempt settled with the preimage. If this is 404 // a multi shard payment, this might implicitly mean that the full payment 405 // succeeded. 406 // 407 // After invoking this method, InitPayment should always return an error to 408 // prevent us from making duplicate payments to the same payment hash. The 409 // provided preimage is atomically saved to the DB for record keeping. 410 func (p *PaymentControl) SettleAttempt(hash lntypes.Hash, 411 attemptID uint64, settleInfo *HTLCSettleInfo) (*MPPayment, error) { 412 413 var b bytes.Buffer 414 if err := serializeHTLCSettleInfo(&b, settleInfo); err != nil { 415 return nil, err 416 } 417 settleBytes := b.Bytes() 418 419 return p.updateHtlcKey(hash, attemptID, htlcSettleInfoKey, settleBytes) 420 } 421 422 // FailAttempt marks the given payment attempt failed. 423 func (p *PaymentControl) FailAttempt(hash lntypes.Hash, 424 attemptID uint64, failInfo *HTLCFailInfo) (*MPPayment, error) { 425 426 var b bytes.Buffer 427 if err := serializeHTLCFailInfo(&b, failInfo); err != nil { 428 return nil, err 429 } 430 failBytes := b.Bytes() 431 432 return p.updateHtlcKey(hash, attemptID, htlcFailInfoKey, failBytes) 433 } 434 435 // updateHtlcKey updates a database key for the specified htlc. 436 func (p *PaymentControl) updateHtlcKey(paymentHash lntypes.Hash, 437 attemptID uint64, key, value []byte) (*MPPayment, error) { 438 439 aid := make([]byte, 8) 440 binary.BigEndian.PutUint64(aid, attemptID) 441 442 var payment *MPPayment 443 err := kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error { 444 payment = nil 445 446 prefetchPayment(tx, paymentHash) 447 bucket, err := fetchPaymentBucketUpdate(tx, paymentHash) 448 if err != nil { 449 return err 450 } 451 452 p, err := fetchPayment(bucket) 453 if err != nil { 454 return err 455 } 456 457 // We can only update keys of in-flight payments. We allow 458 // updating keys even if the payment has reached a terminal 459 // condition, since the HTLC outcomes must still be updated. 460 if err := ensureInFlight(p); err != nil { 461 return err 462 } 463 464 htlcsBucket := bucket.NestedReadWriteBucket(paymentHtlcsBucket) 465 if htlcsBucket == nil { 466 return fmt.Errorf("htlcs bucket not found") 467 } 468 469 if htlcsBucket.Get(htlcBucketKey(htlcAttemptInfoKey, aid)) == nil { 470 return fmt.Errorf("HTLC with ID %v not registered", 471 attemptID) 472 } 473 474 // Make sure the shard is not already failed or settled. 475 if htlcsBucket.Get(htlcBucketKey(htlcFailInfoKey, aid)) != nil { 476 return ErrAttemptAlreadyFailed 477 } 478 479 if htlcsBucket.Get(htlcBucketKey(htlcSettleInfoKey, aid)) != nil { 480 return ErrAttemptAlreadySettled 481 } 482 483 // Add or update the key for this htlc. 484 err = htlcsBucket.Put(htlcBucketKey(key, aid), value) 485 if err != nil { 486 return err 487 } 488 489 // Retrieve attempt info for the notification. 490 payment, err = fetchPayment(bucket) 491 if err != nil { 492 return err 493 } 494 495 err = updatePaymentInflightIndexEntry(tx, payment) 496 return err 497 }) 498 if err != nil { 499 return nil, err 500 } 501 502 return payment, err 503 } 504 505 // Fail transitions a payment into the Failed state, and records the reason the 506 // payment failed. After invoking this method, InitPayment should return nil on 507 // its next call for this payment hash, allowing the switch to make a 508 // subsequent payment. 509 func (p *PaymentControl) Fail(paymentHash lntypes.Hash, 510 reason FailureReason) (*MPPayment, error) { 511 512 var ( 513 updateErr error 514 payment *MPPayment 515 ) 516 err := kvdb.Batch(p.db.Backend, func(tx kvdb.RwTx) error { 517 // Reset the update error, to avoid carrying over an error 518 // from a previous execution of the batched db transaction. 519 updateErr = nil 520 payment = nil 521 522 prefetchPayment(tx, paymentHash) 523 bucket, err := fetchPaymentBucketUpdate(tx, paymentHash) 524 if err == ErrPaymentNotInitiated { 525 updateErr = ErrPaymentNotInitiated 526 return nil 527 } else if err != nil { 528 return err 529 } 530 531 // We mark the payment as failed as long as it is known. This 532 // lets the last attempt to fail with a terminal write its 533 // failure to the PaymentControl without synchronizing with 534 // other attempts. 535 paymentStatus, err := fetchPaymentStatus(bucket) 536 if err != nil { 537 return err 538 } 539 540 if paymentStatus == StatusUnknown { 541 updateErr = ErrPaymentNotInitiated 542 return nil 543 } 544 545 // Put the failure reason in the bucket for record keeping. 546 v := []byte{byte(reason)} 547 err = bucket.Put(paymentFailInfoKey, v) 548 if err != nil { 549 return err 550 } 551 552 // Retrieve attempt info for the notification, if available. 553 payment, err = fetchPayment(bucket) 554 if err != nil { 555 return err 556 } 557 558 err = updatePaymentInflightIndexEntry(tx, payment) 559 return err 560 }) 561 if err != nil { 562 return nil, err 563 } 564 565 return payment, updateErr 566 } 567 568 // FetchPayment returns information about a payment from the database. 569 func (p *PaymentControl) FetchPayment(paymentHash lntypes.Hash) ( 570 *MPPayment, error) { 571 572 var payment *MPPayment 573 err := kvdb.View(p.db, func(tx kvdb.RTx) error { 574 prefetchPayment(tx, paymentHash) 575 bucket, err := fetchPaymentBucket(tx, paymentHash) 576 if err != nil { 577 return err 578 } 579 580 payment, err = fetchPayment(bucket) 581 582 return err 583 }, func() { 584 payment = nil 585 }) 586 if err != nil { 587 return nil, err 588 } 589 590 return payment, nil 591 } 592 593 // prefetchPayment attempts to prefetch as much of the payment as possible to 594 // reduce DB roundtrips. 595 func prefetchPayment(tx kvdb.RTx, paymentHash lntypes.Hash) { 596 rb := kvdb.RootBucket(tx) 597 kvdb.Prefetch( 598 rb, 599 []string{ 600 // Prefetch all keys in the payment's bucket. 601 string(paymentsRootBucket), 602 string(paymentHash[:]), 603 }, 604 []string{ 605 // Prefetch all keys in the payment's htlc bucket. 606 string(paymentsRootBucket), 607 string(paymentHash[:]), 608 string(paymentHtlcsBucket), 609 }, 610 ) 611 } 612 613 // createPaymentBucket creates or fetches the sub-bucket assigned to this 614 // payment hash. 615 func createPaymentBucket(tx kvdb.RwTx, paymentHash lntypes.Hash) ( 616 kvdb.RwBucket, error) { 617 618 payments, err := tx.CreateTopLevelBucket(paymentsRootBucket) 619 if err != nil { 620 return nil, err 621 } 622 623 return payments.CreateBucketIfNotExists(paymentHash[:]) 624 } 625 626 // fetchPaymentBucket fetches the sub-bucket assigned to this payment hash. If 627 // the bucket does not exist, it returns ErrPaymentNotInitiated. 628 func fetchPaymentBucket(tx kvdb.RTx, paymentHash lntypes.Hash) ( 629 kvdb.RBucket, error) { 630 631 payments := tx.ReadBucket(paymentsRootBucket) 632 if payments == nil { 633 return nil, ErrPaymentNotInitiated 634 } 635 636 bucket := payments.NestedReadBucket(paymentHash[:]) 637 if bucket == nil { 638 return nil, ErrPaymentNotInitiated 639 } 640 641 return bucket, nil 642 643 } 644 645 // fetchPaymentBucketUpdate is identical to fetchPaymentBucket, but it returns a 646 // bucket that can be written to. 647 func fetchPaymentBucketUpdate(tx kvdb.RwTx, paymentHash lntypes.Hash) ( 648 kvdb.RwBucket, error) { 649 650 payments := tx.ReadWriteBucket(paymentsRootBucket) 651 if payments == nil { 652 return nil, ErrPaymentNotInitiated 653 } 654 655 bucket := payments.NestedReadWriteBucket(paymentHash[:]) 656 if bucket == nil { 657 return nil, ErrPaymentNotInitiated 658 } 659 660 return bucket, nil 661 } 662 663 // nextPaymentSequence returns the next sequence number to store for a new 664 // payment. 665 func (p *PaymentControl) nextPaymentSequence() ([]byte, error) { 666 p.paymentSeqMx.Lock() 667 defer p.paymentSeqMx.Unlock() 668 669 // Set a new upper bound in the DB every 1000 payments to avoid 670 // conflicts on the sequence when using etcd. 671 if p.currPaymentSeq == p.storedPaymentSeq { 672 var currPaymentSeq, newUpperBound uint64 673 if err := kvdb.Update(p.db.Backend, func(tx kvdb.RwTx) error { 674 paymentsBucket, err := tx.CreateTopLevelBucket( 675 paymentsRootBucket, 676 ) 677 if err != nil { 678 return err 679 } 680 681 currPaymentSeq = paymentsBucket.Sequence() 682 newUpperBound = currPaymentSeq + paymentSeqBlockSize 683 return paymentsBucket.SetSequence(newUpperBound) 684 }, func() {}); err != nil { 685 return nil, err 686 } 687 688 // We lazy initialize the cached currPaymentSeq here using the 689 // first nextPaymentSequence() call. This if statement will auto 690 // initialize our stored currPaymentSeq, since by default both 691 // this variable and storedPaymentSeq are zero which in turn 692 // will have us fetch the current values from the DB. 693 if p.currPaymentSeq == 0 { 694 p.currPaymentSeq = currPaymentSeq 695 } 696 697 p.storedPaymentSeq = newUpperBound 698 } 699 700 p.currPaymentSeq++ 701 b := make([]byte, 8) 702 binary.BigEndian.PutUint64(b, p.currPaymentSeq) 703 704 return b, nil 705 } 706 707 // fetchPaymentStatus fetches the payment status of the payment. If the payment 708 // isn't found, it will default to "StatusUnknown". 709 func fetchPaymentStatus(bucket kvdb.RBucket) (PaymentStatus, error) { 710 // Creation info should be set for all payments, regardless of state. 711 // If not, it is unknown. 712 if bucket.Get(paymentCreationInfoKey) == nil { 713 return StatusUnknown, nil 714 } 715 716 payment, err := fetchPayment(bucket) 717 if err != nil { 718 return 0, err 719 } 720 721 return payment.Status, nil 722 } 723 724 // ensureInFlight checks whether the payment found in the given bucket has 725 // status InFlight, and returns an error otherwise. This should be used to 726 // ensure we only mark in-flight payments as succeeded or failed. 727 func ensureInFlight(payment *MPPayment) error { 728 paymentStatus := payment.Status 729 730 switch { 731 732 // The payment was indeed InFlight. 733 case paymentStatus == StatusInFlight: 734 return nil 735 736 // Our records show the payment as unknown, meaning it never 737 // should have left the switch. 738 case paymentStatus == StatusUnknown: 739 return ErrPaymentNotInitiated 740 741 // The payment succeeded previously. 742 case paymentStatus == StatusSucceeded: 743 return ErrPaymentAlreadySucceeded 744 745 // The payment was already failed. 746 case paymentStatus == StatusFailed: 747 return ErrPaymentAlreadyFailed 748 749 default: 750 return ErrUnknownPaymentStatus 751 } 752 } 753 754 // FetchInFlightPayments returns all payments with status InFlight. 755 func (p *PaymentControl) FetchInFlightPayments() ([]*MPPayment, error) { 756 var inFlights []*MPPayment 757 err := kvdb.View(p.db, func(tx kvdb.RTx) error { 758 payments := tx.ReadBucket(paymentsRootBucket) 759 if payments == nil { 760 return nil 761 } 762 763 // Try to load the list of inflight payments by index. If the 764 // index does not exist (which is signalled by a specific error), 765 // continue attempting to list inflight payments 766 var err error 767 inFlights, err = fetchInflightPaymentsByIndex(tx) 768 if err == nil { 769 return nil 770 } 771 if err != nil && !errors.Is(err, errPaymentsInflightIndexNotExists) { 772 return err 773 } 774 775 return payments.ForEach(func(k, _ []byte) error { 776 bucket := payments.NestedReadBucket(k) 777 if bucket == nil { 778 return fmt.Errorf("non bucket element") 779 } 780 781 // If the status is not InFlight, we can return early. 782 paymentStatus, err := fetchPaymentStatus(bucket) 783 if err != nil { 784 return err 785 } 786 787 if paymentStatus != StatusInFlight { 788 return nil 789 } 790 791 p, err := fetchPayment(bucket) 792 if err != nil { 793 return err 794 } 795 796 inFlights = append(inFlights, p) 797 return nil 798 }) 799 }, func() { 800 inFlights = nil 801 }) 802 if err != nil { 803 return nil, err 804 } 805 806 return inFlights, nil 807 }