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

     1  package migration_01_to_11
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  	"sort"
     9  
    10  	lnwire "github.com/decred/dcrlnd/channeldb/migration/lnwire21"
    11  	"github.com/decred/dcrlnd/kvdb"
    12  	"github.com/decred/dcrlnd/lntypes"
    13  )
    14  
    15  var (
    16  	// paymentBucket is the name of the bucket within the database that
    17  	// stores all data related to payments.
    18  	//
    19  	// Within the payments bucket, each invoice is keyed by its invoice ID
    20  	// which is a monotonically increasing uint64.  BoltDB's sequence
    21  	// feature is used for generating monotonically increasing id.
    22  	//
    23  	// NOTE: Deprecated. Kept around for migration purposes.
    24  	paymentBucket = []byte("payments")
    25  
    26  	// paymentStatusBucket is the name of the bucket within the database
    27  	// that stores the status of a payment indexed by the payment's
    28  	// preimage.
    29  	//
    30  	// NOTE: Deprecated. Kept around for migration purposes.
    31  	paymentStatusBucket = []byte("payment-status")
    32  )
    33  
    34  // outgoingPayment represents a successful payment between the daemon and a
    35  // remote node. Details such as the total fee paid, and the time of the payment
    36  // are stored.
    37  //
    38  // NOTE: Deprecated. Kept around for migration purposes.
    39  type outgoingPayment struct {
    40  	Invoice
    41  
    42  	// Fee is the total fee paid for the payment in milli-satoshis.
    43  	Fee lnwire.MilliAtom
    44  
    45  	// TotalTimeLock is the total cumulative time-lock in the HTLC extended
    46  	// from the second-to-last hop to the destination.
    47  	TimeLockLength uint32
    48  
    49  	// Path encodes the path the payment took through the network. The path
    50  	// excludes the outgoing node and consists of the hex-encoded
    51  	// compressed public key of each of the nodes involved in the payment.
    52  	Path [][33]byte
    53  
    54  	// PaymentPreimage is the preImage of a successful payment. This is used
    55  	// to calculate the PaymentHash as well as serve as a proof of payment.
    56  	PaymentPreimage [32]byte
    57  }
    58  
    59  // addPayment saves a successful payment to the database. It is assumed that
    60  // all payment are sent using unique payment hashes.
    61  //
    62  // NOTE: Deprecated. Kept around for migration purposes.
    63  func (db *DB) addPayment(payment *outgoingPayment) error {
    64  	// Validate the field of the inner voice within the outgoing payment,
    65  	// these must also adhere to the same constraints as regular invoices.
    66  	if err := validateInvoice(&payment.Invoice); err != nil {
    67  		return err
    68  	}
    69  
    70  	// We first serialize the payment before starting the database
    71  	// transaction so we can avoid creating a DB payment in the case of a
    72  	// serialization error.
    73  	var b bytes.Buffer
    74  	if err := serializeOutgoingPayment(&b, payment); err != nil {
    75  		return err
    76  	}
    77  	paymentBytes := b.Bytes()
    78  
    79  	return kvdb.Update(db, func(tx kvdb.RwTx) error {
    80  		payments, err := tx.CreateTopLevelBucket(paymentBucket)
    81  		if err != nil {
    82  			return err
    83  		}
    84  
    85  		// Obtain the new unique sequence number for this payment.
    86  		paymentID, err := payments.NextSequence()
    87  		if err != nil {
    88  			return err
    89  		}
    90  
    91  		// We use BigEndian for keys as it orders keys in
    92  		// ascending order. This allows bucket scans to order payments
    93  		// in the order in which they were created.
    94  		paymentIDBytes := make([]byte, 8)
    95  		binary.BigEndian.PutUint64(paymentIDBytes, paymentID)
    96  
    97  		return payments.Put(paymentIDBytes, paymentBytes)
    98  	}, func() {})
    99  }
   100  
   101  // fetchAllPayments returns all outgoing payments in DB.
   102  //
   103  // NOTE: Deprecated. Kept around for migration purposes.
   104  func (db *DB) fetchAllPayments() ([]*outgoingPayment, error) {
   105  	var payments []*outgoingPayment
   106  
   107  	err := kvdb.View(db, func(tx kvdb.RTx) error {
   108  		bucket := tx.ReadBucket(paymentBucket)
   109  		if bucket == nil {
   110  			return ErrNoPaymentsCreated
   111  		}
   112  
   113  		return bucket.ForEach(func(k, v []byte) error {
   114  			// If the value is nil, then we ignore it as it may be
   115  			// a sub-bucket.
   116  			if v == nil {
   117  				return nil
   118  			}
   119  
   120  			r := bytes.NewReader(v)
   121  			payment, err := deserializeOutgoingPayment(r)
   122  			if err != nil {
   123  				return err
   124  			}
   125  
   126  			payments = append(payments, payment)
   127  			return nil
   128  		})
   129  	}, func() {
   130  		payments = nil
   131  	})
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	return payments, nil
   137  }
   138  
   139  // fetchPaymentStatus returns the payment status for outgoing payment.
   140  // If status of the payment isn't found, it will default to "StatusUnknown".
   141  //
   142  // NOTE: Deprecated. Kept around for migration purposes.
   143  func (db *DB) fetchPaymentStatus(paymentHash [32]byte) (PaymentStatus, error) {
   144  	var paymentStatus = StatusUnknown
   145  	err := kvdb.View(db, func(tx kvdb.RTx) error {
   146  		var err error
   147  		paymentStatus, err = fetchPaymentStatusTx(tx, paymentHash)
   148  		return err
   149  	}, func() {
   150  		paymentStatus = StatusUnknown
   151  	})
   152  	if err != nil {
   153  		return StatusUnknown, err
   154  	}
   155  
   156  	return paymentStatus, nil
   157  }
   158  
   159  // fetchPaymentStatusTx is a helper method that returns the payment status for
   160  // outgoing payment.  If status of the payment isn't found, it will default to
   161  // "StatusUnknown". It accepts the bboltdb transactions such that this method
   162  // can be composed into other atomic operations.
   163  //
   164  // NOTE: Deprecated. Kept around for migration purposes.
   165  func fetchPaymentStatusTx(tx kvdb.RTx, paymentHash [32]byte) (PaymentStatus, error) {
   166  	// The default status for all payments that aren't recorded in database.
   167  	var paymentStatus = StatusUnknown
   168  
   169  	bucket := tx.ReadBucket(paymentStatusBucket)
   170  	if bucket == nil {
   171  		return paymentStatus, nil
   172  	}
   173  
   174  	paymentStatusBytes := bucket.Get(paymentHash[:])
   175  	if paymentStatusBytes == nil {
   176  		return paymentStatus, nil
   177  	}
   178  
   179  	paymentStatus.FromBytes(paymentStatusBytes)
   180  
   181  	return paymentStatus, nil
   182  }
   183  
   184  func serializeOutgoingPayment(w io.Writer, p *outgoingPayment) error {
   185  	var scratch [8]byte
   186  
   187  	if err := serializeInvoiceLegacy(w, &p.Invoice); err != nil {
   188  		return err
   189  	}
   190  
   191  	byteOrder.PutUint64(scratch[:], uint64(p.Fee))
   192  	if _, err := w.Write(scratch[:]); err != nil {
   193  		return err
   194  	}
   195  
   196  	// First write out the length of the bytes to prefix the value.
   197  	pathLen := uint32(len(p.Path))
   198  	byteOrder.PutUint32(scratch[:4], pathLen)
   199  	if _, err := w.Write(scratch[:4]); err != nil {
   200  		return err
   201  	}
   202  
   203  	// Then with the path written, we write out the series of public keys
   204  	// involved in the path.
   205  	for _, hop := range p.Path {
   206  		if _, err := w.Write(hop[:]); err != nil {
   207  			return err
   208  		}
   209  	}
   210  
   211  	byteOrder.PutUint32(scratch[:4], p.TimeLockLength)
   212  	if _, err := w.Write(scratch[:4]); err != nil {
   213  		return err
   214  	}
   215  
   216  	if _, err := w.Write(p.PaymentPreimage[:]); err != nil {
   217  		return err
   218  	}
   219  
   220  	return nil
   221  }
   222  
   223  func deserializeOutgoingPayment(r io.Reader) (*outgoingPayment, error) {
   224  	var scratch [8]byte
   225  
   226  	p := &outgoingPayment{}
   227  
   228  	inv, err := deserializeInvoiceLegacy(r)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	p.Invoice = inv
   233  
   234  	if _, err := r.Read(scratch[:]); err != nil {
   235  		return nil, err
   236  	}
   237  	p.Fee = lnwire.MilliAtom(byteOrder.Uint64(scratch[:]))
   238  
   239  	if _, err = r.Read(scratch[:4]); err != nil {
   240  		return nil, err
   241  	}
   242  	pathLen := byteOrder.Uint32(scratch[:4])
   243  
   244  	path := make([][33]byte, pathLen)
   245  	for i := uint32(0); i < pathLen; i++ {
   246  		if _, err := r.Read(path[i][:]); err != nil {
   247  			return nil, err
   248  		}
   249  	}
   250  	p.Path = path
   251  
   252  	if _, err = r.Read(scratch[:4]); err != nil {
   253  		return nil, err
   254  	}
   255  	p.TimeLockLength = byteOrder.Uint32(scratch[:4])
   256  
   257  	if _, err := r.Read(p.PaymentPreimage[:]); err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	return p, nil
   262  }
   263  
   264  // serializePaymentAttemptInfoMigration9 is the serializePaymentAttemptInfo
   265  // version as existed when migration #9 was created. We keep this around, along
   266  // with the methods below to ensure that clients that upgrade will use the
   267  // correct version of this method.
   268  func serializePaymentAttemptInfoMigration9(w io.Writer, a *PaymentAttemptInfo) error {
   269  	if err := WriteElements(w, a.PaymentID, a.SessionKey); err != nil {
   270  		return err
   271  	}
   272  
   273  	if err := serializeRouteMigration9(w, a.Route); err != nil {
   274  		return err
   275  	}
   276  
   277  	return nil
   278  }
   279  
   280  func serializeHopMigration9(w io.Writer, h *Hop) error {
   281  	if err := WriteElements(w,
   282  		h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
   283  		h.AmtToForward,
   284  	); err != nil {
   285  		return err
   286  	}
   287  
   288  	return nil
   289  }
   290  
   291  func serializeRouteMigration9(w io.Writer, r Route) error {
   292  	if err := WriteElements(w,
   293  		r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
   294  	); err != nil {
   295  		return err
   296  	}
   297  
   298  	if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
   299  		return err
   300  	}
   301  
   302  	for _, h := range r.Hops {
   303  		if err := serializeHopMigration9(w, h); err != nil {
   304  			return err
   305  		}
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  func deserializePaymentAttemptInfoMigration9(r io.Reader) (*PaymentAttemptInfo, error) {
   312  	a := &PaymentAttemptInfo{}
   313  	err := ReadElements(r, &a.PaymentID, &a.SessionKey)
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  	a.Route, err = deserializeRouteMigration9(r)
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  	return a, nil
   322  }
   323  
   324  func deserializeRouteMigration9(r io.Reader) (Route, error) {
   325  	rt := Route{}
   326  	if err := ReadElements(r,
   327  		&rt.TotalTimeLock, &rt.TotalAmount,
   328  	); err != nil {
   329  		return rt, err
   330  	}
   331  
   332  	var pub []byte
   333  	if err := ReadElements(r, &pub); err != nil {
   334  		return rt, err
   335  	}
   336  	copy(rt.SourcePubKey[:], pub)
   337  
   338  	var numHops uint32
   339  	if err := ReadElements(r, &numHops); err != nil {
   340  		return rt, err
   341  	}
   342  
   343  	var hops []*Hop
   344  	for i := uint32(0); i < numHops; i++ {
   345  		hop, err := deserializeHopMigration9(r)
   346  		if err != nil {
   347  			return rt, err
   348  		}
   349  		hops = append(hops, hop)
   350  	}
   351  	rt.Hops = hops
   352  
   353  	return rt, nil
   354  }
   355  
   356  func deserializeHopMigration9(r io.Reader) (*Hop, error) {
   357  	h := &Hop{}
   358  
   359  	var pub []byte
   360  	if err := ReadElements(r, &pub); err != nil {
   361  		return nil, err
   362  	}
   363  	copy(h.PubKeyBytes[:], pub)
   364  
   365  	if err := ReadElements(r,
   366  		&h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
   367  	); err != nil {
   368  		return nil, err
   369  	}
   370  
   371  	return h, nil
   372  }
   373  
   374  // fetchPaymentsMigration9 returns all sent payments found in the DB using the
   375  // payment attempt info format that was present as of migration #9. We need
   376  // this as otherwise, the current FetchPayments version will use the latest
   377  // decoding format. Note that we only need this for the
   378  // TestOutgoingPaymentsMigration migration test case.
   379  func (db *DB) fetchPaymentsMigration9() ([]*Payment, error) {
   380  	var payments []*Payment
   381  
   382  	err := kvdb.View(db, func(tx kvdb.RTx) error {
   383  		paymentsBucket := tx.ReadBucket(paymentsRootBucket)
   384  		if paymentsBucket == nil {
   385  			return nil
   386  		}
   387  
   388  		return paymentsBucket.ForEach(func(k, v []byte) error {
   389  			bucket := paymentsBucket.NestedReadBucket(k)
   390  			if bucket == nil {
   391  				// We only expect sub-buckets to be found in
   392  				// this top-level bucket.
   393  				return fmt.Errorf("non bucket element in " +
   394  					"payments bucket")
   395  			}
   396  
   397  			p, err := fetchPaymentMigration9(bucket)
   398  			if err != nil {
   399  				return err
   400  			}
   401  
   402  			payments = append(payments, p)
   403  
   404  			// For older versions of lnd, duplicate payments to a
   405  			// payment has was possible. These will be found in a
   406  			// sub-bucket indexed by their sequence number if
   407  			// available.
   408  			dup := bucket.NestedReadBucket(paymentDuplicateBucket)
   409  			if dup == nil {
   410  				return nil
   411  			}
   412  
   413  			return dup.ForEach(func(k, v []byte) error {
   414  				subBucket := dup.NestedReadBucket(k)
   415  				if subBucket == nil {
   416  					// We one bucket for each duplicate to
   417  					// be found.
   418  					return fmt.Errorf("non bucket element" +
   419  						"in duplicate bucket")
   420  				}
   421  
   422  				p, err := fetchPaymentMigration9(subBucket)
   423  				if err != nil {
   424  					return err
   425  				}
   426  
   427  				payments = append(payments, p)
   428  				return nil
   429  			})
   430  		})
   431  	}, func() {
   432  		payments = nil
   433  	})
   434  	if err != nil {
   435  		return nil, err
   436  	}
   437  
   438  	// Before returning, sort the payments by their sequence number.
   439  	sort.Slice(payments, func(i, j int) bool {
   440  		return payments[i].sequenceNum < payments[j].sequenceNum
   441  	})
   442  
   443  	return payments, nil
   444  }
   445  
   446  func fetchPaymentMigration9(bucket kvdb.RBucket) (*Payment, error) {
   447  	var (
   448  		err error
   449  		p   = &Payment{}
   450  	)
   451  
   452  	seqBytes := bucket.Get(paymentSequenceKey)
   453  	if seqBytes == nil {
   454  		return nil, fmt.Errorf("sequence number not found")
   455  	}
   456  
   457  	p.sequenceNum = binary.BigEndian.Uint64(seqBytes)
   458  
   459  	// Get the payment status.
   460  	p.Status = fetchPaymentStatus(bucket)
   461  
   462  	// Get the PaymentCreationInfo.
   463  	b := bucket.Get(paymentCreationInfoKey)
   464  	if b == nil {
   465  		return nil, fmt.Errorf("creation info not found")
   466  	}
   467  
   468  	r := bytes.NewReader(b)
   469  	p.Info, err = deserializePaymentCreationInfo(r)
   470  	if err != nil {
   471  		return nil, err
   472  
   473  	}
   474  
   475  	// Get the PaymentAttemptInfo. This can be unset.
   476  	b = bucket.Get(paymentAttemptInfoKey)
   477  	if b != nil {
   478  		r = bytes.NewReader(b)
   479  		p.Attempt, err = deserializePaymentAttemptInfoMigration9(r)
   480  		if err != nil {
   481  			return nil, err
   482  		}
   483  	}
   484  
   485  	// Get the payment preimage. This is only found for
   486  	// completed payments.
   487  	b = bucket.Get(paymentSettleInfoKey)
   488  	if b != nil {
   489  		var preimg lntypes.Preimage
   490  		copy(preimg[:], b[:])
   491  		p.PaymentPreimage = &preimg
   492  	}
   493  
   494  	// Get failure reason if available.
   495  	b = bucket.Get(paymentFailInfoKey)
   496  	if b != nil {
   497  		reason := FailureReason(b[0])
   498  		p.Failure = &reason
   499  	}
   500  
   501  	return p, nil
   502  }