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

     1  package migration13
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  
     7  	"github.com/decred/dcrlnd/kvdb"
     8  )
     9  
    10  var (
    11  	paymentsRootBucket = []byte("payments-root-bucket")
    12  
    13  	// paymentCreationInfoKey is a key used in the payment's sub-bucket to
    14  	// store the creation info of the payment.
    15  	paymentCreationInfoKey = []byte("payment-creation-info")
    16  
    17  	// paymentFailInfoKey is a key used in the payment's sub-bucket to
    18  	// store information about the reason a payment failed.
    19  	paymentFailInfoKey = []byte("payment-fail-info")
    20  
    21  	// paymentAttemptInfoKey is a key used in the payment's sub-bucket to
    22  	// store the info about the latest attempt that was done for the
    23  	// payment in question.
    24  	paymentAttemptInfoKey = []byte("payment-attempt-info")
    25  
    26  	// paymentSettleInfoKey is a key used in the payment's sub-bucket to
    27  	// store the settle info of the payment.
    28  	paymentSettleInfoKey = []byte("payment-settle-info")
    29  
    30  	// paymentHtlcsBucket is a bucket where we'll store the information
    31  	// about the HTLCs that were attempted for a payment.
    32  	paymentHtlcsBucket = []byte("payment-htlcs-bucket")
    33  
    34  	// htlcAttemptInfoKey is a key used in a HTLC's sub-bucket to store the
    35  	// info about the attempt that was done for the HTLC in question.
    36  	htlcAttemptInfoKey = []byte("htlc-attempt-info")
    37  
    38  	// htlcSettleInfoKey is a key used in a HTLC's sub-bucket to store the
    39  	// settle info, if any.
    40  	htlcSettleInfoKey = []byte("htlc-settle-info")
    41  
    42  	// htlcFailInfoKey is a key used in a HTLC's sub-bucket to store
    43  	// failure information, if any.
    44  	htlcFailInfoKey = []byte("htlc-fail-info")
    45  
    46  	byteOrder = binary.BigEndian
    47  )
    48  
    49  // MigrateMPP migrates the payments to a new structure that accommodates for mpp
    50  // payments.
    51  func MigrateMPP(tx kvdb.RwTx) error {
    52  	log.Infof("Migrating payments to mpp structure")
    53  
    54  	// Iterate over all payments and store their indexing keys. This is
    55  	// needed, because no modifications are allowed inside a Bucket.ForEach
    56  	// loop.
    57  	paymentsBucket := tx.ReadWriteBucket(paymentsRootBucket)
    58  	if paymentsBucket == nil {
    59  		return nil
    60  	}
    61  
    62  	var paymentKeys [][]byte
    63  	err := paymentsBucket.ForEach(func(k, v []byte) error {
    64  		paymentKeys = append(paymentKeys, k)
    65  		return nil
    66  	})
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	// With all keys retrieved, start the migration.
    72  	for _, k := range paymentKeys {
    73  		bucket := paymentsBucket.NestedReadWriteBucket(k)
    74  
    75  		// We only expect sub-buckets to be found in
    76  		// this top-level bucket.
    77  		if bucket == nil {
    78  			return fmt.Errorf("non bucket element in " +
    79  				"payments bucket")
    80  		}
    81  
    82  		// Fetch old format creation info.
    83  		creationInfo := bucket.Get(paymentCreationInfoKey)
    84  		if creationInfo == nil {
    85  			return fmt.Errorf("creation info not found")
    86  		}
    87  
    88  		// Make a copy because bbolt doesn't allow this value to be
    89  		// changed in-place.
    90  		newCreationInfo := make([]byte, len(creationInfo))
    91  		copy(newCreationInfo, creationInfo)
    92  
    93  		// Convert to nano seconds.
    94  		timeBytes := newCreationInfo[32+8 : 32+8+8]
    95  		time := byteOrder.Uint64(timeBytes)
    96  		timeNs := time * 1000000000
    97  		byteOrder.PutUint64(timeBytes, timeNs)
    98  
    99  		// Write back new format creation info.
   100  		err := bucket.Put(paymentCreationInfoKey, newCreationInfo)
   101  		if err != nil {
   102  			return err
   103  		}
   104  
   105  		// No migration needed if there is no attempt stored.
   106  		attemptInfo := bucket.Get(paymentAttemptInfoKey)
   107  		if attemptInfo == nil {
   108  			continue
   109  		}
   110  
   111  		// Delete attempt info on the payment level.
   112  		if err := bucket.Delete(paymentAttemptInfoKey); err != nil {
   113  			return err
   114  		}
   115  
   116  		// Save attempt id for later use.
   117  		attemptID := attemptInfo[:8]
   118  
   119  		// Discard attempt id. It will become a bucket key in the new
   120  		// structure.
   121  		attemptInfo = attemptInfo[8:]
   122  
   123  		// Append unknown (zero) attempt time.
   124  		var zero [8]byte
   125  		attemptInfo = append(attemptInfo, zero[:]...)
   126  
   127  		// Create bucket that contains all htlcs.
   128  		htlcsBucket, err := bucket.CreateBucket(paymentHtlcsBucket)
   129  		if err != nil {
   130  			return err
   131  		}
   132  
   133  		// Create an htlc for this attempt.
   134  		htlcBucket, err := htlcsBucket.CreateBucket(attemptID)
   135  		if err != nil {
   136  			return err
   137  		}
   138  
   139  		// Save migrated attempt info.
   140  		err = htlcBucket.Put(htlcAttemptInfoKey, attemptInfo)
   141  		if err != nil {
   142  			return err
   143  		}
   144  
   145  		// Migrate settle info.
   146  		settleInfo := bucket.Get(paymentSettleInfoKey)
   147  		if settleInfo != nil {
   148  			// Payment-level settle info can be deleted.
   149  			err := bucket.Delete(paymentSettleInfoKey)
   150  			if err != nil {
   151  				return err
   152  			}
   153  
   154  			// Append unknown (zero) settle time.
   155  			settleInfo = append(settleInfo, zero[:]...)
   156  
   157  			// Save settle info.
   158  			err = htlcBucket.Put(htlcSettleInfoKey, settleInfo)
   159  			if err != nil {
   160  				return err
   161  			}
   162  
   163  			// Migration for settled htlc completed.
   164  			continue
   165  		}
   166  
   167  		// If there is no payment-level failure reason, the payment is
   168  		// still in flight and nothing else needs to be migrated.
   169  		// Otherwise the payment-level failure reason can remain
   170  		// unchanged.
   171  		inFlight := bucket.Get(paymentFailInfoKey) == nil
   172  		if inFlight {
   173  			continue
   174  		}
   175  
   176  		// The htlc failed. Add htlc fail info with reason unknown. We
   177  		// don't have access to the original failure reason anymore.
   178  		failInfo := []byte{
   179  			// Fail time unknown.
   180  			0, 0, 0, 0, 0, 0, 0, 0,
   181  
   182  			// Zero length wire message.
   183  			0,
   184  
   185  			// Failure reason unknown.
   186  			0,
   187  
   188  			// Failure source index zero.
   189  			0, 0, 0, 0,
   190  		}
   191  
   192  		// Save fail info.
   193  		err = htlcBucket.Put(htlcFailInfoKey, failInfo)
   194  		if err != nil {
   195  			return err
   196  		}
   197  	}
   198  
   199  	log.Infof("Migration of payments to mpp structure complete!")
   200  
   201  	return nil
   202  }