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

lightningnetwork / lnd / 12691608993

09 Jan 2025 02:10PM UTC coverage: 49.396% (-9.2%) from 58.598%
12691608993

Pull #9405

github

yyforyongyu
multi: make `ProofMatureDelta` configurable

We add a new config option to set the `ProofMatureDelta` so the users
can tune their graphs based on their own perference over the num of
confs found in the announcement signatures.
Pull Request #9405: discovery+lnd: make param `ProofMatureDelta` configurable

14 of 17 new or added lines in 3 files covered. (82.35%)

27112 existing lines in 431 files now uncovered.

100556 of 203572 relevant lines covered (49.4%)

1.54 hits per line

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

88.53
/contractcourt/htlc_timeout_resolver.go
1
package contractcourt
2

3
import (
4
        "encoding/binary"
5
        "fmt"
6
        "io"
7
        "sync"
8

9
        "github.com/btcsuite/btcd/btcutil"
10
        "github.com/btcsuite/btcd/chaincfg/chainhash"
11
        "github.com/btcsuite/btcd/txscript"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/davecgh/go-spew/spew"
14
        "github.com/lightningnetwork/lnd/chainntnfs"
15
        "github.com/lightningnetwork/lnd/channeldb"
16
        "github.com/lightningnetwork/lnd/fn/v2"
17
        "github.com/lightningnetwork/lnd/input"
18
        "github.com/lightningnetwork/lnd/lntypes"
19
        "github.com/lightningnetwork/lnd/lnutils"
20
        "github.com/lightningnetwork/lnd/lnwallet"
21
        "github.com/lightningnetwork/lnd/lnwire"
22
        "github.com/lightningnetwork/lnd/sweep"
23
)
24

25
// htlcTimeoutResolver is a ContractResolver that's capable of resolving an
26
// outgoing HTLC. The HTLC may be on our commitment transaction, or on the
27
// commitment transaction of the remote party. An output on our commitment
28
// transaction is considered fully resolved once the second-level transaction
29
// has been confirmed (and reached a sufficient depth). An output on the
30
// commitment transaction of the remote party is resolved once we detect a
31
// spend of the direct HTLC output using the timeout clause.
32
type htlcTimeoutResolver struct {
33
        // htlcResolution contains all the information required to properly
34
        // resolve this outgoing HTLC.
35
        htlcResolution lnwallet.OutgoingHtlcResolution
36

37
        // outputIncubating returns true if we've sent the output to the output
38
        // incubator (utxo nursery).
39
        outputIncubating bool
40

41
        // broadcastHeight is the height that the original contract was
42
        // broadcast to the main-chain at. We'll use this value to bound any
43
        // historical queries to the chain for spends/confirmations.
44
        //
45
        // TODO(roasbeef): wrap above into definite resolution embedding?
46
        broadcastHeight uint32
47

48
        // htlc contains information on the htlc that we are resolving on-chain.
49
        htlc channeldb.HTLC
50

51
        // currentReport stores the current state of the resolver for reporting
52
        // over the rpc interface. This should only be reported in case we have
53
        // a non-nil SignDetails on the htlcResolution, otherwise the nursery
54
        // will produce reports.
55
        currentReport ContractReport
56

57
        // reportLock prevents concurrent access to the resolver report.
58
        reportLock sync.Mutex
59

60
        contractResolverKit
61

62
        htlcLeaseResolver
63

64
        // incomingHTLCExpiryHeight is the absolute block height at which the
65
        // incoming HTLC will expire. This is used as the deadline height as
66
        // the outgoing HTLC must be swept before its incoming HTLC expires.
67
        incomingHTLCExpiryHeight fn.Option[int32]
68
}
69

70
// newTimeoutResolver instantiates a new timeout htlc resolver.
71
func newTimeoutResolver(res lnwallet.OutgoingHtlcResolution,
72
        broadcastHeight uint32, htlc channeldb.HTLC,
73
        resCfg ResolverConfig) *htlcTimeoutResolver {
3✔
74

3✔
75
        h := &htlcTimeoutResolver{
3✔
76
                contractResolverKit: *newContractResolverKit(resCfg),
3✔
77
                htlcResolution:      res,
3✔
78
                broadcastHeight:     broadcastHeight,
3✔
79
                htlc:                htlc,
3✔
80
        }
3✔
81

3✔
82
        h.initReport()
3✔
83
        h.initLogger(fmt.Sprintf("%T(%v)", h, h.outpoint()))
3✔
84

3✔
85
        return h
3✔
86
}
3✔
87

88
// isTaproot returns true if the htlc output is a taproot output.
89
func (h *htlcTimeoutResolver) isTaproot() bool {
3✔
90
        return txscript.IsPayToTaproot(
3✔
91
                h.htlcResolution.SweepSignDesc.Output.PkScript,
3✔
92
        )
3✔
93
}
3✔
94

95
// outpoint returns the outpoint of the HTLC output we're attempting to sweep.
96
func (h *htlcTimeoutResolver) outpoint() wire.OutPoint {
3✔
97
        // The primary key for this resolver will be the outpoint of the HTLC
3✔
98
        // on the commitment transaction itself. If this is our commitment,
3✔
99
        // then the output can be found within the signed timeout tx,
3✔
100
        // otherwise, it's just the ClaimOutpoint.
3✔
101
        if h.htlcResolution.SignedTimeoutTx != nil {
6✔
102
                return h.htlcResolution.SignedTimeoutTx.TxIn[0].PreviousOutPoint
3✔
103
        }
3✔
104

105
        return h.htlcResolution.ClaimOutpoint
3✔
106
}
107

108
// ResolverKey returns an identifier which should be globally unique for this
109
// particular resolver within the chain the original contract resides within.
110
//
111
// NOTE: Part of the ContractResolver interface.
112
func (h *htlcTimeoutResolver) ResolverKey() []byte {
3✔
113
        key := newResolverID(h.outpoint())
3✔
114
        return key[:]
3✔
115
}
3✔
116

117
const (
118
        // expectedRemoteWitnessSuccessSize is the expected size of the witness
119
        // on the remote commitment transaction for an outgoing HTLC that is
120
        // swept on-chain by them with pre-image.
121
        expectedRemoteWitnessSuccessSize = 5
122

123
        // expectedLocalWitnessSuccessSize is the expected size of the witness
124
        // on the local commitment transaction for an outgoing HTLC that is
125
        // swept on-chain by them with pre-image.
126
        expectedLocalWitnessSuccessSize = 3
127

128
        // remotePreimageIndex index within the witness on the remote
129
        // commitment transaction that will hold they pre-image if they go to
130
        // sweep it on chain.
131
        remotePreimageIndex = 3
132

133
        // localPreimageIndex is the index within the witness on the local
134
        // commitment transaction for an outgoing HTLC that will hold the
135
        // pre-image if the remote party sweeps it.
136
        localPreimageIndex = 1
137

138
        // remoteTaprootWitnessSuccessSize is the expected size of the witness
139
        // on the remote commitment for taproot channels. The spend path will
140
        // look like
141
        //   - <sender sig> <receiver sig> <preimage> <success_script>
142
        //     <control_block>
143
        remoteTaprootWitnessSuccessSize = 5
144

145
        // localTaprootWitnessSuccessSize is the expected size of the witness
146
        // on the local commitment for taproot channels. The spend path will
147
        // look like
148
        //  - <receiver sig> <preimage> <success_script> <control_block>
149
        localTaprootWitnessSuccessSize = 4
150

151
        // taprootRemotePreimageIndex is the index within the witness on the
152
        // taproot remote commitment spend that'll hold the pre-image if the
153
        // remote party sweeps it.
154
        taprootRemotePreimageIndex = 2
155
)
156

157
// claimCleanUp is a helper method that's called once the HTLC output is spent
158
// by the remote party. It'll extract the preimage, add it to the global cache,
159
// and finally send the appropriate clean up message.
160
func (h *htlcTimeoutResolver) claimCleanUp(
161
        commitSpend *chainntnfs.SpendDetail) error {
3✔
162

3✔
163
        // Depending on if this is our commitment or not, then we'll be looking
3✔
164
        // for a different witness pattern.
3✔
165
        spenderIndex := commitSpend.SpenderInputIndex
3✔
166
        spendingInput := commitSpend.SpendingTx.TxIn[spenderIndex]
3✔
167

3✔
168
        log.Infof("%T(%v): extracting preimage! remote party spent "+
3✔
169
                "HTLC with tx=%v", h, h.htlcResolution.ClaimOutpoint,
3✔
170
                spew.Sdump(commitSpend.SpendingTx))
3✔
171

3✔
172
        // If this is the remote party's commitment, then we'll be looking for
3✔
173
        // them to spend using the second-level success transaction.
3✔
174
        var preimageBytes []byte
3✔
175
        switch {
3✔
176
        // For taproot channels, if the remote party has swept the HTLC, then
177
        // the witness stack will look like:
178
        //
179
        //   - <sender sig> <receiver sig> <preimage> <success_script>
180
        //     <control_block>
181
        case h.isTaproot() && h.htlcResolution.SignedTimeoutTx == nil:
3✔
182
                //nolint:ll
3✔
183
                preimageBytes = spendingInput.Witness[taprootRemotePreimageIndex]
3✔
184

185
        // The witness stack when the remote party sweeps the output on a
186
        // regular channel to them looks like:
187
        //
188
        //  - <0> <sender sig> <recvr sig> <preimage> <witness script>
189
        case !h.isTaproot() && h.htlcResolution.SignedTimeoutTx == nil:
3✔
190
                preimageBytes = spendingInput.Witness[remotePreimageIndex]
3✔
191

192
        // If this is a taproot channel, and there's only a single witness
193
        // element, then we're actually on the losing side of a breach
194
        // attempt...
195
        case h.isTaproot() && len(spendingInput.Witness) == 1:
3✔
196
                return fmt.Errorf("breach attempt failed")
3✔
197

198
        // Otherwise, they'll be spending directly from our commitment output.
199
        // In which case the witness stack looks like:
200
        //
201
        //  - <sig> <preimage> <witness script>
202
        //
203
        // For taproot channels, this looks like:
204
        //  - <receiver sig> <preimage> <success_script> <control_block>
205
        //
206
        // So we can target the same index.
207
        default:
3✔
208
                preimageBytes = spendingInput.Witness[localPreimageIndex]
3✔
209
        }
210

211
        preimage, err := lntypes.MakePreimage(preimageBytes)
3✔
212
        if err != nil {
3✔
213
                return fmt.Errorf("unable to create pre-image from witness: %w",
×
214
                        err)
×
215
        }
×
216

217
        log.Infof("%T(%v): extracting preimage=%v from on-chain "+
3✔
218
                "spend!", h, h.htlcResolution.ClaimOutpoint, preimage)
3✔
219

3✔
220
        // With the preimage obtained, we can now add it to the global cache.
3✔
221
        if err := h.PreimageDB.AddPreimages(preimage); err != nil {
3✔
222
                log.Errorf("%T(%v): unable to add witness to cache",
×
223
                        h, h.htlcResolution.ClaimOutpoint)
×
224
        }
×
225

226
        var pre [32]byte
3✔
227
        copy(pre[:], preimage[:])
3✔
228

3✔
229
        // Finally, we'll send the clean up message, mark ourselves as
3✔
230
        // resolved, then exit.
3✔
231
        if err := h.DeliverResolutionMsg(ResolutionMsg{
3✔
232
                SourceChan: h.ShortChanID,
3✔
233
                HtlcIndex:  h.htlc.HtlcIndex,
3✔
234
                PreImage:   &pre,
3✔
235
        }); err != nil {
3✔
236
                return err
×
237
        }
×
238
        h.markResolved()
3✔
239

3✔
240
        // Checkpoint our resolver with a report which reflects the preimage
3✔
241
        // claim by the remote party.
3✔
242
        amt := btcutil.Amount(h.htlcResolution.SweepSignDesc.Output.Value)
3✔
243
        report := &channeldb.ResolverReport{
3✔
244
                OutPoint:        h.htlcResolution.ClaimOutpoint,
3✔
245
                Amount:          amt,
3✔
246
                ResolverType:    channeldb.ResolverTypeOutgoingHtlc,
3✔
247
                ResolverOutcome: channeldb.ResolverOutcomeClaimed,
3✔
248
                SpendTxID:       commitSpend.SpenderTxHash,
3✔
249
        }
3✔
250

3✔
251
        return h.Checkpoint(h, report)
3✔
252
}
253

254
// chainDetailsToWatch returns the output and script which we use to watch for
255
// spends from the direct HTLC output on the commitment transaction.
256
func (h *htlcTimeoutResolver) chainDetailsToWatch() (*wire.OutPoint, []byte, error) {
3✔
257
        // If there's no timeout transaction, it means we are spending from a
3✔
258
        // remote commit, then the claim output is the output directly on the
3✔
259
        // commitment transaction, so we'll just use that.
3✔
260
        if h.htlcResolution.SignedTimeoutTx == nil {
6✔
261
                outPointToWatch := h.htlcResolution.ClaimOutpoint
3✔
262
                scriptToWatch := h.htlcResolution.SweepSignDesc.Output.PkScript
3✔
263

3✔
264
                return &outPointToWatch, scriptToWatch, nil
3✔
265
        }
3✔
266

267
        // If SignedTimeoutTx is not nil, this is the local party's commitment,
268
        // and we'll need to grab watch the output that our timeout transaction
269
        // points to. We can directly grab the outpoint, then also extract the
270
        // witness script (the last element of the witness stack) to
271
        // re-construct the pkScript we need to watch.
272
        //
273
        //nolint:ll
274
        outPointToWatch := h.htlcResolution.SignedTimeoutTx.TxIn[0].PreviousOutPoint
3✔
275
        witness := h.htlcResolution.SignedTimeoutTx.TxIn[0].Witness
3✔
276

3✔
277
        var (
3✔
278
                scriptToWatch []byte
3✔
279
                err           error
3✔
280
        )
3✔
281
        switch {
3✔
282
        // For taproot channels, then final witness element is the control
283
        // block, and the one before it the witness script. We can use both of
284
        // these together to reconstruct the taproot output key, then map that
285
        // into a v1 witness program.
286
        case h.isTaproot():
3✔
287
                // First, we'll parse the control block into something we can
3✔
288
                // use.
3✔
289
                ctrlBlockBytes := witness[len(witness)-1]
3✔
290
                ctrlBlock, err := txscript.ParseControlBlock(ctrlBlockBytes)
3✔
291
                if err != nil {
3✔
292
                        return nil, nil, err
×
293
                }
×
294

295
                // With the control block, we'll grab the witness script, then
296
                // use that to derive the tapscript root.
297
                witnessScript := witness[len(witness)-2]
3✔
298
                tapscriptRoot := ctrlBlock.RootHash(witnessScript)
3✔
299

3✔
300
                // Once we have the root, then we can derive the output key
3✔
301
                // from the internal key, then turn that into a witness
3✔
302
                // program.
3✔
303
                outputKey := txscript.ComputeTaprootOutputKey(
3✔
304
                        ctrlBlock.InternalKey, tapscriptRoot,
3✔
305
                )
3✔
306
                scriptToWatch, err = txscript.PayToTaprootScript(outputKey)
3✔
307
                if err != nil {
3✔
308
                        return nil, nil, err
×
309
                }
×
310

311
        // For regular channels, the witness script is the last element on the
312
        // stack. We can then use this to re-derive the output that we're
313
        // watching on chain.
314
        default:
3✔
315
                scriptToWatch, err = input.WitnessScriptHash(
3✔
316
                        witness[len(witness)-1],
3✔
317
                )
3✔
318
        }
319
        if err != nil {
3✔
320
                return nil, nil, err
×
321
        }
×
322

323
        return &outPointToWatch, scriptToWatch, nil
3✔
324
}
325

326
// isPreimageSpend returns true if the passed spend on the specified commitment
327
// is a success spend that reveals the pre-image or not.
328
func isPreimageSpend(isTaproot bool, spend *chainntnfs.SpendDetail,
329
        localCommit bool) bool {
3✔
330

3✔
331
        // Based on the spending input index and transaction, obtain the
3✔
332
        // witness that tells us what type of spend this is.
3✔
333
        spenderIndex := spend.SpenderInputIndex
3✔
334
        spendingInput := spend.SpendingTx.TxIn[spenderIndex]
3✔
335
        spendingWitness := spendingInput.Witness
3✔
336

3✔
337
        switch {
3✔
338
        // If this is a taproot remote commitment, then we can detect the type
339
        // of spend via the leaf revealed in the control block and the witness
340
        // itself.
341
        //
342
        // The keyspend (revocation path) is just a single signature, while the
343
        // timeout and success paths are most distinct.
344
        //
345
        // The success path will look like:
346
        //
347
        //   - <sender sig> <receiver sig> <preimage> <success_script>
348
        //     <control_block>
349
        case isTaproot && !localCommit:
3✔
350
                return checkSizeAndIndex(
3✔
351
                        spendingWitness, remoteTaprootWitnessSuccessSize,
3✔
352
                        taprootRemotePreimageIndex,
3✔
353
                )
3✔
354

355
        // Otherwise, then if this is our local commitment transaction, then if
356
        // they're sweeping the transaction, it'll be directly from the output,
357
        // skipping the second level.
358
        //
359
        // In this case, then there're two main tapscript paths, with the
360
        // success case look like:
361
        //
362
        //  - <receiver sig> <preimage> <success_script> <control_block>
363
        case isTaproot && localCommit:
3✔
364
                return checkSizeAndIndex(
3✔
365
                        spendingWitness, localTaprootWitnessSuccessSize,
3✔
366
                        localPreimageIndex,
3✔
367
                )
3✔
368

369
        // If this is the non-taproot, remote commitment then the only possible
370
        // spends for outgoing HTLCs are:
371
        //
372
        //  RECVR: <0> <sender sig> <recvr sig> <preimage> (2nd level success spend)
373
        //  REVOK: <sig> <key>
374
        //  SENDR: <sig> 0
375
        //
376
        // In this case, if 5 witness elements are present (factoring the
377
        // witness script), and the 3rd element is the size of the pre-image,
378
        // then this is a remote spend. If not, then we swept it ourselves, or
379
        // revoked their output.
380
        case !isTaproot && !localCommit:
3✔
381
                return checkSizeAndIndex(
3✔
382
                        spendingWitness, expectedRemoteWitnessSuccessSize,
3✔
383
                        remotePreimageIndex,
3✔
384
                )
3✔
385

386
        // Otherwise, for our non-taproot commitment, the only possible spends
387
        // for an outgoing HTLC are:
388
        //
389
        //  SENDR: <0> <sendr sig>  <recvr sig> <0> (2nd level timeout)
390
        //  RECVR: <recvr sig>  <preimage>
391
        //  REVOK: <revoke sig> <revoke key>
392
        //
393
        // So the only success case has the pre-image as the 2nd (index 1)
394
        // element in the witness.
395
        case !isTaproot:
3✔
396
                fallthrough
3✔
397

398
        default:
3✔
399
                return checkSizeAndIndex(
3✔
400
                        spendingWitness, expectedLocalWitnessSuccessSize,
3✔
401
                        localPreimageIndex,
3✔
402
                )
3✔
403
        }
404
}
405

406
// checkSizeAndIndex checks that the witness is of the expected size and that
407
// the witness element at the specified index is of the expected size.
408
func checkSizeAndIndex(witness wire.TxWitness, size, index int) bool {
3✔
409
        if len(witness) != size {
6✔
410
                return false
3✔
411
        }
3✔
412

413
        return len(witness[index]) == lntypes.HashSize
3✔
414
}
415

416
// Resolve kicks off full resolution of an outgoing HTLC output. If it's our
417
// commitment, it isn't resolved until we see the second level HTLC txn
418
// confirmed. If it's the remote party's commitment, we don't resolve until we
419
// see a direct sweep via the timeout clause.
420
//
421
// NOTE: Part of the ContractResolver interface.
422
func (h *htlcTimeoutResolver) Resolve() (ContractResolver, error) {
3✔
423
        // If we're already resolved, then we can exit early.
3✔
424
        if h.IsResolved() {
3✔
UNCOV
425
                h.log.Errorf("already resolved")
×
UNCOV
426
                return nil, nil
×
UNCOV
427
        }
×
428

429
        // If this is an output on the remote party's commitment transaction,
430
        // use the direct-spend path to sweep the htlc.
431
        if h.isRemoteCommitOutput() {
6✔
432
                return nil, h.resolveRemoteCommitOutput()
3✔
433
        }
3✔
434

435
        // If this is a zero-fee HTLC, we now handle the spend from our
436
        // commitment transaction.
437
        if h.isZeroFeeOutput() {
6✔
438
                return nil, h.resolveTimeoutTx()
3✔
439
        }
3✔
440

441
        // If this is an output on our own commitment using pre-anchor channel
442
        // type, we will let the utxo nursery handle it.
443
        return nil, h.resolveSecondLevelTxLegacy()
3✔
444
}
445

446
// sweepTimeoutTx sends a second level timeout transaction to the sweeper.
447
// This transaction uses the SINLGE|ANYONECANPAY flag.
448
func (h *htlcTimeoutResolver) sweepTimeoutTx() error {
3✔
449
        var inp input.Input
3✔
450
        if h.isTaproot() {
6✔
451
                inp = lnutils.Ptr(input.MakeHtlcSecondLevelTimeoutTaprootInput(
3✔
452
                        h.htlcResolution.SignedTimeoutTx,
3✔
453
                        h.htlcResolution.SignDetails,
3✔
454
                        h.broadcastHeight,
3✔
455
                        input.WithResolutionBlob(
3✔
456
                                h.htlcResolution.ResolutionBlob,
3✔
457
                        ),
3✔
458
                ))
3✔
459
        } else {
6✔
460
                inp = lnutils.Ptr(input.MakeHtlcSecondLevelTimeoutAnchorInput(
3✔
461
                        h.htlcResolution.SignedTimeoutTx,
3✔
462
                        h.htlcResolution.SignDetails,
3✔
463
                        h.broadcastHeight,
3✔
464
                ))
3✔
465
        }
3✔
466

467
        // Calculate the budget.
468
        //
469
        // TODO(yy): the budget is twice the output's value, which is needed as
470
        // we don't force sweep the output now. To prevent cascading force
471
        // closes, we use all its output value plus a wallet input as the
472
        // budget. This is a temporary solution until we can optionally cancel
473
        // the incoming HTLC, more details in,
474
        // - https://github.com/lightningnetwork/lnd/issues/7969
475
        budget := calculateBudget(
3✔
476
                btcutil.Amount(inp.SignDesc().Output.Value), 2, 0,
3✔
477
        )
3✔
478

3✔
479
        h.log.Infof("offering 2nd-level HTLC timeout tx to sweeper "+
3✔
480
                "with deadline=%v, budget=%v", h.incomingHTLCExpiryHeight,
3✔
481
                budget)
3✔
482

3✔
483
        // For an outgoing HTLC, it must be swept before the RefundTimeout of
3✔
484
        // its incoming HTLC is reached.
3✔
485
        _, err := h.Sweeper.SweepInput(
3✔
486
                inp,
3✔
487
                sweep.Params{
3✔
488
                        Budget:         budget,
3✔
489
                        DeadlineHeight: h.incomingHTLCExpiryHeight,
3✔
490
                },
3✔
491
        )
3✔
492
        if err != nil {
3✔
493
                return err
×
494
        }
×
495

496
        return err
3✔
497
}
498

499
// resolveSecondLevelTxLegacy sends a second level timeout transaction to the
500
// utxo nursery. This transaction uses the legacy SIGHASH_ALL flag.
501
func (h *htlcTimeoutResolver) resolveSecondLevelTxLegacy() error {
3✔
502
        h.log.Debug("incubating htlc output")
3✔
503

3✔
504
        // The utxo nursery will take care of broadcasting the second-level
3✔
505
        // timeout tx and sweeping its output once it confirms.
3✔
506
        err := h.IncubateOutputs(
3✔
507
                h.ChanPoint, fn.Some(h.htlcResolution),
3✔
508
                fn.None[lnwallet.IncomingHtlcResolution](),
3✔
509
                h.broadcastHeight, h.incomingHTLCExpiryHeight,
3✔
510
        )
3✔
511
        if err != nil {
3✔
512
                return err
×
513
        }
×
514

515
        return h.resolveTimeoutTx()
3✔
516
}
517

518
// sweepDirectHtlcOutput sends the direct spend of the HTLC output to the
519
// sweeper. This is used when the remote party goes on chain, and we're able to
520
// sweep an HTLC we offered after a timeout. Only the CLTV encumbered outputs
521
// are resolved via this path.
522
func (h *htlcTimeoutResolver) sweepDirectHtlcOutput() error {
3✔
523
        var htlcWitnessType input.StandardWitnessType
3✔
524
        if h.isTaproot() {
6✔
525
                htlcWitnessType = input.TaprootHtlcOfferedRemoteTimeout
3✔
526
        } else {
6✔
527
                htlcWitnessType = input.HtlcOfferedRemoteTimeout
3✔
528
        }
3✔
529

530
        sweepInput := input.NewCsvInputWithCltv(
3✔
531
                &h.htlcResolution.ClaimOutpoint, htlcWitnessType,
3✔
532
                &h.htlcResolution.SweepSignDesc, h.broadcastHeight,
3✔
533
                h.htlcResolution.CsvDelay, h.htlcResolution.Expiry,
3✔
534
                input.WithResolutionBlob(h.htlcResolution.ResolutionBlob),
3✔
535
        )
3✔
536

3✔
537
        // Calculate the budget.
3✔
538
        //
3✔
539
        // TODO(yy): the budget is twice the output's value, which is needed as
3✔
540
        // we don't force sweep the output now. To prevent cascading force
3✔
541
        // closes, we use all its output value plus a wallet input as the
3✔
542
        // budget. This is a temporary solution until we can optionally cancel
3✔
543
        // the incoming HTLC, more details in,
3✔
544
        // - https://github.com/lightningnetwork/lnd/issues/7969
3✔
545
        budget := calculateBudget(
3✔
546
                btcutil.Amount(sweepInput.SignDesc().Output.Value), 2, 0,
3✔
547
        )
3✔
548

3✔
549
        log.Infof("%T(%x): offering offered remote timeout HTLC output to "+
3✔
550
                "sweeper with deadline %v and budget=%v at height=%v",
3✔
551
                h, h.htlc.RHash[:], h.incomingHTLCExpiryHeight, budget,
3✔
552
                h.broadcastHeight)
3✔
553

3✔
554
        _, err := h.Sweeper.SweepInput(
3✔
555
                sweepInput,
3✔
556
                sweep.Params{
3✔
557
                        Budget: budget,
3✔
558

3✔
559
                        // This is an outgoing HTLC, so we want to make sure
3✔
560
                        // that we sweep it before the incoming HTLC expires.
3✔
561
                        DeadlineHeight: h.incomingHTLCExpiryHeight,
3✔
562
                },
3✔
563
        )
3✔
564
        if err != nil {
3✔
565
                return err
×
566
        }
×
567

568
        return nil
3✔
569
}
570

571
// watchHtlcSpend watches for a spend of the HTLC output. For neutrino backend,
572
// it will check blocks for the confirmed spend. For btcd and bitcoind, it will
573
// check both the mempool and the blocks.
574
func (h *htlcTimeoutResolver) watchHtlcSpend() (*chainntnfs.SpendDetail,
575
        error) {
3✔
576

3✔
577
        // TODO(yy): outpointToWatch is always h.HtlcOutpoint(), can refactor
3✔
578
        // to remove the redundancy.
3✔
579
        outpointToWatch, scriptToWatch, err := h.chainDetailsToWatch()
3✔
580
        if err != nil {
3✔
581
                return nil, err
×
582
        }
×
583

584
        // If there's no mempool configured, which is the case for SPV node
585
        // such as neutrino, then we will watch for confirmed spend only.
586
        if h.Mempool == nil {
4✔
587
                return h.waitForConfirmedSpend(outpointToWatch, scriptToWatch)
1✔
588
        }
1✔
589

590
        // Watch for a spend of the HTLC output in both the mempool and blocks.
591
        return h.waitForMempoolOrBlockSpend(*outpointToWatch, scriptToWatch)
2✔
592
}
593

594
// waitForConfirmedSpend waits for the HTLC output to be spent and confirmed in
595
// a block, returns the spend details.
596
func (h *htlcTimeoutResolver) waitForConfirmedSpend(op *wire.OutPoint,
597
        pkScript []byte) (*chainntnfs.SpendDetail, error) {
1✔
598

1✔
599
        // We'll block here until either we exit, or the HTLC output on the
1✔
600
        // commitment transaction has been spent.
1✔
601
        spend, err := waitForSpend(
1✔
602
                op, pkScript, h.broadcastHeight, h.Notifier, h.quit,
1✔
603
        )
1✔
604
        if err != nil {
2✔
605
                return nil, err
1✔
606
        }
1✔
607

608
        return spend, err
1✔
609
}
610

611
// Stop signals the resolver to cancel any current resolution processes, and
612
// suspend.
613
//
614
// NOTE: Part of the ContractResolver interface.
615
func (h *htlcTimeoutResolver) Stop() {
3✔
616
        h.log.Debugf("stopping...")
3✔
617
        defer h.log.Debugf("stopped")
3✔
618

3✔
619
        close(h.quit)
3✔
620
}
3✔
621

622
// report returns a report on the resolution state of the contract.
623
func (h *htlcTimeoutResolver) report() *ContractReport {
3✔
624
        // If we have a SignedTimeoutTx but no SignDetails, this is a local
3✔
625
        // commitment for a non-anchor channel, which was handled by the utxo
3✔
626
        // nursery.
3✔
627
        if h.htlcResolution.SignDetails == nil && h.
3✔
628
                htlcResolution.SignedTimeoutTx != nil {
3✔
629
                return nil
×
630
        }
×
631

632
        h.reportLock.Lock()
3✔
633
        defer h.reportLock.Unlock()
3✔
634
        cpy := h.currentReport
3✔
635
        return &cpy
3✔
636
}
637

638
func (h *htlcTimeoutResolver) initReport() {
3✔
639
        // We create the initial report. This will only be reported for
3✔
640
        // resolvers not handled by the nursery.
3✔
641
        finalAmt := h.htlc.Amt.ToSatoshis()
3✔
642
        if h.htlcResolution.SignedTimeoutTx != nil {
6✔
643
                finalAmt = btcutil.Amount(
3✔
644
                        h.htlcResolution.SignedTimeoutTx.TxOut[0].Value,
3✔
645
                )
3✔
646
        }
3✔
647

648
        // If there's no timeout transaction, then we're already effectively in
649
        // level two.
650
        stage := uint32(1)
3✔
651
        if h.htlcResolution.SignedTimeoutTx == nil {
6✔
652
                stage = 2
3✔
653
        }
3✔
654

655
        h.currentReport = ContractReport{
3✔
656
                Outpoint:       h.htlcResolution.ClaimOutpoint,
3✔
657
                Type:           ReportOutputOutgoingHtlc,
3✔
658
                Amount:         finalAmt,
3✔
659
                MaturityHeight: h.htlcResolution.Expiry,
3✔
660
                LimboBalance:   finalAmt,
3✔
661
                Stage:          stage,
3✔
662
        }
3✔
663
}
664

665
// Encode writes an encoded version of the ContractResolver into the passed
666
// Writer.
667
//
668
// NOTE: Part of the ContractResolver interface.
669
func (h *htlcTimeoutResolver) Encode(w io.Writer) error {
3✔
670
        // First, we'll write out the relevant fields of the
3✔
671
        // OutgoingHtlcResolution to the writer.
3✔
672
        if err := encodeOutgoingResolution(w, &h.htlcResolution); err != nil {
3✔
673
                return err
×
674
        }
×
675

676
        // With that portion written, we can now write out the fields specific
677
        // to the resolver itself.
678
        if err := binary.Write(w, endian, h.outputIncubating); err != nil {
3✔
679
                return err
×
680
        }
×
681
        if err := binary.Write(w, endian, h.IsResolved()); err != nil {
3✔
682
                return err
×
683
        }
×
684
        if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
3✔
685
                return err
×
686
        }
×
687

688
        if err := binary.Write(w, endian, h.htlc.HtlcIndex); err != nil {
3✔
689
                return err
×
690
        }
×
691

692
        // We encode the sign details last for backwards compatibility.
693
        err := encodeSignDetails(w, h.htlcResolution.SignDetails)
3✔
694
        if err != nil {
3✔
695
                return err
×
696
        }
×
697

698
        return nil
3✔
699
}
700

701
// newTimeoutResolverFromReader attempts to decode an encoded ContractResolver
702
// from the passed Reader instance, returning an active ContractResolver
703
// instance.
704
func newTimeoutResolverFromReader(r io.Reader, resCfg ResolverConfig) (
705
        *htlcTimeoutResolver, error) {
3✔
706

3✔
707
        h := &htlcTimeoutResolver{
3✔
708
                contractResolverKit: *newContractResolverKit(resCfg),
3✔
709
        }
3✔
710

3✔
711
        // First, we'll read out all the mandatory fields of the
3✔
712
        // OutgoingHtlcResolution that we store.
3✔
713
        if err := decodeOutgoingResolution(r, &h.htlcResolution); err != nil {
3✔
714
                return nil, err
×
715
        }
×
716

717
        // With those fields read, we can now read back the fields that are
718
        // specific to the resolver itself.
719
        if err := binary.Read(r, endian, &h.outputIncubating); err != nil {
3✔
720
                return nil, err
×
721
        }
×
722

723
        var resolved bool
3✔
724
        if err := binary.Read(r, endian, &resolved); err != nil {
3✔
725
                return nil, err
×
726
        }
×
727
        if resolved {
6✔
728
                h.markResolved()
3✔
729
        }
3✔
730

731
        if err := binary.Read(r, endian, &h.broadcastHeight); err != nil {
3✔
732
                return nil, err
×
733
        }
×
734

735
        if err := binary.Read(r, endian, &h.htlc.HtlcIndex); err != nil {
3✔
736
                return nil, err
×
737
        }
×
738

739
        // Sign details is a new field that was added to the htlc resolution,
740
        // so it is serialized last for backwards compatibility. We try to read
741
        // it, but don't error out if there are not bytes left.
742
        signDetails, err := decodeSignDetails(r)
3✔
743
        if err == nil {
6✔
744
                h.htlcResolution.SignDetails = signDetails
3✔
745
        } else if err != io.EOF && err != io.ErrUnexpectedEOF {
3✔
746
                return nil, err
×
747
        }
×
748

749
        h.initReport()
3✔
750
        h.initLogger(fmt.Sprintf("%T(%v)", h, h.outpoint()))
3✔
751

3✔
752
        return h, nil
3✔
753
}
754

755
// Supplement adds additional information to the resolver that is required
756
// before Resolve() is called.
757
//
758
// NOTE: Part of the htlcContractResolver interface.
759
func (h *htlcTimeoutResolver) Supplement(htlc channeldb.HTLC) {
3✔
760
        h.htlc = htlc
3✔
761
}
3✔
762

763
// HtlcPoint returns the htlc's outpoint on the commitment tx.
764
//
765
// NOTE: Part of the htlcContractResolver interface.
766
func (h *htlcTimeoutResolver) HtlcPoint() wire.OutPoint {
3✔
767
        return h.htlcResolution.HtlcPoint()
3✔
768
}
3✔
769

770
// SupplementDeadline sets the incomingHTLCExpiryHeight for this outgoing htlc
771
// resolver.
772
//
773
// NOTE: Part of the htlcContractResolver interface.
774
func (h *htlcTimeoutResolver) SupplementDeadline(d fn.Option[int32]) {
3✔
775
        h.incomingHTLCExpiryHeight = d
3✔
776
}
3✔
777

778
// A compile time assertion to ensure htlcTimeoutResolver meets the
779
// ContractResolver interface.
780
var _ htlcContractResolver = (*htlcTimeoutResolver)(nil)
781

782
// spendResult is used to hold the result of a spend event from either a
783
// mempool spend or a block spend.
784
type spendResult struct {
785
        // spend contains the details of the spend.
786
        spend *chainntnfs.SpendDetail
787

788
        // err is the error that occurred during the spend notification.
789
        err error
790
}
791

792
// waitForMempoolOrBlockSpend waits for the htlc output to be spent by a
793
// transaction that's either be found in the mempool or in a block.
794
func (h *htlcTimeoutResolver) waitForMempoolOrBlockSpend(op wire.OutPoint,
795
        pkScript []byte) (*chainntnfs.SpendDetail, error) {
2✔
796

2✔
797
        log.Infof("%T(%v): waiting for spent of HTLC output %v to be found "+
2✔
798
                "in mempool or block", h, h.htlcResolution.ClaimOutpoint, op)
2✔
799

2✔
800
        // Subscribe for block spent(confirmed).
2✔
801
        blockSpent, err := h.Notifier.RegisterSpendNtfn(
2✔
802
                &op, pkScript, h.broadcastHeight,
2✔
803
        )
2✔
804
        if err != nil {
2✔
805
                return nil, fmt.Errorf("register spend: %w", err)
×
806
        }
×
807

808
        // Subscribe for mempool spent(unconfirmed).
809
        mempoolSpent, err := h.Mempool.SubscribeMempoolSpent(op)
2✔
810
        if err != nil {
2✔
811
                return nil, fmt.Errorf("register mempool spend: %w", err)
×
812
        }
×
813

814
        // Create a result chan that will be used to receive the spending
815
        // events.
816
        result := make(chan *spendResult, 2)
2✔
817

2✔
818
        // Create a goroutine that will wait for either a mempool spend or a
2✔
819
        // block spend.
2✔
820
        //
2✔
821
        // NOTE: no need to use waitgroup here as when the resolver exits, the
2✔
822
        // goroutine will return on the quit channel.
2✔
823
        go h.consumeSpendEvents(result, blockSpent.Spend, mempoolSpent.Spend)
2✔
824

2✔
825
        // Wait for the spend event to be received.
2✔
826
        select {
2✔
827
        case event := <-result:
2✔
828
                // Cancel the mempool subscription as we don't need it anymore.
2✔
829
                h.Mempool.CancelMempoolSpendEvent(mempoolSpent)
2✔
830

2✔
831
                return event.spend, event.err
2✔
832

833
        case <-h.quit:
2✔
834
                return nil, errResolverShuttingDown
2✔
835
        }
836
}
837

838
// consumeSpendEvents consumes the spend events from the block and mempool
839
// subscriptions. It exits when a spend event is received from the block, or
840
// the resolver itself quits. When a spend event is received from the mempool,
841
// however, it won't exit but continuing to wait for a spend event from the
842
// block subscription.
843
//
844
// NOTE: there could be a case where we found the preimage in the mempool,
845
// which will be added to our preimage beacon and settle the incoming link,
846
// meanwhile the timeout sweep tx confirms. This outgoing HTLC is "free" money
847
// and is not swept here.
848
//
849
// TODO(yy): sweep the outgoing htlc if it's confirmed.
850
func (h *htlcTimeoutResolver) consumeSpendEvents(resultChan chan *spendResult,
851
        blockSpent, mempoolSpent <-chan *chainntnfs.SpendDetail) {
2✔
852

2✔
853
        op := h.HtlcPoint()
2✔
854

2✔
855
        // Create a result chan to hold the results.
2✔
856
        result := &spendResult{}
2✔
857

2✔
858
        // Wait for a spend event to arrive.
2✔
859
        for {
4✔
860
                select {
2✔
861
                // If a spend event is received from the block, this outgoing
862
                // htlc is spent either by the remote via the preimage or by us
863
                // via the timeout. We can exit the loop and `claimCleanUp`
864
                // will feed the preimage to the beacon if found. This treats
865
                // the block as the final judge and the preimage spent won't
866
                // appear in the mempool afterwards.
867
                //
868
                // NOTE: if a reorg happens, the preimage spend can appear in
869
                // the mempool again. Though a rare case, we should handle it
870
                // in a dedicated reorg system.
871
                case spendDetail, ok := <-blockSpent:
2✔
872
                        if !ok {
2✔
873
                                result.err = fmt.Errorf("block spent err: %w",
×
874
                                        errResolverShuttingDown)
×
875
                        } else {
2✔
876
                                log.Debugf("Found confirmed spend of HTLC "+
2✔
877
                                        "output %s in tx=%s", op,
2✔
878
                                        spendDetail.SpenderTxHash)
2✔
879

2✔
880
                                result.spend = spendDetail
2✔
881

2✔
882
                                // Once confirmed, persist the state on disk if
2✔
883
                                // we haven't seen the output's spending tx in
2✔
884
                                // mempool before.
2✔
885
                        }
2✔
886

887
                        // Send the result and exit the loop.
888
                        resultChan <- result
2✔
889

2✔
890
                        return
2✔
891

892
                // If a spend event is received from the mempool, this can be
893
                // either the 2nd stage timeout tx or a preimage spend from the
894
                // remote. We will further check whether the spend reveals the
895
                // preimage and add it to the preimage beacon to settle the
896
                // incoming link.
897
                //
898
                // NOTE: we won't exit the loop here so we can continue to
899
                // watch for the block spend to check point the resolution.
900
                case spendDetail, ok := <-mempoolSpent:
2✔
901
                        if !ok {
2✔
902
                                result.err = fmt.Errorf("mempool spent err: %w",
×
903
                                        errResolverShuttingDown)
×
904

×
905
                                // This is an internal error so we exit.
×
906
                                resultChan <- result
×
907

×
908
                                return
×
909
                        }
×
910

911
                        log.Debugf("Found mempool spend of HTLC output %s "+
2✔
912
                                "in tx=%s", op, spendDetail.SpenderTxHash)
2✔
913

2✔
914
                        // Check whether the spend reveals the preimage, if not
2✔
915
                        // continue the loop.
2✔
916
                        hasPreimage := isPreimageSpend(
2✔
917
                                h.isTaproot(), spendDetail,
2✔
918
                                !h.isRemoteCommitOutput(),
2✔
919
                        )
2✔
920
                        if !hasPreimage {
4✔
921
                                log.Debugf("HTLC output %s spent doesn't "+
2✔
922
                                        "reveal preimage", op)
2✔
923
                                continue
2✔
924
                        }
925

926
                        // Found the preimage spend, send the result and
927
                        // continue the loop.
928
                        result.spend = spendDetail
2✔
929
                        resultChan <- result
2✔
930

2✔
931
                        continue
2✔
932

933
                // If the resolver exits, we exit the goroutine.
934
                case <-h.quit:
2✔
935
                        result.err = errResolverShuttingDown
2✔
936
                        resultChan <- result
2✔
937

2✔
938
                        return
2✔
939
                }
940
        }
941
}
942

943
// isRemoteCommitOutput returns a bool to indicate whether the htlc output is
944
// on the remote commitment.
945
func (h *htlcTimeoutResolver) isRemoteCommitOutput() bool {
3✔
946
        // If we don't have a timeout transaction, then this means that this is
3✔
947
        // an output on the remote party's commitment transaction.
3✔
948
        return h.htlcResolution.SignedTimeoutTx == nil
3✔
949
}
3✔
950

951
// isZeroFeeOutput returns a boolean indicating whether the htlc output is from
952
// a anchor-enabled channel, which uses the sighash SINGLE|ANYONECANPAY.
953
func (h *htlcTimeoutResolver) isZeroFeeOutput() bool {
3✔
954
        // If we have non-nil SignDetails, this means it has a 2nd level HTLC
3✔
955
        // transaction that is signed using sighash SINGLE|ANYONECANPAY (the
3✔
956
        // case for anchor type channels). In this case we can re-sign it and
3✔
957
        // attach fees at will.
3✔
958
        return h.htlcResolution.SignedTimeoutTx != nil &&
3✔
959
                h.htlcResolution.SignDetails != nil
3✔
960
}
3✔
961

962
// waitHtlcSpendAndCheckPreimage waits for the htlc output to be spent and
963
// checks whether the spending reveals the preimage. If the preimage is found,
964
// it will be added to the preimage beacon to settle the incoming link, and a
965
// nil spend details will be returned. Otherwise, the spend details will be
966
// returned, indicating this is a non-preimage spend.
967
func (h *htlcTimeoutResolver) waitHtlcSpendAndCheckPreimage() (
968
        *chainntnfs.SpendDetail, error) {
3✔
969

3✔
970
        // Wait for the htlc output to be spent, which can happen in one of the
3✔
971
        // paths,
3✔
972
        // 1. The remote party spends the htlc output using the preimage.
3✔
973
        // 2. The local party spends the htlc timeout tx from the local
3✔
974
        //    commitment.
3✔
975
        // 3. The local party spends the htlc output directlt from the remote
3✔
976
        //    commitment.
3✔
977
        spend, err := h.watchHtlcSpend()
3✔
978
        if err != nil {
3✔
979
                return nil, err
×
980
        }
×
981

982
        // If the spend reveals the pre-image, then we'll enter the clean up
983
        // workflow to pass the preimage back to the incoming link, add it to
984
        // the witness cache, and exit.
985
        if isPreimageSpend(h.isTaproot(), spend, !h.isRemoteCommitOutput()) {
3✔
986
                return nil, h.claimCleanUp(spend)
×
987
        }
×
988

989
        return spend, nil
3✔
990
}
991

992
// sweepTimeoutTxOutput attempts to sweep the output of the second level
993
// timeout tx.
994
func (h *htlcTimeoutResolver) sweepTimeoutTxOutput() error {
3✔
995
        h.log.Debugf("sweeping output %v from 2nd-level HTLC timeout tx",
3✔
996
                h.htlcResolution.ClaimOutpoint)
3✔
997

3✔
998
        // This should be non-blocking as we will only attempt to sweep the
3✔
999
        // output when the second level tx has already been confirmed. In other
3✔
1000
        // words, waitHtlcSpendAndCheckPreimage will return immediately.
3✔
1001
        commitSpend, err := h.waitHtlcSpendAndCheckPreimage()
3✔
1002
        if err != nil {
3✔
1003
                return err
×
1004
        }
×
1005

1006
        // Exit early if the spend is nil, as this means it's a remote spend
1007
        // using the preimage path, which is handled in claimCleanUp.
1008
        if commitSpend == nil {
3✔
1009
                h.log.Infof("preimage spend detected, skipping 2nd-level " +
×
1010
                        "HTLC output sweep")
×
1011

×
1012
                return nil
×
1013
        }
×
1014

1015
        waitHeight := h.deriveWaitHeight(h.htlcResolution.CsvDelay, commitSpend)
3✔
1016

3✔
1017
        // Now that the sweeper has broadcasted the second-level transaction,
3✔
1018
        // it has confirmed, and we have checkpointed our state, we'll sweep
3✔
1019
        // the second level output. We report the resolver has moved the next
3✔
1020
        // stage.
3✔
1021
        h.reportLock.Lock()
3✔
1022
        h.currentReport.Stage = 2
3✔
1023
        h.currentReport.MaturityHeight = waitHeight
3✔
1024
        h.reportLock.Unlock()
3✔
1025

3✔
1026
        if h.hasCLTV() {
6✔
1027
                h.log.Infof("waiting for CSV and CLTV lock to expire at "+
3✔
1028
                        "height %v", waitHeight)
3✔
1029
        } else {
6✔
1030
                h.log.Infof("waiting for CSV lock to expire at height %v",
3✔
1031
                        waitHeight)
3✔
1032
        }
3✔
1033

1034
        // We'll use this input index to determine the second-level output
1035
        // index on the transaction, as the signatures requires the indexes to
1036
        // be the same. We don't look for the second-level output script
1037
        // directly, as there might be more than one HTLC output to the same
1038
        // pkScript.
1039
        op := &wire.OutPoint{
3✔
1040
                Hash:  *commitSpend.SpenderTxHash,
3✔
1041
                Index: commitSpend.SpenderInputIndex,
3✔
1042
        }
3✔
1043

3✔
1044
        var witType input.StandardWitnessType
3✔
1045
        if h.isTaproot() {
6✔
1046
                witType = input.TaprootHtlcOfferedTimeoutSecondLevel
3✔
1047
        } else {
6✔
1048
                witType = input.HtlcOfferedTimeoutSecondLevel
3✔
1049
        }
3✔
1050

1051
        // Let the sweeper sweep the second-level output now that the CSV/CLTV
1052
        // locks have expired.
1053
        inp := h.makeSweepInput(
3✔
1054
                op, witType,
3✔
1055
                input.LeaseHtlcOfferedTimeoutSecondLevel,
3✔
1056
                &h.htlcResolution.SweepSignDesc,
3✔
1057
                h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight),
3✔
1058
                h.htlc.RHash, h.htlcResolution.ResolutionBlob,
3✔
1059
        )
3✔
1060

3✔
1061
        // Calculate the budget for this sweep.
3✔
1062
        budget := calculateBudget(
3✔
1063
                btcutil.Amount(inp.SignDesc().Output.Value),
3✔
1064
                h.Budget.NoDeadlineHTLCRatio,
3✔
1065
                h.Budget.NoDeadlineHTLC,
3✔
1066
        )
3✔
1067

3✔
1068
        h.log.Infof("offering output from 2nd-level timeout tx to sweeper "+
3✔
1069
                "with no deadline and budget=%v", budget)
3✔
1070

3✔
1071
        // TODO(yy): use the result chan returned from SweepInput to get the
3✔
1072
        // confirmation status of this sweeping tx so we don't need to make
3✔
1073
        // anothe subscription via `RegisterSpendNtfn` for this outpoint here
3✔
1074
        // in the resolver.
3✔
1075
        _, err = h.Sweeper.SweepInput(
3✔
1076
                inp,
3✔
1077
                sweep.Params{
3✔
1078
                        Budget: budget,
3✔
1079

3✔
1080
                        // For second level success tx, there's no rush
3✔
1081
                        // to get it confirmed, so we use a nil
3✔
1082
                        // deadline.
3✔
1083
                        DeadlineHeight: fn.None[int32](),
3✔
1084
                },
3✔
1085
        )
3✔
1086

3✔
1087
        return err
3✔
1088
}
1089

1090
// checkpointStageOne creates a checkpoint for the first stage of the htlc
1091
// timeout transaction. This is used to ensure that the resolver can resume
1092
// watching for the second stage spend in case of a restart.
1093
func (h *htlcTimeoutResolver) checkpointStageOne(
1094
        spendTxid chainhash.Hash) error {
3✔
1095

3✔
1096
        h.log.Debugf("checkpoint stage one spend of HTLC output %v, spent "+
3✔
1097
                "in tx %v", h.outpoint(), spendTxid)
3✔
1098

3✔
1099
        // Now that the second-level transaction has confirmed, we checkpoint
3✔
1100
        // the state so we'll go to the next stage in case of restarts.
3✔
1101
        h.outputIncubating = true
3✔
1102

3✔
1103
        // Create stage-one report.
3✔
1104
        report := &channeldb.ResolverReport{
3✔
1105
                OutPoint:        h.outpoint(),
3✔
1106
                Amount:          h.htlc.Amt.ToSatoshis(),
3✔
1107
                ResolverType:    channeldb.ResolverTypeOutgoingHtlc,
3✔
1108
                ResolverOutcome: channeldb.ResolverOutcomeFirstStage,
3✔
1109
                SpendTxID:       &spendTxid,
3✔
1110
        }
3✔
1111

3✔
1112
        // At this point, the second-level transaction is sufficiently
3✔
1113
        // confirmed. We can now send back our clean up message, failing the
3✔
1114
        // HTLC on the incoming link.
3✔
1115
        failureMsg := &lnwire.FailPermanentChannelFailure{}
3✔
1116
        err := h.DeliverResolutionMsg(ResolutionMsg{
3✔
1117
                SourceChan: h.ShortChanID,
3✔
1118
                HtlcIndex:  h.htlc.HtlcIndex,
3✔
1119
                Failure:    failureMsg,
3✔
1120
        })
3✔
1121
        if err != nil {
3✔
1122
                return err
×
1123
        }
×
1124

1125
        return h.Checkpoint(h, report)
3✔
1126
}
1127

1128
// checkpointClaim checkpoints the timeout resolver with the reports it needs.
1129
func (h *htlcTimeoutResolver) checkpointClaim(
1130
        spendDetail *chainntnfs.SpendDetail) error {
3✔
1131

3✔
1132
        h.log.Infof("resolving htlc with incoming fail msg, output=%v "+
3✔
1133
                "confirmed in tx=%v", spendDetail.SpentOutPoint,
3✔
1134
                spendDetail.SpenderTxHash)
3✔
1135

3✔
1136
        // Create a resolver report for the claiming of the HTLC.
3✔
1137
        amt := btcutil.Amount(h.htlcResolution.SweepSignDesc.Output.Value)
3✔
1138
        report := &channeldb.ResolverReport{
3✔
1139
                OutPoint:        *spendDetail.SpentOutPoint,
3✔
1140
                Amount:          amt,
3✔
1141
                ResolverType:    channeldb.ResolverTypeOutgoingHtlc,
3✔
1142
                ResolverOutcome: channeldb.ResolverOutcomeTimeout,
3✔
1143
                SpendTxID:       spendDetail.SpenderTxHash,
3✔
1144
        }
3✔
1145

3✔
1146
        // Finally, we checkpoint the resolver with our report(s).
3✔
1147
        h.markResolved()
3✔
1148

3✔
1149
        return h.Checkpoint(h, report)
3✔
1150
}
3✔
1151

1152
// resolveRemoteCommitOutput handles sweeping an HTLC output on the remote
1153
// commitment with via the timeout path. In this case we can sweep the output
1154
// directly, and don't have to broadcast a second-level transaction.
1155
func (h *htlcTimeoutResolver) resolveRemoteCommitOutput() error {
3✔
1156
        h.log.Debug("waiting for direct-timeout spend of the htlc to confirm")
3✔
1157

3✔
1158
        // Wait for the direct-timeout HTLC sweep tx to confirm.
3✔
1159
        spend, err := h.watchHtlcSpend()
3✔
1160
        if err != nil {
3✔
1161
                return err
×
1162
        }
×
1163

1164
        // If the spend reveals the preimage, then we'll enter the clean up
1165
        // workflow to pass the preimage back to the incoming link, add it to
1166
        // the witness cache, and exit.
1167
        if isPreimageSpend(h.isTaproot(), spend, !h.isRemoteCommitOutput()) {
5✔
1168
                return h.claimCleanUp(spend)
2✔
1169
        }
2✔
1170

1171
        // Send the clean up msg to fail the incoming HTLC.
1172
        failureMsg := &lnwire.FailPermanentChannelFailure{}
3✔
1173
        err = h.DeliverResolutionMsg(ResolutionMsg{
3✔
1174
                SourceChan: h.ShortChanID,
3✔
1175
                HtlcIndex:  h.htlc.HtlcIndex,
3✔
1176
                Failure:    failureMsg,
3✔
1177
        })
3✔
1178
        if err != nil {
3✔
1179
                return err
×
1180
        }
×
1181

1182
        // TODO(yy): should also update the `RecoveredBalance` and
1183
        // `LimboBalance` like other paths?
1184

1185
        // Checkpoint the resolver, and write the outcome to disk.
1186
        return h.checkpointClaim(spend)
3✔
1187
}
1188

1189
// resolveTimeoutTx waits for the sweeping tx of the second-level
1190
// timeout tx to confirm and offers the output from the timeout tx to the
1191
// sweeper.
1192
func (h *htlcTimeoutResolver) resolveTimeoutTx() error {
3✔
1193
        h.log.Debug("waiting for first-stage 2nd-level HTLC timeout tx to " +
3✔
1194
                "confirm")
3✔
1195

3✔
1196
        // Wait for the second level transaction to confirm.
3✔
1197
        spend, err := h.watchHtlcSpend()
3✔
1198
        if err != nil {
6✔
1199
                return err
3✔
1200
        }
3✔
1201

1202
        // If the spend reveals the preimage, then we'll enter the clean up
1203
        // workflow to pass the preimage back to the incoming link, add it to
1204
        // the witness cache, and exit.
1205
        if isPreimageSpend(h.isTaproot(), spend, !h.isRemoteCommitOutput()) {
5✔
1206
                return h.claimCleanUp(spend)
2✔
1207
        }
2✔
1208

1209
        op := h.htlcResolution.ClaimOutpoint
3✔
1210
        spenderTxid := *spend.SpenderTxHash
3✔
1211

3✔
1212
        // If the timeout tx is a re-signed tx, we will need to find the actual
3✔
1213
        // spent outpoint from the spending tx.
3✔
1214
        if h.isZeroFeeOutput() {
6✔
1215
                op = wire.OutPoint{
3✔
1216
                        Hash:  spenderTxid,
3✔
1217
                        Index: spend.SpenderInputIndex,
3✔
1218
                }
3✔
1219
        }
3✔
1220

1221
        // If the 2nd-stage sweeping has already been started, we can
1222
        // fast-forward to start the resolving process for the stage two
1223
        // output.
1224
        if h.outputIncubating {
6✔
1225
                return h.resolveTimeoutTxOutput(op)
3✔
1226
        }
3✔
1227

1228
        h.log.Infof("2nd-level HTLC timeout tx=%v confirmed", spenderTxid)
3✔
1229

3✔
1230
        // Start the process to sweep the output from the timeout tx.
3✔
1231
        if h.isZeroFeeOutput() {
6✔
1232
                err = h.sweepTimeoutTxOutput()
3✔
1233
                if err != nil {
3✔
1234
                        return err
×
1235
                }
×
1236
        }
1237

1238
        // Create a checkpoint since the timeout tx is confirmed and the sweep
1239
        // request has been made.
1240
        if err := h.checkpointStageOne(spenderTxid); err != nil {
3✔
1241
                return err
×
1242
        }
×
1243

1244
        // Start the resolving process for the stage two output.
1245
        return h.resolveTimeoutTxOutput(op)
3✔
1246
}
1247

1248
// resolveTimeoutTxOutput waits for the spend of the output from the 2nd-level
1249
// timeout tx.
1250
func (h *htlcTimeoutResolver) resolveTimeoutTxOutput(op wire.OutPoint) error {
3✔
1251
        h.log.Debugf("waiting for second-stage 2nd-level timeout tx output %v "+
3✔
1252
                "to be spent after csv_delay=%v", op, h.htlcResolution.CsvDelay)
3✔
1253

3✔
1254
        spend, err := waitForSpend(
3✔
1255
                &op, h.htlcResolution.SweepSignDesc.Output.PkScript,
3✔
1256
                h.broadcastHeight, h.Notifier, h.quit,
3✔
1257
        )
3✔
1258
        if err != nil {
6✔
1259
                return err
3✔
1260
        }
3✔
1261

1262
        h.reportLock.Lock()
3✔
1263
        h.currentReport.RecoveredBalance = h.currentReport.LimboBalance
3✔
1264
        h.currentReport.LimboBalance = 0
3✔
1265
        h.reportLock.Unlock()
3✔
1266

3✔
1267
        return h.checkpointClaim(spend)
3✔
1268
}
1269

1270
// Launch creates an input based on the details of the outgoing htlc resolution
1271
// and offers it to the sweeper.
1272
func (h *htlcTimeoutResolver) Launch() error {
3✔
1273
        if h.isLaunched() {
6✔
1274
                h.log.Tracef("already launched")
3✔
1275
                return nil
3✔
1276
        }
3✔
1277

1278
        h.log.Debugf("launching resolver...")
3✔
1279
        h.markLaunched()
3✔
1280

3✔
1281
        switch {
3✔
1282
        // If we're already resolved, then we can exit early.
UNCOV
1283
        case h.IsResolved():
×
UNCOV
1284
                h.log.Errorf("already resolved")
×
UNCOV
1285
                return nil
×
1286

1287
        // If this is an output on the remote party's commitment transaction,
1288
        // use the direct timeout spend path.
1289
        //
1290
        // NOTE: When the outputIncubating is false, it means that the output
1291
        // has been offered to the utxo nursery as starting in 0.18.4, we
1292
        // stopped marking this flag for direct timeout spends (#9062). In that
1293
        // case, we will do nothing and let the utxo nursery handle it.
1294
        case h.isRemoteCommitOutput() && !h.outputIncubating:
3✔
1295
                return h.sweepDirectHtlcOutput()
3✔
1296

1297
        // If this is an anchor type channel, we now sweep either the
1298
        // second-level timeout tx or the output from the second-level timeout
1299
        // tx.
1300
        case h.isZeroFeeOutput():
3✔
1301
                // If the second-level timeout tx has already been swept, we
3✔
1302
                // can go ahead and sweep its output.
3✔
1303
                if h.outputIncubating {
6✔
1304
                        return h.sweepTimeoutTxOutput()
3✔
1305
                }
3✔
1306

1307
                // Otherwise, sweep the second level tx.
1308
                return h.sweepTimeoutTx()
3✔
1309

1310
        // If this is an output on our own commitment using pre-anchor channel
1311
        // type, we will let the utxo nursery handle it via Resolve.
1312
        //
1313
        // TODO(yy): handle the legacy output by offering it to the sweeper.
1314
        default:
3✔
1315
                return nil
3✔
1316
        }
1317
}
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