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

     1  package channeldb
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  	"time"
     9  
    10  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    11  	"github.com/decred/dcrlnd/kvdb"
    12  	"github.com/decred/dcrlnd/lntypes"
    13  	"github.com/decred/dcrlnd/lnwire"
    14  	"github.com/decred/dcrlnd/routing/route"
    15  )
    16  
    17  var (
    18  	// duplicatePaymentsBucket is the name of a optional sub-bucket within
    19  	// the payment hash bucket, that is used to hold duplicate payments to a
    20  	// payment hash. This is needed to support information from earlier
    21  	// versions of lnd, where it was possible to pay to a payment hash more
    22  	// than once.
    23  	duplicatePaymentsBucket = []byte("payment-duplicate-bucket")
    24  
    25  	// duplicatePaymentSettleInfoKey is a key used in the payment's
    26  	// sub-bucket to store the settle info of the payment.
    27  	duplicatePaymentSettleInfoKey = []byte("payment-settle-info")
    28  
    29  	// duplicatePaymentAttemptInfoKey is a key used in the payment's
    30  	// sub-bucket to store the info about the latest attempt that was done
    31  	// for the payment in question.
    32  	duplicatePaymentAttemptInfoKey = []byte("payment-attempt-info")
    33  
    34  	// duplicatePaymentCreationInfoKey is a key used in the payment's
    35  	// sub-bucket to store the creation info of the payment.
    36  	duplicatePaymentCreationInfoKey = []byte("payment-creation-info")
    37  
    38  	// duplicatePaymentFailInfoKey is a key used in the payment's sub-bucket
    39  	// to store information about the reason a payment failed.
    40  	duplicatePaymentFailInfoKey = []byte("payment-fail-info")
    41  
    42  	// duplicatePaymentSequenceKey is a key used in the payment's sub-bucket
    43  	// to store the sequence number of the payment.
    44  	duplicatePaymentSequenceKey = []byte("payment-sequence-key")
    45  )
    46  
    47  // duplicateHTLCAttemptInfo contains static information about a specific HTLC
    48  // attempt for a payment. This information is used by the router to handle any
    49  // errors coming back after an attempt is made, and to query the switch about
    50  // the status of the attempt.
    51  type duplicateHTLCAttemptInfo struct {
    52  	// attemptID is the unique ID used for this attempt.
    53  	attemptID uint64
    54  
    55  	// sessionKey is the ephemeral key used for this attempt.
    56  	sessionKey [secp256k1.PrivKeyBytesLen]byte
    57  
    58  	// route is the route attempted to send the HTLC.
    59  	route route.Route
    60  }
    61  
    62  // fetchDuplicatePaymentStatus fetches the payment status of the payment. If the
    63  // payment isn't found, it will default to "StatusUnknown".
    64  func fetchDuplicatePaymentStatus(bucket kvdb.RBucket) PaymentStatus {
    65  	if bucket.Get(duplicatePaymentSettleInfoKey) != nil {
    66  		return StatusSucceeded
    67  	}
    68  
    69  	if bucket.Get(duplicatePaymentFailInfoKey) != nil {
    70  		return StatusFailed
    71  	}
    72  
    73  	if bucket.Get(duplicatePaymentCreationInfoKey) != nil {
    74  		return StatusInFlight
    75  	}
    76  
    77  	return StatusUnknown
    78  }
    79  
    80  func deserializeDuplicateHTLCAttemptInfo(r io.Reader) (
    81  	*duplicateHTLCAttemptInfo, error) {
    82  
    83  	a := &duplicateHTLCAttemptInfo{}
    84  	err := ReadElements(r, &a.attemptID, &a.sessionKey)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	a.route, err = DeserializeRoute(r)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	return a, nil
    93  }
    94  
    95  func deserializeDuplicatePaymentCreationInfo(r io.Reader) (
    96  	*PaymentCreationInfo, error) {
    97  
    98  	var scratch [8]byte
    99  
   100  	c := &PaymentCreationInfo{}
   101  
   102  	if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	if _, err := io.ReadFull(r, scratch[:]); err != nil {
   107  		return nil, err
   108  	}
   109  	c.Value = lnwire.MilliAtom(byteOrder.Uint64(scratch[:]))
   110  
   111  	if _, err := io.ReadFull(r, scratch[:]); err != nil {
   112  		return nil, err
   113  	}
   114  	c.CreationTime = time.Unix(int64(byteOrder.Uint64(scratch[:])), 0)
   115  
   116  	if _, err := io.ReadFull(r, scratch[:4]); err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	reqLen := byteOrder.Uint32(scratch[:4])
   121  	payReq := make([]byte, reqLen)
   122  	if reqLen > 0 {
   123  		if _, err := io.ReadFull(r, payReq); err != nil {
   124  			return nil, err
   125  		}
   126  	}
   127  	c.PaymentRequest = payReq
   128  
   129  	return c, nil
   130  }
   131  
   132  func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) {
   133  	seqBytes := bucket.Get(duplicatePaymentSequenceKey)
   134  	if seqBytes == nil {
   135  		return nil, fmt.Errorf("sequence number not found")
   136  	}
   137  
   138  	sequenceNum := binary.BigEndian.Uint64(seqBytes)
   139  
   140  	// Get the payment status.
   141  	paymentStatus := fetchDuplicatePaymentStatus(bucket)
   142  
   143  	// Get the PaymentCreationInfo.
   144  	b := bucket.Get(duplicatePaymentCreationInfoKey)
   145  	if b == nil {
   146  		return nil, fmt.Errorf("creation info not found")
   147  	}
   148  
   149  	r := bytes.NewReader(b)
   150  	creationInfo, err := deserializeDuplicatePaymentCreationInfo(r)
   151  	if err != nil {
   152  		return nil, err
   153  
   154  	}
   155  
   156  	// Get failure reason if available.
   157  	var failureReason *FailureReason
   158  	b = bucket.Get(duplicatePaymentFailInfoKey)
   159  	if b != nil {
   160  		reason := FailureReason(b[0])
   161  		failureReason = &reason
   162  	}
   163  
   164  	payment := &MPPayment{
   165  		SequenceNum:   sequenceNum,
   166  		Info:          creationInfo,
   167  		FailureReason: failureReason,
   168  		Status:        paymentStatus,
   169  	}
   170  
   171  	// Get the HTLCAttemptInfo. It can be absent.
   172  	b = bucket.Get(duplicatePaymentAttemptInfoKey)
   173  	if b != nil {
   174  		r = bytes.NewReader(b)
   175  		attempt, err := deserializeDuplicateHTLCAttemptInfo(r)
   176  		if err != nil {
   177  			return nil, err
   178  		}
   179  
   180  		htlc := HTLCAttempt{
   181  			HTLCAttemptInfo: HTLCAttemptInfo{
   182  				AttemptID:  attempt.attemptID,
   183  				Route:      attempt.route,
   184  				sessionKey: attempt.sessionKey,
   185  			},
   186  		}
   187  
   188  		// Get the payment preimage. This is only found for
   189  		// successful payments.
   190  		b = bucket.Get(duplicatePaymentSettleInfoKey)
   191  		if b != nil {
   192  			var preimg lntypes.Preimage
   193  			copy(preimg[:], b)
   194  
   195  			htlc.Settle = &HTLCSettleInfo{
   196  				Preimage:   preimg,
   197  				SettleTime: time.Time{},
   198  			}
   199  		} else {
   200  			// Otherwise the payment must have failed.
   201  			htlc.Failure = &HTLCFailInfo{
   202  				FailTime: time.Time{},
   203  			}
   204  		}
   205  
   206  		payment.HTLCs = []HTLCAttempt{htlc}
   207  	}
   208  
   209  	return payment, nil
   210  }
   211  
   212  func fetchDuplicatePayments(paymentHashBucket kvdb.RBucket) ([]*MPPayment,
   213  	error) {
   214  
   215  	var payments []*MPPayment
   216  
   217  	// For older versions of lnd, duplicate payments to a payment has was
   218  	// possible. These will be found in a sub-bucket indexed by their
   219  	// sequence number if available.
   220  	dup := paymentHashBucket.NestedReadBucket(duplicatePaymentsBucket)
   221  	if dup == nil {
   222  		return nil, nil
   223  	}
   224  
   225  	err := dup.ForEach(func(k, v []byte) error {
   226  		subBucket := dup.NestedReadBucket(k)
   227  		if subBucket == nil {
   228  			// We one bucket for each duplicate to be found.
   229  			return fmt.Errorf("non bucket element" +
   230  				"in duplicate bucket")
   231  		}
   232  
   233  		p, err := fetchDuplicatePayment(subBucket)
   234  		if err != nil {
   235  			return err
   236  		}
   237  
   238  		payments = append(payments, p)
   239  		return nil
   240  	})
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	return payments, nil
   246  }