2020-02-19 16:34:27 +03:00
|
|
|
package channeldb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/btcsuite/btcd/btcec"
|
2019-12-13 05:22:19 +03:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb/kvdb"
|
2020-02-19 16:34:27 +03:00
|
|
|
"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.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".
|
2020-05-07 01:48:00 +03:00
|
|
|
func fetchDuplicatePaymentStatus(bucket kvdb.RBucket) PaymentStatus {
|
2020-02-19 16:34:27 +03:00
|
|
|
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 deserializeDuplicatePaymentCreationInfo(r io.Reader) (
|
|
|
|
*PaymentCreationInfo, error) {
|
|
|
|
|
|
|
|
var scratch [8]byte
|
|
|
|
|
|
|
|
c := &PaymentCreationInfo{}
|
|
|
|
|
|
|
|
if _, err := io.ReadFull(r, c.PaymentHash[:]); 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
|
|
|
|
}
|
|
|
|
|
2020-05-07 01:48:00 +03:00
|
|
|
func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) {
|
2020-02-19 16:34:27 +03:00
|
|
|
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 := 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{
|
2020-03-30 11:45:38 +03:00
|
|
|
SequenceNum: sequenceNum,
|
2020-02-19 16:34:27 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-05-07 01:48:00 +03:00
|
|
|
func fetchDuplicatePayments(paymentHashBucket kvdb.RBucket) ([]*MPPayment,
|
2020-02-19 16:34:27 +03:00
|
|
|
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.
|
2019-12-13 05:22:19 +03:00
|
|
|
dup := paymentHashBucket.NestedReadBucket(duplicatePaymentsBucket)
|
2020-02-19 16:34:27 +03:00
|
|
|
if dup == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err := dup.ForEach(func(k, v []byte) error {
|
2019-12-13 05:22:19 +03:00
|
|
|
subBucket := dup.NestedReadBucket(k)
|
2020-02-19 16:34:27 +03:00
|
|
|
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
|
|
|
|
}
|