• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 16883452553

11 Aug 2025 02:44PM UTC coverage: 66.909% (+0.009%) from 66.9%
16883452553

Pull #9844

github

web-flow
Merge 4e0af2f49 into f3e1f2f35
Pull Request #9844: Refactor Payment PR 3

106 of 147 new or added lines in 11 files covered. (72.11%)

61 existing lines in 19 files now uncovered.

135735 of 202866 relevant lines covered (66.91%)

21586.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.02
/channeldb/duplicate_payments.go
1
package channeldb
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "fmt"
7
        "io"
8
        "time"
9

10
        "github.com/btcsuite/btcd/btcec/v2"
11
        "github.com/lightningnetwork/lnd/kvdb"
12
        "github.com/lightningnetwork/lnd/lntypes"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
        paymentsdb "github.com/lightningnetwork/lnd/payments/db"
15
        "github.com/lightningnetwork/lnd/routing/route"
16
)
17

18
var (
19
        // duplicatePaymentsBucket is the name of a optional sub-bucket within
20
        // the payment hash bucket, that is used to hold duplicate payments to a
21
        // payment hash. This is needed to support information from earlier
22
        // versions of lnd, where it was possible to pay to a payment hash more
23
        // than once.
24
        duplicatePaymentsBucket = []byte("payment-duplicate-bucket")
25

26
        // duplicatePaymentSettleInfoKey is a key used in the payment's
27
        // sub-bucket to store the settle info of the payment.
28
        duplicatePaymentSettleInfoKey = []byte("payment-settle-info")
29

30
        // duplicatePaymentAttemptInfoKey is a key used in the payment's
31
        // sub-bucket to store the info about the latest attempt that was done
32
        // for the payment in question.
33
        duplicatePaymentAttemptInfoKey = []byte("payment-attempt-info")
34

35
        // duplicatePaymentCreationInfoKey is a key used in the payment's
36
        // sub-bucket to store the creation info of the payment.
37
        duplicatePaymentCreationInfoKey = []byte("payment-creation-info")
38

39
        // duplicatePaymentFailInfoKey is a key used in the payment's sub-bucket
40
        // to store information about the reason a payment failed.
41
        duplicatePaymentFailInfoKey = []byte("payment-fail-info")
42

43
        // duplicatePaymentSequenceKey is a key used in the payment's sub-bucket
44
        // to store the sequence number of the payment.
45
        duplicatePaymentSequenceKey = []byte("payment-sequence-key")
46
)
47

48
// duplicateHTLCAttemptInfo contains static information about a specific HTLC
49
// attempt for a payment. This information is used by the router to handle any
50
// errors coming back after an attempt is made, and to query the switch about
51
// the status of the attempt.
52
type duplicateHTLCAttemptInfo struct {
53
        // attemptID is the unique ID used for this attempt.
54
        attemptID uint64
55

56
        // sessionKey is the ephemeral key used for this attempt.
57
        sessionKey [btcec.PrivKeyBytesLen]byte
58

59
        // route is the route attempted to send the HTLC.
60
        route route.Route
61
}
62

63
// fetchDuplicatePaymentStatus fetches the payment status of the payment. If
64
// the payment isn't found, it will return error `ErrPaymentNotInitiated`.
65
func fetchDuplicatePaymentStatus(bucket kvdb.RBucket) (PaymentStatus, error) {
30✔
66
        if bucket.Get(duplicatePaymentSettleInfoKey) != nil {
60✔
67
                return StatusSucceeded, nil
30✔
68
        }
30✔
69

70
        if bucket.Get(duplicatePaymentFailInfoKey) != nil {
×
71
                return StatusFailed, nil
×
72
        }
×
73

74
        if bucket.Get(duplicatePaymentCreationInfoKey) != nil {
×
75
                return StatusInFlight, nil
×
76
        }
×
77

NEW
78
        return 0, paymentsdb.ErrPaymentNotInitiated
×
79
}
80

81
func deserializeDuplicateHTLCAttemptInfo(r io.Reader) (
82
        *duplicateHTLCAttemptInfo, error) {
×
83

×
84
        a := &duplicateHTLCAttemptInfo{}
×
85
        err := ReadElements(r, &a.attemptID, &a.sessionKey)
×
86
        if err != nil {
×
87
                return nil, err
×
88
        }
×
89
        a.route, err = DeserializeRoute(r)
×
90
        if err != nil {
×
91
                return nil, err
×
92
        }
×
93
        return a, nil
×
94
}
95

96
func deserializeDuplicatePaymentCreationInfo(r io.Reader) (
97
        *PaymentCreationInfo, error) {
30✔
98

30✔
99
        var scratch [8]byte
30✔
100

30✔
101
        c := &PaymentCreationInfo{}
30✔
102

30✔
103
        if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil {
30✔
104
                return nil, err
×
105
        }
×
106

107
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
30✔
108
                return nil, err
×
109
        }
×
110
        c.Value = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:]))
30✔
111

30✔
112
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
30✔
113
                return nil, err
×
114
        }
×
115
        c.CreationTime = time.Unix(int64(byteOrder.Uint64(scratch[:])), 0)
30✔
116

30✔
117
        if _, err := io.ReadFull(r, scratch[:4]); err != nil {
30✔
118
                return nil, err
×
119
        }
×
120

121
        reqLen := byteOrder.Uint32(scratch[:4])
30✔
122
        payReq := make([]byte, reqLen)
30✔
123
        if reqLen > 0 {
30✔
124
                if _, err := io.ReadFull(r, payReq); err != nil {
×
125
                        return nil, err
×
126
                }
×
127
        }
128
        c.PaymentRequest = payReq
30✔
129

30✔
130
        return c, nil
30✔
131
}
132

133
func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) {
30✔
134
        seqBytes := bucket.Get(duplicatePaymentSequenceKey)
30✔
135
        if seqBytes == nil {
30✔
136
                return nil, fmt.Errorf("sequence number not found")
×
137
        }
×
138

139
        sequenceNum := binary.BigEndian.Uint64(seqBytes)
30✔
140

30✔
141
        // Get the payment status.
30✔
142
        paymentStatus, err := fetchDuplicatePaymentStatus(bucket)
30✔
143
        if err != nil {
30✔
144
                return nil, err
×
145
        }
×
146

147
        // Get the PaymentCreationInfo.
148
        b := bucket.Get(duplicatePaymentCreationInfoKey)
30✔
149
        if b == nil {
30✔
150
                return nil, fmt.Errorf("creation info not found")
×
151
        }
×
152

153
        r := bytes.NewReader(b)
30✔
154
        creationInfo, err := deserializeDuplicatePaymentCreationInfo(r)
30✔
155
        if err != nil {
30✔
156
                return nil, err
×
157
        }
×
158

159
        // Get failure reason if available.
160
        var failureReason *FailureReason
30✔
161
        b = bucket.Get(duplicatePaymentFailInfoKey)
30✔
162
        if b != nil {
30✔
163
                reason := FailureReason(b[0])
×
164
                failureReason = &reason
×
165
        }
×
166

167
        payment := &MPPayment{
30✔
168
                SequenceNum:   sequenceNum,
30✔
169
                Info:          creationInfo,
30✔
170
                FailureReason: failureReason,
30✔
171
                Status:        paymentStatus,
30✔
172
        }
30✔
173

30✔
174
        // Get the HTLCAttemptInfo. It can be absent.
30✔
175
        b = bucket.Get(duplicatePaymentAttemptInfoKey)
30✔
176
        if b != nil {
30✔
177
                r = bytes.NewReader(b)
×
178
                attempt, err := deserializeDuplicateHTLCAttemptInfo(r)
×
179
                if err != nil {
×
180
                        return nil, err
×
181
                }
×
182

183
                htlc := HTLCAttempt{
×
184
                        HTLCAttemptInfo: HTLCAttemptInfo{
×
185
                                AttemptID:  attempt.attemptID,
×
186
                                Route:      attempt.route,
×
187
                                sessionKey: attempt.sessionKey,
×
188
                        },
×
189
                }
×
190

×
191
                // Get the payment preimage. This is only found for
×
192
                // successful payments.
×
193
                b = bucket.Get(duplicatePaymentSettleInfoKey)
×
194
                if b != nil {
×
195
                        var preimg lntypes.Preimage
×
196
                        copy(preimg[:], b)
×
197

×
198
                        htlc.Settle = &HTLCSettleInfo{
×
199
                                Preimage:   preimg,
×
200
                                SettleTime: time.Time{},
×
201
                        }
×
202
                } else {
×
203
                        // Otherwise the payment must have failed.
×
204
                        htlc.Failure = &HTLCFailInfo{
×
205
                                FailTime: time.Time{},
×
206
                        }
×
207
                }
×
208

209
                payment.HTLCs = []HTLCAttempt{htlc}
×
210
        }
211

212
        return payment, nil
30✔
213
}
214

215
func fetchDuplicatePayments(paymentHashBucket kvdb.RBucket) ([]*MPPayment,
216
        error) {
148✔
217

148✔
218
        var payments []*MPPayment
148✔
219

148✔
220
        // For older versions of lnd, duplicate payments to a payment has was
148✔
221
        // possible. These will be found in a sub-bucket indexed by their
148✔
222
        // sequence number if available.
148✔
223
        dup := paymentHashBucket.NestedReadBucket(duplicatePaymentsBucket)
148✔
224
        if dup == nil {
277✔
225
                return nil, nil
129✔
226
        }
129✔
227

228
        err := dup.ForEach(func(k, v []byte) error {
38✔
229
                subBucket := dup.NestedReadBucket(k)
19✔
230
                if subBucket == nil {
19✔
231
                        // We one bucket for each duplicate to be found.
×
232
                        return fmt.Errorf("non bucket element" +
×
233
                                "in duplicate bucket")
×
234
                }
×
235

236
                p, err := fetchDuplicatePayment(subBucket)
19✔
237
                if err != nil {
19✔
238
                        return err
×
239
                }
×
240

241
                payments = append(payments, p)
19✔
242
                return nil
19✔
243
        })
244
        if err != nil {
19✔
245
                return nil, err
×
246
        }
×
247

248
        return payments, nil
19✔
249
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc