Skip to content

Commit

Permalink
Merge 51c72a0 into 92b79f6
Browse files Browse the repository at this point in the history
  • Loading branch information
joostjager committed Feb 20, 2020
2 parents 92b79f6 + 51c72a0 commit 54371e4
Show file tree
Hide file tree
Showing 19 changed files with 2,598 additions and 1,844 deletions.
206 changes: 206 additions & 0 deletions channeldb/duplicate_payments.go
@@ -0,0 +1,206 @@
package channeldb

import (
"bytes"
"encoding/binary"
"fmt"
"io"
"time"

"github.com/btcsuite/btcd/btcec"
"github.com/coreos/bbolt"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/routing/route"
)

var (
// duplicatePayments 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.
duplicatePayments = []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.PrivateKey

// 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 default to "StatusUnknown".
func fetchDuplicatePaymentStatus(bucket *bbolt.Bucket) PaymentStatus {
if bucket.Get(duplicatePaymentSettleInfoKey) != nil {
return StatusSucceeded
}

if bucket.Get(duplicatePaymentFailInfoKey) != nil {
return StatusFailed
}

if bucket.Get(duplicatePaymentCreationInfoKey) != nil {
return StatusInFlight
}

return StatusUnknown
}

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 fetchDuplicatePayment(bucket *bbolt.Bucket) (*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 := fetchDuplicatePaymentStatus(bucket)

// Get the PaymentCreationInfo.
b := bucket.Get(duplicatePaymentCreationInfoKey)
if b == nil {
return nil, fmt.Errorf("creation info not found")
}

r := bytes.NewReader(b)
creationInfo, err := deserializePaymentCreationInfo(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 *bbolt.Bucket) ([]*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.Bucket(duplicatePayments)
if dup == nil {
return nil, nil
}

err := dup.ForEach(func(k, v []byte) error {
subBucket := dup.Bucket(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
}

0 comments on commit 54371e4

Please sign in to comment.