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

lightningnetwork / lnd / 15736109134

18 Jun 2025 02:46PM UTC coverage: 58.197% (-10.1%) from 68.248%
15736109134

Pull #9752

github

web-flow
Merge d2634a68c into 31c74f20f
Pull Request #9752: routerrpc: reject payment to invoice that don't have payment secret or blinded paths

6 of 13 new or added lines in 2 files covered. (46.15%)

28331 existing lines in 455 files now uncovered.

97860 of 168153 relevant lines covered (58.2%)

1.81 hits per line

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

0.0
/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
        "github.com/lightningnetwork/lnd/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 [btcec.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
63
// the payment isn't found, it will return error `ErrPaymentNotInitiated`.
UNCOV
64
func fetchDuplicatePaymentStatus(bucket kvdb.RBucket) (PaymentStatus, error) {
×
UNCOV
65
        if bucket.Get(duplicatePaymentSettleInfoKey) != nil {
×
UNCOV
66
                return StatusSucceeded, nil
×
UNCOV
67
        }
×
68

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

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

77
        return 0, ErrPaymentNotInitiated
×
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) (
UNCOV
96
        *PaymentCreationInfo, error) {
×
UNCOV
97

×
UNCOV
98
        var scratch [8]byte
×
UNCOV
99

×
UNCOV
100
        c := &PaymentCreationInfo{}
×
UNCOV
101

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

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

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

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

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

×
UNCOV
129
        return c, nil
×
130
}
131

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

UNCOV
138
        sequenceNum := binary.BigEndian.Uint64(seqBytes)
×
UNCOV
139

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

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

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

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

UNCOV
166
        payment := &MPPayment{
×
UNCOV
167
                SequenceNum:   sequenceNum,
×
UNCOV
168
                Info:          creationInfo,
×
UNCOV
169
                FailureReason: failureReason,
×
UNCOV
170
                Status:        paymentStatus,
×
UNCOV
171
        }
×
UNCOV
172

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

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

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

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

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

UNCOV
211
        return payment, nil
×
212
}
213

214
func fetchDuplicatePayments(paymentHashBucket kvdb.RBucket) ([]*MPPayment,
UNCOV
215
        error) {
×
UNCOV
216

×
UNCOV
217
        var payments []*MPPayment
×
UNCOV
218

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

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

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

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

UNCOV
247
        return payments, nil
×
248
}
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