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

     1  package migration16
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/decred/dcrd/wire"
    10  	"github.com/decred/dcrlnd/kvdb"
    11  )
    12  
    13  var (
    14  	paymentsRootBucket = []byte("payments-root-bucket")
    15  
    16  	paymentSequenceKey = []byte("payment-sequence-key")
    17  
    18  	duplicatePaymentsBucket = []byte("payment-duplicate-bucket")
    19  
    20  	paymentsIndexBucket = []byte("payments-index-bucket")
    21  
    22  	byteOrder = binary.BigEndian
    23  )
    24  
    25  // paymentIndexType indicates the type of index we have recorded in the payment
    26  // indexes bucket.
    27  type paymentIndexType uint8
    28  
    29  // paymentIndexTypeHash is a payment index type which indicates that we have
    30  // created an index of payment sequence number to payment hash.
    31  const paymentIndexTypeHash paymentIndexType = 0
    32  
    33  // paymentIndex stores all the information we require to create an index by
    34  // sequence number for a payment.
    35  type paymentIndex struct {
    36  	// paymentHash is the hash of the payment, which is its key in the
    37  	// payment root bucket.
    38  	paymentHash []byte
    39  
    40  	// sequenceNumbers is the set of sequence numbers associated with this
    41  	// payment hash. There will be more than one sequence number in the
    42  	// case where duplicate payments are present.
    43  	sequenceNumbers [][]byte
    44  }
    45  
    46  // MigrateSequenceIndex migrates the payments db to contain a new bucket which
    47  // provides an index from sequence number to payment hash. This is required
    48  // for more efficient sequential lookup of payments, which are keyed by payment
    49  // hash before this migration.
    50  func MigrateSequenceIndex(tx kvdb.RwTx) error {
    51  	log.Infof("Migrating payments to add sequence number index")
    52  
    53  	// Get a list of indices we need to write.
    54  	indexList, err := getPaymentIndexList(tx)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	// Create the top level bucket that we will use to index payments in.
    60  	bucket, err := tx.CreateTopLevelBucket(paymentsIndexBucket)
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	// Write an index for each of our payments.
    66  	for _, index := range indexList {
    67  		// Write indexes for each of our sequence numbers.
    68  		for _, seqNr := range index.sequenceNumbers {
    69  			err := putIndex(bucket, seqNr, index.paymentHash)
    70  			if err != nil {
    71  				return err
    72  			}
    73  		}
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  // putIndex performs a sanity check that ensures we are not writing duplicate
    80  // indexes to disk then creates the index provided.
    81  func putIndex(bucket kvdb.RwBucket, sequenceNr, paymentHash []byte) error {
    82  	// Add a sanity check that we do not already have an entry with
    83  	// this sequence number.
    84  	existingEntry := bucket.Get(sequenceNr)
    85  	if existingEntry != nil {
    86  		return fmt.Errorf("sequence number: %x duplicated",
    87  			sequenceNr)
    88  	}
    89  
    90  	bytes, err := serializePaymentIndexEntry(paymentHash)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	return bucket.Put(sequenceNr, bytes)
    96  }
    97  
    98  // serializePaymentIndexEntry serializes a payment hash typed index. The value
    99  // produced contains a payment index type (which can be used in future to
   100  // signal different payment index types) and the payment hash.
   101  func serializePaymentIndexEntry(hash []byte) ([]byte, error) {
   102  	var b bytes.Buffer
   103  
   104  	err := binary.Write(&b, byteOrder, paymentIndexTypeHash)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	if err := wire.WriteVarBytes(&b, 0, hash); err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	return b.Bytes(), nil
   114  }
   115  
   116  // getPaymentIndexList gets a list of indices we need to write for our current
   117  // set of payments.
   118  func getPaymentIndexList(tx kvdb.RTx) ([]paymentIndex, error) {
   119  	// Iterate over all payments and store their indexing keys. This is
   120  	// needed, because no modifications are allowed inside a Bucket.ForEach
   121  	// loop.
   122  	paymentsBucket := tx.ReadBucket(paymentsRootBucket)
   123  	if paymentsBucket == nil {
   124  		return nil, nil
   125  	}
   126  
   127  	var indexList []paymentIndex
   128  	err := paymentsBucket.ForEach(func(k, v []byte) error {
   129  		// Get the bucket which contains the payment, fail if the key
   130  		// does not have a bucket.
   131  		bucket := paymentsBucket.NestedReadBucket(k)
   132  		if bucket == nil {
   133  			return fmt.Errorf("non bucket element in " +
   134  				"payments bucket")
   135  		}
   136  		seqBytes := bucket.Get(paymentSequenceKey)
   137  		if seqBytes == nil {
   138  			return fmt.Errorf("nil sequence number bytes")
   139  		}
   140  
   141  		seqNrs, err := fetchSequenceNumbers(bucket)
   142  		if err != nil {
   143  			return err
   144  		}
   145  
   146  		// Create an index object with our payment hash and sequence
   147  		// numbers and append it to our set of indexes.
   148  		index := paymentIndex{
   149  			paymentHash:     k,
   150  			sequenceNumbers: seqNrs,
   151  		}
   152  
   153  		indexList = append(indexList, index)
   154  		return nil
   155  	})
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	return indexList, nil
   161  }
   162  
   163  // fetchSequenceNumbers fetches all the sequence numbers associated with a
   164  // payment, including those belonging to any duplicate payments.
   165  func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) {
   166  	seqNum := paymentBucket.Get(paymentSequenceKey)
   167  	if seqNum == nil {
   168  		return nil, errors.New("expected sequence number")
   169  	}
   170  
   171  	sequenceNumbers := [][]byte{seqNum}
   172  
   173  	// Get the duplicate payments bucket, if it has no duplicates, just
   174  	// return early with the payment sequence number.
   175  	duplicates := paymentBucket.NestedReadBucket(duplicatePaymentsBucket)
   176  	if duplicates == nil {
   177  		return sequenceNumbers, nil
   178  	}
   179  
   180  	// If we do have duplicated, they are keyed by sequence number, so we
   181  	// iterate through the duplicates bucket and add them to our set of
   182  	// sequence numbers.
   183  	if err := duplicates.ForEach(func(k, v []byte) error {
   184  		sequenceNumbers = append(sequenceNumbers, k)
   185  		return nil
   186  	}); err != nil {
   187  		return nil, err
   188  	}
   189  
   190  	return sequenceNumbers, nil
   191  }