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

lightningnetwork / lnd / 12986279612

27 Jan 2025 09:51AM UTC coverage: 57.652% (-1.1%) from 58.788%
12986279612

Pull #9447

github

yyforyongyu
sweep: rename methods for clarity

We now rename "third party" to "unknown" as the inputs can be spent via
an older sweeping tx, a third party (anchor), or a remote party (pin).
In fee bumper we don't have the info to distinguish the above cases, and
leave them to be further handled by the sweeper as it has more context.
Pull Request #9447: sweep: start tracking input spending status in the fee bumper

83 of 87 new or added lines in 2 files covered. (95.4%)

19578 existing lines in 256 files now uncovered.

103448 of 179434 relevant lines covered (57.65%)

24884.58 hits per line

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

88.01
/invoices/update.go
1
package invoices
2

3
import (
4
        "bytes"
5
        "encoding/hex"
6
        "errors"
7

8
        "github.com/btcsuite/btcd/chaincfg/chainhash"
9
        "github.com/lightningnetwork/lnd/amp"
10
        "github.com/lightningnetwork/lnd/lntypes"
11
        "github.com/lightningnetwork/lnd/lnwire"
12
        "github.com/lightningnetwork/lnd/record"
13
)
14

15
// invoiceUpdateCtx is an object that describes the context for the invoice
16
// update to be carried out.
17
type invoiceUpdateCtx struct {
18
        hash                 lntypes.Hash
19
        circuitKey           CircuitKey
20
        amtPaid              lnwire.MilliSatoshi
21
        expiry               uint32
22
        currentHeight        int32
23
        finalCltvRejectDelta int32
24

25
        // wireCustomRecords are the custom records that were included with the
26
        // HTLC wire message.
27
        wireCustomRecords lnwire.CustomRecords
28

29
        // customRecords is a map of custom records that were included with the
30
        // HTLC onion payload.
31
        customRecords record.CustomSet
32

33
        mpp          *record.MPP
34
        amp          *record.AMP
35
        metadata     []byte
36
        pathID       *chainhash.Hash
37
        totalAmtMsat lnwire.MilliSatoshi
38
}
39

40
// invoiceRef returns an identifier that can be used to lookup or update the
41
// invoice this HTLC is targeting.
42
func (i *invoiceUpdateCtx) invoiceRef() InvoiceRef {
2,182✔
43
        switch {
2,182✔
UNCOV
44
        case i.pathID != nil:
×
UNCOV
45
                return InvoiceRefByHashAndAddr(i.hash, *i.pathID)
×
46

47
        case i.amp != nil && i.mpp != nil:
57✔
48
                payAddr := i.mpp.PaymentAddr()
57✔
49
                return InvoiceRefByAddr(payAddr)
57✔
50

51
        case i.mpp != nil:
1,578✔
52
                payAddr := i.mpp.PaymentAddr()
1,578✔
53
                return InvoiceRefByHashAndAddr(i.hash, payAddr)
1,578✔
54

55
        default:
547✔
56
                return InvoiceRefByHash(i.hash)
547✔
57
        }
58
}
59

60
// setID returns an identifier that identifies other possible HTLCs that this
61
// particular one is related to. If nil is returned this means the HTLC is an
62
// MPP or legacy payment, otherwise the HTLC belongs AMP payment.
63
func (i invoiceUpdateCtx) setID() *[32]byte {
2,549✔
64
        if i.amp != nil {
2,636✔
65
                setID := i.amp.SetID()
87✔
66
                return &setID
87✔
67
        }
87✔
68
        return nil
2,462✔
69
}
70

71
// log logs a message specific to this update context.
72
func (i *invoiceUpdateCtx) log(s string) {
924✔
73
        // Don't use %x in the log statement below, because it doesn't
924✔
74
        // distinguish between nil and empty metadata.
924✔
75
        metadata := "<nil>"
924✔
76
        if i.metadata != nil {
924✔
77
                metadata = hex.EncodeToString(i.metadata)
×
78
        }
×
79

80
        log.Debugf("Invoice%v: %v, amt=%v, expiry=%v, circuit=%v, mpp=%v, "+
924✔
81
                "amp=%v, metadata=%v", i.invoiceRef(), s, i.amtPaid, i.expiry,
924✔
82
                i.circuitKey, i.mpp, i.amp, metadata)
924✔
83
}
84

85
// failRes is a helper function which creates a failure resolution with
86
// the information contained in the invoiceUpdateCtx and the fail resolution
87
// result provided.
88
func (i invoiceUpdateCtx) failRes(outcome FailResolutionResult) *HtlcFailResolution {
26✔
89
        return NewFailResolution(i.circuitKey, i.currentHeight, outcome)
26✔
90
}
26✔
91

92
// settleRes is a helper function which creates a settle resolution with
93
// the information contained in the invoiceUpdateCtx and the preimage and
94
// the settle resolution result provided.
95
func (i invoiceUpdateCtx) settleRes(preimage lntypes.Preimage,
96
        outcome SettleResolutionResult) *HtlcSettleResolution {
473✔
97

473✔
98
        return NewSettleResolution(
473✔
99
                preimage, i.circuitKey, i.currentHeight, outcome,
473✔
100
        )
473✔
101
}
473✔
102

103
// acceptRes is a helper function which creates an accept resolution with
104
// the information contained in the invoiceUpdateCtx and the accept resolution
105
// result provided.
106
func (i invoiceUpdateCtx) acceptRes(
107
        outcome acceptResolutionResult) *htlcAcceptResolution {
419✔
108

419✔
109
        return newAcceptResolution(i.circuitKey, outcome)
419✔
110
}
419✔
111

112
// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice
113
// settlement logic. It returns a HTLC resolution that indicates what the
114
// outcome of the update was.
115
func updateInvoice(ctx *invoiceUpdateCtx, inv *Invoice) (
116
        *InvoiceUpdateDesc, HtlcResolution, error) {
918✔
117

918✔
118
        // Don't update the invoice when this is a replayed htlc.
918✔
119
        htlc, ok := inv.Htlcs[ctx.circuitKey]
918✔
120
        if ok {
934✔
121
                switch htlc.State {
16✔
122
                case HtlcStateCanceled:
3✔
123
                        return nil, ctx.failRes(ResultReplayToCanceled), nil
3✔
124

125
                case HtlcStateAccepted:
7✔
126
                        return nil, ctx.acceptRes(resultReplayToAccepted), nil
7✔
127

128
                case HtlcStateSettled:
6✔
129
                        pre := inv.Terms.PaymentPreimage
6✔
130

6✔
131
                        // Terms.PaymentPreimage will be nil for AMP invoices.
6✔
132
                        // Set it to the HTLCs AMP Preimage instead.
6✔
133
                        if pre == nil {
6✔
134
                                pre = htlc.AMP.Preimage
×
135
                        }
×
136

137
                        return nil, ctx.settleRes(
6✔
138
                                *pre,
6✔
139
                                ResultReplayToSettled,
6✔
140
                        ), nil
6✔
141

142
                default:
×
143
                        return nil, nil, errors.New("unknown htlc state")
×
144
                }
145
        }
146

147
        // If no MPP payload was provided, then we expect this to be a keysend,
148
        // or a payment to an invoice created before we started to require the
149
        // MPP payload.
150
        if ctx.mpp == nil && ctx.pathID == nil {
1,150✔
151
                return updateLegacy(ctx, inv)
248✔
152
        }
248✔
153

154
        return updateMpp(ctx, inv)
654✔
155
}
156

157
// updateMpp is a callback for DB.UpdateInvoice that contains the invoice
158
// settlement logic for mpp payments.
159
func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc,
160
        HtlcResolution, error) {
654✔
161

654✔
162
        // Reject HTLCs to AMP invoices if they are missing an AMP payload, and
654✔
163
        // HTLCs to MPP invoices if they have an AMP payload.
654✔
164
        switch {
654✔
165
        case inv.Terms.Features.RequiresFeature(lnwire.AMPRequired) &&
166
                ctx.amp == nil:
×
167

×
168
                return nil, ctx.failRes(ResultHtlcInvoiceTypeMismatch), nil
×
169

170
        case !inv.Terms.Features.RequiresFeature(lnwire.AMPRequired) &&
171
                ctx.amp != nil:
×
172

×
173
                return nil, ctx.failRes(ResultHtlcInvoiceTypeMismatch), nil
×
174
        }
175

176
        setID := ctx.setID()
654✔
177

654✔
178
        var (
654✔
179
                totalAmt    = ctx.totalAmtMsat
654✔
180
                paymentAddr []byte
654✔
181
        )
654✔
182
        // If an MPP record is present, then the payment address and total
654✔
183
        // payment amount is extracted from it. Otherwise, the pathID is used
654✔
184
        // to extract the payment address.
654✔
185
        if ctx.mpp != nil {
1,308✔
186
                totalAmt = ctx.mpp.TotalMsat()
654✔
187
                payAddr := ctx.mpp.PaymentAddr()
654✔
188
                paymentAddr = payAddr[:]
654✔
189
        } else {
654✔
UNCOV
190
                paymentAddr = ctx.pathID[:]
×
UNCOV
191
        }
×
192

193
        // For storage, we don't really care where the custom records came from.
194
        // So we merge them together and store them in the same field.
195
        customRecords := lnwire.CustomRecords(
654✔
196
                ctx.customRecords,
654✔
197
        ).MergedCopy(ctx.wireCustomRecords)
654✔
198

654✔
199
        // Start building the accept descriptor.
654✔
200
        acceptDesc := &HtlcAcceptDesc{
654✔
201
                Amt:           ctx.amtPaid,
654✔
202
                Expiry:        ctx.expiry,
654✔
203
                AcceptHeight:  ctx.currentHeight,
654✔
204
                MppTotalAmt:   totalAmt,
654✔
205
                CustomRecords: record.CustomSet(customRecords),
654✔
206
        }
654✔
207

654✔
208
        if ctx.amp != nil {
678✔
209
                acceptDesc.AMP = &InvoiceHtlcAMPData{
24✔
210
                        Record:   *ctx.amp,
24✔
211
                        Hash:     ctx.hash,
24✔
212
                        Preimage: nil,
24✔
213
                }
24✔
214
        }
24✔
215

216
        // Only accept payments to open invoices. This behaviour differs from
217
        // non-mpp payments that are accepted even after the invoice is settled.
218
        // Because non-mpp payments don't have a payment address, this is needed
219
        // to thwart probing.
220
        if inv.State != ContractOpen {
654✔
221
                return nil, ctx.failRes(ResultInvoiceNotOpen), nil
×
222
        }
×
223

224
        // Check the payment address that authorizes the payment.
225
        if !bytes.Equal(paymentAddr, inv.Terms.PaymentAddr[:]) {
654✔
226
                return nil, ctx.failRes(ResultAddressMismatch), nil
×
227
        }
×
228

229
        // Don't accept zero-valued sets.
230
        if totalAmt == 0 {
654✔
231
                return nil, ctx.failRes(ResultHtlcSetTotalTooLow), nil
×
232
        }
×
233

234
        // Check that the total amt of the htlc set is high enough. In case this
235
        // is a zero-valued invoice, it will always be enough.
236
        if totalAmt < inv.Terms.Value {
654✔
237
                return nil, ctx.failRes(ResultHtlcSetTotalTooLow), nil
×
238
        }
×
239

240
        htlcSet := inv.HTLCSet(setID, HtlcStateAccepted)
654✔
241

654✔
242
        // Check whether total amt matches other HTLCs in the set.
654✔
243
        var newSetTotal lnwire.MilliSatoshi
654✔
244
        for _, htlc := range htlcSet {
984✔
245
                if totalAmt != htlc.MppTotalAmt {
330✔
246
                        return nil, ctx.failRes(ResultHtlcSetTotalMismatch), nil
×
247
                }
×
248

249
                newSetTotal += htlc.Amt
330✔
250
        }
251

252
        // Add amount of new htlc.
253
        newSetTotal += ctx.amtPaid
654✔
254

654✔
255
        // The invoice is still open. Check the expiry.
654✔
256
        if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
654✔
257
                return nil, ctx.failRes(ResultExpiryTooSoon), nil
×
258
        }
×
259

260
        if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
654✔
261
                return nil, ctx.failRes(ResultExpiryTooSoon), nil
×
262
        }
×
263

264
        if setID != nil && *setID == BlankPayAddr {
654✔
265
                return nil, ctx.failRes(ResultAmpError), nil
×
266
        }
×
267

268
        // Record HTLC in the invoice database.
269
        newHtlcs := map[CircuitKey]*HtlcAcceptDesc{
654✔
270
                ctx.circuitKey: acceptDesc,
654✔
271
        }
654✔
272

654✔
273
        update := InvoiceUpdateDesc{
654✔
274
                UpdateType: AddHTLCsUpdate,
654✔
275
                AddHtlcs:   newHtlcs,
654✔
276
        }
654✔
277

654✔
278
        // If the invoice cannot be settled yet, only record the htlc.
654✔
279
        setComplete := newSetTotal >= totalAmt
654✔
280
        if !setComplete {
984✔
281
                return &update, ctx.acceptRes(resultPartialAccepted), nil
330✔
282
        }
330✔
283

284
        // Check to see if we can settle or this is a hold invoice, and
285
        // we need to wait for the preimage.
286
        if inv.HodlInvoice {
333✔
287
                update.State = &InvoiceStateUpdateDesc{
9✔
288
                        NewState: ContractAccepted,
9✔
289
                }
9✔
290
                return &update, ctx.acceptRes(resultAccepted), nil
9✔
291
        }
9✔
292

293
        var (
315✔
294
                htlcPreimages map[CircuitKey]lntypes.Preimage
315✔
295
                htlcPreimage  lntypes.Preimage
315✔
296
        )
315✔
297
        if ctx.amp != nil {
327✔
298
                var failRes *HtlcFailResolution
12✔
299
                htlcPreimages, failRes = reconstructAMPPreimages(ctx, htlcSet)
12✔
300
                if failRes != nil {
18✔
301
                        update.UpdateType = CancelInvoiceUpdate
6✔
302
                        update.State = &InvoiceStateUpdateDesc{
6✔
303
                                NewState: ContractCanceled,
6✔
304
                                SetID:    setID,
6✔
305
                        }
6✔
306
                        return &update, failRes, nil
6✔
307
                }
6✔
308

309
                // The preimage for _this_ HTLC will be the one with context's
310
                // circuit key.
311
                htlcPreimage = htlcPreimages[ctx.circuitKey]
6✔
312
        } else {
303✔
313
                htlcPreimage = *inv.Terms.PaymentPreimage
303✔
314
        }
303✔
315

316
        update.State = &InvoiceStateUpdateDesc{
309✔
317
                NewState:      ContractSettled,
309✔
318
                Preimage:      inv.Terms.PaymentPreimage,
309✔
319
                HTLCPreimages: htlcPreimages,
309✔
320
                SetID:         setID,
309✔
321
        }
309✔
322

309✔
323
        return &update, ctx.settleRes(htlcPreimage, ResultSettled), nil
309✔
324
}
325

326
// HTLCSet is a map of CircuitKey to InvoiceHTLC.
327
type HTLCSet = map[CircuitKey]*InvoiceHTLC
328

329
// HTLCPreimages is a map of CircuitKey to preimage.
330
type HTLCPreimages = map[CircuitKey]lntypes.Preimage
331

332
// reconstructAMPPreimages reconstructs the root seed for an AMP HTLC set and
333
// verifies that all derived child hashes match the payment hashes of the HTLCs
334
// in the set. This method is meant to be called after receiving the full amount
335
// committed to via mpp_total_msat. This method will return a fail resolution if
336
// any of the child hashes fail to match their corresponding HTLCs.
337
func reconstructAMPPreimages(ctx *invoiceUpdateCtx,
338
        htlcSet HTLCSet) (HTLCPreimages, *HtlcFailResolution) {
12✔
339

12✔
340
        // Create a slice containing all the child descriptors to be used for
12✔
341
        // reconstruction. This should include all HTLCs currently in the HTLC
12✔
342
        // set, plus the incoming HTLC.
12✔
343
        childDescs := make([]amp.ChildDesc, 0, 1+len(htlcSet))
12✔
344

12✔
345
        // Add the new HTLC's child descriptor at index 0.
12✔
346
        childDescs = append(childDescs, amp.ChildDesc{
12✔
347
                Share: ctx.amp.RootShare(),
12✔
348
                Index: ctx.amp.ChildIndex(),
12✔
349
        })
12✔
350

12✔
351
        // Next, construct an index mapping the position in childDescs to a
12✔
352
        // circuit key for all preexisting HTLCs.
12✔
353
        indexToCircuitKey := make(map[int]CircuitKey)
12✔
354

12✔
355
        // Add the child descriptor for each HTLC in the HTLC set, recording
12✔
356
        // it's position within the slice.
12✔
357
        var htlcSetIndex int
12✔
358
        for circuitKey, htlc := range htlcSet {
24✔
359
                childDescs = append(childDescs, amp.ChildDesc{
12✔
360
                        Share: htlc.AMP.Record.RootShare(),
12✔
361
                        Index: htlc.AMP.Record.ChildIndex(),
12✔
362
                })
12✔
363
                indexToCircuitKey[htlcSetIndex] = circuitKey
12✔
364
                htlcSetIndex++
12✔
365
        }
12✔
366

367
        // Using the child descriptors, reconstruct the root seed and derive the
368
        // child hash/preimage pairs for each of the HTLCs.
369
        children := amp.ReconstructChildren(childDescs...)
12✔
370

12✔
371
        // Validate that the derived child preimages match the hash of each
12✔
372
        // HTLC's respective hash.
12✔
373
        if ctx.hash != children[0].Hash {
18✔
374
                return nil, ctx.failRes(ResultAmpReconstruction)
6✔
375
        }
6✔
376
        for idx, child := range children[1:] {
12✔
377
                circuitKey := indexToCircuitKey[idx]
6✔
378
                htlc := htlcSet[circuitKey]
6✔
379
                if htlc.AMP.Hash != child.Hash {
6✔
380
                        return nil, ctx.failRes(ResultAmpReconstruction)
×
381
                }
×
382
        }
383

384
        // Finally, construct the map of learned preimages indexed by circuit
385
        // key, so that they can be persisted along with each HTLC when updating
386
        // the invoice.
387
        htlcPreimages := make(map[CircuitKey]lntypes.Preimage)
6✔
388
        htlcPreimages[ctx.circuitKey] = children[0].Preimage
6✔
389
        for idx, child := range children[1:] {
12✔
390
                circuitKey := indexToCircuitKey[idx]
6✔
391
                htlcPreimages[circuitKey] = child.Preimage
6✔
392
        }
6✔
393

394
        return htlcPreimages, nil
6✔
395
}
396

397
// updateLegacy is a callback for DB.UpdateInvoice that contains the invoice
398
// settlement logic for legacy payments.
399
//
400
// NOTE: This function is only kept in place in order to be able to handle key
401
// send payments and any invoices we created in the past that are valid and
402
// still had the optional mpp bit set.
403
func updateLegacy(ctx *invoiceUpdateCtx,
404
        inv *Invoice) (*InvoiceUpdateDesc, HtlcResolution, error) {
248✔
405

248✔
406
        // If the invoice is already canceled, there is no further
248✔
407
        // checking to do.
248✔
408
        if inv.State == ContractCanceled {
252✔
409
                return nil, ctx.failRes(ResultInvoiceAlreadyCanceled), nil
4✔
410
        }
4✔
411

412
        // If an invoice amount is specified, check that enough is paid. Also
413
        // check this for duplicate payments if the invoice is already settled
414
        // or accepted. In case this is a zero-valued invoice, it will always be
415
        // enough.
416
        if ctx.amtPaid < inv.Terms.Value {
247✔
417
                return nil, ctx.failRes(ResultAmountTooLow), nil
3✔
418
        }
3✔
419

420
        // If the invoice had the required feature bit set at this point, then
421
        // if we're in this method it means that the remote party didn't supply
422
        // the expected payload. However if this is a keysend payment, then
423
        // we'll permit it to pass.
424
        _, isKeySend := ctx.customRecords[record.KeySendType]
241✔
425
        invoiceFeatures := inv.Terms.Features
241✔
426
        paymentAddrRequired := invoiceFeatures.RequiresFeature(
241✔
427
                lnwire.PaymentAddrRequired,
241✔
428
        )
241✔
429
        if !isKeySend && paymentAddrRequired {
244✔
430
                log.Warnf("Payment to pay_hash=%v doesn't include MPP "+
3✔
431
                        "payload, rejecting", ctx.hash)
3✔
432
                return nil, ctx.failRes(ResultAddressMismatch), nil
3✔
433
        }
3✔
434

435
        // Don't allow settling the invoice with an old style
436
        // htlc if we are already in the process of gathering an
437
        // mpp set.
438
        for _, htlc := range inv.HTLCSet(nil, HtlcStateAccepted) {
241✔
439
                if htlc.MppTotalAmt > 0 {
3✔
440
                        return nil, ctx.failRes(ResultMppInProgress), nil
×
441
                }
×
442
        }
443

444
        // The invoice is still open. Check the expiry.
445
        if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
244✔
446
                return nil, ctx.failRes(ResultExpiryTooSoon), nil
6✔
447
        }
6✔
448

449
        if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
233✔
450
                return nil, ctx.failRes(ResultExpiryTooSoon), nil
1✔
451
        }
1✔
452

453
        // For storage, we don't really care where the custom records came from.
454
        // So we merge them together and store them in the same field.
455
        customRecords := lnwire.CustomRecords(
231✔
456
                ctx.customRecords,
231✔
457
        ).MergedCopy(ctx.wireCustomRecords)
231✔
458

231✔
459
        // Record HTLC in the invoice database.
231✔
460
        newHtlcs := map[CircuitKey]*HtlcAcceptDesc{
231✔
461
                ctx.circuitKey: {
231✔
462
                        Amt:           ctx.amtPaid,
231✔
463
                        Expiry:        ctx.expiry,
231✔
464
                        AcceptHeight:  ctx.currentHeight,
231✔
465
                        CustomRecords: record.CustomSet(customRecords),
231✔
466
                },
231✔
467
        }
231✔
468

231✔
469
        update := InvoiceUpdateDesc{
231✔
470
                AddHtlcs:   newHtlcs,
231✔
471
                UpdateType: AddHTLCsUpdate,
231✔
472
        }
231✔
473

231✔
474
        // Don't update invoice state if we are accepting a duplicate payment.
231✔
475
        // We do accept or settle the HTLC.
231✔
476
        switch inv.State {
231✔
477
        case ContractAccepted:
×
478
                return &update, ctx.acceptRes(resultDuplicateToAccepted), nil
×
479

480
        case ContractSettled:
3✔
481
                return &update, ctx.settleRes(
3✔
482
                        *inv.Terms.PaymentPreimage, ResultDuplicateToSettled,
3✔
483
                ), nil
3✔
484
        }
485

486
        // Check to see if we can settle or this is an hold invoice and we need
487
        // to wait for the preimage.
488
        if inv.HodlInvoice {
301✔
489
                update.State = &InvoiceStateUpdateDesc{
73✔
490
                        NewState: ContractAccepted,
73✔
491
                }
73✔
492

73✔
493
                return &update, ctx.acceptRes(resultAccepted), nil
73✔
494
        }
73✔
495

496
        update.State = &InvoiceStateUpdateDesc{
155✔
497
                NewState: ContractSettled,
155✔
498
                Preimage: inv.Terms.PaymentPreimage,
155✔
499
        }
155✔
500

155✔
501
        return &update, ctx.settleRes(
155✔
502
                *inv.Terms.PaymentPreimage, ResultSettled,
155✔
503
        ), nil
155✔
504
}
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