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

lightningnetwork / lnd / 17830307614

18 Sep 2025 01:29PM UTC coverage: 54.617% (-12.0%) from 66.637%
17830307614

Pull #10200

github

web-flow
Merge 181a0a7bc into b34fc964b
Pull Request #10200: github: change to form-based issue template

109249 of 200028 relevant lines covered (54.62%)

21896.43 hits per line

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

71.47
/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/lightningnetwork/lnd/chainntnfs"
14
        "github.com/lightningnetwork/lnd/channeldb"
15
        "github.com/lightningnetwork/lnd/fn/v2"
16
        "github.com/lightningnetwork/lnd/input"
17
        "github.com/lightningnetwork/lnd/lntypes"
18
        "github.com/lightningnetwork/lnd/lnutils"
19
        "github.com/lightningnetwork/lnd/lnwallet"
20
        "github.com/lightningnetwork/lnd/lnwire"
21
        "github.com/lightningnetwork/lnd/sweep"
22
)
23

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

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

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

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

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

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

59
        contractResolverKit
60

61
        htlcLeaseResolver
62

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

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

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

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

1✔
84
        return h
1✔
85
}
1✔
86

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

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

104
        return h.htlcResolution.ClaimOutpoint
17✔
105
}
106

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

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

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

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

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

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

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

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

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

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

6✔
167
        log.Infof("%T(%v): extracting preimage! remote party spent "+
6✔
168
                "HTLC with tx=%v", h, h.htlcResolution.ClaimOutpoint,
6✔
169
                lnutils.SpewLogClosure(commitSpend.SpendingTx))
6✔
170

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

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

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

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

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

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

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

225
        var pre [32]byte
6✔
226
        copy(pre[:], preimage[:])
6✔
227

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

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

6✔
250
        return h.Checkpoint(h, report)
6✔
251
}
252

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

6✔
263
                return &outPointToWatch, scriptToWatch, nil
6✔
264
        }
6✔
265

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

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

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

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

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

322
        return &outPointToWatch, scriptToWatch, nil
13✔
323
}
324

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

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

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

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

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

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

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

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

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

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

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

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

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

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

466
        // Calculate the budget.
467
        budget := calculateBudget(
2✔
468
                btcutil.Amount(inp.SignDesc().Output.Value),
2✔
469
                h.Budget.DeadlineHTLCRatio, h.Budget.DeadlineHTLC,
2✔
470
        )
2✔
471

2✔
472
        h.log.Infof("offering 2nd-level HTLC timeout tx to sweeper "+
2✔
473
                "with deadline=%v, budget=%v", h.incomingHTLCExpiryHeight,
2✔
474
                budget)
2✔
475

2✔
476
        // For an outgoing HTLC, it must be swept before the RefundTimeout of
2✔
477
        // its incoming HTLC is reached.
2✔
478
        _, err := h.Sweeper.SweepInput(
2✔
479
                inp,
2✔
480
                sweep.Params{
2✔
481
                        Budget:         budget,
2✔
482
                        DeadlineHeight: h.incomingHTLCExpiryHeight,
2✔
483
                },
2✔
484
        )
2✔
485
        if err != nil {
2✔
486
                return err
×
487
        }
×
488

489
        return nil
2✔
490
}
491

492
// resolveSecondLevelTxLegacy sends a second level timeout transaction to the
493
// utxo nursery. This transaction uses the legacy SIGHASH_ALL flag.
494
func (h *htlcTimeoutResolver) resolveSecondLevelTxLegacy() error {
6✔
495
        h.log.Debug("incubating htlc output")
6✔
496

6✔
497
        // The utxo nursery will take care of broadcasting the second-level
6✔
498
        // timeout tx and sweeping its output once it confirms.
6✔
499
        err := h.IncubateOutputs(
6✔
500
                h.ChanPoint, fn.Some(h.htlcResolution),
6✔
501
                fn.None[lnwallet.IncomingHtlcResolution](),
6✔
502
                h.broadcastHeight, h.incomingHTLCExpiryHeight,
6✔
503
        )
6✔
504
        if err != nil {
6✔
505
                return err
×
506
        }
×
507

508
        return h.resolveTimeoutTx()
6✔
509
}
510

511
// sweepDirectHtlcOutput sends the direct spend of the HTLC output to the
512
// sweeper. This is used when the remote party goes on chain, and we're able to
513
// sweep an HTLC we offered after a timeout. Only the CLTV encumbered outputs
514
// are resolved via this path.
515
func (h *htlcTimeoutResolver) sweepDirectHtlcOutput() error {
4✔
516
        var htlcWitnessType input.StandardWitnessType
4✔
517
        if h.isTaproot() {
4✔
518
                htlcWitnessType = input.TaprootHtlcOfferedRemoteTimeout
×
519
        } else {
4✔
520
                htlcWitnessType = input.HtlcOfferedRemoteTimeout
4✔
521
        }
4✔
522

523
        sweepInput := input.NewCsvInputWithCltv(
4✔
524
                &h.htlcResolution.ClaimOutpoint, htlcWitnessType,
4✔
525
                &h.htlcResolution.SweepSignDesc, h.broadcastHeight,
4✔
526
                h.htlcResolution.CsvDelay, h.htlcResolution.Expiry,
4✔
527
                input.WithResolutionBlob(h.htlcResolution.ResolutionBlob),
4✔
528
        )
4✔
529

4✔
530
        // Calculate the budget.
4✔
531
        budget := calculateBudget(
4✔
532
                btcutil.Amount(sweepInput.SignDesc().Output.Value),
4✔
533
                h.Budget.DeadlineHTLCRatio, h.Budget.DeadlineHTLC,
4✔
534
        )
4✔
535

4✔
536
        log.Infof("%T(%x): offering offered remote timeout HTLC output to "+
4✔
537
                "sweeper with deadline %v and budget=%v at height=%v",
4✔
538
                h, h.htlc.RHash[:], h.incomingHTLCExpiryHeight, budget,
4✔
539
                h.broadcastHeight)
4✔
540

4✔
541
        _, err := h.Sweeper.SweepInput(
4✔
542
                sweepInput,
4✔
543
                sweep.Params{
4✔
544
                        Budget: budget,
4✔
545

4✔
546
                        // This is an outgoing HTLC, so we want to make sure
4✔
547
                        // that we sweep it before the incoming HTLC expires.
4✔
548
                        DeadlineHeight: h.incomingHTLCExpiryHeight,
4✔
549
                },
4✔
550
        )
4✔
551
        if err != nil {
4✔
552
                return err
×
553
        }
×
554

555
        return nil
4✔
556
}
557

558
// watchHtlcSpend watches for a spend of the HTLC output. For neutrino backend,
559
// it will check blocks for the confirmed spend. For btcd and bitcoind, it will
560
// check both the mempool and the blocks.
561
func (h *htlcTimeoutResolver) watchHtlcSpend() (*chainntnfs.SpendDetail,
562
        error) {
15✔
563

15✔
564
        // TODO(yy): outpointToWatch is always h.HtlcOutpoint(), can refactor
15✔
565
        // to remove the redundancy.
15✔
566
        outpointToWatch, scriptToWatch, err := h.chainDetailsToWatch()
15✔
567
        if err != nil {
15✔
568
                return nil, err
×
569
        }
×
570

571
        // If there's no mempool configured, which is the case for SPV node
572
        // such as neutrino, then we will watch for confirmed spend only.
573
        if h.Mempool == nil {
30✔
574
                return h.waitForConfirmedSpend(outpointToWatch, scriptToWatch)
15✔
575
        }
15✔
576

577
        // Watch for a spend of the HTLC output in both the mempool and blocks.
578
        return h.waitForMempoolOrBlockSpend(*outpointToWatch, scriptToWatch)
×
579
}
580

581
// waitForConfirmedSpend waits for the HTLC output to be spent and confirmed in
582
// a block, returns the spend details.
583
func (h *htlcTimeoutResolver) waitForConfirmedSpend(op *wire.OutPoint,
584
        pkScript []byte) (*chainntnfs.SpendDetail, error) {
15✔
585

15✔
586
        // We'll block here until either we exit, or the HTLC output on the
15✔
587
        // commitment transaction has been spent.
15✔
588
        spend, err := waitForSpend(
15✔
589
                op, pkScript, h.broadcastHeight, h.Notifier, h.quit,
15✔
590
        )
15✔
591
        if err != nil {
15✔
592
                return nil, err
×
593
        }
×
594

595
        return spend, nil
15✔
596
}
597

598
// Stop signals the resolver to cancel any current resolution processes, and
599
// suspend.
600
//
601
// NOTE: Part of the ContractResolver interface.
602
func (h *htlcTimeoutResolver) Stop() {
1✔
603
        h.log.Debugf("stopping...")
1✔
604
        defer h.log.Debugf("stopped")
1✔
605

1✔
606
        close(h.quit)
1✔
607
}
1✔
608

609
// report returns a report on the resolution state of the contract.
610
func (h *htlcTimeoutResolver) report() *ContractReport {
×
611
        // If we have a SignedTimeoutTx but no SignDetails, this is a local
×
612
        // commitment for a non-anchor channel, which was handled by the utxo
×
613
        // nursery.
×
614
        if h.htlcResolution.SignDetails == nil && h.
×
615
                htlcResolution.SignedTimeoutTx != nil {
×
616
                return nil
×
617
        }
×
618

619
        h.reportLock.Lock()
×
620
        defer h.reportLock.Unlock()
×
621
        cpy := h.currentReport
×
622
        return &cpy
×
623
}
624

625
func (h *htlcTimeoutResolver) initReport() {
16✔
626
        // We create the initial report. This will only be reported for
16✔
627
        // resolvers not handled by the nursery.
16✔
628
        finalAmt := h.htlc.Amt.ToSatoshis()
16✔
629
        if h.htlcResolution.SignedTimeoutTx != nil {
26✔
630
                finalAmt = btcutil.Amount(
10✔
631
                        h.htlcResolution.SignedTimeoutTx.TxOut[0].Value,
10✔
632
                )
10✔
633
        }
10✔
634

635
        // If there's no timeout transaction, then we're already effectively in
636
        // level two.
637
        stage := uint32(1)
16✔
638
        if h.htlcResolution.SignedTimeoutTx == nil {
22✔
639
                stage = 2
6✔
640
        }
6✔
641

642
        h.currentReport = ContractReport{
16✔
643
                Outpoint:       h.htlcResolution.ClaimOutpoint,
16✔
644
                Type:           ReportOutputOutgoingHtlc,
16✔
645
                Amount:         finalAmt,
16✔
646
                MaturityHeight: h.htlcResolution.Expiry,
16✔
647
                LimboBalance:   finalAmt,
16✔
648
                Stage:          stage,
16✔
649
        }
16✔
650
}
651

652
// Encode writes an encoded version of the ContractResolver into the passed
653
// Writer.
654
//
655
// NOTE: Part of the ContractResolver interface.
656
func (h *htlcTimeoutResolver) Encode(w io.Writer) error {
19✔
657
        // First, we'll write out the relevant fields of the
19✔
658
        // OutgoingHtlcResolution to the writer.
19✔
659
        if err := encodeOutgoingResolution(w, &h.htlcResolution); err != nil {
19✔
660
                return err
×
661
        }
×
662

663
        // With that portion written, we can now write out the fields specific
664
        // to the resolver itself.
665
        if err := binary.Write(w, endian, h.outputIncubating); err != nil {
19✔
666
                return err
×
667
        }
×
668
        if err := binary.Write(w, endian, h.IsResolved()); err != nil {
19✔
669
                return err
×
670
        }
×
671
        if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
19✔
672
                return err
×
673
        }
×
674

675
        if err := binary.Write(w, endian, h.htlc.HtlcIndex); err != nil {
19✔
676
                return err
×
677
        }
×
678

679
        // We encode the sign details last for backwards compatibility.
680
        err := encodeSignDetails(w, h.htlcResolution.SignDetails)
19✔
681
        if err != nil {
19✔
682
                return err
×
683
        }
×
684

685
        return nil
19✔
686
}
687

688
// newTimeoutResolverFromReader attempts to decode an encoded ContractResolver
689
// from the passed Reader instance, returning an active ContractResolver
690
// instance.
691
func newTimeoutResolverFromReader(r io.Reader, resCfg ResolverConfig) (
692
        *htlcTimeoutResolver, error) {
15✔
693

15✔
694
        h := &htlcTimeoutResolver{
15✔
695
                contractResolverKit: *newContractResolverKit(resCfg),
15✔
696
        }
15✔
697

15✔
698
        // First, we'll read out all the mandatory fields of the
15✔
699
        // OutgoingHtlcResolution that we store.
15✔
700
        if err := decodeOutgoingResolution(r, &h.htlcResolution); err != nil {
15✔
701
                return nil, err
×
702
        }
×
703

704
        // With those fields read, we can now read back the fields that are
705
        // specific to the resolver itself.
706
        if err := binary.Read(r, endian, &h.outputIncubating); err != nil {
15✔
707
                return nil, err
×
708
        }
×
709

710
        var resolved bool
15✔
711
        if err := binary.Read(r, endian, &resolved); err != nil {
15✔
712
                return nil, err
×
713
        }
×
714
        if resolved {
24✔
715
                h.markResolved()
9✔
716
        }
9✔
717

718
        if err := binary.Read(r, endian, &h.broadcastHeight); err != nil {
15✔
719
                return nil, err
×
720
        }
×
721

722
        if err := binary.Read(r, endian, &h.htlc.HtlcIndex); err != nil {
15✔
723
                return nil, err
×
724
        }
×
725

726
        // Sign details is a new field that was added to the htlc resolution,
727
        // so it is serialized last for backwards compatibility. We try to read
728
        // it, but don't error out if there are not bytes left.
729
        signDetails, err := decodeSignDetails(r)
15✔
730
        if err == nil {
30✔
731
                h.htlcResolution.SignDetails = signDetails
15✔
732
        } else if err != io.EOF && err != io.ErrUnexpectedEOF {
15✔
733
                return nil, err
×
734
        }
×
735

736
        h.initReport()
15✔
737
        h.initLogger(fmt.Sprintf("%T(%v)", h, h.outpoint()))
15✔
738

15✔
739
        return h, nil
15✔
740
}
741

742
// Supplement adds additional information to the resolver that is required
743
// before Resolve() is called.
744
//
745
// NOTE: Part of the htlcContractResolver interface.
746
func (h *htlcTimeoutResolver) Supplement(htlc channeldb.HTLC) {
9✔
747
        h.htlc = htlc
9✔
748
}
9✔
749

750
// HtlcPoint returns the htlc's outpoint on the commitment tx.
751
//
752
// NOTE: Part of the htlcContractResolver interface.
753
func (h *htlcTimeoutResolver) HtlcPoint() wire.OutPoint {
1✔
754
        return h.htlcResolution.HtlcPoint()
1✔
755
}
1✔
756

757
// SupplementDeadline sets the incomingHTLCExpiryHeight for this outgoing htlc
758
// resolver.
759
//
760
// NOTE: Part of the htlcContractResolver interface.
761
func (h *htlcTimeoutResolver) SupplementDeadline(d fn.Option[int32]) {
×
762
        h.incomingHTLCExpiryHeight = d
×
763
}
×
764

765
// A compile time assertion to ensure htlcTimeoutResolver meets the
766
// ContractResolver interface.
767
var _ htlcContractResolver = (*htlcTimeoutResolver)(nil)
768

769
// spendResult is used to hold the result of a spend event from either a
770
// mempool spend or a block spend.
771
type spendResult struct {
772
        // spend contains the details of the spend.
773
        spend *chainntnfs.SpendDetail
774

775
        // err is the error that occurred during the spend notification.
776
        err error
777
}
778

779
// waitForMempoolOrBlockSpend waits for the htlc output to be spent by a
780
// transaction that's either be found in the mempool or in a block.
781
func (h *htlcTimeoutResolver) waitForMempoolOrBlockSpend(op wire.OutPoint,
782
        pkScript []byte) (*chainntnfs.SpendDetail, error) {
×
783

×
784
        log.Infof("%T(%v): waiting for spent of HTLC output %v to be found "+
×
785
                "in mempool or block", h, h.htlcResolution.ClaimOutpoint, op)
×
786

×
787
        // Subscribe for block spent(confirmed).
×
788
        blockSpent, err := h.Notifier.RegisterSpendNtfn(
×
789
                &op, pkScript, h.broadcastHeight,
×
790
        )
×
791
        if err != nil {
×
792
                return nil, fmt.Errorf("register spend: %w", err)
×
793
        }
×
794

795
        // Subscribe for mempool spent(unconfirmed).
796
        mempoolSpent, err := h.Mempool.SubscribeMempoolSpent(op)
×
797
        if err != nil {
×
798
                return nil, fmt.Errorf("register mempool spend: %w", err)
×
799
        }
×
800

801
        // Create a result chan that will be used to receive the spending
802
        // events.
803
        result := make(chan *spendResult, 2)
×
804

×
805
        // Create a goroutine that will wait for either a mempool spend or a
×
806
        // block spend.
×
807
        //
×
808
        // NOTE: no need to use waitgroup here as when the resolver exits, the
×
809
        // goroutine will return on the quit channel.
×
810
        go h.consumeSpendEvents(result, blockSpent.Spend, mempoolSpent.Spend)
×
811

×
812
        // Wait for the spend event to be received.
×
813
        select {
×
814
        case event := <-result:
×
815
                // Cancel the mempool subscription as we don't need it anymore.
×
816
                h.Mempool.CancelMempoolSpendEvent(mempoolSpent)
×
817

×
818
                return event.spend, event.err
×
819

820
        case <-h.quit:
×
821
                return nil, errResolverShuttingDown
×
822
        }
823
}
824

825
// consumeSpendEvents consumes the spend events from the block and mempool
826
// subscriptions. It exits when a spend event is received from the block, or
827
// the resolver itself quits. When a spend event is received from the mempool,
828
// however, it won't exit but continuing to wait for a spend event from the
829
// block subscription.
830
//
831
// NOTE: there could be a case where we found the preimage in the mempool,
832
// which will be added to our preimage beacon and settle the incoming link,
833
// meanwhile the timeout sweep tx confirms. This outgoing HTLC is "free" money
834
// and is not swept here.
835
//
836
// TODO(yy): sweep the outgoing htlc if it's confirmed.
837
func (h *htlcTimeoutResolver) consumeSpendEvents(resultChan chan *spendResult,
838
        blockSpent, mempoolSpent <-chan *chainntnfs.SpendDetail) {
×
839

×
840
        op := h.HtlcPoint()
×
841

×
842
        // Create a result chan to hold the results.
×
843
        result := &spendResult{}
×
844

×
845
        // Wait for a spend event to arrive.
×
846
        for {
×
847
                select {
×
848
                // If a spend event is received from the block, this outgoing
849
                // htlc is spent either by the remote via the preimage or by us
850
                // via the timeout. We can exit the loop and `claimCleanUp`
851
                // will feed the preimage to the beacon if found. This treats
852
                // the block as the final judge and the preimage spent won't
853
                // appear in the mempool afterwards.
854
                //
855
                // NOTE: if a reorg happens, the preimage spend can appear in
856
                // the mempool again. Though a rare case, we should handle it
857
                // in a dedicated reorg system.
858
                case spendDetail, ok := <-blockSpent:
×
859
                        if !ok {
×
860
                                result.err = fmt.Errorf("block spent err: %w",
×
861
                                        errResolverShuttingDown)
×
862
                        } else {
×
863
                                log.Debugf("Found confirmed spend of HTLC "+
×
864
                                        "output %s in tx=%s", op,
×
865
                                        spendDetail.SpenderTxHash)
×
866

×
867
                                result.spend = spendDetail
×
868

×
869
                                // Once confirmed, persist the state on disk if
×
870
                                // we haven't seen the output's spending tx in
×
871
                                // mempool before.
×
872
                        }
×
873

874
                        // Send the result and exit the loop.
875
                        resultChan <- result
×
876

×
877
                        return
×
878

879
                // If a spend event is received from the mempool, this can be
880
                // either the 2nd stage timeout tx or a preimage spend from the
881
                // remote. We will further check whether the spend reveals the
882
                // preimage and add it to the preimage beacon to settle the
883
                // incoming link.
884
                //
885
                // NOTE: we won't exit the loop here so we can continue to
886
                // watch for the block spend to check point the resolution.
887
                case spendDetail, ok := <-mempoolSpent:
×
888
                        if !ok {
×
889
                                result.err = fmt.Errorf("mempool spent err: %w",
×
890
                                        errResolverShuttingDown)
×
891

×
892
                                // This is an internal error so we exit.
×
893
                                resultChan <- result
×
894

×
895
                                return
×
896
                        }
×
897

898
                        log.Debugf("Found mempool spend of HTLC output %s "+
×
899
                                "in tx=%s", op, spendDetail.SpenderTxHash)
×
900

×
901
                        // Check whether the spend reveals the preimage, if not
×
902
                        // continue the loop.
×
903
                        hasPreimage := isPreimageSpend(
×
904
                                h.isTaproot(), spendDetail,
×
905
                                !h.isRemoteCommitOutput(),
×
906
                        )
×
907
                        if !hasPreimage {
×
908
                                log.Debugf("HTLC output %s spent doesn't "+
×
909
                                        "reveal preimage", op)
×
910
                                continue
×
911
                        }
912

913
                        // Found the preimage spend, send the result and
914
                        // continue the loop.
915
                        result.spend = spendDetail
×
916
                        resultChan <- result
×
917

×
918
                        continue
×
919

920
                // If the resolver exits, we exit the goroutine.
921
                case <-h.quit:
×
922
                        result.err = errResolverShuttingDown
×
923
                        resultChan <- result
×
924

×
925
                        return
×
926
                }
927
        }
928
}
929

930
// isRemoteCommitOutput returns a bool to indicate whether the htlc output is
931
// on the remote commitment.
932
func (h *htlcTimeoutResolver) isRemoteCommitOutput() bool {
41✔
933
        // If we don't have a timeout transaction, then this means that this is
41✔
934
        // an output on the remote party's commitment transaction.
41✔
935
        return h.htlcResolution.SignedTimeoutTx == nil
41✔
936
}
41✔
937

938
// isZeroFeeOutput returns a boolean indicating whether the htlc output is from
939
// a anchor-enabled channel, which uses the sighash SINGLE|ANYONECANPAY.
940
func (h *htlcTimeoutResolver) isZeroFeeOutput() bool {
28✔
941
        // If we have non-nil SignDetails, this means it has a 2nd level HTLC
28✔
942
        // transaction that is signed using sighash SINGLE|ANYONECANPAY (the
28✔
943
        // case for anchor type channels). In this case we can re-sign it and
28✔
944
        // attach fees at will.
28✔
945
        return h.htlcResolution.SignedTimeoutTx != nil &&
28✔
946
                h.htlcResolution.SignDetails != nil
28✔
947
}
28✔
948

949
// waitHtlcSpendAndCheckPreimage waits for the htlc output to be spent and
950
// checks whether the spending reveals the preimage. If the preimage is found,
951
// it will be added to the preimage beacon to settle the incoming link, and a
952
// nil spend details will be returned. Otherwise, the spend details will be
953
// returned, indicating this is a non-preimage spend.
954
func (h *htlcTimeoutResolver) waitHtlcSpendAndCheckPreimage() (
955
        *chainntnfs.SpendDetail, error) {
2✔
956

2✔
957
        // Wait for the htlc output to be spent, which can happen in one of the
2✔
958
        // paths,
2✔
959
        // 1. The remote party spends the htlc output using the preimage.
2✔
960
        // 2. The local party spends the htlc timeout tx from the local
2✔
961
        //    commitment.
2✔
962
        // 3. The local party spends the htlc output directlt from the remote
2✔
963
        //    commitment.
2✔
964
        spend, err := h.watchHtlcSpend()
2✔
965
        if err != nil {
2✔
966
                return nil, err
×
967
        }
×
968

969
        // If the spend reveals the pre-image, then we'll enter the clean up
970
        // workflow to pass the preimage back to the incoming link, add it to
971
        // the witness cache, and exit.
972
        if isPreimageSpend(h.isTaproot(), spend, !h.isRemoteCommitOutput()) {
2✔
973
                return nil, h.claimCleanUp(spend)
×
974
        }
×
975

976
        return spend, nil
2✔
977
}
978

979
// sweepTimeoutTxOutput attempts to sweep the output of the second level
980
// timeout tx.
981
func (h *htlcTimeoutResolver) sweepTimeoutTxOutput() error {
2✔
982
        h.log.Debugf("sweeping output %v from 2nd-level HTLC timeout tx",
2✔
983
                h.htlcResolution.ClaimOutpoint)
2✔
984

2✔
985
        // This should be non-blocking as we will only attempt to sweep the
2✔
986
        // output when the second level tx has already been confirmed. In other
2✔
987
        // words, waitHtlcSpendAndCheckPreimage will return immediately.
2✔
988
        commitSpend, err := h.waitHtlcSpendAndCheckPreimage()
2✔
989
        if err != nil {
2✔
990
                return err
×
991
        }
×
992

993
        // Exit early if the spend is nil, as this means it's a remote spend
994
        // using the preimage path, which is handled in claimCleanUp.
995
        if commitSpend == nil {
2✔
996
                h.log.Infof("preimage spend detected, skipping 2nd-level " +
×
997
                        "HTLC output sweep")
×
998

×
999
                return nil
×
1000
        }
×
1001

1002
        waitHeight := h.deriveWaitHeight(h.htlcResolution.CsvDelay, commitSpend)
2✔
1003

2✔
1004
        // Now that the sweeper has broadcasted the second-level transaction,
2✔
1005
        // it has confirmed, and we have checkpointed our state, we'll sweep
2✔
1006
        // the second level output. We report the resolver has moved the next
2✔
1007
        // stage.
2✔
1008
        h.reportLock.Lock()
2✔
1009
        h.currentReport.Stage = 2
2✔
1010
        h.currentReport.MaturityHeight = waitHeight
2✔
1011
        h.reportLock.Unlock()
2✔
1012

2✔
1013
        if h.hasCLTV() {
2✔
1014
                h.log.Infof("waiting for CSV and CLTV lock to expire at "+
×
1015
                        "height %v", waitHeight)
×
1016
        } else {
2✔
1017
                h.log.Infof("waiting for CSV lock to expire at height %v",
2✔
1018
                        waitHeight)
2✔
1019
        }
2✔
1020

1021
        // We'll use this input index to determine the second-level output
1022
        // index on the transaction, as the signatures requires the indexes to
1023
        // be the same. We don't look for the second-level output script
1024
        // directly, as there might be more than one HTLC output to the same
1025
        // pkScript.
1026
        op := &wire.OutPoint{
2✔
1027
                Hash:  *commitSpend.SpenderTxHash,
2✔
1028
                Index: commitSpend.SpenderInputIndex,
2✔
1029
        }
2✔
1030

2✔
1031
        var witType input.StandardWitnessType
2✔
1032
        if h.isTaproot() {
2✔
1033
                witType = input.TaprootHtlcOfferedTimeoutSecondLevel
×
1034
        } else {
2✔
1035
                witType = input.HtlcOfferedTimeoutSecondLevel
2✔
1036
        }
2✔
1037

1038
        // Let the sweeper sweep the second-level output now that the CSV/CLTV
1039
        // locks have expired.
1040
        inp := h.makeSweepInput(
2✔
1041
                op, witType,
2✔
1042
                input.LeaseHtlcOfferedTimeoutSecondLevel,
2✔
1043
                &h.htlcResolution.SweepSignDesc,
2✔
1044
                h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight),
2✔
1045
                h.htlc.RHash, h.htlcResolution.ResolutionBlob,
2✔
1046
        )
2✔
1047

2✔
1048
        // Calculate the budget for this sweep.
2✔
1049
        budget := calculateBudget(
2✔
1050
                btcutil.Amount(inp.SignDesc().Output.Value),
2✔
1051
                h.Budget.NoDeadlineHTLCRatio,
2✔
1052
                h.Budget.NoDeadlineHTLC,
2✔
1053
        )
2✔
1054

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

2✔
1058
        // TODO(yy): use the result chan returned from SweepInput to get the
2✔
1059
        // confirmation status of this sweeping tx so we don't need to make
2✔
1060
        // another subscription via `RegisterSpendNtfn` for this outpoint here
2✔
1061
        // in the resolver.
2✔
1062
        _, err = h.Sweeper.SweepInput(
2✔
1063
                inp,
2✔
1064
                sweep.Params{
2✔
1065
                        Budget: budget,
2✔
1066

2✔
1067
                        // For second level success tx, there's no rush
2✔
1068
                        // to get it confirmed, so we use a nil
2✔
1069
                        // deadline.
2✔
1070
                        DeadlineHeight: fn.None[int32](),
2✔
1071
                },
2✔
1072
        )
2✔
1073

2✔
1074
        return err
2✔
1075
}
1076

1077
// checkpointStageOne creates a checkpoint for the first stage of the htlc
1078
// timeout transaction. This is used to ensure that the resolver can resume
1079
// watching for the second stage spend in case of a restart.
1080
func (h *htlcTimeoutResolver) checkpointStageOne(
1081
        spendTxid chainhash.Hash) error {
4✔
1082

4✔
1083
        h.log.Debugf("checkpoint stage one spend of HTLC output %v, spent "+
4✔
1084
                "in tx %v", h.outpoint(), spendTxid)
4✔
1085

4✔
1086
        // Now that the second-level transaction has confirmed, we checkpoint
4✔
1087
        // the state so we'll go to the next stage in case of restarts.
4✔
1088
        h.outputIncubating = true
4✔
1089

4✔
1090
        // Create stage-one report.
4✔
1091
        report := &channeldb.ResolverReport{
4✔
1092
                OutPoint:        h.outpoint(),
4✔
1093
                Amount:          h.htlc.Amt.ToSatoshis(),
4✔
1094
                ResolverType:    channeldb.ResolverTypeOutgoingHtlc,
4✔
1095
                ResolverOutcome: channeldb.ResolverOutcomeFirstStage,
4✔
1096
                SpendTxID:       &spendTxid,
4✔
1097
        }
4✔
1098

4✔
1099
        // At this point, the second-level transaction is sufficiently
4✔
1100
        // confirmed. We can now send back our clean up message, failing the
4✔
1101
        // HTLC on the incoming link.
4✔
1102
        failureMsg := &lnwire.FailPermanentChannelFailure{}
4✔
1103
        err := h.DeliverResolutionMsg(ResolutionMsg{
4✔
1104
                SourceChan: h.ShortChanID,
4✔
1105
                HtlcIndex:  h.htlc.HtlcIndex,
4✔
1106
                Failure:    failureMsg,
4✔
1107
        })
4✔
1108
        if err != nil {
4✔
1109
                return err
×
1110
        }
×
1111

1112
        return h.Checkpoint(h, report)
4✔
1113
}
1114

1115
// checkpointClaim checkpoints the timeout resolver with the reports it needs.
1116
func (h *htlcTimeoutResolver) checkpointClaim(
1117
        spendDetail *chainntnfs.SpendDetail) error {
8✔
1118

8✔
1119
        h.log.Infof("resolving htlc with incoming fail msg, output=%v "+
8✔
1120
                "confirmed in tx=%v", spendDetail.SpentOutPoint,
8✔
1121
                spendDetail.SpenderTxHash)
8✔
1122

8✔
1123
        // Create a resolver report for the claiming of the HTLC.
8✔
1124
        amt := btcutil.Amount(h.htlcResolution.SweepSignDesc.Output.Value)
8✔
1125
        report := &channeldb.ResolverReport{
8✔
1126
                OutPoint:        *spendDetail.SpentOutPoint,
8✔
1127
                Amount:          amt,
8✔
1128
                ResolverType:    channeldb.ResolverTypeOutgoingHtlc,
8✔
1129
                ResolverOutcome: channeldb.ResolverOutcomeTimeout,
8✔
1130
                SpendTxID:       spendDetail.SpenderTxHash,
8✔
1131
        }
8✔
1132

8✔
1133
        // Finally, we checkpoint the resolver with our report(s).
8✔
1134
        h.markResolved()
8✔
1135

8✔
1136
        return h.Checkpoint(h, report)
8✔
1137
}
8✔
1138

1139
// resolveRemoteCommitOutput handles sweeping an HTLC output on the remote
1140
// commitment with via the timeout path. In this case we can sweep the output
1141
// directly, and don't have to broadcast a second-level transaction.
1142
func (h *htlcTimeoutResolver) resolveRemoteCommitOutput() error {
4✔
1143
        h.log.Debug("waiting for direct-timeout spend of the htlc to confirm")
4✔
1144

4✔
1145
        // Wait for the direct-timeout HTLC sweep tx to confirm.
4✔
1146
        spend, err := h.watchHtlcSpend()
4✔
1147
        if err != nil {
4✔
1148
                return err
×
1149
        }
×
1150

1151
        // If the spend reveals the preimage, then we'll enter the clean up
1152
        // workflow to pass the preimage back to the incoming link, add it to
1153
        // the witness cache, and exit.
1154
        if isPreimageSpend(h.isTaproot(), spend, !h.isRemoteCommitOutput()) {
6✔
1155
                return h.claimCleanUp(spend)
2✔
1156
        }
2✔
1157

1158
        // Send the clean up msg to fail the incoming HTLC.
1159
        failureMsg := &lnwire.FailPermanentChannelFailure{}
2✔
1160
        err = h.DeliverResolutionMsg(ResolutionMsg{
2✔
1161
                SourceChan: h.ShortChanID,
2✔
1162
                HtlcIndex:  h.htlc.HtlcIndex,
2✔
1163
                Failure:    failureMsg,
2✔
1164
        })
2✔
1165
        if err != nil {
2✔
1166
                return err
×
1167
        }
×
1168

1169
        // TODO(yy): should also update the `RecoveredBalance` and
1170
        // `LimboBalance` like other paths?
1171

1172
        // Checkpoint the resolver, and write the outcome to disk.
1173
        return h.checkpointClaim(spend)
2✔
1174
}
1175

1176
// resolveTimeoutTx waits for the sweeping tx of the second-level
1177
// timeout tx to confirm and offers the output from the timeout tx to the
1178
// sweeper.
1179
func (h *htlcTimeoutResolver) resolveTimeoutTx() error {
9✔
1180
        h.log.Debug("waiting for first-stage 2nd-level HTLC timeout tx to " +
9✔
1181
                "confirm")
9✔
1182

9✔
1183
        // Wait for the second level transaction to confirm.
9✔
1184
        spend, err := h.watchHtlcSpend()
9✔
1185
        if err != nil {
9✔
1186
                return err
×
1187
        }
×
1188

1189
        // If the spend reveals the preimage, then we'll enter the clean up
1190
        // workflow to pass the preimage back to the incoming link, add it to
1191
        // the witness cache, and exit.
1192
        if isPreimageSpend(h.isTaproot(), spend, !h.isRemoteCommitOutput()) {
12✔
1193
                return h.claimCleanUp(spend)
3✔
1194
        }
3✔
1195

1196
        op := h.htlcResolution.ClaimOutpoint
6✔
1197
        spenderTxid := *spend.SpenderTxHash
6✔
1198

6✔
1199
        // If the timeout tx is a re-signed tx, we will need to find the actual
6✔
1200
        // spent outpoint from the spending tx.
6✔
1201
        if h.isZeroFeeOutput() {
8✔
1202
                op = wire.OutPoint{
2✔
1203
                        Hash:  spenderTxid,
2✔
1204
                        Index: spend.SpenderInputIndex,
2✔
1205
                }
2✔
1206
        }
2✔
1207

1208
        // If the 2nd-stage sweeping has already been started, we can
1209
        // fast-forward to start the resolving process for the stage two
1210
        // output.
1211
        if h.outputIncubating {
8✔
1212
                return h.resolveTimeoutTxOutput(op)
2✔
1213
        }
2✔
1214

1215
        h.log.Infof("2nd-level HTLC timeout tx=%v confirmed", spenderTxid)
4✔
1216

4✔
1217
        // Start the process to sweep the output from the timeout tx.
4✔
1218
        if h.isZeroFeeOutput() {
5✔
1219
                err = h.sweepTimeoutTxOutput()
1✔
1220
                if err != nil {
1✔
1221
                        return err
×
1222
                }
×
1223
        }
1224

1225
        // Create a checkpoint since the timeout tx is confirmed and the sweep
1226
        // request has been made.
1227
        if err := h.checkpointStageOne(spenderTxid); err != nil {
4✔
1228
                return err
×
1229
        }
×
1230

1231
        // Start the resolving process for the stage two output.
1232
        return h.resolveTimeoutTxOutput(op)
4✔
1233
}
1234

1235
// resolveTimeoutTxOutput waits for the spend of the output from the 2nd-level
1236
// timeout tx.
1237
func (h *htlcTimeoutResolver) resolveTimeoutTxOutput(op wire.OutPoint) error {
6✔
1238
        h.log.Debugf("waiting for second-stage 2nd-level timeout tx output %v "+
6✔
1239
                "to be spent after csv_delay=%v", op, h.htlcResolution.CsvDelay)
6✔
1240

6✔
1241
        spend, err := waitForSpend(
6✔
1242
                &op, h.htlcResolution.SweepSignDesc.Output.PkScript,
6✔
1243
                h.broadcastHeight, h.Notifier, h.quit,
6✔
1244
        )
6✔
1245
        if err != nil {
6✔
1246
                return err
×
1247
        }
×
1248

1249
        h.reportLock.Lock()
6✔
1250
        h.currentReport.RecoveredBalance = h.currentReport.LimboBalance
6✔
1251
        h.currentReport.LimboBalance = 0
6✔
1252
        h.reportLock.Unlock()
6✔
1253

6✔
1254
        return h.checkpointClaim(spend)
6✔
1255
}
1256

1257
// Launch creates an input based on the details of the outgoing htlc resolution
1258
// and offers it to the sweeper.
1259
func (h *htlcTimeoutResolver) Launch() error {
19✔
1260
        if h.isLaunched() {
19✔
1261
                h.log.Tracef("already launched")
×
1262
                return nil
×
1263
        }
×
1264

1265
        h.log.Debugf("launching resolver...")
19✔
1266
        h.markLaunched()
19✔
1267

19✔
1268
        switch {
19✔
1269
        // If we're already resolved, then we can exit early.
1270
        case h.IsResolved():
6✔
1271
                h.log.Errorf("already resolved")
6✔
1272
                return nil
6✔
1273

1274
        // If this is an output on the remote party's commitment transaction,
1275
        // use the direct timeout spend path.
1276
        //
1277
        // NOTE: When the outputIncubating is false, it means that the output
1278
        // has been offered to the utxo nursery as starting in 0.18.4, we
1279
        // stopped marking this flag for direct timeout spends (#9062). In that
1280
        // case, we will do nothing and let the utxo nursery handle it.
1281
        case h.isRemoteCommitOutput() && !h.outputIncubating:
4✔
1282
                return h.sweepDirectHtlcOutput()
4✔
1283

1284
        // If this is an anchor type channel, we now sweep either the
1285
        // second-level timeout tx or the output from the second-level timeout
1286
        // tx.
1287
        case h.isZeroFeeOutput():
3✔
1288
                // If the second-level timeout tx has already been swept, we
3✔
1289
                // can go ahead and sweep its output.
3✔
1290
                if h.outputIncubating {
4✔
1291
                        return h.sweepTimeoutTxOutput()
1✔
1292
                }
1✔
1293

1294
                // Otherwise, sweep the second level tx.
1295
                return h.sweepTimeoutTx()
2✔
1296

1297
        // If this is an output on our own commitment using pre-anchor channel
1298
        // type, we will let the utxo nursery handle it via Resolve.
1299
        //
1300
        // TODO(yy): handle the legacy output by offering it to the sweeper.
1301
        default:
6✔
1302
                return nil
6✔
1303
        }
1304
}
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