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

lightningnetwork / lnd / 12312390362

13 Dec 2024 08:44AM UTC coverage: 57.458% (+8.5%) from 48.92%
12312390362

Pull #9343

github

ellemouton
fn: rework the ContextGuard and add tests

In this commit, the ContextGuard struct is re-worked such that the
context that its new main WithCtx method provides is cancelled in sync
with a parent context being cancelled or with it's quit channel being
cancelled. Tests are added to assert the behaviour. In order for the
close of the quit channel to be consistent with the cancelling of the
derived context, the quit channel _must_ be contained internal to the
ContextGuard so that callers are only able to close the channel via the
exposed Quit method which will then take care to first cancel any
derived context that depend on the quit channel before returning.
Pull Request #9343: fn: expand the ContextGuard and add tests

101853 of 177264 relevant lines covered (57.46%)

24972.93 hits per line

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

67.05
/contractcourt/commit_sweep_resolver.go
1
package contractcourt
2

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

10
        "github.com/btcsuite/btcd/btcutil"
11
        "github.com/btcsuite/btcd/chaincfg/chainhash"
12
        "github.com/btcsuite/btcd/txscript"
13
        "github.com/btcsuite/btcd/wire"
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/lnwallet"
19
        "github.com/lightningnetwork/lnd/sweep"
20
)
21

22
// commitSweepResolver is a resolver that will attempt to sweep the commitment
23
// output paying to us (local channel balance). In the case that the local
24
// party (we) broadcasts their version of the commitment transaction, we have
25
// to wait before sweeping it, as it has a CSV delay. For anchor channel
26
// type, even if the remote party broadcasts the commitment transaction,
27
// we have to wait one block after commitment transaction is confirmed,
28
// because CSV 1 is put into the script of UTXO representing local balance.
29
// Additionally, if the channel is a channel lease, we have to wait for
30
// CLTV to expire.
31
// https://docs.lightning.engineering/lightning-network-tools/pool/overview
32
type commitSweepResolver struct {
33
        // localChanCfg is used to provide the resolver with the keys required
34
        // to identify whether the commitment transaction was broadcast by the
35
        // local or remote party.
36
        localChanCfg channeldb.ChannelConfig
37

38
        // commitResolution contains all data required to successfully sweep
39
        // this HTLC on-chain.
40
        commitResolution lnwallet.CommitOutputResolution
41

42
        // resolved reflects if the contract has been fully resolved or not.
43
        resolved bool
44

45
        // broadcastHeight is the height that the original contract was
46
        // broadcast to the main-chain at. We'll use this value to bound any
47
        // historical queries to the chain for spends/confirmations.
48
        broadcastHeight uint32
49

50
        // chanPoint is the channel point of the original contract.
51
        chanPoint wire.OutPoint
52

53
        // channelInitiator denotes whether the party responsible for resolving
54
        // the contract initiated the channel.
55
        channelInitiator bool
56

57
        // leaseExpiry denotes the additional waiting period the contract must
58
        // hold until it can be resolved. This waiting period is known as the
59
        // expiration of a script-enforced leased channel and only applies to
60
        // the channel initiator.
61
        //
62
        // NOTE: This value should only be set when the contract belongs to a
63
        // leased channel.
64
        leaseExpiry uint32
65

66
        // chanType denotes the type of channel the contract belongs to.
67
        chanType channeldb.ChannelType
68

69
        // currentReport stores the current state of the resolver for reporting
70
        // over the rpc interface.
71
        currentReport ContractReport
72

73
        // reportLock prevents concurrent access to the resolver report.
74
        reportLock sync.Mutex
75

76
        contractResolverKit
77
}
78

79
// newCommitSweepResolver instantiates a new direct commit output resolver.
80
func newCommitSweepResolver(res lnwallet.CommitOutputResolution,
81
        broadcastHeight uint32, chanPoint wire.OutPoint,
82
        resCfg ResolverConfig) *commitSweepResolver {
3✔
83

3✔
84
        r := &commitSweepResolver{
3✔
85
                contractResolverKit: *newContractResolverKit(resCfg),
3✔
86
                commitResolution:    res,
3✔
87
                broadcastHeight:     broadcastHeight,
3✔
88
                chanPoint:           chanPoint,
3✔
89
        }
3✔
90

3✔
91
        r.initLogger(r)
3✔
92
        r.initReport()
3✔
93

3✔
94
        return r
3✔
95
}
3✔
96

97
// ResolverKey returns an identifier which should be globally unique for this
98
// particular resolver within the chain the original contract resides within.
99
func (c *commitSweepResolver) ResolverKey() []byte {
3✔
100
        key := newResolverID(c.commitResolution.SelfOutPoint)
3✔
101
        return key[:]
3✔
102
}
3✔
103

104
// waitForHeight registers for block notifications and waits for the provided
105
// block height to be reached.
106
func waitForHeight(waitHeight uint32, notifier chainntnfs.ChainNotifier,
107
        quit <-chan struct{}) error {
6✔
108

6✔
109
        // Register for block epochs. After registration, the current height
6✔
110
        // will be sent on the channel immediately.
6✔
111
        blockEpochs, err := notifier.RegisterBlockEpochNtfn(nil)
6✔
112
        if err != nil {
6✔
113
                return err
×
114
        }
×
115
        defer blockEpochs.Cancel()
6✔
116

6✔
117
        for {
14✔
118
                select {
8✔
119
                case newBlock, ok := <-blockEpochs.Epochs:
8✔
120
                        if !ok {
8✔
121
                                return errResolverShuttingDown
×
122
                        }
×
123
                        height := newBlock.Height
8✔
124
                        if height >= int32(waitHeight) {
14✔
125
                                return nil
6✔
126
                        }
6✔
127

128
                case <-quit:
×
129
                        return errResolverShuttingDown
×
130
                }
131
        }
132
}
133

134
// waitForSpend waits for the given outpoint to be spent, and returns the
135
// details of the spending tx.
136
func waitForSpend(op *wire.OutPoint, pkScript []byte, heightHint uint32,
137
        notifier chainntnfs.ChainNotifier, quit <-chan struct{}) (
138
        *chainntnfs.SpendDetail, error) {
28✔
139

28✔
140
        spendNtfn, err := notifier.RegisterSpendNtfn(
28✔
141
                op, pkScript, heightHint,
28✔
142
        )
28✔
143
        if err != nil {
28✔
144
                return nil, err
×
145
        }
×
146

147
        select {
28✔
148
        case spendDetail, ok := <-spendNtfn.Spend:
28✔
149
                if !ok {
28✔
150
                        return nil, errResolverShuttingDown
×
151
                }
×
152

153
                return spendDetail, nil
28✔
154

155
        case <-quit:
×
156
                return nil, errResolverShuttingDown
×
157
        }
158
}
159

160
// getCommitTxConfHeight waits for confirmation of the commitment tx and
161
// returns the confirmation height.
162
func (c *commitSweepResolver) getCommitTxConfHeight() (uint32, error) {
3✔
163
        txID := c.commitResolution.SelfOutPoint.Hash
3✔
164
        signDesc := c.commitResolution.SelfOutputSignDesc
3✔
165
        pkScript := signDesc.Output.PkScript
3✔
166

3✔
167
        const confDepth = 1
3✔
168

3✔
169
        confChan, err := c.Notifier.RegisterConfirmationsNtfn(
3✔
170
                &txID, pkScript, confDepth, c.broadcastHeight,
3✔
171
        )
3✔
172
        if err != nil {
3✔
173
                return 0, err
×
174
        }
×
175
        defer confChan.Cancel()
3✔
176

3✔
177
        select {
3✔
178
        case txConfirmation, ok := <-confChan.Confirmed:
3✔
179
                if !ok {
3✔
180
                        return 0, fmt.Errorf("cannot get confirmation "+
×
181
                                "for commit tx %v", txID)
×
182
                }
×
183

184
                return txConfirmation.BlockHeight, nil
3✔
185

186
        case <-c.quit:
×
187
                return 0, errResolverShuttingDown
×
188
        }
189
}
190

191
// Resolve instructs the contract resolver to resolve the output on-chain. Once
192
// the output has been *fully* resolved, the function should return immediately
193
// with a nil ContractResolver value for the first return value.  In the case
194
// that the contract requires further resolution, then another resolve is
195
// returned.
196
//
197
// NOTE: This function MUST be run as a goroutine.
198
//
199
//nolint:funlen
200
func (c *commitSweepResolver) Resolve(_ bool) (ContractResolver, error) {
3✔
201
        // If we're already resolved, then we can exit early.
3✔
202
        if c.resolved {
3✔
203
                return nil, nil
×
204
        }
×
205

206
        confHeight, err := c.getCommitTxConfHeight()
3✔
207
        if err != nil {
3✔
208
                return nil, err
×
209
        }
×
210

211
        // Wait up until the CSV expires, unless we also have a CLTV that
212
        // expires after.
213
        unlockHeight := confHeight + c.commitResolution.MaturityDelay
3✔
214
        if c.hasCLTV() {
3✔
215
                unlockHeight = uint32(math.Max(
×
216
                        float64(unlockHeight), float64(c.leaseExpiry),
×
217
                ))
×
218
        }
×
219

220
        c.log.Debugf("commit conf_height=%v, unlock_height=%v",
3✔
221
                confHeight, unlockHeight)
3✔
222

3✔
223
        // Update report now that we learned the confirmation height.
3✔
224
        c.reportLock.Lock()
3✔
225
        c.currentReport.MaturityHeight = unlockHeight
3✔
226
        c.reportLock.Unlock()
3✔
227

3✔
228
        // If there is a csv/cltv lock, we'll wait for that.
3✔
229
        if c.commitResolution.MaturityDelay > 0 || c.hasCLTV() {
5✔
230
                // Determine what height we should wait until for the locks to
2✔
231
                // expire.
2✔
232
                var waitHeight uint32
2✔
233
                switch {
2✔
234
                // If we have both a csv and cltv lock, we'll need to look at
235
                // both and see which expires later.
236
                case c.commitResolution.MaturityDelay > 0 && c.hasCLTV():
×
237
                        c.log.Debugf("waiting for CSV and CLTV lock to expire "+
×
238
                                "at height %v", unlockHeight)
×
239
                        // If the CSV expires after the CLTV, or there is no
×
240
                        // CLTV, then we can broadcast a sweep a block before.
×
241
                        // Otherwise, we need to broadcast at our expected
×
242
                        // unlock height.
×
243
                        waitHeight = uint32(math.Max(
×
244
                                float64(unlockHeight-1), float64(c.leaseExpiry),
×
245
                        ))
×
246

247
                // If we only have a csv lock, wait for the height before the
248
                // lock expires as the spend path should be unlocked by then.
249
                case c.commitResolution.MaturityDelay > 0:
2✔
250
                        c.log.Debugf("waiting for CSV lock to expire at "+
2✔
251
                                "height %v", unlockHeight)
2✔
252
                        waitHeight = unlockHeight - 1
2✔
253
                }
254

255
                err := waitForHeight(waitHeight, c.Notifier, c.quit)
2✔
256
                if err != nil {
2✔
257
                        return nil, err
×
258
                }
×
259
        }
260

261
        var (
3✔
262
                isLocalCommitTx bool
3✔
263

3✔
264
                signDesc = c.commitResolution.SelfOutputSignDesc
3✔
265
        )
3✔
266

3✔
267
        switch {
3✔
268
        // For taproot channels, we'll know if this is the local commit based
269
        // on the timelock value. For remote commitment transactions, the
270
        // witness script has a timelock of 1.
271
        case c.chanType.IsTaproot():
×
272
                delayKey := c.localChanCfg.DelayBasePoint.PubKey
×
273
                nonDelayKey := c.localChanCfg.PaymentBasePoint.PubKey
×
274

×
275
                signKey := c.commitResolution.SelfOutputSignDesc.KeyDesc.PubKey
×
276

×
277
                // If the key in the script is neither of these, we shouldn't
×
278
                // proceed. This should be impossible.
×
279
                if !signKey.IsEqual(delayKey) && !signKey.IsEqual(nonDelayKey) {
×
280
                        return nil, fmt.Errorf("unknown sign key %v", signKey)
×
281
                }
×
282

283
                // The commitment transaction is ours iff the signing key is
284
                // the delay key.
285
                isLocalCommitTx = signKey.IsEqual(delayKey)
×
286

287
        // The output is on our local commitment if the script starts with
288
        // OP_IF for the revocation clause. On the remote commitment it will
289
        // either be a regular P2WKH or a simple sig spend with a CSV delay.
290
        default:
3✔
291
                isLocalCommitTx = signDesc.WitnessScript[0] == txscript.OP_IF
3✔
292
        }
293
        isDelayedOutput := c.commitResolution.MaturityDelay != 0
3✔
294

3✔
295
        c.log.Debugf("isDelayedOutput=%v, isLocalCommitTx=%v", isDelayedOutput,
3✔
296
                isLocalCommitTx)
3✔
297

3✔
298
        // There're three types of commitments, those that have tweaks for the
3✔
299
        // remote key (us in this case), those that don't, and a third where
3✔
300
        // there is no tweak and the output is delayed. On the local commitment
3✔
301
        // our output will always be delayed. We'll rely on the presence of the
3✔
302
        // commitment tweak to discern which type of commitment this is.
3✔
303
        var witnessType input.WitnessType
3✔
304
        switch {
3✔
305
        // The local delayed output for a taproot channel.
306
        case isLocalCommitTx && c.chanType.IsTaproot():
×
307
                witnessType = input.TaprootLocalCommitSpend
×
308

309
        // The CSV 1 delayed output for a taproot channel.
310
        case !isLocalCommitTx && c.chanType.IsTaproot():
×
311
                witnessType = input.TaprootRemoteCommitSpend
×
312

313
        // Delayed output to us on our local commitment for a channel lease in
314
        // which we are the initiator.
315
        case isLocalCommitTx && c.hasCLTV():
×
316
                witnessType = input.LeaseCommitmentTimeLock
×
317

318
        // Delayed output to us on our local commitment.
319
        case isLocalCommitTx:
×
320
                witnessType = input.CommitmentTimeLock
×
321

322
        // A confirmed output to us on the remote commitment for a channel lease
323
        // in which we are the initiator.
324
        case isDelayedOutput && c.hasCLTV():
×
325
                witnessType = input.LeaseCommitmentToRemoteConfirmed
×
326

327
        // A confirmed output to us on the remote commitment.
328
        case isDelayedOutput:
2✔
329
                witnessType = input.CommitmentToRemoteConfirmed
2✔
330

331
        // A non-delayed output on the remote commitment where the key is
332
        // tweakless.
333
        case c.commitResolution.SelfOutputSignDesc.SingleTweak == nil:
1✔
334
                witnessType = input.CommitSpendNoDelayTweakless
1✔
335

336
        // A non-delayed output on the remote commitment where the key is
337
        // tweaked.
338
        default:
×
339
                witnessType = input.CommitmentNoDelay
×
340
        }
341

342
        c.log.Infof("Sweeping with witness type: %v", witnessType)
3✔
343

3✔
344
        // We'll craft an input with all the information required for the
3✔
345
        // sweeper to create a fully valid sweeping transaction to recover
3✔
346
        // these coins.
3✔
347
        var inp *input.BaseInput
3✔
348
        if c.hasCLTV() {
3✔
349
                inp = input.NewCsvInputWithCltv(
×
350
                        &c.commitResolution.SelfOutPoint, witnessType,
×
351
                        &c.commitResolution.SelfOutputSignDesc,
×
352
                        c.broadcastHeight, c.commitResolution.MaturityDelay,
×
353
                        c.leaseExpiry,
×
354
                        input.WithResolutionBlob(
×
355
                                c.commitResolution.ResolutionBlob,
×
356
                        ),
×
357
                )
×
358
        } else {
3✔
359
                inp = input.NewCsvInput(
3✔
360
                        &c.commitResolution.SelfOutPoint, witnessType,
3✔
361
                        &c.commitResolution.SelfOutputSignDesc,
3✔
362
                        c.broadcastHeight, c.commitResolution.MaturityDelay,
3✔
363
                        input.WithResolutionBlob(
3✔
364
                                c.commitResolution.ResolutionBlob,
3✔
365
                        ),
3✔
366
                )
3✔
367
        }
3✔
368

369
        // TODO(roasbeef): instead of ading ctrl block to the sign desc, make
370
        // new input type, have sweeper set it?
371

372
        // Calculate the budget for the sweeping this input.
373
        budget := calculateBudget(
3✔
374
                btcutil.Amount(inp.SignDesc().Output.Value),
3✔
375
                c.Budget.ToLocalRatio, c.Budget.ToLocal,
3✔
376
        )
3✔
377
        c.log.Infof("Sweeping commit output using budget=%v", budget)
3✔
378

3✔
379
        // With our input constructed, we'll now offer it to the sweeper.
3✔
380
        resultChan, err := c.Sweeper.SweepInput(
3✔
381
                inp, sweep.Params{
3✔
382
                        Budget: budget,
3✔
383

3✔
384
                        // Specify a nil deadline here as there's no time
3✔
385
                        // pressure.
3✔
386
                        DeadlineHeight: fn.None[int32](),
3✔
387
                },
3✔
388
        )
3✔
389
        if err != nil {
3✔
390
                c.log.Errorf("unable to sweep input: %v", err)
×
391

×
392
                return nil, err
×
393
        }
×
394

395
        var sweepTxID chainhash.Hash
3✔
396

3✔
397
        // Sweeper is going to join this input with other inputs if possible
3✔
398
        // and publish the sweep tx. When the sweep tx confirms, it signals us
3✔
399
        // through the result channel with the outcome. Wait for this to
3✔
400
        // happen.
3✔
401
        outcome := channeldb.ResolverOutcomeClaimed
3✔
402
        select {
3✔
403
        case sweepResult := <-resultChan:
3✔
404
                switch sweepResult.Err {
3✔
405
                // If the remote party was able to sweep this output it's
406
                // likely what we sent was actually a revoked commitment.
407
                // Report the error and continue to wrap up the contract.
408
                case sweep.ErrRemoteSpend:
1✔
409
                        c.log.Warnf("local commitment output was swept by "+
1✔
410
                                "remote party via %v", sweepResult.Tx.TxHash())
1✔
411
                        outcome = channeldb.ResolverOutcomeUnclaimed
1✔
412

413
                // No errors, therefore continue processing.
414
                case nil:
2✔
415
                        c.log.Infof("local commitment output fully resolved by "+
2✔
416
                                "sweep tx: %v", sweepResult.Tx.TxHash())
2✔
417
                // Unknown errors.
418
                default:
×
419
                        c.log.Errorf("unable to sweep input: %v",
×
420
                                sweepResult.Err)
×
421

×
422
                        return nil, sweepResult.Err
×
423
                }
424

425
                sweepTxID = sweepResult.Tx.TxHash()
3✔
426

427
        case <-c.quit:
×
428
                return nil, errResolverShuttingDown
×
429
        }
430

431
        // Funds have been swept and balance is no longer in limbo.
432
        c.reportLock.Lock()
3✔
433
        if outcome == channeldb.ResolverOutcomeClaimed {
5✔
434
                // We only record the balance as recovered if it actually came
2✔
435
                // back to us.
2✔
436
                c.currentReport.RecoveredBalance = c.currentReport.LimboBalance
2✔
437
        }
2✔
438
        c.currentReport.LimboBalance = 0
3✔
439
        c.reportLock.Unlock()
3✔
440
        report := c.currentReport.resolverReport(
3✔
441
                &sweepTxID, channeldb.ResolverTypeCommit, outcome,
3✔
442
        )
3✔
443
        c.resolved = true
3✔
444

3✔
445
        // Checkpoint the resolver with a closure that will write the outcome
3✔
446
        // of the resolver and its sweep transaction to disk.
3✔
447
        return nil, c.Checkpoint(c, report)
3✔
448
}
449

450
// Stop signals the resolver to cancel any current resolution processes, and
451
// suspend.
452
//
453
// NOTE: Part of the ContractResolver interface.
454
func (c *commitSweepResolver) Stop() {
×
455
        close(c.quit)
×
456
}
×
457

458
// IsResolved returns true if the stored state in the resolve is fully
459
// resolved. In this case the target output can be forgotten.
460
//
461
// NOTE: Part of the ContractResolver interface.
462
func (c *commitSweepResolver) IsResolved() bool {
×
463
        return c.resolved
×
464
}
×
465

466
// SupplementState allows the user of a ContractResolver to supplement it with
467
// state required for the proper resolution of a contract.
468
//
469
// NOTE: Part of the ContractResolver interface.
470
func (c *commitSweepResolver) SupplementState(state *channeldb.OpenChannel) {
×
471
        if state.ChanType.HasLeaseExpiration() {
×
472
                c.leaseExpiry = state.ThawHeight
×
473
        }
×
474
        c.localChanCfg = state.LocalChanCfg
×
475
        c.channelInitiator = state.IsInitiator
×
476
        c.chanType = state.ChanType
×
477
}
478

479
// hasCLTV denotes whether the resolver must wait for an additional CLTV to
480
// expire before resolving the contract.
481
func (c *commitSweepResolver) hasCLTV() bool {
11✔
482
        return c.channelInitiator && c.leaseExpiry > 0
11✔
483
}
11✔
484

485
// Encode writes an encoded version of the ContractResolver into the passed
486
// Writer.
487
//
488
// NOTE: Part of the ContractResolver interface.
489
func (c *commitSweepResolver) Encode(w io.Writer) error {
1✔
490
        if err := encodeCommitResolution(w, &c.commitResolution); err != nil {
1✔
491
                return err
×
492
        }
×
493

494
        if err := binary.Write(w, endian, c.resolved); err != nil {
1✔
495
                return err
×
496
        }
×
497
        if err := binary.Write(w, endian, c.broadcastHeight); err != nil {
1✔
498
                return err
×
499
        }
×
500
        if _, err := w.Write(c.chanPoint.Hash[:]); err != nil {
1✔
501
                return err
×
502
        }
×
503
        err := binary.Write(w, endian, c.chanPoint.Index)
1✔
504
        if err != nil {
1✔
505
                return err
×
506
        }
×
507

508
        // Previously a sweep tx was serialized at this point. Refactoring
509
        // removed this, but keep in mind that this data may still be present in
510
        // the database.
511

512
        return nil
1✔
513
}
514

515
// newCommitSweepResolverFromReader attempts to decode an encoded
516
// ContractResolver from the passed Reader instance, returning an active
517
// ContractResolver instance.
518
func newCommitSweepResolverFromReader(r io.Reader, resCfg ResolverConfig) (
519
        *commitSweepResolver, error) {
1✔
520

1✔
521
        c := &commitSweepResolver{
1✔
522
                contractResolverKit: *newContractResolverKit(resCfg),
1✔
523
        }
1✔
524

1✔
525
        if err := decodeCommitResolution(r, &c.commitResolution); err != nil {
1✔
526
                return nil, err
×
527
        }
×
528

529
        if err := binary.Read(r, endian, &c.resolved); err != nil {
1✔
530
                return nil, err
×
531
        }
×
532
        if err := binary.Read(r, endian, &c.broadcastHeight); err != nil {
1✔
533
                return nil, err
×
534
        }
×
535
        _, err := io.ReadFull(r, c.chanPoint.Hash[:])
1✔
536
        if err != nil {
1✔
537
                return nil, err
×
538
        }
×
539
        err = binary.Read(r, endian, &c.chanPoint.Index)
1✔
540
        if err != nil {
1✔
541
                return nil, err
×
542
        }
×
543

544
        // Previously a sweep tx was deserialized at this point. Refactoring
545
        // removed this, but keep in mind that this data may still be present in
546
        // the database.
547

548
        c.initLogger(c)
1✔
549
        c.initReport()
1✔
550

1✔
551
        return c, nil
1✔
552
}
553

554
// report returns a report on the resolution state of the contract.
555
func (c *commitSweepResolver) report() *ContractReport {
6✔
556
        c.reportLock.Lock()
6✔
557
        defer c.reportLock.Unlock()
6✔
558

6✔
559
        cpy := c.currentReport
6✔
560
        return &cpy
6✔
561
}
6✔
562

563
// initReport initializes the pending channels report for this resolver.
564
func (c *commitSweepResolver) initReport() {
4✔
565
        amt := btcutil.Amount(
4✔
566
                c.commitResolution.SelfOutputSignDesc.Output.Value,
4✔
567
        )
4✔
568

4✔
569
        // Set the initial report. All fields are filled in, except for the
4✔
570
        // maturity height which remains 0 until Resolve() is executed.
4✔
571
        //
4✔
572
        // TODO(joostjager): Resolvers only activate after the commit tx
4✔
573
        // confirms. With more refactoring in channel arbitrator, it would be
4✔
574
        // possible to make the confirmation height part of ResolverConfig and
4✔
575
        // populate MaturityHeight here.
4✔
576
        c.currentReport = ContractReport{
4✔
577
                Outpoint:         c.commitResolution.SelfOutPoint,
4✔
578
                Type:             ReportOutputUnencumbered,
4✔
579
                Amount:           amt,
4✔
580
                LimboBalance:     amt,
4✔
581
                RecoveredBalance: 0,
4✔
582
        }
4✔
583
}
4✔
584

585
// A compile time assertion to ensure commitSweepResolver meets the
586
// ContractResolver interface.
587
var _ reportingContractResolver = (*commitSweepResolver)(nil)
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