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

lightningnetwork / lnd / 12343072627

15 Dec 2024 11:09PM UTC coverage: 57.504% (-1.1%) from 58.636%
12343072627

Pull #9315

github

yyforyongyu
contractcourt: offer outgoing htlc one block earlier before its expiry

We need to offer the outgoing htlc one block earlier to make sure when
the expiry height hits, the sweeper will not miss sweeping it in the
same block. This also means the outgoing contest resolver now only does
one thing - watch for preimage spend till height expiry-1, which can
easily be moved into the timeout resolver instead in the future.
Pull Request #9315: Implement `blockbeat`

1445 of 2007 new or added lines in 26 files covered. (72.0%)

19246 existing lines in 249 files now uncovered.

102342 of 177975 relevant lines covered (57.5%)

24772.24 hits per line

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

71.65
/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 {
1✔
74

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

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

1✔
85
        return h
1✔
86
}
1✔
87

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

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

105
        return h.htlcResolution.ClaimOutpoint
17✔
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 {
19✔
113
        key := newResolverID(h.outpoint())
19✔
114
        return key[:]
19✔
115
}
19✔
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 {
6✔
162

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

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

6✔
172
        // If this is the remote party's commitment, then we'll be looking for
6✔
173
        // them to spend using the second-level success transaction.
6✔
174
        var preimageBytes []byte
6✔
175
        switch {
6✔
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>
UNCOV
181
        case h.isTaproot() && h.htlcResolution.SignedTimeoutTx == nil:
×
UNCOV
182
                //nolint:ll
×
UNCOV
183
                preimageBytes = spendingInput.Witness[taprootRemotePreimageIndex]
×
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...
UNCOV
195
        case h.isTaproot() && len(spendingInput.Witness) == 1:
×
NEW
196
                return fmt.Errorf("breach attempt failed")
×
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)
6✔
212
        if err != nil {
6✔
NEW
213
                return fmt.Errorf("unable to create pre-image from witness: %w",
×
NEW
214
                        err)
×
UNCOV
215
        }
×
216

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

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

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

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

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

6✔
251
        return h.Checkpoint(h, report)
6✔
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) {
19✔
257
        // If there's no timeout transaction, it means we are spending from a
19✔
258
        // remote commit, then the claim output is the output directly on the
19✔
259
        // commitment transaction, so we'll just use that.
19✔
260
        if h.htlcResolution.SignedTimeoutTx == nil {
25✔
261
                outPointToWatch := h.htlcResolution.ClaimOutpoint
6✔
262
                scriptToWatch := h.htlcResolution.SweepSignDesc.Output.PkScript
6✔
263

6✔
264
                return &outPointToWatch, scriptToWatch, nil
6✔
265
        }
6✔
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
13✔
275
        witness := h.htlcResolution.SignedTimeoutTx.TxIn[0].Witness
13✔
276

13✔
277
        var (
13✔
278
                scriptToWatch []byte
13✔
279
                err           error
13✔
280
        )
13✔
281
        switch {
13✔
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.
UNCOV
286
        case h.isTaproot():
×
UNCOV
287
                // First, we'll parse the control block into something we can
×
UNCOV
288
                // use.
×
UNCOV
289
                ctrlBlockBytes := witness[len(witness)-1]
×
UNCOV
290
                ctrlBlock, err := txscript.ParseControlBlock(ctrlBlockBytes)
×
UNCOV
291
                if err != nil {
×
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.
UNCOV
297
                witnessScript := witness[len(witness)-2]
×
UNCOV
298
                tapscriptRoot := ctrlBlock.RootHash(witnessScript)
×
UNCOV
299

×
UNCOV
300
                // Once we have the root, then we can derive the output key
×
UNCOV
301
                // from the internal key, then turn that into a witness
×
UNCOV
302
                // program.
×
UNCOV
303
                outputKey := txscript.ComputeTaprootOutputKey(
×
UNCOV
304
                        ctrlBlock.InternalKey, tapscriptRoot,
×
UNCOV
305
                )
×
UNCOV
306
                scriptToWatch, err = txscript.PayToTaprootScript(outputKey)
×
UNCOV
307
                if err != nil {
×
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:
13✔
315
                scriptToWatch, err = input.WitnessScriptHash(
13✔
316
                        witness[len(witness)-1],
13✔
317
                )
13✔
318
        }
319
        if err != nil {
13✔
320
                return nil, nil, err
×
321
        }
×
322

323
        return &outPointToWatch, scriptToWatch, nil
13✔
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 {
19✔
330

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

19✔
337
        switch {
19✔
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:
1✔
350
                return checkSizeAndIndex(
1✔
351
                        spendingWitness, remoteTaprootWitnessSuccessSize,
1✔
352
                        taprootRemotePreimageIndex,
1✔
353
                )
1✔
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:
1✔
364
                return checkSizeAndIndex(
1✔
365
                        spendingWitness, localTaprootWitnessSuccessSize,
1✔
366
                        localPreimageIndex,
1✔
367
                )
1✔
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:
5✔
381
                return checkSizeAndIndex(
5✔
382
                        spendingWitness, expectedRemoteWitnessSuccessSize,
5✔
383
                        remotePreimageIndex,
5✔
384
                )
5✔
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:
12✔
396
                fallthrough
12✔
397

398
        default:
12✔
399
                return checkSizeAndIndex(
12✔
400
                        spendingWitness, expectedLocalWitnessSuccessSize,
12✔
401
                        localPreimageIndex,
12✔
402
                )
12✔
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 {
22✔
409
        if len(witness) != size {
33✔
410
                return false
11✔
411
        }
11✔
412

413
        return len(witness[index]) == lntypes.HashSize
11✔
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) {
19✔
423
        // If we're already resolved, then we can exit early.
19✔
424
        if h.IsResolved() {
25✔
425
                h.log.Errorf("already resolved")
6✔
426
                return nil, nil
6✔
427
        }
6✔
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() {
17✔
432
                return nil, h.resolveRemoteCommitOutput()
4✔
433
        }
4✔
434

435
        // If this is a zero-fee HTLC, we now handle the spend from our
436
        // commitment transaction.
437
        if h.isZeroFeeOutput() {
12✔
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()
6✔
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 {
2✔
449
        var inp input.Input
2✔
450
        if h.isTaproot() {
2✔
UNCOV
451
                inp = lnutils.Ptr(input.MakeHtlcSecondLevelTimeoutTaprootInput(
×
UNCOV
452
                        h.htlcResolution.SignedTimeoutTx,
×
UNCOV
453
                        h.htlcResolution.SignDetails,
×
UNCOV
454
                        h.broadcastHeight,
×
UNCOV
455
                        input.WithResolutionBlob(
×
UNCOV
456
                                h.htlcResolution.ResolutionBlob,
×
UNCOV
457
                        ),
×
UNCOV
458
                ))
×
459
        } else {
2✔
460
                inp = lnutils.Ptr(input.MakeHtlcSecondLevelTimeoutAnchorInput(
2✔
461
                        h.htlcResolution.SignedTimeoutTx,
2✔
462
                        h.htlcResolution.SignDetails,
2✔
463
                        h.broadcastHeight,
2✔
464
                ))
2✔
465
        }
2✔
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(
2✔
476
                btcutil.Amount(inp.SignDesc().Output.Value), 2, 0,
2✔
477
        )
2✔
478

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

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

496
        return err
2✔
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 {
6✔
502
        h.log.Debug("incubating htlc output")
6✔
503

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

515
        return h.resolveTimeoutTx()
6✔
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 {
4✔
523
        var htlcWitnessType input.StandardWitnessType
4✔
524
        if h.isTaproot() {
4✔
UNCOV
525
                htlcWitnessType = input.TaprootHtlcOfferedRemoteTimeout
×
526
        } else {
4✔
527
                htlcWitnessType = input.HtlcOfferedRemoteTimeout
4✔
528
        }
4✔
529

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

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

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

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

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

568
        return nil
4✔
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) {
15✔
576

15✔
577
        // TODO(yy): outpointToWatch is always h.HtlcOutpoint(), can refactor
15✔
578
        // to remove the redundancy.
15✔
579
        outpointToWatch, scriptToWatch, err := h.chainDetailsToWatch()
15✔
580
        if err != nil {
15✔
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 {
30✔
587
                return h.waitForConfirmedSpend(outpointToWatch, scriptToWatch)
15✔
588
        }
15✔
589

590
        // Watch for a spend of the HTLC output in both the mempool and blocks.
UNCOV
591
        return h.waitForMempoolOrBlockSpend(*outpointToWatch, scriptToWatch)
×
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) {
15✔
598

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

608
        return spend, err
15✔
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() {
1✔
616
        h.log.Debugf("stopping...")
1✔
617
        defer h.log.Debugf("stopped")
1✔
618

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

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

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

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

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

655
        h.currentReport = ContractReport{
16✔
656
                Outpoint:       h.htlcResolution.ClaimOutpoint,
16✔
657
                Type:           ReportOutputOutgoingHtlc,
16✔
658
                Amount:         finalAmt,
16✔
659
                MaturityHeight: h.htlcResolution.Expiry,
16✔
660
                LimboBalance:   finalAmt,
16✔
661
                Stage:          stage,
16✔
662
        }
16✔
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 {
19✔
670
        // First, we'll write out the relevant fields of the
19✔
671
        // OutgoingHtlcResolution to the writer.
19✔
672
        if err := encodeOutgoingResolution(w, &h.htlcResolution); err != nil {
19✔
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 {
19✔
679
                return err
×
680
        }
×
681
        if err := binary.Write(w, endian, h.IsResolved()); err != nil {
19✔
682
                return err
×
683
        }
×
684
        if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
19✔
685
                return err
×
686
        }
×
687

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

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

698
        return nil
19✔
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) {
15✔
706

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

15✔
711
        // First, we'll read out all the mandatory fields of the
15✔
712
        // OutgoingHtlcResolution that we store.
15✔
713
        if err := decodeOutgoingResolution(r, &h.htlcResolution); err != nil {
15✔
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 {
15✔
720
                return nil, err
×
721
        }
×
722

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

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

735
        if err := binary.Read(r, endian, &h.htlc.HtlcIndex); err != nil {
15✔
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)
15✔
743
        if err == nil {
30✔
744
                h.htlcResolution.SignDetails = signDetails
15✔
745
        } else if err != io.EOF && err != io.ErrUnexpectedEOF {
15✔
746
                return nil, err
×
747
        }
×
748

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

15✔
752
        return h, nil
15✔
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) {
9✔
760
        h.htlc = htlc
9✔
761
}
9✔
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 {
1✔
767
        return h.htlcResolution.HtlcPoint()
1✔
768
}
1✔
769

770
// SupplementDeadline sets the incomingHTLCExpiryHeight for this outgoing htlc
771
// resolver.
772
//
773
// NOTE: Part of the htlcContractResolver interface.
UNCOV
774
func (h *htlcTimeoutResolver) SupplementDeadline(d fn.Option[int32]) {
×
UNCOV
775
        h.incomingHTLCExpiryHeight = d
×
UNCOV
776
}
×
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,
UNCOV
795
        pkScript []byte) (*chainntnfs.SpendDetail, error) {
×
UNCOV
796

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

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

808
        // Subscribe for mempool spent(unconfirmed).
UNCOV
809
        mempoolSpent, err := h.Mempool.SubscribeMempoolSpent(op)
×
UNCOV
810
        if err != nil {
×
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.
UNCOV
816
        result := make(chan *spendResult, 2)
×
UNCOV
817

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

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

×
UNCOV
831
                return event.spend, event.err
×
832

UNCOV
833
        case <-h.quit:
×
UNCOV
834
                return nil, errResolverShuttingDown
×
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,
UNCOV
851
        blockSpent, mempoolSpent <-chan *chainntnfs.SpendDetail) {
×
UNCOV
852

×
UNCOV
853
        op := h.HtlcPoint()
×
UNCOV
854

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

×
UNCOV
858
        // Wait for a spend event to arrive.
×
UNCOV
859
        for {
×
UNCOV
860
                select {
×
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.
UNCOV
871
                case spendDetail, ok := <-blockSpent:
×
UNCOV
872
                        if !ok {
×
873
                                result.err = fmt.Errorf("block spent err: %w",
×
874
                                        errResolverShuttingDown)
×
UNCOV
875
                        } else {
×
UNCOV
876
                                log.Debugf("Found confirmed spend of HTLC "+
×
UNCOV
877
                                        "output %s in tx=%s", op,
×
UNCOV
878
                                        spendDetail.SpenderTxHash)
×
UNCOV
879

×
UNCOV
880
                                result.spend = spendDetail
×
UNCOV
881

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

887
                        // Send the result and exit the loop.
UNCOV
888
                        resultChan <- result
×
UNCOV
889

×
UNCOV
890
                        return
×
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.
UNCOV
900
                case spendDetail, ok := <-mempoolSpent:
×
UNCOV
901
                        if !ok {
×
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

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

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

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

×
UNCOV
931
                        continue
×
932

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

×
UNCOV
938
                        return
×
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 {
41✔
946
        // If we don't have a timeout transaction, then this means that this is
41✔
947
        // an output on the remote party's commitment transaction.
41✔
948
        return h.htlcResolution.SignedTimeoutTx == nil
41✔
949
}
41✔
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 {
28✔
954
        // If we have non-nil SignDetails, this means it has a 2nd level HTLC
28✔
955
        // transaction that is signed using sighash SINGLE|ANYONECANPAY (the
28✔
956
        // case for anchor type channels). In this case we can re-sign it and
28✔
957
        // attach fees at will.
28✔
958
        return h.htlcResolution.SignedTimeoutTx != nil &&
28✔
959
                h.htlcResolution.SignDetails != nil
28✔
960
}
28✔
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) {
2✔
969

2✔
970
        // Wait for the htlc output to be spent, which can happen in one of the
2✔
971
        // paths,
2✔
972
        // 1. The remote party spends the htlc output using the preimage.
2✔
973
        // 2. The local party spends the htlc timeout tx from the local
2✔
974
        //    commitment.
2✔
975
        // 3. The local party spends the htlc output directlt from the remote
2✔
976
        //    commitment.
2✔
977
        spend, err := h.watchHtlcSpend()
2✔
978
        if err != nil {
2✔
NEW
979
                return nil, err
×
NEW
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()) {
2✔
NEW
986
                return nil, h.claimCleanUp(spend)
×
NEW
987
        }
×
988

989
        return spend, nil
2✔
990
}
991

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

2✔
998
        // This should be non-blocking as we will only attempt to sweep the
2✔
999
        // output when the second level tx has already been confirmed. In other
2✔
1000
        // words, waitHtlcSpendAndCheckPreimage will return immediately.
2✔
1001
        commitSpend, err := h.waitHtlcSpendAndCheckPreimage()
2✔
1002
        if err != nil {
2✔
NEW
1003
                return err
×
NEW
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 {
2✔
NEW
1009
                h.log.Infof("preimage spend detected, skipping 2nd-level " +
×
NEW
1010
                        "HTLC output sweep")
×
NEW
1011

×
NEW
1012
                return nil
×
NEW
1013
        }
×
1014

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

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

2✔
1026
        if h.hasCLTV() {
2✔
NEW
1027
                h.log.Infof("waiting for CSV and CLTV lock to expire at "+
×
NEW
1028
                        "height %v", waitHeight)
×
1029
        } else {
2✔
1030
                h.log.Infof("waiting for CSV lock to expire at height %v",
2✔
1031
                        waitHeight)
2✔
1032
        }
2✔
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{
2✔
1040
                Hash:  *commitSpend.SpenderTxHash,
2✔
1041
                Index: commitSpend.SpenderInputIndex,
2✔
1042
        }
2✔
1043

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

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

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

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

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

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

2✔
1087
        return err
2✔
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 {
4✔
1095

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

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

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

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

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

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

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

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

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

8✔
1149
        return h.Checkpoint(h, report)
8✔
1150
}
8✔
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 {
4✔
1156
        h.log.Debug("waiting for direct-timeout spend of the htlc to confirm")
4✔
1157

4✔
1158
        // Wait for the direct-timeout HTLC sweep tx to confirm.
4✔
1159
        spend, err := h.watchHtlcSpend()
4✔
1160
        if err != nil {
4✔
NEW
1161
                return err
×
NEW
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()) {
6✔
1168
                return h.claimCleanUp(spend)
2✔
1169
        }
2✔
1170

1171
        // Send the clean up msg to fail the incoming HTLC.
1172
        failureMsg := &lnwire.FailPermanentChannelFailure{}
2✔
1173
        err = h.DeliverResolutionMsg(ResolutionMsg{
2✔
1174
                SourceChan: h.ShortChanID,
2✔
1175
                HtlcIndex:  h.htlc.HtlcIndex,
2✔
1176
                Failure:    failureMsg,
2✔
1177
        })
2✔
1178
        if err != nil {
2✔
NEW
1179
                return err
×
NEW
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)
2✔
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 {
9✔
1193
        h.log.Debug("waiting for first-stage 2nd-level HTLC timeout tx to " +
9✔
1194
                "confirm")
9✔
1195

9✔
1196
        // Wait for the second level transaction to confirm.
9✔
1197
        spend, err := h.watchHtlcSpend()
9✔
1198
        if err != nil {
9✔
NEW
1199
                return err
×
NEW
1200
        }
×
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()) {
12✔
1206
                return h.claimCleanUp(spend)
3✔
1207
        }
3✔
1208

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

6✔
1212
        // If the timeout tx is a re-signed tx, we will need to find the actual
6✔
1213
        // spent outpoint from the spending tx.
6✔
1214
        if h.isZeroFeeOutput() {
8✔
1215
                op = wire.OutPoint{
2✔
1216
                        Hash:  spenderTxid,
2✔
1217
                        Index: spend.SpenderInputIndex,
2✔
1218
                }
2✔
1219
        }
2✔
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 {
8✔
1225
                return h.resolveTimeoutTxOutput(op)
2✔
1226
        }
2✔
1227

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

4✔
1230
        // Start the process to sweep the output from the timeout tx.
4✔
1231
        if h.isZeroFeeOutput() {
5✔
1232
                err = h.sweepTimeoutTxOutput()
1✔
1233
                if err != nil {
1✔
NEW
1234
                        return err
×
NEW
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 {
4✔
NEW
1241
                return err
×
NEW
1242
        }
×
1243

1244
        // Start the resolving process for the stage two output.
1245
        return h.resolveTimeoutTxOutput(op)
4✔
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 {
6✔
1251
        h.log.Debugf("waiting for second-stage 2nd-level timeout tx output %v "+
6✔
1252
                "to be spent after csv_delay=%v", op, h.htlcResolution.CsvDelay)
6✔
1253

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

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

6✔
1267
        return h.checkpointClaim(spend)
6✔
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 {
19✔
1273
        if h.isLaunched() {
19✔
NEW
1274
                h.log.Tracef("already launched")
×
NEW
1275
                return nil
×
NEW
1276
        }
×
1277

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

19✔
1281
        switch {
19✔
1282
        // If we're already resolved, then we can exit early.
1283
        case h.IsResolved():
6✔
1284
                h.log.Errorf("already resolved")
6✔
1285
                return nil
6✔
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:
4✔
1295
                return h.sweepDirectHtlcOutput()
4✔
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 {
4✔
1304
                        return h.sweepTimeoutTxOutput()
1✔
1305
                }
1✔
1306

1307
                // Otherwise, sweep the second level tx.
1308
                return h.sweepTimeoutTx()
2✔
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:
6✔
1315
                return nil
6✔
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