123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- package channeldb
- import (
- "bytes"
- "encoding/binary"
- "fmt"
- "io"
- "time"
- "github.com/btcsuite/btcd/btcec/v2"
- "github.com/lightningnetwork/lnd/kvdb"
- "github.com/lightningnetwork/lnd/lntypes"
- "github.com/lightningnetwork/lnd/lnwire"
- "github.com/lightningnetwork/lnd/routing/route"
- )
- var (
- // duplicatePaymentsBucket is the name of a optional sub-bucket within
- // the payment hash bucket, that is used to hold duplicate payments to a
- // payment hash. This is needed to support information from earlier
- // versions of lnd, where it was possible to pay to a payment hash more
- // than once.
- duplicatePaymentsBucket = []byte("payment-duplicate-bucket")
- // duplicatePaymentSettleInfoKey is a key used in the payment's
- // sub-bucket to store the settle info of the payment.
- duplicatePaymentSettleInfoKey = []byte("payment-settle-info")
- // duplicatePaymentAttemptInfoKey is a key used in the payment's
- // sub-bucket to store the info about the latest attempt that was done
- // for the payment in question.
- duplicatePaymentAttemptInfoKey = []byte("payment-attempt-info")
- // duplicatePaymentCreationInfoKey is a key used in the payment's
- // sub-bucket to store the creation info of the payment.
- duplicatePaymentCreationInfoKey = []byte("payment-creation-info")
- // duplicatePaymentFailInfoKey is a key used in the payment's sub-bucket
- // to store information about the reason a payment failed.
- duplicatePaymentFailInfoKey = []byte("payment-fail-info")
- // duplicatePaymentSequenceKey is a key used in the payment's sub-bucket
- // to store the sequence number of the payment.
- duplicatePaymentSequenceKey = []byte("payment-sequence-key")
- )
- // duplicateHTLCAttemptInfo contains static information about a specific HTLC
- // attempt for a payment. This information is used by the router to handle any
- // errors coming back after an attempt is made, and to query the switch about
- // the status of the attempt.
- type duplicateHTLCAttemptInfo struct {
- // attemptID is the unique ID used for this attempt.
- attemptID uint64
- // sessionKey is the ephemeral key used for this attempt.
- sessionKey [btcec.PrivKeyBytesLen]byte
- // route is the route attempted to send the HTLC.
- route route.Route
- }
- // fetchDuplicatePaymentStatus fetches the payment status of the payment. If
- // the payment isn't found, it will return error `ErrPaymentNotInitiated`.
- func fetchDuplicatePaymentStatus(bucket kvdb.RBucket) (PaymentStatus, error) {
- if bucket.Get(duplicatePaymentSettleInfoKey) != nil {
- return StatusSucceeded, nil
- }
- if bucket.Get(duplicatePaymentFailInfoKey) != nil {
- return StatusFailed, nil
- }
- if bucket.Get(duplicatePaymentCreationInfoKey) != nil {
- return StatusInFlight, nil
- }
- return 0, ErrPaymentNotInitiated
- }
- func deserializeDuplicateHTLCAttemptInfo(r io.Reader) (
- *duplicateHTLCAttemptInfo, error) {
- a := &duplicateHTLCAttemptInfo{}
- err := ReadElements(r, &a.attemptID, &a.sessionKey)
- if err != nil {
- return nil, err
- }
- a.route, err = DeserializeRoute(r)
- if err != nil {
- return nil, err
- }
- return a, nil
- }
- func deserializeDuplicatePaymentCreationInfo(r io.Reader) (
- *PaymentCreationInfo, error) {
- var scratch [8]byte
- c := &PaymentCreationInfo{}
- if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil {
- return nil, err
- }
- if _, err := io.ReadFull(r, scratch[:]); err != nil {
- return nil, err
- }
- c.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
- if _, err := io.ReadFull(r, scratch[:]); err != nil {
- return nil, err
- }
- c.CreationTime = time.Unix(int64(byteOrder.Uint64(scratch[:])), 0)
- if _, err := io.ReadFull(r, scratch[:4]); err != nil {
- return nil, err
- }
- reqLen := byteOrder.Uint32(scratch[:4])
- payReq := make([]byte, reqLen)
- if reqLen > 0 {
- if _, err := io.ReadFull(r, payReq); err != nil {
- return nil, err
- }
- }
- c.PaymentRequest = payReq
- return c, nil
- }
- func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) {
- seqBytes := bucket.Get(duplicatePaymentSequenceKey)
- if seqBytes == nil {
- return nil, fmt.Errorf("sequence number not found")
- }
- sequenceNum := binary.BigEndian.Uint64(seqBytes)
- // Get the payment status.
- paymentStatus, err := fetchDuplicatePaymentStatus(bucket)
- if err != nil {
- return nil, err
- }
- // Get the PaymentCreationInfo.
- b := bucket.Get(duplicatePaymentCreationInfoKey)
- if b == nil {
- return nil, fmt.Errorf("creation info not found")
- }
- r := bytes.NewReader(b)
- creationInfo, err := deserializeDuplicatePaymentCreationInfo(r)
- if err != nil {
- return nil, err
- }
- // Get failure reason if available.
- var failureReason *FailureReason
- b = bucket.Get(duplicatePaymentFailInfoKey)
- if b != nil {
- reason := FailureReason(b[0])
- failureReason = &reason
- }
- payment := &MPPayment{
- SequenceNum: sequenceNum,
- Info: creationInfo,
- FailureReason: failureReason,
- Status: paymentStatus,
- }
- // Get the HTLCAttemptInfo. It can be absent.
- b = bucket.Get(duplicatePaymentAttemptInfoKey)
- if b != nil {
- r = bytes.NewReader(b)
- attempt, err := deserializeDuplicateHTLCAttemptInfo(r)
- if err != nil {
- return nil, err
- }
- htlc := HTLCAttempt{
- HTLCAttemptInfo: HTLCAttemptInfo{
- AttemptID: attempt.attemptID,
- Route: attempt.route,
- sessionKey: attempt.sessionKey,
- },
- }
- // Get the payment preimage. This is only found for
- // successful payments.
- b = bucket.Get(duplicatePaymentSettleInfoKey)
- if b != nil {
- var preimg lntypes.Preimage
- copy(preimg[:], b)
- htlc.Settle = &HTLCSettleInfo{
- Preimage: preimg,
- SettleTime: time.Time{},
- }
- } else {
- // Otherwise the payment must have failed.
- htlc.Failure = &HTLCFailInfo{
- FailTime: time.Time{},
- }
- }
- payment.HTLCs = []HTLCAttempt{htlc}
- }
- return payment, nil
- }
- func fetchDuplicatePayments(paymentHashBucket kvdb.RBucket) ([]*MPPayment,
- error) {
- var payments []*MPPayment
- // For older versions of lnd, duplicate payments to a payment has was
- // possible. These will be found in a sub-bucket indexed by their
- // sequence number if available.
- dup := paymentHashBucket.NestedReadBucket(duplicatePaymentsBucket)
- if dup == nil {
- return nil, nil
- }
- err := dup.ForEach(func(k, v []byte) error {
- subBucket := dup.NestedReadBucket(k)
- if subBucket == nil {
- // We one bucket for each duplicate to be found.
- return fmt.Errorf("non bucket element" +
- "in duplicate bucket")
- }
- p, err := fetchDuplicatePayment(subBucket)
- if err != nil {
- return err
- }
- payments = append(payments, p)
- return nil
- })
- if err != nil {
- return nil, err
- }
- return payments, nil
- }
|