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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

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

88.46
/contractcourt/htlc_timeout_resolver.go
1
package contractcourt
2

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

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

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

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

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

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

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

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

60
        contractResolverKit
61

62
        htlcLeaseResolver
63

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

490
        return err
3✔
491
}
492

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

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

509
        return h.resolveTimeoutTx()
3✔
510
}
511

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

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

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

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

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

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

556
        return nil
3✔
557
}
558

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

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

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

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

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

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

596
        return spend, err
1✔
597
}
598

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

3✔
607
        close(h.quit)
3✔
608
}
3✔
609

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

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

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

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

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

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

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

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

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

686
        return nil
3✔
687
}
688

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

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

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

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

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

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

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

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

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

3✔
740
        return h, nil
3✔
741
}
742

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

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

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

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

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

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

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

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

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

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

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

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

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

2✔
819
                return event.spend, event.err
2✔
820

821
        case <-h.quit:
2✔
822
                return nil, errResolverShuttingDown
2✔
823
        }
824
}
825

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

2✔
841
        op := h.HtlcPoint()
2✔
842

2✔
843
        // Create a result chan to hold the results.
2✔
844
        result := &spendResult{}
2✔
845

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

2✔
868
                                result.spend = spendDetail
2✔
869

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

875
                        // Send the result and exit the loop.
876
                        resultChan <- result
2✔
877

2✔
878
                        return
2✔
879

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

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

×
896
                                return
×
897
                        }
×
898

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

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

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

2✔
919
                        continue
2✔
920

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

2✔
926
                        return
2✔
927
                }
928
        }
929
}
930

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

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

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

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

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

977
        return spend, nil
3✔
978
}
979

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

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

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

×
1000
                return nil
×
1001
        }
×
1002

1003
        waitHeight := h.deriveWaitHeight(h.htlcResolution.CsvDelay, commitSpend)
3✔
1004

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

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

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

3✔
1032
        var witType input.StandardWitnessType
3✔
1033
        if h.isTaproot() {
6✔
1034
                witType = input.TaprootHtlcOfferedTimeoutSecondLevel
3✔
1035
        } else {
6✔
1036
                witType = input.HtlcOfferedTimeoutSecondLevel
3✔
1037
        }
3✔
1038

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

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

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

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

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

3✔
1075
        return err
3✔
1076
}
1077

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

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

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

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

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

1113
        return h.Checkpoint(h, report)
3✔
1114
}
1115

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

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

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

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

3✔
1137
        return h.Checkpoint(h, report)
3✔
1138
}
3✔
1139

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

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

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

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

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

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

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

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

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

1197
        op := h.htlcResolution.ClaimOutpoint
3✔
1198
        spenderTxid := *spend.SpenderTxHash
3✔
1199

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

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

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

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

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

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

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

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

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

3✔
1255
        return h.checkpointClaim(spend)
3✔
1256
}
1257

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

1266
        h.log.Debugf("launching resolver...")
3✔
1267
        h.markLaunched()
3✔
1268

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

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

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

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

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