github.com/decred/dcrlnd@v0.7.6/channeldb/payments_inflight.go (about)

     1  package channeldb
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/btcsuite/btcwallet/walletdb"
    11  )
    12  
    13  var (
    14  	// paymentsInflightIndexBucket is the name of the top-level bucket
    15  	// within the database that stores an index of payment hashes that are
    16  	// still inflight.
    17  	//
    18  	// This is used to speedup startup by having a faster way to determine
    19  	// payments that are still inflight.
    20  	paymentsInflightIndexBucket = []byte("payments-inflight-index-bucket")
    21  
    22  	// errPaymentsInflightIndexNotExists is used to signal that the
    23  	// inflight payments index is disabled.
    24  	errPaymentsInflightIndexNotExists = errors.New("payments inflight index does not exist")
    25  )
    26  
    27  // paymentInflightIndexValueVersion is set as the value used in the inflight
    28  // payments index.
    29  const paymentInflightIndexValueVersion = 0
    30  
    31  // createPaymentInflightIndexEntry marks a payment with the given sequence
    32  // number as inflight.
    33  func createPaymentInflightIndexEntry(tx walletdb.ReadWriteTx, seqNum []byte) error {
    34  	indexBucket := tx.ReadWriteBucket(paymentsInflightIndexBucket)
    35  
    36  	// If the bucket doesn't exist, indexing of inflight payments is disabled.
    37  	if indexBucket == nil {
    38  		return nil
    39  	}
    40  
    41  	v := []byte{paymentInflightIndexValueVersion}
    42  	err := indexBucket.Put(seqNum, v)
    43  	if err != nil {
    44  		return fmt.Errorf("unable to mark inflight: %v", err)
    45  	}
    46  	return err
    47  }
    48  
    49  // deletePaymentInflightIndexEntry marks a payment with the given sequence
    50  // number as not inflight.
    51  func deletePaymentInflightIndexEntry(tx walletdb.ReadWriteTx, seqNum []byte) error {
    52  	indexBucket := tx.ReadWriteBucket(paymentsInflightIndexBucket)
    53  
    54  	// If the bucket doesn't exist, the entry can't be removed.
    55  	if indexBucket == nil {
    56  		return nil
    57  	}
    58  
    59  	return indexBucket.Delete(seqNum)
    60  }
    61  
    62  // updatePaymentInflightIndexEntry updates the entry of the given payment
    63  // in the inflight payments index based on the payment's status.
    64  func updatePaymentInflightIndexEntry(tx walletdb.ReadWriteTx, payment *MPPayment) error {
    65  	var seqBytes [8]byte
    66  	binary.BigEndian.PutUint64(seqBytes[:], payment.SequenceNum)
    67  
    68  	if payment.Status == StatusInFlight {
    69  		return createPaymentInflightIndexEntry(tx, seqBytes[:])
    70  	}
    71  
    72  	return deletePaymentInflightIndexEntry(tx, seqBytes[:])
    73  }
    74  
    75  // recreatePaymentsInflightIndex recreates the index of inflight payments in
    76  // the DB.
    77  func recreatePaymentsInflightIndex(tx walletdb.ReadWriteTx) error {
    78  	indexBucket := tx.ReadWriteBucket(paymentsInflightIndexBucket)
    79  	if indexBucket == nil {
    80  		var err error
    81  		_, err = tx.CreateTopLevelBucket(paymentsInflightIndexBucket)
    82  		if err != nil {
    83  			return err
    84  		}
    85  	}
    86  
    87  	payments := tx.ReadBucket(paymentsRootBucket)
    88  	if payments == nil {
    89  		return nil
    90  	}
    91  
    92  	var nbPayments, nbInflight uint64
    93  	err := payments.ForEach(func(k, _ []byte) error {
    94  		bucket := payments.NestedReadBucket(k)
    95  		if bucket == nil {
    96  			return fmt.Errorf("non bucket element")
    97  		}
    98  
    99  		p, err := fetchPayment(bucket)
   100  		if err != nil {
   101  			return err
   102  		}
   103  
   104  		nbPayments += 1
   105  		if p.Status == StatusInFlight {
   106  			nbInflight += 1
   107  		}
   108  
   109  		return updatePaymentInflightIndexEntry(tx, p)
   110  	})
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	log.Infof("Recreated in-flight payments index: %d payments, %d in-flight",
   116  		nbPayments, nbInflight)
   117  
   118  	return nil
   119  }
   120  
   121  // fetchInflightPaymentsByIndex fetches the list of inflight payments using the
   122  // inflight payments index. If the index is disabled, errPaymentsInflightIndexNotExists
   123  // is returned.
   124  func fetchInflightPaymentsByIndex(tx walletdb.ReadTx) ([]*MPPayment, error) {
   125  	indexBucket := tx.ReadBucket(paymentsInflightIndexBucket)
   126  
   127  	// If the bucket doesn't exist, notify caller.
   128  	if indexBucket == nil {
   129  		return nil, errPaymentsInflightIndexNotExists
   130  	}
   131  
   132  	payments := tx.ReadBucket(paymentsRootBucket)
   133  	if payments == nil {
   134  		return nil, fmt.Errorf("payments root bucket does not exist")
   135  	}
   136  
   137  	seqIndexes := tx.ReadBucket(paymentsIndexBucket)
   138  	if seqIndexes == nil {
   139  		return nil, fmt.Errorf("index bucket does not exist")
   140  	}
   141  
   142  	var inFlights []*MPPayment
   143  	err := indexBucket.ForEach(func(k, v []byte) error {
   144  		hash := seqIndexes.Get(k)
   145  		if hash == nil {
   146  			return fmt.Errorf("seqnum %s does not exist in index db",
   147  				hex.EncodeToString(k))
   148  		}
   149  		r := bytes.NewReader(hash)
   150  		paymentHash, err := deserializePaymentIndex(r)
   151  		if err != nil {
   152  			return err
   153  		}
   154  
   155  		bucket := payments.NestedReadBucket(paymentHash[:])
   156  		p, err := fetchPayment(bucket)
   157  		if err != nil {
   158  			return err
   159  		}
   160  
   161  		// Double check our assumption that this payment is in fact
   162  		// in-flight. If it is not, we warn about an inconsistency
   163  		// but otherwise don't completely fail to load the inflight
   164  		// payments to avoid breaking the ability to open the app.
   165  		if p.Status != StatusInFlight {
   166  			log.Warnf("Broken assumption: Payment hash %s (seqnum %d) "+
   167  				"in inflight payments index is not actually "+
   168  				"inflight (status %s - %d)",
   169  				paymentHash, p.SequenceNum, p.Status, p.Status)
   170  			return nil
   171  		}
   172  
   173  		inFlights = append(inFlights, p)
   174  		return nil
   175  	})
   176  	return inFlights, err
   177  }