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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 hits per line

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

70.65
/contractcourt/utxonursery.go
1
package contractcourt
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "errors"
7
        "fmt"
8
        "io"
9
        "sync"
10
        "sync/atomic"
11

12
        "github.com/btcsuite/btcd/btcutil"
13
        "github.com/btcsuite/btcd/txscript"
14
        "github.com/btcsuite/btcd/wire"
15
        "github.com/davecgh/go-spew/spew"
16
        "github.com/lightningnetwork/lnd/chainntnfs"
17
        "github.com/lightningnetwork/lnd/channeldb"
18
        "github.com/lightningnetwork/lnd/fn"
19
        "github.com/lightningnetwork/lnd/input"
20
        "github.com/lightningnetwork/lnd/labels"
21
        "github.com/lightningnetwork/lnd/lnutils"
22
        "github.com/lightningnetwork/lnd/lnwallet"
23
        "github.com/lightningnetwork/lnd/sweep"
24
)
25

26
//                          SUMMARY OF OUTPUT STATES
27
//
28
//  - CRIB
29
//    - SerializedType: babyOutput
30
//    - OriginalOutputType: HTLC
31
//    - Awaiting: First-stage HTLC CLTV expiry
32
//    - HeightIndexEntry: Absolute block height of CLTV expiry.
33
//    - NextState: KNDR
34
//  - PSCL
35
//    - SerializedType: kidOutput
36
//    - OriginalOutputType: Commitment
37
//    - Awaiting: Confirmation of commitment txn
38
//    - HeightIndexEntry: None.
39
//    - NextState: KNDR
40
//  - KNDR
41
//    - SerializedType: kidOutput
42
//    - OriginalOutputType: Commitment or HTLC
43
//    - Awaiting: Commitment CSV expiry or second-stage HTLC CSV expiry.
44
//    - HeightIndexEntry: Input confirmation height + relative CSV delay
45
//    - NextState: GRAD
46
//  - GRAD:
47
//    - SerializedType: kidOutput
48
//    - OriginalOutputType: Commitment or HTLC
49
//    - Awaiting: All other outputs in channel to become GRAD.
50
//    - NextState: Mark channel fully closed in channeldb and remove.
51
//
52
//                        DESCRIPTION OF OUTPUT STATES
53
//
54
// TODO(roasbeef): update comment with both new output types
55
//
56
//  - CRIB (babyOutput) outputs are two-stage htlc outputs that are initially
57
//    locked using a CLTV delay, followed by a CSV delay. The first stage of a
58
//    crib output requires broadcasting a presigned htlc timeout txn generated
59
//    by the wallet after an absolute expiry height. Since the timeout txns are
60
//    predetermined, they cannot be batched after-the-fact, meaning that all
61
//    CRIB outputs are broadcast and confirmed independently. After the first
62
//    stage is complete, a CRIB output is moved to the KNDR state, which will
63
//    finishing sweeping the second-layer CSV delay.
64
//
65
//  - PSCL (kidOutput) outputs are commitment outputs locked under a CSV delay.
66
//    These outputs are stored temporarily in this state until the commitment
67
//    transaction confirms, as this solidifies an absolute height that the
68
//    relative time lock will expire. Once this maturity height is determined,
69
//    the PSCL output is moved into KNDR.
70
//
71
//  - KNDR (kidOutput) outputs are CSV delayed outputs for which the maturity
72
//    height has been fully determined. This results from having received
73
//    confirmation of the UTXO we are trying to spend, contained in either the
74
//    commitment txn or htlc timeout txn. Once the maturity height is reached,
75
//    the utxo nursery will sweep all KNDR outputs scheduled for that height
76
//    using a single txn.
77
//
78
//  - GRAD (kidOutput) outputs are KNDR outputs that have successfully been
79
//    swept into the user's wallet. A channel is considered mature once all of
80
//    its outputs, including two-stage htlcs, have entered the GRAD state,
81
//    indicating that it safe to mark the channel as fully closed.
82
//
83
//
84
//                     OUTPUT STATE TRANSITIONS IN UTXO NURSERY
85
//
86
//      ┌────────────────┐            ┌──────────────┐
87
//      │ Commit Outputs │            │ HTLC Outputs │
88
//      └────────────────┘            └──────────────┘
89
//               │                            │
90
//               │                            │
91
//               │                            │               UTXO NURSERY
92
//   ┌───────────┼────────────────┬───────────┼───────────────────────────────┐
93
//   │           │                            │                               │
94
//   │           │                │           │                               │
95
//   │           │                            │           CLTV-Delayed        │
96
//   │           │                │           V            babyOutputs        │
97
//   │           │                        ┌──────┐                            │
98
//   │           │                │       │ CRIB │                            │
99
//   │           │                        └──────┘                            │
100
//   │           │                │           │                               │
101
//   │           │                            │                               │
102
//   │           │                │           |                               │
103
//   │           │                            V    Wait CLTV                  │
104
//   │           │                │          [ ]       +                      │
105
//   │           │                            |   Publish Txn                 │
106
//   │           │                │           │                               │
107
//   │           │                            │                               │
108
//   │           │                │           V ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐        │
109
//   │           │                           ( )  waitForTimeoutConf          │
110
//   │           │                │           | └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘        │
111
//   │           │                            │                               │
112
//   │           │                │           │                               │
113
//   │           │                            │                               │
114
//   │           V                │           │                               │
115
//   │       ┌──────┐                         │                               │
116
//   │       │ PSCL │             └  ──  ──  ─┼  ──  ──  ──  ──  ──  ──  ──  ─┤
117
//   │       └──────┘                         │                               │
118
//   │           │                            │                               │
119
//   │           │                            │                               │
120
//   │           V ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐     │            CSV-Delayed        │
121
//   │          ( )  waitForCommitConf        │             kidOutputs        │
122
//   │           | └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘     │                               │
123
//   │           │                            │                               │
124
//   │           │                            │                               │
125
//   │           │                            V                               │
126
//   │           │                        ┌──────┐                            │
127
//   │           └─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─▶│ KNDR │                            │
128
//   │                                    └──────┘                            │
129
//   │                                        │                               │
130
//   │                                        │                               │
131
//   │                                        |                               │
132
//   │                                        V     Wait CSV                  │
133
//   │                                       [ ]       +                      │
134
//   │                                        |   Publish Txn                 │
135
//   │                                        │                               │
136
//   │                                        │                               │
137
//   │                                        V ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐         │
138
//   │                                       ( )  waitForSweepConf            │
139
//   │                                        | └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘         │
140
//   │                                        │                               │
141
//   │                                        │                               │
142
//   │                                        V                               │
143
//   │                                     ┌──────┐                           │
144
//   │                                     │ GRAD │                           │
145
//   │                                     └──────┘                           │
146
//   │                                        │                               │
147
//   │                                        │                               │
148
//   │                                        │                               │
149
//   └────────────────────────────────────────┼───────────────────────────────┘
150
//                                            │
151
//                                            │
152
//                                            │
153
//                                            │
154
//                                            V
155
//                                   ┌────────────────┐
156
//                                   │ Wallet Outputs │
157
//                                   └────────────────┘
158

159
var byteOrder = binary.BigEndian
160

161
const (
162
        // kgtnOutputConfTarget is the default confirmation target we'll use for
163
        // sweeps of CSV delayed outputs.
164
        kgtnOutputConfTarget = 6
165
)
166

167
var (
168
        // ErrContractNotFound is returned when the nursery is unable to
169
        // retrieve information about a queried contract.
170
        ErrContractNotFound = fmt.Errorf("unable to locate contract")
171
)
172

173
// NurseryConfig abstracts the required subsystems used by the utxo nursery. An
174
// instance of NurseryConfig is passed to newUtxoNursery during instantiation.
175
type NurseryConfig struct {
176
        // ChainIO is used by the utxo nursery to determine the current block
177
        // height, which drives the incubation of the nursery's outputs.
178
        ChainIO lnwallet.BlockChainIO
179

180
        // ConfDepth is the number of blocks the nursery store waits before
181
        // determining outputs in the chain as confirmed.
182
        ConfDepth uint32
183

184
        // FetchClosedChannels provides access to a user's channels, such that
185
        // they can be marked fully closed after incubation has concluded.
186
        FetchClosedChannels func(pendingOnly bool) (
187
                []*channeldb.ChannelCloseSummary, error)
188

189
        // FetchClosedChannel provides access to the close summary to extract a
190
        // height hint from.
191
        FetchClosedChannel func(chanID *wire.OutPoint) (
192
                *channeldb.ChannelCloseSummary, error)
193

194
        // Notifier provides the utxo nursery the ability to subscribe to
195
        // transaction confirmation events, which advance outputs through their
196
        // persistence state transitions.
197
        Notifier chainntnfs.ChainNotifier
198

199
        // PublishTransaction facilitates the process of broadcasting a signed
200
        // transaction to the appropriate network.
201
        PublishTransaction func(*wire.MsgTx, string) error
202

203
        // Store provides access to and modification of the persistent state
204
        // maintained about the utxo nursery's incubating outputs.
205
        Store NurseryStorer
206

207
        // Sweep sweeps an input back to the wallet.
208
        SweepInput func(input.Input, sweep.Params) (chan sweep.Result, error)
209

210
        // Budget is the configured budget for the nursery.
211
        Budget *BudgetConfig
212
}
213

214
// UtxoNursery is a system dedicated to incubating time-locked outputs created
215
// by the broadcast of a commitment transaction either by us, or the remote
216
// peer. The nursery accepts outputs and "incubates" them until they've reached
217
// maturity, then sweep the outputs into the source wallet. An output is
218
// considered mature after the relative time-lock within the pkScript has
219
// passed. As outputs reach their maturity age, they're swept in batches into
220
// the source wallet, returning the outputs so they can be used within future
221
// channels, or regular Bitcoin transactions.
222
type UtxoNursery struct {
223
        started uint32 // To be used atomically.
224
        stopped uint32 // To be used atomically.
225

226
        cfg *NurseryConfig
227

228
        mu         sync.Mutex
229
        bestHeight uint32
230

231
        quit chan struct{}
232
        wg   sync.WaitGroup
233
}
234

235
// NewUtxoNursery creates a new instance of the UtxoNursery from a
236
// ChainNotifier and LightningWallet instance.
237
func NewUtxoNursery(cfg *NurseryConfig) *UtxoNursery {
238
        return &UtxoNursery{
42✔
239
                cfg:  cfg,
42✔
240
                quit: make(chan struct{}),
42✔
241
        }
42✔
242
}
42✔
243

42✔
244
// Start launches all goroutines the UtxoNursery needs to properly carry out
245
// its duties.
246
func (u *UtxoNursery) Start() error {
247
        if !atomic.CompareAndSwapUint32(&u.started, 0, 1) {
42✔
248
                return nil
42✔
249
        }
×
UNCOV
250

×
251
        utxnLog.Info("UTXO nursery starting")
252

42✔
253
        // Retrieve the currently best known block. This is needed to have the
42✔
254
        // state machine catch up with the blocks we missed when we were down.
42✔
255
        bestHash, bestHeight, err := u.cfg.ChainIO.GetBestBlock()
42✔
256
        if err != nil {
42✔
257
                return err
42✔
258
        }
×
UNCOV
259

×
260
        // Set best known height to schedule late registrations properly.
261
        atomic.StoreUint32(&u.bestHeight, uint32(bestHeight))
262

42✔
263
        // 2. Flush all fully-graduated channels from the pipeline.
42✔
264

42✔
265
        // Load any pending close channels, which represents the super set of
42✔
266
        // all channels that may still be incubating.
42✔
267
        pendingCloseChans, err := u.cfg.FetchClosedChannels(true)
42✔
268
        if err != nil {
42✔
269
                return err
42✔
270
        }
×
UNCOV
271

×
272
        // Ensure that all mature channels have been marked as fully closed in
273
        // the channeldb.
274
        for _, pendingClose := range pendingCloseChans {
275
                err := u.closeAndRemoveIfMature(&pendingClose.ChanPoint)
42✔
UNCOV
276
                if err != nil {
×
277
                        return err
×
278
                }
×
UNCOV
279
        }
×
280

281
        // TODO(conner): check if any fully closed channels can be removed from
282
        // utxn.
283

284
        // 2. Restart spend ntfns for any preschool outputs, which are waiting
285
        // for the force closed commitment txn to confirm, or any second-layer
286
        // HTLC success transactions.
287
        //
288
        // NOTE: The next two steps *may* spawn go routines, thus from this
289
        // point forward, we must close the nursery's quit channel if we detect
290
        // any failures during startup to ensure they terminate.
291
        if err := u.reloadPreschool(); err != nil {
292
                close(u.quit)
42✔
293
                return err
×
294
        }
×
UNCOV
295

×
296
        // 3. Replay all crib and kindergarten outputs up to the current best
297
        // height.
298
        if err := u.reloadClasses(uint32(bestHeight)); err != nil {
299
                close(u.quit)
42✔
300
                return err
×
301
        }
×
UNCOV
302

×
303
        // Start watching for new blocks, as this will drive the nursery store's
304
        // state machine.
305
        newBlockChan, err := u.cfg.Notifier.RegisterBlockEpochNtfn(&chainntnfs.BlockEpoch{
306
                Height: bestHeight,
42✔
307
                Hash:   bestHash,
42✔
308
        })
42✔
309
        if err != nil {
42✔
310
                close(u.quit)
42✔
311
                return err
×
312
        }
×
UNCOV
313

×
314
        u.wg.Add(1)
315
        go u.incubator(newBlockChan)
42✔
316

42✔
317
        return nil
42✔
318
}
42✔
319

320
// Stop gracefully shuts down any lingering goroutines launched during normal
321
// operation of the UtxoNursery.
322
func (u *UtxoNursery) Stop() error {
323
        if !atomic.CompareAndSwapUint32(&u.stopped, 0, 1) {
41✔
324
                return nil
41✔
325
        }
×
UNCOV
326

×
327
        utxnLog.Infof("UTXO nursery shutting down...")
328
        defer utxnLog.Debug("UTXO nursery shutdown complete")
41✔
329

41✔
330
        close(u.quit)
41✔
331
        u.wg.Wait()
41✔
332

41✔
333
        return nil
41✔
334
}
41✔
335

336
// IncubateOutputs sends a request to the UtxoNursery to incubate a set of
337
// outputs from an existing commitment transaction. Outputs need to incubate if
338
// they're CLTV absolute time locked, or if they're CSV relative time locked.
339
// Once all outputs reach maturity, they'll be swept back into the wallet.
340
func (u *UtxoNursery) IncubateOutputs(chanPoint wire.OutPoint,
341
        outgoingHtlc fn.Option[lnwallet.OutgoingHtlcResolution],
342
        incomingHtlc fn.Option[lnwallet.IncomingHtlcResolution],
343
        broadcastHeight uint32, deadlineHeight fn.Option[int32]) error {
344

18✔
345
        // Add to wait group because nursery might shut down during execution of
18✔
346
        // this function. Otherwise it could happen that nursery thinks it is
18✔
347
        // shut down, but in this function new goroutines were started and stay
18✔
348
        // around.
18✔
349
        u.wg.Add(1)
18✔
350
        defer u.wg.Done()
18✔
351

18✔
352
        // Check quit channel for the case where the waitgroup wait was finished
18✔
353
        // right before this function's add call was made.
18✔
354
        select {
18✔
355
        case <-u.quit:
18✔
356
                return fmt.Errorf("nursery shutting down")
×
UNCOV
357
        default:
×
358
        }
18✔
359

360
        var (
361
                // Kid outputs can be swept after an initial confirmation
18✔
362
                // followed by a maturity period.Baby outputs are two stage and
18✔
363
                // will need to wait for an absolute time out to reach a
18✔
364
                // confirmation, then require a relative confirmation delay.
18✔
365
                kidOutputs  = make([]kidOutput, 0)
18✔
366
                babyOutputs = make([]babyOutput, 0)
18✔
367
        )
18✔
368

18✔
369
        // 1. Build all the spendable outputs that we will try to incubate.
18✔
370

18✔
371
        // TODO(roasbeef): query and see if we already have, if so don't add?
18✔
372

18✔
373
        // For each incoming HTLC, we'll register a kid output marked as a
18✔
374
        // second-layer HTLC output. We effectively skip the baby stage (as the
18✔
375
        // timelock is zero), and enter the kid stage.
18✔
376
        incomingHtlc.WhenSome(func(htlcRes lnwallet.IncomingHtlcResolution) {
18✔
377
                // Based on the input pk script of the sign descriptor, we can
18✔
378
                // determine if this is a taproot output or not. This'll
×
379
                // determine the witness type we try to set below.
×
380
                isTaproot := txscript.IsPayToTaproot(
×
381
                        htlcRes.SweepSignDesc.Output.PkScript,
×
382
                )
×
383

×
384
                var witType input.StandardWitnessType
×
385
                if isTaproot {
×
386
                        witType = input.TaprootHtlcAcceptedSuccessSecondLevel
×
387
                } else {
×
388
                        witType = input.HtlcAcceptedSuccessSecondLevel
×
389
                }
×
UNCOV
390

×
391
                htlcOutput := makeKidOutput(
392
                        &htlcRes.ClaimOutpoint, &chanPoint, htlcRes.CsvDelay,
×
393
                        witType, &htlcRes.SweepSignDesc, 0, deadlineHeight,
×
394
                )
×
395

×
396
                if htlcOutput.Amount() > 0 {
×
397
                        kidOutputs = append(kidOutputs, htlcOutput)
×
398
                }
×
UNCOV
399
        })
×
400

401
        // For each outgoing HTLC, we'll create a baby output. If this is our
402
        // commitment transaction, then we'll broadcast a second-layer
403
        // transaction to transition to a kid output. Otherwise, we'll directly
404
        // spend once the CLTV delay us up.
405
        outgoingHtlc.WhenSome(func(htlcRes lnwallet.OutgoingHtlcResolution) {
406
                // If this HTLC is on our commitment transaction, then it'll be
36✔
407
                // a baby output as we need to go to the second level to sweep
18✔
408
                // it.
18✔
409
                if htlcRes.SignedTimeoutTx != nil {
18✔
410
                        htlcOutput := makeBabyOutput(
29✔
411
                                &chanPoint, &htlcRes, deadlineHeight,
11✔
412
                        )
11✔
413

11✔
414
                        if htlcOutput.Amount() > 0 {
11✔
415
                                babyOutputs = append(babyOutputs, htlcOutput)
22✔
416
                        }
11✔
417

11✔
418
                        return
419
                }
11✔
420

421
                // Based on the input pk script of the sign descriptor, we can
422
                // determine if this is a taproot output or not. This'll
423
                // determine the witness type we try to set below.
424
                isTaproot := txscript.IsPayToTaproot(
425
                        htlcRes.SweepSignDesc.Output.PkScript,
7✔
426
                )
7✔
427

7✔
428
                var witType input.StandardWitnessType
7✔
429
                if isTaproot {
7✔
430
                        witType = input.TaprootHtlcOfferedRemoteTimeout
7✔
UNCOV
431
                } else {
×
432
                        witType = input.HtlcOfferedRemoteTimeout
7✔
433
                }
7✔
434

7✔
435
                // Otherwise, this is actually a kid output as we can sweep it
436
                // once the commitment transaction confirms, and the absolute
437
                // CLTV lock has expired. We set the CSV delay what the
438
                // resolution encodes, since the sequence number must be set
439
                // accordingly.
440
                htlcOutput := makeKidOutput(
441
                        &htlcRes.ClaimOutpoint, &chanPoint, htlcRes.CsvDelay,
7✔
442
                        witType, &htlcRes.SweepSignDesc, htlcRes.Expiry,
7✔
443
                        deadlineHeight,
7✔
444
                )
7✔
445
                kidOutputs = append(kidOutputs, htlcOutput)
7✔
446
        })
7✔
447

448
        // TODO(roasbeef): if want to handle outgoing on remote commit
449
        //  * need ability to cancel in the case that we learn of pre-image or
450
        //    remote party pulls
451

452
        numHtlcs := len(babyOutputs) + len(kidOutputs)
453
        utxnLog.Infof("Incubating Channel(%s) num-htlcs=%d",
18✔
454
                chanPoint, numHtlcs)
18✔
455

18✔
456
        u.mu.Lock()
18✔
457
        defer u.mu.Unlock()
18✔
458

18✔
459
        // 2. Persist the outputs we intended to sweep in the nursery store
18✔
460
        if err := u.cfg.Store.Incubate(kidOutputs, babyOutputs); err != nil {
18✔
461
                utxnLog.Errorf("unable to begin incubation of Channel(%s): %v",
18✔
462
                        chanPoint, err)
×
463
                return err
×
464
        }
×
UNCOV
465

×
466
        // As an intermediate step, we'll now check to see if any of the baby
467
        // outputs has actually _already_ expired. This may be the case if
468
        // blocks were mined while we processed this message.
469
        _, bestHeight, err := u.cfg.ChainIO.GetBestBlock()
470
        if err != nil {
18✔
471
                return err
18✔
472
        }
×
UNCOV
473

×
474
        // We'll examine all the baby outputs just inserted into the database,
475
        // if the output has already expired, then we'll *immediately* sweep
476
        // it. This may happen if the caller raced a block to call this method.
477
        for i, babyOutput := range babyOutputs {
478
                if uint32(bestHeight) >= babyOutput.expiry {
29✔
479
                        err = u.sweepCribOutput(
14✔
480
                                babyOutput.expiry, &babyOutputs[i],
3✔
481
                        )
3✔
482
                        if err != nil {
3✔
483
                                return err
4✔
484
                        }
1✔
485
                }
1✔
486
        }
487

488
        // 3. If we are incubating any preschool outputs, register for a
489
        // confirmation notification that will transition it to the
490
        // kindergarten bucket.
491
        if len(kidOutputs) != 0 {
492
                for i := range kidOutputs {
24✔
493
                        err := u.registerPreschoolConf(
14✔
494
                                &kidOutputs[i], broadcastHeight,
7✔
495
                        )
7✔
496
                        if err != nil {
7✔
497
                                return err
7✔
498
                        }
×
UNCOV
499
                }
×
500
        }
501

502
        return nil
503
}
17✔
504

505
// NurseryReport attempts to return a nursery report stored for the target
506
// outpoint. A nursery report details the maturity/sweeping progress for a
507
// contract that was previously force closed. If a report entry for the target
508
// chanPoint is unable to be constructed, then an error will be returned.
509
func (u *UtxoNursery) NurseryReport(
510
        chanPoint *wire.OutPoint) (*ContractMaturityReport, error) {
511

49✔
512
        u.mu.Lock()
49✔
513
        defer u.mu.Unlock()
49✔
514

49✔
515
        utxnLog.Debugf("NurseryReport: building nursery report for channel %v",
49✔
516
                chanPoint)
49✔
517

49✔
518
        var report *ContractMaturityReport
49✔
519

49✔
520
        if err := u.cfg.Store.ForChanOutputs(chanPoint, func(k, v []byte) error {
49✔
521
                switch {
81✔
522
                case bytes.HasPrefix(k, cribPrefix):
32✔
523
                        // Cribs outputs are the only kind currently stored as
8✔
524
                        // baby outputs.
8✔
525
                        var baby babyOutput
8✔
526
                        err := baby.Decode(bytes.NewReader(v))
8✔
527
                        if err != nil {
8✔
528
                                return err
8✔
529
                        }
×
UNCOV
530

×
531
                        // Each crib output represents a stage one htlc, and
532
                        // will contribute towards the limbo balance.
533
                        report.AddLimboStage1TimeoutHtlc(&baby)
534

8✔
535
                case bytes.HasPrefix(k, psclPrefix),
536
                        bytes.HasPrefix(k, kndrPrefix),
537
                        bytes.HasPrefix(k, gradPrefix):
538

24✔
539
                        // All others states can be deserialized as kid outputs.
24✔
540
                        var kid kidOutput
24✔
541
                        err := kid.Decode(bytes.NewReader(v))
24✔
542
                        if err != nil {
24✔
543
                                return err
24✔
544
                        }
×
UNCOV
545

×
546
                        // Now, use the state prefixes to determine how the
547
                        // this output should be represented in the nursery
548
                        // report.  An output's funds are always in limbo until
549
                        // reaching the graduate state.
550
                        switch {
551
                        case bytes.HasPrefix(k, psclPrefix):
24✔
552
                                // Preschool outputs are awaiting the
7✔
553
                                // confirmation of the commitment transaction.
7✔
554
                                switch kid.WitnessType() {
7✔
555

7✔
556
                                //nolint:lll
557
                                case input.TaprootHtlcAcceptedSuccessSecondLevel:
558
                                        fallthrough
×
559
                                case input.HtlcAcceptedSuccessSecondLevel:
×
560
                                        // An HTLC output on our commitment
×
561
                                        // transaction where the second-layer
×
562
                                        // transaction hasn't
×
563
                                        // yet confirmed.
×
564
                                        report.AddLimboStage1SuccessHtlc(&kid)
×
UNCOV
565

×
566
                                case input.HtlcOfferedRemoteTimeout,
567
                                        input.TaprootHtlcOfferedRemoteTimeout:
568
                                        // This is an HTLC output on the
7✔
569
                                        // commitment transaction of the remote
7✔
570
                                        // party. We are waiting for the CLTV
7✔
571
                                        // timelock expire.
7✔
572
                                        report.AddLimboDirectHtlc(&kid)
7✔
573
                                }
7✔
574

575
                        case bytes.HasPrefix(k, kndrPrefix):
576
                                // Kindergarten outputs may originate from
17✔
577
                                // either the commitment transaction or an htlc.
17✔
578
                                // We can distinguish them via their witness
17✔
579
                                // types.
17✔
580
                                switch kid.WitnessType() {
17✔
581

17✔
582
                                case input.HtlcOfferedRemoteTimeout,
583
                                        input.TaprootHtlcOfferedRemoteTimeout:
584
                                        // This is an HTLC output on the
7✔
585
                                        // commitment transaction of the remote
7✔
586
                                        // party. The CLTV timelock has
7✔
587
                                        // expired, and we only need to sweep
7✔
588
                                        // it.
7✔
589
                                        report.AddLimboDirectHtlc(&kid)
7✔
590

7✔
591
                                //nolint:lll
592
                                case input.TaprootHtlcAcceptedSuccessSecondLevel:
593
                                        fallthrough
×
594
                                case input.TaprootHtlcOfferedTimeoutSecondLevel:
×
595
                                        fallthrough
×
596
                                case input.HtlcAcceptedSuccessSecondLevel:
×
597
                                        fallthrough
×
UNCOV
598
                                case input.HtlcOfferedTimeoutSecondLevel:
×
599
                                        // The htlc timeout or success
10✔
600
                                        // transaction has confirmed, and the
10✔
601
                                        // CSV delay has begun ticking.
10✔
602
                                        report.AddLimboStage2Htlc(&kid)
10✔
603
                                }
10✔
604

605
                        case bytes.HasPrefix(k, gradPrefix):
606
                                // Graduate outputs are those whose funds have
×
607
                                // been swept back into the wallet. Each output
×
608
                                // will contribute towards the recovered
×
609
                                // balance.
×
610
                                switch kid.WitnessType() {
×
UNCOV
611

×
612
                                //nolint:lll
613
                                case input.TaprootHtlcAcceptedSuccessSecondLevel:
614
                                        fallthrough
×
615
                                case input.TaprootHtlcOfferedTimeoutSecondLevel:
×
616
                                        fallthrough
×
617
                                case input.HtlcAcceptedSuccessSecondLevel:
×
618
                                        fallthrough
×
619
                                case input.HtlcOfferedTimeoutSecondLevel:
×
620
                                        fallthrough
×
621
                                case input.TaprootHtlcOfferedRemoteTimeout:
×
622
                                        fallthrough
×
623
                                case input.HtlcOfferedRemoteTimeout:
×
624
                                        // This htlc output successfully
×
625
                                        // resides in a p2wkh output belonging
×
626
                                        // to the user.
×
627
                                        report.AddRecoveredHtlc(&kid)
×
UNCOV
628
                                }
×
629
                        }
630

631
                default:
UNCOV
632
                }
×
633

634
                return nil
635
        }, func() {
32✔
636
                report = &ContractMaturityReport{}
49✔
637
        }); err != nil {
49✔
638
                return nil, err
66✔
639
        }
17✔
640

17✔
641
        return report, nil
642
}
32✔
643

644
// reloadPreschool re-initializes the chain notifier with all of the outputs
645
// that had been saved to the "preschool" database bucket prior to shutdown.
646
func (u *UtxoNursery) reloadPreschool() error {
647
        psclOutputs, err := u.cfg.Store.FetchPreschools()
42✔
648
        if err != nil {
42✔
649
                return err
42✔
650
        }
×
UNCOV
651

×
652
        // For each of the preschool outputs stored in the nursery store, load
653
        // its close summary from disk so that we can get an accurate height
654
        // hint from which to start our range for spend notifications.
655
        for i := range psclOutputs {
656
                kid := &psclOutputs[i]
43✔
657
                chanPoint := kid.OriginChanPoint()
1✔
658

1✔
659
                // Load the close summary for this output's channel point.
1✔
660
                closeSummary, err := u.cfg.FetchClosedChannel(chanPoint)
1✔
661
                if err == channeldb.ErrClosedChannelNotFound {
1✔
662
                        // This should never happen since the close summary
1✔
663
                        // should only be removed after the channel has been
×
664
                        // swept completely.
×
665
                        utxnLog.Warnf("Close summary not found for "+
×
666
                                "chan_point=%v, can't determine height hint"+
×
667
                                "to sweep commit txn", chanPoint)
×
668
                        continue
×
UNCOV
669

×
670
                } else if err != nil {
671
                        return err
1✔
672
                }
×
UNCOV
673

×
674
                // Use the close height from the channel summary as our height
675
                // hint to drive our spend notifications, with our confirmation
676
                // depth as a buffer for reorgs.
677
                heightHint := closeSummary.CloseHeight - u.cfg.ConfDepth
678
                err = u.registerPreschoolConf(kid, heightHint)
1✔
679
                if err != nil {
1✔
680
                        return err
1✔
681
                }
×
UNCOV
682
        }
×
683

684
        return nil
685
}
42✔
686

687
// reloadClasses reinitializes any height-dependent state transitions for which
688
// the utxonursery has not received confirmation, and replays the graduation of
689
// all kindergarten and crib outputs for all heights up to the current block.
690
// This allows the nursery to reinitialize all state to continue sweeping
691
// outputs, even in the event that we missed blocks while offline. reloadClasses
692
// is called during the startup of the UTXO Nursery.
693
func (u *UtxoNursery) reloadClasses(bestHeight uint32) error {
694
        // Loading all active heights up to and including the current block.
42✔
695
        activeHeights, err := u.cfg.Store.HeightsBelowOrEqual(
42✔
696
                uint32(bestHeight))
42✔
697
        if err != nil {
42✔
698
                return err
42✔
699
        }
×
UNCOV
700

×
701
        // Return early if nothing to sweep.
702
        if len(activeHeights) == 0 {
703
                return nil
77✔
704
        }
35✔
705

35✔
706
        utxnLog.Infof("(Re)-sweeping %d heights below height=%d",
707
                len(activeHeights), bestHeight)
7✔
708

7✔
709
        // Attempt to re-register notifications for any outputs still at these
7✔
710
        // heights.
7✔
711
        for _, classHeight := range activeHeights {
7✔
712
                utxnLog.Debugf("Attempting to sweep outputs at height=%v",
14✔
713
                        classHeight)
7✔
714

7✔
715
                if err = u.graduateClass(classHeight); err != nil {
7✔
716
                        utxnLog.Errorf("Failed to sweep outputs at "+
7✔
717
                                "height=%v: %v", classHeight, err)
×
718
                        return err
×
719
                }
×
UNCOV
720
        }
×
721

722
        utxnLog.Infof("UTXO Nursery is now fully synced")
723

7✔
724
        return nil
7✔
725
}
7✔
726

727
// incubator is tasked with driving all state transitions that are dependent on
728
// the current height of the blockchain. As new blocks arrive, the incubator
729
// will attempt spend outputs at the latest height. The asynchronous
730
// confirmation of these spends will either 1) move a crib output into the
731
// kindergarten bucket or 2) move a kindergarten output into the graduated
732
// bucket.
733
func (u *UtxoNursery) incubator(newBlockChan *chainntnfs.BlockEpochEvent) {
734
        defer u.wg.Done()
42✔
735
        defer newBlockChan.Cancel()
42✔
736

42✔
737
        for {
42✔
738
                select {
112✔
739
                case epoch, ok := <-newBlockChan.Epochs:
70✔
740
                        // If the epoch channel has been closed, then the
28✔
741
                        // ChainNotifier is exiting which means the daemon is
28✔
742
                        // as well. Therefore, we exit early also in order to
28✔
743
                        // ensure the daemon shuts down gracefully, yet
28✔
744
                        // swiftly.
28✔
745
                        if !ok {
28✔
746
                                return
28✔
747
                        }
×
UNCOV
748

×
749
                        // TODO(roasbeef): if the BlockChainIO is rescanning
750
                        // will give stale data
751

752
                        // A new block has just been connected to the main
753
                        // chain, which means we might be able to graduate crib
754
                        // or kindergarten outputs at this height. This involves
755
                        // broadcasting any presigned htlc timeout txns, as well
756
                        // as signing and broadcasting a sweep txn that spends
757
                        // from all kindergarten outputs at this height.
758
                        height := uint32(epoch.Height)
759

28✔
760
                        // Update best known block height for late registrations
28✔
761
                        // to be scheduled properly.
28✔
762
                        atomic.StoreUint32(&u.bestHeight, height)
28✔
763

28✔
764
                        if err := u.graduateClass(height); err != nil {
28✔
765
                                utxnLog.Errorf("error while graduating "+
29✔
766
                                        "class at height=%d: %v", height, err)
1✔
767

1✔
768
                                // TODO(conner): signal fatal error to daemon
1✔
769
                        }
1✔
770

1✔
771
                case <-u.quit:
772
                        return
41✔
773
                }
41✔
774
        }
775
}
776

777
// graduateClass handles the steps involved in spending outputs whose CSV or
778
// CLTV delay expires at the nursery's current height. This method is called
779
// each time a new block arrives, or during startup to catch up on heights we
780
// may have missed while the nursery was offline.
781
func (u *UtxoNursery) graduateClass(classHeight uint32) error {
782
        // Record this height as the nursery's current best height.
35✔
783
        u.mu.Lock()
35✔
784
        defer u.mu.Unlock()
35✔
785

35✔
786
        // Fetch all information about the crib and kindergarten outputs at
35✔
787
        // this height.
35✔
788
        kgtnOutputs, cribOutputs, err := u.cfg.Store.FetchClass(
35✔
789
                classHeight,
35✔
790
        )
35✔
791
        if err != nil {
35✔
792
                return err
36✔
793
        }
1✔
794

1✔
795
        utxnLog.Infof("Attempting to graduate height=%v: num_kids=%v, "+
796
                "num_babies=%v", classHeight, len(kgtnOutputs), len(cribOutputs))
34✔
797

34✔
798
        // Offer the outputs to the sweeper and set up notifications that will
34✔
799
        // transition the swept kindergarten outputs and cltvCrib into graduated
34✔
800
        // outputs.
34✔
801
        if len(kgtnOutputs) > 0 {
34✔
802
                if err := u.sweepMatureOutputs(classHeight, kgtnOutputs); err != nil {
55✔
803
                        utxnLog.Errorf("Failed to sweep %d kindergarten "+
21✔
804
                                "outputs at height=%d: %v",
×
805
                                len(kgtnOutputs), classHeight, err)
×
806
                        return err
×
807
                }
×
UNCOV
808
        }
×
809

810
        // Now, we broadcast all pre-signed htlc txns from the csv crib outputs
811
        // at this height.
812
        for i := range cribOutputs {
813
                err := u.sweepCribOutput(classHeight, &cribOutputs[i])
47✔
814
                if err != nil {
13✔
815
                        utxnLog.Errorf("Failed to sweep first-stage HTLC "+
13✔
816
                                "(CLTV-delayed) output %v",
×
817
                                cribOutputs[i].OutPoint())
×
818
                        return err
×
819
                }
×
UNCOV
820
        }
×
821

822
        return nil
823
}
34✔
824

825
// decideDeadlineAndBudget returns the deadline and budget for a given output.
826
func (u *UtxoNursery) decideDeadlineAndBudget(k kidOutput) (fn.Option[int32],
827
        btcutil.Amount) {
828

21✔
829
        // Assume this is a to_local output and use a None deadline.
21✔
830
        deadline := fn.None[int32]()
21✔
831

21✔
832
        // Exit early if this is not HTLC.
21✔
833
        if !k.isHtlc {
21✔
834
                budget := calculateBudget(
34✔
835
                        k.amt, u.cfg.Budget.ToLocalRatio, u.cfg.Budget.ToLocal,
13✔
836
                )
13✔
837

13✔
838
                return deadline, budget
13✔
839
        }
13✔
840

13✔
841
        // Otherwise it's the first-level HTLC output, we'll use the
842
        // time-sensitive settings for it.
843
        budget := calculateBudget(
844
                k.amt, u.cfg.Budget.DeadlineHTLCRatio,
8✔
845
                u.cfg.Budget.DeadlineHTLC,
8✔
846
        )
8✔
847

8✔
848
        return k.deadlineHeight, budget
8✔
849
}
8✔
850

851
// sweepMatureOutputs generates and broadcasts the transaction that transfers
852
// control of funds from a prior channel commitment transaction to the user's
853
// wallet. The outputs swept were previously time locked (either absolute or
854
// relative), but are not mature enough to sweep into the wallet.
855
func (u *UtxoNursery) sweepMatureOutputs(classHeight uint32,
856
        kgtnOutputs []kidOutput) error {
857

21✔
858
        utxnLog.Infof("Sweeping %v CSV-delayed outputs with sweep tx for "+
21✔
859
                "height %v", len(kgtnOutputs), classHeight)
21✔
860

21✔
861
        for _, output := range kgtnOutputs {
21✔
862
                // Create local copy to prevent pointer to loop variable to be
42✔
863
                // passed in with disastrous consequences.
21✔
864
                local := output
21✔
865

21✔
866
                // Calculate the deadline height and budget for this output.
21✔
867
                deadline, budget := u.decideDeadlineAndBudget(local)
21✔
868

21✔
869
                resultChan, err := u.cfg.SweepInput(&local, sweep.Params{
21✔
870
                        DeadlineHeight: deadline,
21✔
871
                        Budget:         budget,
21✔
872
                })
21✔
873
                if err != nil {
21✔
874
                        return err
21✔
875
                }
×
UNCOV
876
                u.wg.Add(1)
×
877
                go u.waitForSweepConf(classHeight, &local, resultChan)
21✔
878
        }
21✔
879

880
        return nil
881
}
21✔
882

883
// waitForSweepConf watches for the confirmation of a sweep transaction
884
// containing a batch of kindergarten outputs. Once confirmation has been
885
// received, the nursery will mark those outputs as fully graduated, and proceed
886
// to mark any mature channels as fully closed in channeldb.
887
// NOTE(conner): this method MUST be called as a go routine.
888
func (u *UtxoNursery) waitForSweepConf(classHeight uint32,
889
        output *kidOutput, resultChan chan sweep.Result) {
890

21✔
891
        defer u.wg.Done()
21✔
892

21✔
893
        select {
21✔
894
        case result, ok := <-resultChan:
21✔
895
                if !ok {
17✔
896
                        utxnLog.Errorf("Notification chan closed, can't" +
17✔
897
                                " advance graduating output")
×
898
                        return
×
899
                }
×
UNCOV
900

×
901
                // In case of a remote spend, still graduate the output. There
902
                // is no way to sweep it anymore.
903
                if result.Err == sweep.ErrRemoteSpend {
904
                        utxnLog.Infof("Output %v was spend by remote party",
17✔
905
                                output.OutPoint())
×
906
                        break
×
UNCOV
907
                }
×
908

909
                if result.Err != nil {
910
                        utxnLog.Errorf("Failed to sweep %v at "+
17✔
911
                                "height=%d", output.OutPoint(),
×
912
                                classHeight)
×
913
                        return
×
914
                }
×
UNCOV
915

×
916
        case <-u.quit:
917
                return
4✔
918
        }
4✔
919

920
        u.mu.Lock()
921
        defer u.mu.Unlock()
17✔
922

17✔
923
        // TODO(conner): add retry utxnLogic?
17✔
924

17✔
925
        // Mark the confirmed kindergarten output as graduated.
17✔
926
        if err := u.cfg.Store.GraduateKinder(classHeight, output); err != nil {
17✔
927
                utxnLog.Errorf("Unable to graduate kindergarten output %v: %v",
17✔
928
                        output.OutPoint(), err)
×
929
                return
×
930
        }
×
UNCOV
931

×
932
        utxnLog.Infof("Graduated kindergarten output from height=%d",
933
                classHeight)
17✔
934

17✔
935
        // Attempt to close the channel, only doing so if all of the channel's
17✔
936
        // outputs have been graduated.
17✔
937
        chanPoint := output.OriginChanPoint()
17✔
938
        if err := u.closeAndRemoveIfMature(chanPoint); err != nil {
17✔
939
                utxnLog.Errorf("Failed to close and remove channel %v",
17✔
940
                        *chanPoint)
×
941
                return
×
942
        }
×
UNCOV
943
}
×
944

945
// sweepCribOutput broadcasts the crib output's htlc timeout txn, and sets up a
946
// notification that will advance it to the kindergarten bucket upon
947
// confirmation.
948
func (u *UtxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) error {
949
        utxnLog.Infof("Publishing CLTV-delayed HTLC output using timeout tx "+
16✔
950
                "(txid=%v): %v", baby.timeoutTx.TxHash(),
16✔
951
                lnutils.SpewLogClosure(baby.timeoutTx))
16✔
952

16✔
953
        // We'll now broadcast the HTLC transaction, then wait for it to be
16✔
954
        // confirmed before transitioning it to kindergarten.
16✔
955
        label := labels.MakeLabel(labels.LabelTypeSweepTransaction, nil)
16✔
956
        err := u.cfg.PublishTransaction(baby.timeoutTx, label)
16✔
957

16✔
958
        // In case the tx does not meet mempool fee requirements we continue
16✔
959
        // because the tx is rebroadcasted in the background and there is
16✔
960
        // nothing we can do to bump this transaction anyways.
16✔
961
        if err != nil && !errors.Is(err, lnwallet.ErrDoubleSpend) &&
16✔
962
                !errors.Is(err, lnwallet.ErrMempoolFee) {
16✔
963

17✔
964
                utxnLog.Errorf("Unable to broadcast baby tx: "+
1✔
965
                        "%v, %v", err, spew.Sdump(baby.timeoutTx))
1✔
966
                return err
1✔
967
        }
1✔
968

1✔
969
        return u.registerTimeoutConf(baby, classHeight)
970
}
15✔
971

972
// registerTimeoutConf is responsible for subscribing to confirmation
973
// notification for an htlc timeout transaction. If successful, a goroutine
974
// will be spawned that will transition the provided baby output into the
975
// kindergarten state within the nursery store.
976
func (u *UtxoNursery) registerTimeoutConf(baby *babyOutput,
977
        heightHint uint32) error {
978

15✔
979
        birthTxID := baby.timeoutTx.TxHash()
15✔
980

15✔
981
        // Register for the confirmation of presigned htlc txn.
15✔
982
        confChan, err := u.cfg.Notifier.RegisterConfirmationsNtfn(
15✔
983
                &birthTxID, baby.timeoutTx.TxOut[0].PkScript, u.cfg.ConfDepth,
15✔
984
                heightHint,
15✔
985
        )
15✔
986
        if err != nil {
15✔
987
                return err
15✔
988
        }
×
UNCOV
989

×
990
        utxnLog.Infof("Htlc output %v registered for promotion "+
991
                "notification.", baby.OutPoint())
15✔
992

15✔
993
        u.wg.Add(1)
15✔
994
        go u.waitForTimeoutConf(baby, confChan)
15✔
995

15✔
996
        return nil
15✔
997
}
15✔
998

999
// waitForTimeoutConf watches for the confirmation of an htlc timeout
1000
// transaction, and attempts to move the htlc output from the crib bucket to the
1001
// kindergarten bucket upon success.
1002
func (u *UtxoNursery) waitForTimeoutConf(baby *babyOutput,
1003
        confChan *chainntnfs.ConfirmationEvent) {
1004

15✔
1005
        defer u.wg.Done()
15✔
1006

15✔
1007
        select {
15✔
1008
        case txConfirmation, ok := <-confChan.Confirmed:
15✔
1009
                if !ok {
10✔
1010
                        utxnLog.Debugf("Notification chan "+
10✔
1011
                                "closed, can't advance baby output %v",
×
1012
                                baby.OutPoint())
×
1013
                        return
×
1014
                }
×
UNCOV
1015

×
1016
                baby.SetConfHeight(txConfirmation.BlockHeight)
1017

10✔
1018
        case <-u.quit:
1019
                return
5✔
1020
        }
5✔
1021

1022
        u.mu.Lock()
1023
        defer u.mu.Unlock()
10✔
1024

10✔
1025
        // TODO(conner): add retry utxnLogic?
10✔
1026

10✔
1027
        err := u.cfg.Store.CribToKinder(baby)
10✔
1028
        if err != nil {
10✔
1029
                utxnLog.Errorf("Unable to move htlc output from "+
10✔
1030
                        "crib to kindergarten bucket: %v", err)
×
1031
                return
×
1032
        }
×
UNCOV
1033

×
1034
        utxnLog.Infof("Htlc output %v promoted to "+
1035
                "kindergarten", baby.OutPoint())
10✔
1036
}
10✔
1037

1038
// registerPreschoolConf is responsible for subscribing to the confirmation of
1039
// a commitment transaction, or an htlc success transaction for an incoming
1040
// HTLC on our commitment transaction.. If successful, the provided preschool
1041
// output will be moved persistently into the kindergarten state within the
1042
// nursery store.
1043
func (u *UtxoNursery) registerPreschoolConf(kid *kidOutput, heightHint uint32) error {
1044
        txID := kid.OutPoint().Hash
8✔
1045

8✔
1046
        // TODO(roasbeef): ensure we don't already have one waiting, need to
8✔
1047
        // de-duplicate
8✔
1048
        //  * need to do above?
8✔
1049

8✔
1050
        pkScript := kid.signDesc.Output.PkScript
8✔
1051
        confChan, err := u.cfg.Notifier.RegisterConfirmationsNtfn(
8✔
1052
                &txID, pkScript, u.cfg.ConfDepth, heightHint,
8✔
1053
        )
8✔
1054
        if err != nil {
8✔
1055
                return err
8✔
1056
        }
×
UNCOV
1057

×
1058
        var outputType string
1059
        if kid.isHtlc {
8✔
1060
                outputType = "HTLC"
16✔
1061
        } else {
8✔
1062
                outputType = "Commitment"
8✔
1063
        }
×
UNCOV
1064

×
1065
        utxnLog.Infof("%v outpoint %v registered for "+
1066
                "confirmation notification.", outputType, kid.OutPoint())
8✔
1067

8✔
1068
        u.wg.Add(1)
8✔
1069
        go u.waitForPreschoolConf(kid, confChan)
8✔
1070

8✔
1071
        return nil
8✔
1072
}
8✔
1073

1074
// waitForPreschoolConf is intended to be run as a goroutine that will wait until
1075
// a channel force close commitment transaction, or a second layer HTLC success
1076
// transaction has been included in a confirmed block. Once the transaction has
1077
// been confirmed (as reported by the Chain Notifier), waitForPreschoolConf
1078
// will delete the output from the "preschool" database bucket and atomically
1079
// add it to the "kindergarten" database bucket.  This is the second step in
1080
// the output incubation process.
1081
func (u *UtxoNursery) waitForPreschoolConf(kid *kidOutput,
1082
        confChan *chainntnfs.ConfirmationEvent) {
1083

8✔
1084
        defer u.wg.Done()
8✔
1085

8✔
1086
        select {
8✔
1087
        case txConfirmation, ok := <-confChan.Confirmed:
8✔
1088
                if !ok {
7✔
1089
                        utxnLog.Errorf("Notification chan "+
7✔
1090
                                "closed, can't advance output %v",
×
1091
                                kid.OutPoint())
×
1092
                        return
×
1093
                }
×
UNCOV
1094

×
1095
                kid.SetConfHeight(txConfirmation.BlockHeight)
1096

7✔
1097
        case <-u.quit:
1098
                return
1✔
1099
        }
1✔
1100

1101
        u.mu.Lock()
1102
        defer u.mu.Unlock()
7✔
1103

7✔
1104
        // TODO(conner): add retry utxnLogic?
7✔
1105

7✔
1106
        var outputType string
7✔
1107
        if kid.isHtlc {
7✔
1108
                outputType = "HTLC"
14✔
1109
        } else {
7✔
1110
                outputType = "Commitment"
7✔
1111
        }
×
UNCOV
1112

×
1113
        bestHeight := atomic.LoadUint32(&u.bestHeight)
1114
        err := u.cfg.Store.PreschoolToKinder(kid, bestHeight)
7✔
1115
        if err != nil {
7✔
1116
                utxnLog.Errorf("Unable to move %v output "+
7✔
1117
                        "from preschool to kindergarten bucket: %v",
×
1118
                        outputType, err)
×
1119
                return
×
1120
        }
×
UNCOV
1121
}
×
1122

1123
// RemoveChannel channel erases all entries from the channel bucket for the
1124
// provided channel point.
1125
func (u *UtxoNursery) RemoveChannel(op *wire.OutPoint) error {
UNCOV
1126
        return u.cfg.Store.RemoveChannel(op)
×
UNCOV
1127
}
×
UNCOV
1128

×
1129
// ContractMaturityReport is a report that details the maturity progress of a
1130
// particular force closed contract.
1131
type ContractMaturityReport struct {
1132
        // limboBalance is the total number of frozen coins within this
1133
        // contract.
1134
        LimboBalance btcutil.Amount
1135

1136
        // recoveredBalance is the total value that has been successfully swept
1137
        // back to the user's wallet.
1138
        RecoveredBalance btcutil.Amount
1139

1140
        // htlcs records a maturity report for each htlc output in this channel.
1141
        Htlcs []HtlcMaturityReport
1142
}
1143

1144
// HtlcMaturityReport provides a summary of a single htlc output, and is
1145
// embedded as party of the overarching ContractMaturityReport.
1146
type HtlcMaturityReport struct {
1147
        // Outpoint is the final output that will be swept back to the wallet.
1148
        Outpoint wire.OutPoint
1149

1150
        // Amount is the final value that will be swept in back to the wallet.
1151
        Amount btcutil.Amount
1152

1153
        // MaturityHeight is the absolute block height that this output will
1154
        // mature at.
1155
        MaturityHeight uint32
1156

1157
        // Stage indicates whether the htlc is in the CLTV-timeout stage (1) or
1158
        // the CSV-delay stage (2). A stage 1 htlc's maturity height will be set
1159
        // to its expiry height, while a stage 2 htlc's maturity height will be
1160
        // set to its confirmation height plus the maturity requirement.
1161
        Stage uint32
1162
}
1163

1164
// AddLimboStage1TimeoutHtlc adds an htlc crib output to the maturity report's
1165
// htlcs, and contributes its amount to the limbo balance.
1166
func (c *ContractMaturityReport) AddLimboStage1TimeoutHtlc(baby *babyOutput) {
1167
        c.LimboBalance += baby.Amount()
8✔
1168

8✔
1169
        // TODO(roasbeef): bool to indicate stage 1 vs stage 2?
8✔
1170
        c.Htlcs = append(c.Htlcs, HtlcMaturityReport{
8✔
1171
                Outpoint:       baby.OutPoint(),
8✔
1172
                Amount:         baby.Amount(),
8✔
1173
                MaturityHeight: baby.expiry,
8✔
1174
                Stage:          1,
8✔
1175
        })
8✔
1176
}
8✔
1177

8✔
1178
// AddLimboDirectHtlc adds a direct HTLC on the commitment transaction of the
1179
// remote party to the maturity report. This a CLTV time-locked output that
1180
// has or hasn't expired yet.
1181
func (c *ContractMaturityReport) AddLimboDirectHtlc(kid *kidOutput) {
1182
        c.LimboBalance += kid.Amount()
14✔
1183

14✔
1184
        htlcReport := HtlcMaturityReport{
14✔
1185
                Outpoint:       kid.OutPoint(),
14✔
1186
                Amount:         kid.Amount(),
14✔
1187
                MaturityHeight: kid.absoluteMaturity,
14✔
1188
                Stage:          2,
14✔
1189
        }
14✔
1190

14✔
1191
        c.Htlcs = append(c.Htlcs, htlcReport)
14✔
1192
}
14✔
1193

14✔
1194
// AddLimboStage1SuccessHtlcHtlc adds an htlc crib output to the maturity
1195
// report's set of HTLC's. We'll use this to report any incoming HTLC sweeps
1196
// where the second level transaction hasn't yet confirmed.
1197
func (c *ContractMaturityReport) AddLimboStage1SuccessHtlc(kid *kidOutput) {
1198
        c.LimboBalance += kid.Amount()
×
1199

×
1200
        c.Htlcs = append(c.Htlcs, HtlcMaturityReport{
×
1201
                Outpoint: kid.OutPoint(),
×
1202
                Amount:   kid.Amount(),
×
1203
                Stage:    1,
×
1204
        })
×
1205
}
×
UNCOV
1206

×
1207
// AddLimboStage2Htlc adds an htlc kindergarten output to the maturity report's
1208
// htlcs, and contributes its amount to the limbo balance.
1209
func (c *ContractMaturityReport) AddLimboStage2Htlc(kid *kidOutput) {
1210
        c.LimboBalance += kid.Amount()
10✔
1211

10✔
1212
        htlcReport := HtlcMaturityReport{
10✔
1213
                Outpoint: kid.OutPoint(),
10✔
1214
                Amount:   kid.Amount(),
10✔
1215
                Stage:    2,
10✔
1216
        }
10✔
1217

10✔
1218
        // If the confirmation height is set, then this means the first stage
10✔
1219
        // has been confirmed, and we know the final maturity height of the CSV
10✔
1220
        // delay.
10✔
1221
        if kid.ConfHeight() != 0 {
10✔
1222
                htlcReport.MaturityHeight = kid.ConfHeight() + kid.BlocksToMaturity()
20✔
1223
        }
10✔
1224

10✔
1225
        c.Htlcs = append(c.Htlcs, htlcReport)
1226
}
10✔
1227

1228
// AddRecoveredHtlc adds a graduate output to the maturity report's htlcs, and
1229
// contributes its amount to the recovered balance.
1230
func (c *ContractMaturityReport) AddRecoveredHtlc(kid *kidOutput) {
1231
        c.RecoveredBalance += kid.Amount()
×
1232

×
1233
        c.Htlcs = append(c.Htlcs, HtlcMaturityReport{
×
1234
                Outpoint:       kid.OutPoint(),
×
1235
                Amount:         kid.Amount(),
×
1236
                MaturityHeight: kid.ConfHeight() + kid.BlocksToMaturity(),
×
1237
        })
×
1238
}
×
UNCOV
1239

×
1240
// closeAndRemoveIfMature removes a particular channel from the channel index
1241
// if and only if all of its outputs have been marked graduated. If the channel
1242
// still has ungraduated outputs, the method will succeed without altering the
1243
// database state.
1244
func (u *UtxoNursery) closeAndRemoveIfMature(chanPoint *wire.OutPoint) error {
1245
        isMature, err := u.cfg.Store.IsMatureChannel(chanPoint)
17✔
1246
        if err == ErrContractNotFound {
17✔
1247
                return nil
17✔
UNCOV
1248
        } else if err != nil {
×
1249
                utxnLog.Errorf("Unable to determine maturity of "+
17✔
1250
                        "channel=%s", chanPoint)
×
1251
                return err
×
1252
        }
×
UNCOV
1253

×
1254
        // Nothing to do if we are still incubating.
1255
        if !isMature {
1256
                return nil
17✔
1257
        }
×
UNCOV
1258

×
1259
        // Now that the channel is fully closed, we remove the channel from the
1260
        // nursery store here. This preserves the invariant that we never remove
1261
        // a channel unless it is mature, as this is the only place the utxo
1262
        // nursery removes a channel.
1263
        if err := u.cfg.Store.RemoveChannel(chanPoint); err != nil {
1264
                utxnLog.Errorf("Unable to remove channel=%s from "+
17✔
1265
                        "nursery store: %v", chanPoint, err)
×
1266
                return err
×
1267
        }
×
UNCOV
1268

×
1269
        utxnLog.Infof("Removed channel %v from nursery store", chanPoint)
1270

17✔
1271
        return nil
17✔
1272
}
17✔
1273

1274
// babyOutput represents a two-stage CSV locked output, and is used to track
1275
// htlc outputs through incubation. The first stage requires broadcasting a
1276
// presigned timeout txn that spends from the CLTV locked output on the
1277
// commitment txn. A babyOutput is treated as a subset of CsvSpendableOutputs,
1278
// with the additional constraint that a transaction must be broadcast before
1279
// it can be spent. Each baby transaction embeds the kidOutput that can later
1280
// be used to spend the CSV output contained in the timeout txn.
1281
//
1282
// TODO(roasbeef): re-rename to timeout tx
1283
//   - create CltvCsvSpendableOutput
1284
type babyOutput struct {
1285
        // expiry is the absolute block height at which the secondLevelTx
1286
        // should be broadcast to the network.
1287
        //
1288
        // NOTE: This value will be zero if this is a baby output for a prior
1289
        // incoming HTLC.
1290
        expiry uint32
1291

1292
        // timeoutTx is a fully-signed transaction that, upon confirmation,
1293
        // transitions the htlc into the delay+claim stage.
1294
        timeoutTx *wire.MsgTx
1295

1296
        // kidOutput represents the CSV output to be swept from the
1297
        // secondLevelTx after it has been broadcast and confirmed.
1298
        kidOutput
1299
}
1300

1301
// makeBabyOutput constructs a baby output that wraps a future kidOutput. The
1302
// provided sign descriptors and witness types will be used once the output
1303
// reaches the delay and claim stage.
1304
func makeBabyOutput(chanPoint *wire.OutPoint,
1305
        htlcResolution *lnwallet.OutgoingHtlcResolution,
1306
        deadlineHeight fn.Option[int32]) babyOutput {
1307

11✔
1308
        htlcOutpoint := htlcResolution.ClaimOutpoint
11✔
1309
        blocksToMaturity := htlcResolution.CsvDelay
11✔
1310

11✔
1311
        isTaproot := txscript.IsPayToTaproot(
11✔
1312
                htlcResolution.SweepSignDesc.Output.PkScript,
11✔
1313
        )
11✔
1314

11✔
1315
        var witnessType input.StandardWitnessType
11✔
1316
        if isTaproot {
11✔
1317
                witnessType = input.TaprootHtlcOfferedTimeoutSecondLevel
11✔
UNCOV
1318
        } else {
×
1319
                witnessType = input.HtlcOfferedTimeoutSecondLevel
11✔
1320
        }
11✔
1321

11✔
1322
        kid := makeKidOutput(
1323
                &htlcOutpoint, chanPoint, blocksToMaturity, witnessType,
11✔
1324
                &htlcResolution.SweepSignDesc, 0, deadlineHeight,
11✔
1325
        )
11✔
1326

11✔
1327
        return babyOutput{
11✔
1328
                kidOutput: kid,
11✔
1329
                expiry:    htlcResolution.Expiry,
11✔
1330
                timeoutTx: htlcResolution.SignedTimeoutTx,
11✔
1331
        }
11✔
1332
}
11✔
1333

1334
// Encode writes the baby output to the given io.Writer.
1335
func (bo *babyOutput) Encode(w io.Writer) error {
1336
        var scratch [4]byte
17✔
1337
        byteOrder.PutUint32(scratch[:], bo.expiry)
17✔
1338
        if _, err := w.Write(scratch[:]); err != nil {
17✔
1339
                return err
17✔
1340
        }
×
UNCOV
1341

×
1342
        if err := bo.timeoutTx.Serialize(w); err != nil {
1343
                return err
17✔
1344
        }
×
UNCOV
1345

×
1346
        return bo.kidOutput.Encode(w)
1347
}
17✔
1348

1349
// Decode reconstructs a baby output using the provided io.Reader.
1350
func (bo *babyOutput) Decode(r io.Reader) error {
1351
        var scratch [4]byte
45✔
1352
        if _, err := r.Read(scratch[:]); err != nil {
45✔
1353
                return err
45✔
1354
        }
×
UNCOV
1355
        bo.expiry = byteOrder.Uint32(scratch[:])
×
1356

45✔
1357
        bo.timeoutTx = new(wire.MsgTx)
45✔
1358
        if err := bo.timeoutTx.Deserialize(r); err != nil {
45✔
1359
                return err
45✔
1360
        }
×
UNCOV
1361

×
1362
        return bo.kidOutput.Decode(r)
1363
}
45✔
1364

1365
// kidOutput represents an output that's waiting for a required blockheight
1366
// before its funds will be available to be moved into the user's wallet.  The
1367
// struct includes a WitnessGenerator closure which will be used to generate
1368
// the witness required to sweep the output once it's mature.
1369
//
1370
// TODO(roasbeef): rename to immatureOutput?
1371
type kidOutput struct {
1372
        breachedOutput
1373

1374
        originChanPoint wire.OutPoint
1375

1376
        // isHtlc denotes if this kid output is an HTLC output or not. This
1377
        // value will be used to determine how to report this output within the
1378
        // nursery report.
1379
        isHtlc bool
1380

1381
        // blocksToMaturity is the relative CSV delay required after initial
1382
        // confirmation of the commitment transaction before we can sweep this
1383
        // output.
1384
        //
1385
        // NOTE: This will be set for: commitment outputs, and incoming HTLC's.
1386
        // Otherwise, this will be zero. It will also be non-zero for
1387
        // commitment types which requires confirmed spends.
1388
        blocksToMaturity uint32
1389

1390
        // absoluteMaturity is the absolute height that this output will be
1391
        // mature at. In order to sweep the output after this height, the
1392
        // locktime of sweep transaction will need to be set to this value.
1393
        //
1394
        // NOTE: This will only be set for: outgoing HTLC's on the commitment
1395
        // transaction of the remote party.
1396
        absoluteMaturity uint32
1397

1398
        // deadlineHeight is the absolute height that this output should be
1399
        // confirmed at. For an incoming HTLC, this is the CLTV expiry height.
1400
        // For outgoing HTLC, this is its corresponding incoming HTLC's CLTV
1401
        // expiry height.
1402
        deadlineHeight fn.Option[int32]
1403
}
1404

1405
func makeKidOutput(outpoint, originChanPoint *wire.OutPoint,
1406
        blocksToMaturity uint32, witnessType input.StandardWitnessType,
1407
        signDescriptor *input.SignDescriptor, absoluteMaturity uint32,
1408
        deadlineHeight fn.Option[int32]) kidOutput {
1409

18✔
1410
        // This is an HTLC either if it's an incoming HTLC on our commitment
18✔
1411
        // transaction, or is an outgoing HTLC on the commitment transaction of
18✔
1412
        // the remote peer.
18✔
1413
        isHtlc := (witnessType == input.HtlcAcceptedSuccessSecondLevel ||
18✔
1414
                witnessType == input.TaprootHtlcAcceptedSuccessSecondLevel ||
18✔
1415
                witnessType == input.TaprootHtlcOfferedRemoteTimeout ||
18✔
1416
                witnessType == input.HtlcOfferedRemoteTimeout)
18✔
1417

18✔
1418
        // heightHint can be safely set to zero here, because after this
18✔
1419
        // function returns, nursery will set a proper confirmation height in
18✔
1420
        // waitForTimeoutConf or waitForPreschoolConf.
18✔
1421
        heightHint := uint32(0)
18✔
1422

18✔
1423
        return kidOutput{
18✔
1424
                breachedOutput: makeBreachedOutput(
18✔
1425
                        outpoint, witnessType, nil, signDescriptor, heightHint,
18✔
1426
                ),
18✔
1427
                isHtlc:           isHtlc,
18✔
1428
                originChanPoint:  *originChanPoint,
18✔
1429
                blocksToMaturity: blocksToMaturity,
18✔
1430
                absoluteMaturity: absoluteMaturity,
18✔
1431
                deadlineHeight:   deadlineHeight,
18✔
1432
        }
18✔
1433
}
18✔
1434

18✔
1435
func (k *kidOutput) OriginChanPoint() *wire.OutPoint {
18✔
1436
        return &k.originChanPoint
1437
}
165✔
1438

165✔
1439
func (k *kidOutput) BlocksToMaturity() uint32 {
165✔
1440
        return k.blocksToMaturity
1441
}
161✔
1442

161✔
1443
func (k *kidOutput) SetConfHeight(height uint32) {
161✔
1444
        k.confHeight = height
1445
}
17✔
1446

17✔
1447
func (k *kidOutput) ConfHeight() uint32 {
17✔
1448
        return k.confHeight
1449
}
161✔
1450

161✔
1451
func (k *kidOutput) RequiredLockTime() (uint32, bool) {
161✔
1452
        return k.absoluteMaturity, k.absoluteMaturity > 0
UNCOV
1453
}
×
UNCOV
1454

×
UNCOV
1455
// Encode converts a KidOutput struct into a form suitable for on-disk database
×
1456
// storage. Note that the signDescriptor struct field is included so that the
1457
// output's witness can be generated by createSweepTx() when the output becomes
1458
// spendable.
1459
func (k *kidOutput) Encode(w io.Writer) error {
1460
        var scratch [8]byte
1461
        byteOrder.PutUint64(scratch[:], uint64(k.Amount()))
77✔
1462
        if _, err := w.Write(scratch[:]); err != nil {
77✔
1463
                return err
77✔
1464
        }
77✔
UNCOV
1465

×
UNCOV
1466
        op := k.OutPoint()
×
1467
        if err := writeOutpoint(w, &op); err != nil {
1468
                return err
77✔
1469
        }
77✔
UNCOV
1470
        if err := writeOutpoint(w, k.OriginChanPoint()); err != nil {
×
1471
                return err
×
1472
        }
77✔
UNCOV
1473

×
UNCOV
1474
        if err := binary.Write(w, byteOrder, k.isHtlc); err != nil {
×
1475
                return err
1476
        }
77✔
UNCOV
1477

×
UNCOV
1478
        byteOrder.PutUint32(scratch[:4], k.BlocksToMaturity())
×
1479
        if _, err := w.Write(scratch[:4]); err != nil {
1480
                return err
77✔
1481
        }
77✔
UNCOV
1482

×
UNCOV
1483
        byteOrder.PutUint32(scratch[:4], k.absoluteMaturity)
×
1484
        if _, err := w.Write(scratch[:4]); err != nil {
1485
                return err
77✔
1486
        }
77✔
UNCOV
1487

×
UNCOV
1488
        byteOrder.PutUint32(scratch[:4], k.ConfHeight())
×
1489
        if _, err := w.Write(scratch[:4]); err != nil {
1490
                return err
77✔
1491
        }
77✔
UNCOV
1492

×
UNCOV
1493
        byteOrder.PutUint16(scratch[:2], uint16(k.witnessType))
×
1494
        if _, err := w.Write(scratch[:2]); err != nil {
1495
                return err
77✔
1496
        }
77✔
UNCOV
1497

×
UNCOV
1498
        if err := input.WriteSignDescriptor(w, k.SignDesc()); err != nil {
×
1499
                return err
1500
        }
77✔
UNCOV
1501

×
UNCOV
1502
        if k.SignDesc().ControlBlock == nil {
×
1503
                return nil
1504
        }
154✔
1505

77✔
1506
        // If this is a taproot output, then it'll also have a control block,
77✔
1507
        // so we'll go ahead and write that now.
1508
        return wire.WriteVarBytes(w, 1000, k.SignDesc().ControlBlock)
1509
}
UNCOV
1510

×
1511
// Decode takes a byte array representation of a kidOutput and converts it to an
1512
// struct. Note that the witnessFunc method isn't added during deserialization
1513
// and must be added later based on the value of the witnessType field.
1514
func (k *kidOutput) Decode(r io.Reader) error {
1515
        var scratch [8]byte
1516

132✔
1517
        if _, err := r.Read(scratch[:]); err != nil {
132✔
1518
                return err
132✔
1519
        }
132✔
UNCOV
1520
        k.amt = btcutil.Amount(byteOrder.Uint64(scratch[:]))
×
UNCOV
1521

×
1522
        if err := readOutpoint(io.LimitReader(r, 40), &k.outpoint); err != nil {
132✔
1523
                return err
132✔
1524
        }
132✔
UNCOV
1525

×
UNCOV
1526
        err := readOutpoint(io.LimitReader(r, 40), &k.originChanPoint)
×
1527
        if err != nil {
1528
                return err
132✔
1529
        }
132✔
UNCOV
1530

×
UNCOV
1531
        if err := binary.Read(r, byteOrder, &k.isHtlc); err != nil {
×
1532
                return err
1533
        }
132✔
UNCOV
1534

×
UNCOV
1535
        if _, err := r.Read(scratch[:4]); err != nil {
×
1536
                return err
1537
        }
132✔
UNCOV
1538
        k.blocksToMaturity = byteOrder.Uint32(scratch[:4])
×
UNCOV
1539

×
1540
        if _, err := r.Read(scratch[:4]); err != nil {
132✔
1541
                return err
132✔
1542
        }
132✔
UNCOV
1543
        k.absoluteMaturity = byteOrder.Uint32(scratch[:4])
×
UNCOV
1544

×
1545
        if _, err := r.Read(scratch[:4]); err != nil {
132✔
1546
                return err
132✔
1547
        }
132✔
UNCOV
1548
        k.confHeight = byteOrder.Uint32(scratch[:4])
×
UNCOV
1549

×
1550
        if _, err := r.Read(scratch[:2]); err != nil {
132✔
1551
                return err
132✔
1552
        }
132✔
UNCOV
1553
        k.witnessType = input.StandardWitnessType(byteOrder.Uint16(scratch[:2]))
×
UNCOV
1554

×
1555
        if err := input.ReadSignDescriptor(r, &k.signDesc); err != nil {
132✔
1556
                return err
132✔
1557
        }
132✔
UNCOV
1558

×
UNCOV
1559
        // If there's anything left in the reader, then this is a taproot
×
1560
        // output that also wrote a control block.
1561
        ctrlBlock, err := wire.ReadVarBytes(r, 0, 1000, "control block")
1562
        switch {
1563
        // If there're no bytes remaining, then we'll return early.
132✔
1564
        case errors.Is(err, io.EOF):
132✔
1565
                fallthrough
1566
        case errors.Is(err, io.ErrUnexpectedEOF):
132✔
1567
                return nil
132✔
1568

132✔
1569
        case err != nil:
132✔
1570
                return err
UNCOV
1571
        }
×
UNCOV
1572

×
1573
        k.signDesc.ControlBlock = ctrlBlock
1574

1575
        return nil
×
UNCOV
1576
}
×
UNCOV
1577

×
1578
// TODO(bvu): copied from channeldb, remove repetition
1579
func writeOutpoint(w io.Writer, o *wire.OutPoint) error {
1580
        // TODO(roasbeef): make all scratch buffers on the stack
1581
        scratch := make([]byte, 4)
627✔
1582

627✔
1583
        // TODO(roasbeef): write raw 32 bytes instead of wasting the extra
627✔
1584
        // byte.
627✔
1585
        if err := wire.WriteVarBytes(w, 0, o.Hash[:]); err != nil {
627✔
1586
                return err
627✔
1587
        }
627✔
UNCOV
1588

×
UNCOV
1589
        byteOrder.PutUint32(scratch, o.Index)
×
1590
        _, err := w.Write(scratch)
1591
        return err
627✔
1592
}
627✔
1593

627✔
1594
// TODO(bvu): copied from channeldb, remove repetition
1595
func readOutpoint(r io.Reader, o *wire.OutPoint) error {
1596
        scratch := make([]byte, 4)
1597

397✔
1598
        txid, err := wire.ReadVarBytes(r, 0, 32, "prevout")
397✔
1599
        if err != nil {
397✔
1600
                return err
397✔
1601
        }
397✔
UNCOV
1602
        copy(o.Hash[:], txid)
×
UNCOV
1603

×
1604
        if _, err := r.Read(scratch); err != nil {
397✔
1605
                return err
397✔
1606
        }
397✔
UNCOV
1607
        o.Index = byteOrder.Uint32(scratch)
×
UNCOV
1608

×
1609
        return nil
397✔
1610
}
397✔
1611

397✔
1612
// Compile-time constraint to ensure kidOutput implements the
1613
// Input interface.
1614

1615
var _ input.Input = (*kidOutput)(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