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

lightningnetwork / lnd / 13536249039

26 Feb 2025 03:42AM UTC coverage: 57.462% (-1.4%) from 58.835%
13536249039

Pull #8453

github

Roasbeef
peer: update chooseDeliveryScript to gen script if needed

In this commit, we update `chooseDeliveryScript` to generate a new
script if needed. This allows us to fold in a few other lines that
always followed this function into this expanded function.

The tests have been updated accordingly.
Pull Request #8453: [4/4] - multi: integrate new rbf coop close FSM into the existing peer flow

275 of 1318 new or added lines in 22 files covered. (20.86%)

19521 existing lines in 257 files now uncovered.

103858 of 180741 relevant lines covered (57.46%)

24750.23 hits per line

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

78.66
/channeldb/db.go
1
package channeldb
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "fmt"
7
        "net"
8
        "os"
9
        "testing"
10

11
        "github.com/btcsuite/btcd/btcec/v2"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/btcsuite/btcwallet/walletdb"
14
        "github.com/go-errors/errors"
15
        mig "github.com/lightningnetwork/lnd/channeldb/migration"
16
        "github.com/lightningnetwork/lnd/channeldb/migration12"
17
        "github.com/lightningnetwork/lnd/channeldb/migration13"
18
        "github.com/lightningnetwork/lnd/channeldb/migration16"
19
        "github.com/lightningnetwork/lnd/channeldb/migration20"
20
        "github.com/lightningnetwork/lnd/channeldb/migration21"
21
        "github.com/lightningnetwork/lnd/channeldb/migration23"
22
        "github.com/lightningnetwork/lnd/channeldb/migration24"
23
        "github.com/lightningnetwork/lnd/channeldb/migration25"
24
        "github.com/lightningnetwork/lnd/channeldb/migration26"
25
        "github.com/lightningnetwork/lnd/channeldb/migration27"
26
        "github.com/lightningnetwork/lnd/channeldb/migration29"
27
        "github.com/lightningnetwork/lnd/channeldb/migration30"
28
        "github.com/lightningnetwork/lnd/channeldb/migration31"
29
        "github.com/lightningnetwork/lnd/channeldb/migration32"
30
        "github.com/lightningnetwork/lnd/channeldb/migration33"
31
        "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
32
        "github.com/lightningnetwork/lnd/clock"
33
        graphdb "github.com/lightningnetwork/lnd/graph/db"
34
        "github.com/lightningnetwork/lnd/invoices"
35
        "github.com/lightningnetwork/lnd/kvdb"
36
        "github.com/lightningnetwork/lnd/lnwire"
37
        "github.com/stretchr/testify/require"
38
)
39

40
const (
41
        dbName = "channel.db"
42
)
43

44
var (
45
        // ErrDryRunMigrationOK signals that a migration executed successful,
46
        // but we intentionally did not commit the result.
47
        ErrDryRunMigrationOK = errors.New("dry run migration successful")
48

49
        // ErrFinalHtlcsBucketNotFound signals that the top-level final htlcs
50
        // bucket does not exist.
51
        ErrFinalHtlcsBucketNotFound = errors.New("final htlcs bucket not " +
52
                "found")
53

54
        // ErrFinalChannelBucketNotFound signals that the channel bucket for
55
        // final htlc outcomes does not exist.
56
        ErrFinalChannelBucketNotFound = errors.New("final htlcs channel " +
57
                "bucket not found")
58
)
59

60
// migration is a function which takes a prior outdated version of the database
61
// instances and mutates the key/bucket structure to arrive at a more
62
// up-to-date version of the database.
63
type migration func(tx kvdb.RwTx) error
64

65
// mandatoryVersion defines a db version that must be applied before the lnd
66
// starts.
67
type mandatoryVersion struct {
68
        number    uint32
69
        migration migration
70
}
71

72
// MigrationConfig is an interface combines the config interfaces of all
73
// optional migrations.
74
type MigrationConfig interface {
75
        migration30.MigrateRevLogConfig
76
}
77

78
// MigrationConfigImpl is a super set of all the various migration configs and
79
// an implementation of MigrationConfig.
80
type MigrationConfigImpl struct {
81
        migration30.MigrateRevLogConfigImpl
82
}
83

84
// optionalMigration defines an optional migration function. When a migration
85
// is optional, it usually involves a large scale of changes that might touch
86
// millions of keys. Due to OOM concern, the update cannot be safely done
87
// within one db transaction. Thus, for optional migrations, they must take the
88
// db backend and construct transactions as needed.
89
type optionalMigration func(db kvdb.Backend, cfg MigrationConfig) error
90

91
// optionalVersion defines a db version that can be optionally applied. When
92
// applying migrations, we must apply all the mandatory migrations first before
93
// attempting optional ones.
94
type optionalVersion struct {
95
        name      string
96
        migration optionalMigration
97
}
98

99
var (
100
        // dbVersions is storing all mandatory versions of database. If current
101
        // version of database don't match with latest version this list will
102
        // be used for retrieving all migration function that are need to apply
103
        // to the current db.
104
        dbVersions = []mandatoryVersion{
105
                {
106
                        // The base DB version requires no migration.
107
                        number:    0,
108
                        migration: nil,
109
                },
110
                {
111
                        // The version of the database where two new indexes
112
                        // for the update time of node and channel updates were
113
                        // added.
114
                        number:    1,
115
                        migration: migration_01_to_11.MigrateNodeAndEdgeUpdateIndex,
116
                },
117
                {
118
                        // The DB version that added the invoice event time
119
                        // series.
120
                        number:    2,
121
                        migration: migration_01_to_11.MigrateInvoiceTimeSeries,
122
                },
123
                {
124
                        // The DB version that updated the embedded invoice in
125
                        // outgoing payments to match the new format.
126
                        number:    3,
127
                        migration: migration_01_to_11.MigrateInvoiceTimeSeriesOutgoingPayments,
128
                },
129
                {
130
                        // The version of the database where every channel
131
                        // always has two entries in the edges bucket. If
132
                        // a policy is unknown, this will be represented
133
                        // by a special byte sequence.
134
                        number:    4,
135
                        migration: migration_01_to_11.MigrateEdgePolicies,
136
                },
137
                {
138
                        // The DB version where we persist each attempt to send
139
                        // an HTLC to a payment hash, and track whether the
140
                        // payment is in-flight, succeeded, or failed.
141
                        number:    5,
142
                        migration: migration_01_to_11.PaymentStatusesMigration,
143
                },
144
                {
145
                        // The DB version that properly prunes stale entries
146
                        // from the edge update index.
147
                        number:    6,
148
                        migration: migration_01_to_11.MigratePruneEdgeUpdateIndex,
149
                },
150
                {
151
                        // The DB version that migrates the ChannelCloseSummary
152
                        // to a format where optional fields are indicated with
153
                        // boolean flags.
154
                        number:    7,
155
                        migration: migration_01_to_11.MigrateOptionalChannelCloseSummaryFields,
156
                },
157
                {
158
                        // The DB version that changes the gossiper's message
159
                        // store keys to account for the message's type and
160
                        // ShortChannelID.
161
                        number:    8,
162
                        migration: migration_01_to_11.MigrateGossipMessageStoreKeys,
163
                },
164
                {
165
                        // The DB version where the payments and payment
166
                        // statuses are moved to being stored in a combined
167
                        // bucket.
168
                        number:    9,
169
                        migration: migration_01_to_11.MigrateOutgoingPayments,
170
                },
171
                {
172
                        // The DB version where we started to store legacy
173
                        // payload information for all routes, as well as the
174
                        // optional TLV records.
175
                        number:    10,
176
                        migration: migration_01_to_11.MigrateRouteSerialization,
177
                },
178
                {
179
                        // Add invoice htlc and cltv delta fields.
180
                        number:    11,
181
                        migration: migration_01_to_11.MigrateInvoices,
182
                },
183
                {
184
                        // Migrate to TLV invoice bodies, add payment address
185
                        // and features, remove receipt.
186
                        number:    12,
187
                        migration: migration12.MigrateInvoiceTLV,
188
                },
189
                {
190
                        // Migrate to multi-path payments.
191
                        number:    13,
192
                        migration: migration13.MigrateMPP,
193
                },
194
                {
195
                        // Initialize payment address index and begin using it
196
                        // as the default index, falling back to payment hash
197
                        // index.
198
                        number:    14,
199
                        migration: mig.CreateTLB(payAddrIndexBucket),
200
                },
201
                {
202
                        // Initialize payment index bucket which will be used
203
                        // to index payments by sequence number. This index will
204
                        // be used to allow more efficient ListPayments queries.
205
                        number:    15,
206
                        migration: mig.CreateTLB(paymentsIndexBucket),
207
                },
208
                {
209
                        // Add our existing payments to the index bucket created
210
                        // in migration 15.
211
                        number:    16,
212
                        migration: migration16.MigrateSequenceIndex,
213
                },
214
                {
215
                        // Create a top level bucket which will store extra
216
                        // information about channel closes.
217
                        number:    17,
218
                        migration: mig.CreateTLB(closeSummaryBucket),
219
                },
220
                {
221
                        // Create a top level bucket which holds information
222
                        // about our peers.
223
                        number:    18,
224
                        migration: mig.CreateTLB(peersBucket),
225
                },
226
                {
227
                        // Create a top level bucket which holds outpoint
228
                        // information.
229
                        number:    19,
230
                        migration: mig.CreateTLB(outpointBucket),
231
                },
232
                {
233
                        // Migrate some data to the outpoint index.
234
                        number:    20,
235
                        migration: migration20.MigrateOutpointIndex,
236
                },
237
                {
238
                        // Migrate to length prefixed wire messages everywhere
239
                        // in the database.
240
                        number:    21,
241
                        migration: migration21.MigrateDatabaseWireMessages,
242
                },
243
                {
244
                        // Initialize set id index so that invoices can be
245
                        // queried by individual htlc sets.
246
                        number:    22,
247
                        migration: mig.CreateTLB(setIDIndexBucket),
248
                },
249
                {
250
                        number:    23,
251
                        migration: migration23.MigrateHtlcAttempts,
252
                },
253
                {
254
                        // Remove old forwarding packages of closed channels.
255
                        number:    24,
256
                        migration: migration24.MigrateFwdPkgCleanup,
257
                },
258
                {
259
                        // Save the initial local/remote balances in channel
260
                        // info.
261
                        number:    25,
262
                        migration: migration25.MigrateInitialBalances,
263
                },
264
                {
265
                        // Migrate the initial local/remote balance fields into
266
                        // tlv records.
267
                        number:    26,
268
                        migration: migration26.MigrateBalancesToTlvRecords,
269
                },
270
                {
271
                        // Patch the initial local/remote balance fields with
272
                        // empty values for historical channels.
273
                        number:    27,
274
                        migration: migration27.MigrateHistoricalBalances,
275
                },
276
                {
277
                        number:    28,
278
                        migration: mig.CreateTLB(chanIDBucket),
279
                },
280
                {
281
                        number:    29,
282
                        migration: migration29.MigrateChanID,
283
                },
284
                {
285
                        // Removes the "sweeper-last-tx" bucket. Although we
286
                        // do not have a mandatory version 30 we skip this
287
                        // version because its naming is already used for the
288
                        // first optional migration.
289
                        number:    31,
290
                        migration: migration31.DeleteLastPublishedTxTLB,
291
                },
292
                {
293
                        number:    32,
294
                        migration: migration32.MigrateMCRouteSerialisation,
295
                },
296
                {
297
                        number:    33,
298
                        migration: migration33.MigrateMCStoreNameSpacedResults,
299
                },
300
        }
301

302
        // optionalVersions stores all optional migrations that are applied
303
        // after dbVersions.
304
        //
305
        // NOTE: optional migrations must be fault-tolerant and re-run already
306
        // migrated data must be noop, which means the migration must be able
307
        // to determine its state.
308
        optionalVersions = []optionalVersion{
309
                {
310
                        name: "prune revocation log",
311
                        migration: func(db kvdb.Backend,
312
                                cfg MigrationConfig) error {
×
313

×
314
                                return migration30.MigrateRevocationLog(db, cfg)
×
315
                        },
×
316
                },
317
        }
318

319
        // Big endian is the preferred byte order, due to cursor scans over
320
        // integer keys iterating in order.
321
        byteOrder = binary.BigEndian
322

323
        // channelOpeningStateBucket is the database bucket used to store the
324
        // channelOpeningState for each channel that is currently in the process
325
        // of being opened.
326
        channelOpeningStateBucket = []byte("channelOpeningState")
327
)
328

329
// DB is the primary datastore for the lnd daemon. The database stores
330
// information related to nodes, routing data, open/closed channels, fee
331
// schedules, and reputation data.
332
type DB struct {
333
        kvdb.Backend
334

335
        // channelStateDB separates all DB operations on channel state.
336
        channelStateDB *ChannelStateDB
337

338
        dbPath                    string
339
        clock                     clock.Clock
340
        dryRun                    bool
341
        keepFailedPaymentAttempts bool
342
        storeFinalHtlcResolutions bool
343

344
        // noRevLogAmtData if true, means that commitment transaction amount
345
        // data should not be stored in the revocation log.
346
        noRevLogAmtData bool
347
}
348

349
// OpenForTesting opens or creates a channeldb to be used for tests. Any
350
// necessary schemas migrations due to updates will take place as necessary.
351
func OpenForTesting(t testing.TB, dbPath string,
352
        modifiers ...OptionModifier) *DB {
1,455✔
353

1,455✔
354
        backend, err := kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
1,455✔
355
                DBPath:            dbPath,
1,455✔
356
                DBFileName:        dbName,
1,455✔
357
                NoFreelistSync:    true,
1,455✔
358
                AutoCompact:       false,
1,455✔
359
                AutoCompactMinAge: kvdb.DefaultBoltAutoCompactMinAge,
1,455✔
360
                DBTimeout:         kvdb.DefaultDBTimeout,
1,455✔
361
        })
1,455✔
362
        require.NoError(t, err)
1,455✔
363

1,455✔
364
        db, err := CreateWithBackend(backend, modifiers...)
1,455✔
365
        require.NoError(t, err)
1,455✔
366

1,455✔
367
        db.dbPath = dbPath
1,455✔
368

1,455✔
369
        t.Cleanup(func() {
2,910✔
370
                require.NoError(t, db.Close())
1,455✔
371
        })
1,455✔
372

373
        return db
1,455✔
374
}
375

376
// CreateWithBackend creates channeldb instance using the passed kvdb.Backend.
377
// Any necessary schemas migrations due to updates will take place as necessary.
378
func CreateWithBackend(backend kvdb.Backend, modifiers ...OptionModifier) (*DB,
379
        error) {
1,747✔
380

1,747✔
381
        opts := DefaultOptions()
1,747✔
382
        for _, modifier := range modifiers {
1,924✔
383
                modifier(&opts)
177✔
384
        }
177✔
385

386
        if !opts.NoMigration {
3,494✔
387
                if err := initChannelDB(backend); err != nil {
1,748✔
388
                        return nil, err
1✔
389
                }
1✔
390
        }
391

392
        chanDB := &DB{
1,746✔
393
                Backend: backend,
1,746✔
394
                channelStateDB: &ChannelStateDB{
1,746✔
395
                        linkNodeDB: &LinkNodeDB{
1,746✔
396
                                backend: backend,
1,746✔
397
                        },
1,746✔
398
                        backend: backend,
1,746✔
399
                },
1,746✔
400
                clock:                     opts.clock,
1,746✔
401
                dryRun:                    opts.dryRun,
1,746✔
402
                keepFailedPaymentAttempts: opts.keepFailedPaymentAttempts,
1,746✔
403
                storeFinalHtlcResolutions: opts.storeFinalHtlcResolutions,
1,746✔
404
                noRevLogAmtData:           opts.NoRevLogAmtData,
1,746✔
405
        }
1,746✔
406

1,746✔
407
        // Set the parent pointer (only used in tests).
1,746✔
408
        chanDB.channelStateDB.parent = chanDB
1,746✔
409

1,746✔
410
        // Synchronize the version of database and apply migrations if needed.
1,746✔
411
        if !opts.NoMigration {
3,492✔
412
                if err := chanDB.syncVersions(dbVersions); err != nil {
1,747✔
413
                        backend.Close()
1✔
414
                        return nil, err
1✔
415
                }
1✔
416

417
                // Grab the optional migration config.
418
                omc := opts.OptionalMiragtionConfig
1,745✔
419
                if err := chanDB.applyOptionalVersions(omc); err != nil {
1,745✔
420
                        backend.Close()
×
421
                        return nil, err
×
422
                }
×
423
        }
424

425
        return chanDB, nil
1,745✔
426
}
427

428
// Path returns the file path to the channel database.
429
func (d *DB) Path() string {
197✔
430
        return d.dbPath
197✔
431
}
197✔
432

433
var dbTopLevelBuckets = [][]byte{
434
        openChannelBucket,
435
        closedChannelBucket,
436
        forwardingLogBucket,
437
        fwdPackagesKey,
438
        invoiceBucket,
439
        payAddrIndexBucket,
440
        setIDIndexBucket,
441
        paymentsIndexBucket,
442
        peersBucket,
443
        nodeInfoBucket,
444
        metaBucket,
445
        closeSummaryBucket,
446
        outpointBucket,
447
        chanIDBucket,
448
        historicalChannelBucket,
449
}
450

451
// Wipe completely deletes all saved state within all used buckets within the
452
// database. The deletion is done in a single transaction, therefore this
453
// operation is fully atomic.
454
func (d *DB) Wipe() error {
177✔
455
        err := kvdb.Update(d, func(tx kvdb.RwTx) error {
354✔
456
                for _, tlb := range dbTopLevelBuckets {
2,832✔
457
                        err := tx.DeleteTopLevelBucket(tlb)
2,655✔
458
                        if err != nil && err != kvdb.ErrBucketNotFound {
2,655✔
459
                                return err
×
460
                        }
×
461
                }
462
                return nil
177✔
463
        }, func() {})
177✔
464
        if err != nil {
177✔
465
                return err
×
466
        }
×
467

468
        return initChannelDB(d.Backend)
177✔
469
}
470

471
// initChannelDB creates and initializes a fresh version of channeldb. In the
472
// case that the target path has not yet been created or doesn't yet exist, then
473
// the path is created. Additionally, all required top-level buckets used within
474
// the database are created.
475
func initChannelDB(db kvdb.Backend) error {
1,924✔
476
        err := kvdb.Update(db, func(tx kvdb.RwTx) error {
3,848✔
477
                // Check if DB was marked as inactive with a tomb stone.
1,924✔
478
                if err := EnsureNoTombstone(tx); err != nil {
1,925✔
479
                        return err
1✔
480
                }
1✔
481

482
                meta := &Meta{}
1,923✔
483
                // Check if DB is already initialized.
1,923✔
484
                err := FetchMeta(meta, tx)
1,923✔
485
                if err == nil {
2,130✔
486
                        return nil
207✔
487
                }
207✔
488

489
                for _, tlb := range dbTopLevelBuckets {
27,456✔
490
                        if _, err := tx.CreateTopLevelBucket(tlb); err != nil {
25,740✔
491
                                return err
×
492
                        }
×
493
                }
494

495
                meta.DbVersionNumber = getLatestDBVersion(dbVersions)
1,716✔
496
                return putMeta(meta, tx)
1,716✔
497
        }, func() {})
1,924✔
498
        if err != nil {
1,925✔
499
                return fmt.Errorf("unable to create new channeldb: %w", err)
1✔
500
        }
1✔
501

502
        return nil
1,923✔
503
}
504

505
// fileExists returns true if the file exists, and false otherwise.
506
func fileExists(path string) bool {
1✔
507
        if _, err := os.Stat(path); err != nil {
1✔
508
                if os.IsNotExist(err) {
×
509
                        return false
×
510
                }
×
511
        }
512

513
        return true
1✔
514
}
515

516
// ChannelStateDB is a database that keeps track of all channel state.
517
type ChannelStateDB struct {
518
        // linkNodeDB separates all DB operations on LinkNodes.
519
        linkNodeDB *LinkNodeDB
520

521
        // parent holds a pointer to the "main" channeldb.DB object. This is
522
        // only used for testing and should never be used in production code.
523
        // For testing use the ChannelStateDB.GetParentDB() function to retrieve
524
        // this pointer.
525
        parent *DB
526

527
        // backend points to the actual backend holding the channel state
528
        // database. This may be a real backend or a cache middleware.
529
        backend kvdb.Backend
530
}
531

532
// GetParentDB returns the "main" channeldb.DB object that is the owner of this
533
// ChannelStateDB instance. Use this function only in tests where passing around
534
// pointers makes testing less readable. Never to be used in production code!
535
func (c *ChannelStateDB) GetParentDB() *DB {
517✔
536
        return c.parent
517✔
537
}
517✔
538

539
// LinkNodeDB returns the current instance of the link node database.
UNCOV
540
func (c *ChannelStateDB) LinkNodeDB() *LinkNodeDB {
×
UNCOV
541
        return c.linkNodeDB
×
UNCOV
542
}
×
543

544
// FetchOpenChannels starts a new database transaction and returns all stored
545
// currently active/open channels associated with the target nodeID. In the case
546
// that no active channels are known to have been created with this node, then a
547
// zero-length slice is returned.
548
func (c *ChannelStateDB) FetchOpenChannels(nodeID *btcec.PublicKey) (
549
        []*OpenChannel, error) {
254✔
550

254✔
551
        var channels []*OpenChannel
254✔
552
        err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
508✔
553
                var err error
254✔
554
                channels, err = c.fetchOpenChannels(tx, nodeID)
254✔
555
                return err
254✔
556
        }, func() {
508✔
557
                channels = nil
254✔
558
        })
254✔
559

560
        return channels, err
254✔
561
}
562

563
// fetchOpenChannels uses and existing database transaction and returns all
564
// stored currently active/open channels associated with the target nodeID. In
565
// the case that no active channels are known to have been created with this
566
// node, then a zero-length slice is returned.
567
func (c *ChannelStateDB) fetchOpenChannels(tx kvdb.RTx,
568
        nodeID *btcec.PublicKey) ([]*OpenChannel, error) {
261✔
569

261✔
570
        // Get the bucket dedicated to storing the metadata for open channels.
261✔
571
        openChanBucket := tx.ReadBucket(openChannelBucket)
261✔
572
        if openChanBucket == nil {
261✔
573
                return nil, nil
×
574
        }
×
575

576
        // Within this top level bucket, fetch the bucket dedicated to storing
577
        // open channel data specific to the remote node.
578
        pub := nodeID.SerializeCompressed()
261✔
579
        nodeChanBucket := openChanBucket.NestedReadBucket(pub)
261✔
580
        if nodeChanBucket == nil {
313✔
581
                return nil, nil
52✔
582
        }
52✔
583

584
        // Next, we'll need to go down an additional layer in order to retrieve
585
        // the channels for each chain the node knows of.
586
        var channels []*OpenChannel
209✔
587
        err := nodeChanBucket.ForEach(func(chainHash, v []byte) error {
418✔
588
                // If there's a value, it's not a bucket so ignore it.
209✔
589
                if v != nil {
209✔
590
                        return nil
×
591
                }
×
592

593
                // If we've found a valid chainhash bucket, then we'll retrieve
594
                // that so we can extract all the channels.
595
                chainBucket := nodeChanBucket.NestedReadBucket(chainHash)
209✔
596
                if chainBucket == nil {
209✔
597
                        return fmt.Errorf("unable to read bucket for chain=%x",
×
598
                                chainHash[:])
×
599
                }
×
600

601
                // Finally, we both of the necessary buckets retrieved, fetch
602
                // all the active channels related to this node.
603
                nodeChannels, err := c.fetchNodeChannels(chainBucket)
209✔
604
                if err != nil {
209✔
605
                        return fmt.Errorf("unable to read channel for "+
×
606
                                "chain_hash=%x, node_key=%x: %v",
×
607
                                chainHash[:], pub, err)
×
608
                }
×
609

610
                channels = append(channels, nodeChannels...)
209✔
611
                return nil
209✔
612
        })
613

614
        return channels, err
209✔
615
}
616

617
// fetchNodeChannels retrieves all active channels from the target chainBucket
618
// which is under a node's dedicated channel bucket. This function is typically
619
// used to fetch all the active channels related to a particular node.
620
func (c *ChannelStateDB) fetchNodeChannels(chainBucket kvdb.RBucket) (
621
        []*OpenChannel, error) {
715✔
622

715✔
623
        var channels []*OpenChannel
715✔
624

715✔
625
        // A node may have channels on several chains, so for each known chain,
715✔
626
        // we'll extract all the channels.
715✔
627
        err := chainBucket.ForEach(func(chanPoint, v []byte) error {
1,491✔
628
                // If there's a value, it's not a bucket so ignore it.
776✔
629
                if v != nil {
776✔
630
                        return nil
×
631
                }
×
632

633
                // Once we've found a valid channel bucket, we'll extract it
634
                // from the node's chain bucket.
635
                chanBucket := chainBucket.NestedReadBucket(chanPoint)
776✔
636

776✔
637
                var outPoint wire.OutPoint
776✔
638
                err := graphdb.ReadOutpoint(
776✔
639
                        bytes.NewReader(chanPoint), &outPoint,
776✔
640
                )
776✔
641
                if err != nil {
776✔
642
                        return err
×
643
                }
×
644
                oChannel, err := fetchOpenChannel(chanBucket, &outPoint)
776✔
645
                if err != nil {
776✔
646
                        return fmt.Errorf("unable to read channel data for "+
×
647
                                "chan_point=%v: %w", outPoint, err)
×
648
                }
×
649
                oChannel.Db = c
776✔
650

776✔
651
                channels = append(channels, oChannel)
776✔
652

776✔
653
                return nil
776✔
654
        })
655
        if err != nil {
715✔
656
                return nil, err
×
657
        }
×
658

659
        return channels, nil
715✔
660
}
661

662
// FetchChannel attempts to locate a channel specified by the passed channel
663
// point. If the channel cannot be found, then an error will be returned.
664
func (c *ChannelStateDB) FetchChannel(chanPoint wire.OutPoint) (*OpenChannel,
665
        error) {
8✔
666

8✔
667
        var targetChanPoint bytes.Buffer
8✔
668
        err := graphdb.WriteOutpoint(&targetChanPoint, &chanPoint)
8✔
669
        if err != nil {
8✔
670
                return nil, err
×
671
        }
×
672

673
        targetChanPointBytes := targetChanPoint.Bytes()
8✔
674
        selector := func(chainBkt walletdb.ReadBucket) ([]byte, *wire.OutPoint,
8✔
675
                error) {
15✔
676

7✔
677
                return targetChanPointBytes, &chanPoint, nil
7✔
678
        }
7✔
679

680
        return c.channelScanner(nil, selector)
8✔
681
}
682

683
// FetchChannelByID attempts to locate a channel specified by the passed channel
684
// ID. If the channel cannot be found, then an error will be returned.
685
// Optionally an existing db tx can be supplied.
686
func (c *ChannelStateDB) FetchChannelByID(tx kvdb.RTx, id lnwire.ChannelID) (
687
        *OpenChannel, error) {
2✔
688

2✔
689
        selector := func(chainBkt walletdb.ReadBucket) ([]byte, *wire.OutPoint,
2✔
690
                error) {
4✔
691

2✔
692
                var (
2✔
693
                        targetChanPointBytes []byte
2✔
694
                        targetChanPoint      *wire.OutPoint
2✔
695

2✔
696
                        // errChanFound is used to signal that the channel has
2✔
697
                        // been found so that iteration through the DB buckets
2✔
698
                        // can stop.
2✔
699
                        errChanFound = errors.New("channel found")
2✔
700
                )
2✔
701
                err := chainBkt.ForEach(func(k, _ []byte) error {
4✔
702
                        var outPoint wire.OutPoint
2✔
703
                        err := graphdb.ReadOutpoint(
2✔
704
                                bytes.NewReader(k), &outPoint,
2✔
705
                        )
2✔
706
                        if err != nil {
2✔
707
                                return err
×
708
                        }
×
709

710
                        chanID := lnwire.NewChanIDFromOutPoint(outPoint)
2✔
711
                        if chanID != id {
3✔
712
                                return nil
1✔
713
                        }
1✔
714

715
                        targetChanPoint = &outPoint
1✔
716
                        targetChanPointBytes = k
1✔
717

1✔
718
                        return errChanFound
1✔
719
                })
720
                if err != nil && !errors.Is(err, errChanFound) {
2✔
721
                        return nil, nil, err
×
722
                }
×
723
                if targetChanPoint == nil {
3✔
724
                        return nil, nil, ErrChannelNotFound
1✔
725
                }
1✔
726

727
                return targetChanPointBytes, targetChanPoint, nil
1✔
728
        }
729

730
        return c.channelScanner(tx, selector)
2✔
731
}
732

733
// channelSelector describes a function that takes a chain-hash bucket from
734
// within the open-channel DB and returns the wanted channel point bytes, and
735
// channel point. It must return the ErrChannelNotFound error if the wanted
736
// channel is not in the given bucket.
737
type channelSelector func(chainBkt walletdb.ReadBucket) ([]byte, *wire.OutPoint,
738
        error)
739

740
// channelScanner will traverse the DB to each chain-hash bucket of each node
741
// pub-key bucket in the open-channel-bucket. The chanSelector will then be used
742
// to fetch the wanted channel outpoint from the chain bucket.
743
func (c *ChannelStateDB) channelScanner(tx kvdb.RTx,
744
        chanSelect channelSelector) (*OpenChannel, error) {
10✔
745

10✔
746
        var (
10✔
747
                targetChan *OpenChannel
10✔
748

10✔
749
                // errChanFound is used to signal that the channel has been
10✔
750
                // found so that iteration through the DB buckets can stop.
10✔
751
                errChanFound = errors.New("channel found")
10✔
752
        )
10✔
753

10✔
754
        // chanScan will traverse the following bucket structure:
10✔
755
        //  * nodePub => chainHash => chanPoint
10✔
756
        //
10✔
757
        // At each level we go one further, ensuring that we're traversing the
10✔
758
        // proper key (that's actually a bucket). By only reading the bucket
10✔
759
        // structure and skipping fully decoding each channel, we save a good
10✔
760
        // bit of CPU as we don't need to do things like decompress public
10✔
761
        // keys.
10✔
762
        chanScan := func(tx kvdb.RTx) error {
20✔
763
                // Get the bucket dedicated to storing the metadata for open
10✔
764
                // channels.
10✔
765
                openChanBucket := tx.ReadBucket(openChannelBucket)
10✔
766
                if openChanBucket == nil {
10✔
767
                        return ErrNoActiveChannels
×
768
                }
×
769

770
                // Within the node channel bucket, are the set of node pubkeys
771
                // we have channels with, we don't know the entire set, so we'll
772
                // check them all.
773
                return openChanBucket.ForEach(func(nodePub, v []byte) error {
19✔
774
                        // Ensure that this is a key the same size as a pubkey,
9✔
775
                        // and also that it leads directly to a bucket.
9✔
776
                        if len(nodePub) != 33 || v != nil {
9✔
777
                                return nil
×
778
                        }
×
779

780
                        nodeChanBucket := openChanBucket.NestedReadBucket(
9✔
781
                                nodePub,
9✔
782
                        )
9✔
783
                        if nodeChanBucket == nil {
9✔
784
                                return nil
×
785
                        }
×
786

787
                        // The next layer down is all the chains that this node
788
                        // has channels on with us.
789
                        return nodeChanBucket.ForEach(func(chainHash,
9✔
790
                                v []byte) error {
18✔
791

9✔
792
                                // If there's a value, it's not a bucket so
9✔
793
                                // ignore it.
9✔
794
                                if v != nil {
9✔
795
                                        return nil
×
796
                                }
×
797

798
                                chainBucket := nodeChanBucket.NestedReadBucket(
9✔
799
                                        chainHash,
9✔
800
                                )
9✔
801
                                if chainBucket == nil {
9✔
802
                                        return fmt.Errorf("unable to read "+
×
803
                                                "bucket for chain=%x",
×
804
                                                chainHash)
×
805
                                }
×
806

807
                                // Finally, we reach the leaf bucket that stores
808
                                // all the chanPoints for this node.
809
                                targetChanBytes, chanPoint, err := chanSelect(
9✔
810
                                        chainBucket,
9✔
811
                                )
9✔
812
                                if errors.Is(err, ErrChannelNotFound) {
10✔
813
                                        return nil
1✔
814
                                } else if err != nil {
9✔
815
                                        return err
×
816
                                }
×
817

818
                                chanBucket := chainBucket.NestedReadBucket(
8✔
819
                                        targetChanBytes,
8✔
820
                                )
8✔
821
                                if chanBucket == nil {
11✔
822
                                        return nil
3✔
823
                                }
3✔
824

825
                                channel, err := fetchOpenChannel(
5✔
826
                                        chanBucket, chanPoint,
5✔
827
                                )
5✔
828
                                if err != nil {
5✔
829
                                        return err
×
830
                                }
×
831

832
                                targetChan = channel
5✔
833
                                targetChan.Db = c
5✔
834

5✔
835
                                return errChanFound
5✔
836
                        })
837
                })
838
        }
839

840
        var err error
10✔
841
        if tx == nil {
20✔
842
                err = kvdb.View(c.backend, chanScan, func() {})
20✔
843
        } else {
×
844
                err = chanScan(tx)
×
845
        }
×
846
        if err != nil && !errors.Is(err, errChanFound) {
10✔
847
                return nil, err
×
848
        }
×
849

850
        if targetChan != nil {
15✔
851
                return targetChan, nil
5✔
852
        }
5✔
853

854
        // If we can't find the channel, then we return with an error, as we
855
        // have nothing to back up.
856
        return nil, ErrChannelNotFound
5✔
857
}
858

859
// FetchAllChannels attempts to retrieve all open channels currently stored
860
// within the database, including pending open, fully open and channels waiting
861
// for a closing transaction to confirm.
862
func (c *ChannelStateDB) FetchAllChannels() ([]*OpenChannel, error) {
563✔
863
        return fetchChannels(c)
563✔
864
}
563✔
865

866
// FetchAllOpenChannels will return all channels that have the funding
867
// transaction confirmed, and is not waiting for a closing transaction to be
868
// confirmed.
869
func (c *ChannelStateDB) FetchAllOpenChannels() ([]*OpenChannel, error) {
405✔
870
        return fetchChannels(
405✔
871
                c,
405✔
872
                pendingChannelFilter(false),
405✔
873
                waitingCloseFilter(false),
405✔
874
        )
405✔
875
}
405✔
876

877
// FetchPendingChannels will return channels that have completed the process of
878
// generating and broadcasting funding transactions, but whose funding
879
// transactions have yet to be confirmed on the blockchain.
880
func (c *ChannelStateDB) FetchPendingChannels() ([]*OpenChannel, error) {
79✔
881
        return fetchChannels(c,
79✔
882
                pendingChannelFilter(true),
79✔
883
                waitingCloseFilter(false),
79✔
884
        )
79✔
885
}
79✔
886

887
// FetchWaitingCloseChannels will return all channels that have been opened,
888
// but are now waiting for a closing transaction to be confirmed.
889
//
890
// NOTE: This includes channels that are also pending to be opened.
891
func (c *ChannelStateDB) FetchWaitingCloseChannels() ([]*OpenChannel, error) {
1✔
892
        return fetchChannels(
1✔
893
                c, waitingCloseFilter(true),
1✔
894
        )
1✔
895
}
1✔
896

897
// fetchChannelsFilter applies a filter to channels retrieved in fetchchannels.
898
// A set of filters can be combined to filter across multiple dimensions.
899
type fetchChannelsFilter func(channel *OpenChannel) bool
900

901
// pendingChannelFilter returns a filter based on whether channels are pending
902
// (ie, their funding transaction still needs to confirm). If pending is false,
903
// channels with confirmed funding transactions are returned.
904
func pendingChannelFilter(pending bool) fetchChannelsFilter {
493✔
905
        return func(channel *OpenChannel) bool {
715✔
906
                return channel.IsPending == pending
222✔
907
        }
222✔
908
}
909

910
// waitingCloseFilter returns a filter which filters channels based on whether
911
// they are awaiting the confirmation of their closing transaction. If waiting
912
// close is true, channels that have had their closing tx broadcast are
913
// included. If it is false, channels that are not awaiting confirmation of
914
// their close transaction are returned.
915
func waitingCloseFilter(waitingClose bool) fetchChannelsFilter {
491✔
916
        return func(channel *OpenChannel) bool {
699✔
917
                // If the channel is in any other state than Default,
208✔
918
                // then it means it is waiting to be closed.
208✔
919
                channelWaitingClose :=
208✔
920
                        channel.ChanStatus() != ChanStatusDefault
208✔
921

208✔
922
                // Include the channel if it matches the value for
208✔
923
                // waiting close that we are filtering on.
208✔
924
                return channelWaitingClose == waitingClose
208✔
925
        }
208✔
926
}
927

928
// fetchChannels attempts to retrieve channels currently stored in the
929
// database. It takes a set of filters which are applied to each channel to
930
// obtain a set of channels with the desired set of properties. Only channels
931
// which have a true value returned for *all* of the filters will be returned.
932
// If no filters are provided, every channel in the open channels bucket will
933
// be returned.
934
func fetchChannels(c *ChannelStateDB, filters ...fetchChannelsFilter) (
935
        []*OpenChannel, error) {
1,060✔
936

1,060✔
937
        var channels []*OpenChannel
1,060✔
938

1,060✔
939
        err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
2,120✔
940
                // Get the bucket dedicated to storing the metadata for open
1,060✔
941
                // channels.
1,060✔
942
                openChanBucket := tx.ReadBucket(openChannelBucket)
1,060✔
943
                if openChanBucket == nil {
1,060✔
944
                        return ErrNoActiveChannels
×
945
                }
×
946

947
                // Next, fetch the bucket dedicated to storing metadata related
948
                // to all nodes. All keys within this bucket are the serialized
949
                // public keys of all our direct counterparties.
950
                nodeMetaBucket := tx.ReadBucket(nodeInfoBucket)
1,060✔
951
                if nodeMetaBucket == nil {
1,060✔
952
                        return fmt.Errorf("node bucket not created")
×
953
                }
×
954

955
                // Finally for each node public key in the bucket, fetch all
956
                // the channels related to this particular node.
957
                return nodeMetaBucket.ForEach(func(k, v []byte) error {
1,566✔
958
                        nodeChanBucket := openChanBucket.NestedReadBucket(k)
506✔
959
                        if nodeChanBucket == nil {
506✔
960
                                return nil
×
961
                        }
×
962

963
                        return nodeChanBucket.ForEach(func(chainHash, v []byte) error {
1,012✔
964
                                // If there's a value, it's not a bucket so
506✔
965
                                // ignore it.
506✔
966
                                if v != nil {
506✔
967
                                        return nil
×
968
                                }
×
969

970
                                // If we've found a valid chainhash bucket,
971
                                // then we'll retrieve that so we can extract
972
                                // all the channels.
973
                                chainBucket := nodeChanBucket.NestedReadBucket(
506✔
974
                                        chainHash,
506✔
975
                                )
506✔
976
                                if chainBucket == nil {
506✔
977
                                        return fmt.Errorf("unable to read "+
×
978
                                                "bucket for chain=%x", chainHash[:])
×
979
                                }
×
980

981
                                nodeChans, err := c.fetchNodeChannels(chainBucket)
506✔
982
                                if err != nil {
506✔
983
                                        return fmt.Errorf("unable to read "+
×
984
                                                "channel for chain_hash=%x, "+
×
985
                                                "node_key=%x: %v", chainHash[:], k, err)
×
986
                                }
×
987
                                for _, channel := range nodeChans {
1,049✔
988
                                        // includeChannel indicates whether the channel
543✔
989
                                        // meets the criteria specified by our filters.
543✔
990
                                        includeChannel := true
543✔
991

543✔
992
                                        // Run through each filter and check whether the
543✔
993
                                        // channel should be included.
543✔
994
                                        for _, f := range filters {
973✔
995
                                                // If the channel fails the filter, set
430✔
996
                                                // includeChannel to false and don't bother
430✔
997
                                                // checking the remaining filters.
430✔
998
                                                if !f(channel) {
455✔
999
                                                        includeChannel = false
25✔
1000
                                                        break
25✔
1001
                                                }
1002
                                        }
1003

1004
                                        // If the channel passed every filter, include it in
1005
                                        // our set of channels.
1006
                                        if includeChannel {
1,061✔
1007
                                                channels = append(channels, channel)
518✔
1008
                                        }
518✔
1009
                                }
1010
                                return nil
506✔
1011
                        })
1012

1013
                })
1014
        }, func() {
1,060✔
1015
                channels = nil
1,060✔
1016
        })
1,060✔
1017
        if err != nil {
1,060✔
1018
                return nil, err
×
1019
        }
×
1020

1021
        return channels, nil
1,060✔
1022
}
1023

1024
// FetchClosedChannels attempts to fetch all closed channels from the database.
1025
// The pendingOnly bool toggles if channels that aren't yet fully closed should
1026
// be returned in the response or not. When a channel was cooperatively closed,
1027
// it becomes fully closed after a single confirmation.  When a channel was
1028
// forcibly closed, it will become fully closed after _all_ the pending funds
1029
// (if any) have been swept.
1030
func (c *ChannelStateDB) FetchClosedChannels(pendingOnly bool) (
1031
        []*ChannelCloseSummary, error) {
500✔
1032

500✔
1033
        var chanSummaries []*ChannelCloseSummary
500✔
1034

500✔
1035
        if err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
1,000✔
1036
                closeBucket := tx.ReadBucket(closedChannelBucket)
500✔
1037
                if closeBucket == nil {
500✔
1038
                        return ErrNoClosedChannels
×
1039
                }
×
1040

1041
                return closeBucket.ForEach(func(chanID []byte, summaryBytes []byte) error {
520✔
1042
                        summaryReader := bytes.NewReader(summaryBytes)
20✔
1043
                        chanSummary, err := deserializeCloseChannelSummary(summaryReader)
20✔
1044
                        if err != nil {
20✔
1045
                                return err
×
1046
                        }
×
1047

1048
                        // If the query specified to only include pending
1049
                        // channels, then we'll skip any channels which aren't
1050
                        // currently pending.
1051
                        if !chanSummary.IsPending && pendingOnly {
21✔
1052
                                return nil
1✔
1053
                        }
1✔
1054

1055
                        chanSummaries = append(chanSummaries, chanSummary)
19✔
1056
                        return nil
19✔
1057
                })
1058
        }, func() {
500✔
1059
                chanSummaries = nil
500✔
1060
        }); err != nil {
500✔
1061
                return nil, err
×
1062
        }
×
1063

1064
        return chanSummaries, nil
500✔
1065
}
1066

1067
// ErrClosedChannelNotFound signals that a closed channel could not be found in
1068
// the channeldb.
1069
var ErrClosedChannelNotFound = errors.New("unable to find closed channel summary")
1070

1071
// FetchClosedChannel queries for a channel close summary using the channel
1072
// point of the channel in question.
1073
func (c *ChannelStateDB) FetchClosedChannel(chanID *wire.OutPoint) (
1074
        *ChannelCloseSummary, error) {
3✔
1075

3✔
1076
        var chanSummary *ChannelCloseSummary
3✔
1077
        if err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
6✔
1078
                closeBucket := tx.ReadBucket(closedChannelBucket)
3✔
1079
                if closeBucket == nil {
3✔
1080
                        return ErrClosedChannelNotFound
×
1081
                }
×
1082

1083
                var b bytes.Buffer
3✔
1084
                var err error
3✔
1085
                if err = graphdb.WriteOutpoint(&b, chanID); err != nil {
3✔
1086
                        return err
×
1087
                }
×
1088

1089
                summaryBytes := closeBucket.Get(b.Bytes())
3✔
1090
                if summaryBytes == nil {
4✔
1091
                        return ErrClosedChannelNotFound
1✔
1092
                }
1✔
1093

1094
                summaryReader := bytes.NewReader(summaryBytes)
2✔
1095
                chanSummary, err = deserializeCloseChannelSummary(summaryReader)
2✔
1096

2✔
1097
                return err
2✔
1098
        }, func() {
3✔
1099
                chanSummary = nil
3✔
1100
        }); err != nil {
4✔
1101
                return nil, err
1✔
1102
        }
1✔
1103

1104
        return chanSummary, nil
2✔
1105
}
1106

1107
// FetchClosedChannelForID queries for a channel close summary using the
1108
// channel ID of the channel in question.
1109
func (c *ChannelStateDB) FetchClosedChannelForID(cid lnwire.ChannelID) (
1110
        *ChannelCloseSummary, error) {
102✔
1111

102✔
1112
        var chanSummary *ChannelCloseSummary
102✔
1113
        if err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
204✔
1114
                closeBucket := tx.ReadBucket(closedChannelBucket)
102✔
1115
                if closeBucket == nil {
102✔
1116
                        return ErrClosedChannelNotFound
×
1117
                }
×
1118

1119
                // The first 30 bytes of the channel ID and outpoint will be
1120
                // equal.
1121
                cursor := closeBucket.ReadCursor()
102✔
1122
                op, c := cursor.Seek(cid[:30])
102✔
1123

102✔
1124
                // We scan over all possible candidates for this channel ID.
102✔
1125
                for ; op != nil && bytes.Compare(cid[:30], op[:30]) <= 0; op, c = cursor.Next() {
5,354✔
1126
                        var outPoint wire.OutPoint
5,252✔
1127
                        err := graphdb.ReadOutpoint(
5,252✔
1128
                                bytes.NewReader(op), &outPoint,
5,252✔
1129
                        )
5,252✔
1130
                        if err != nil {
5,252✔
1131
                                return err
×
1132
                        }
×
1133

1134
                        // If the found outpoint does not correspond to this
1135
                        // channel ID, we continue.
1136
                        if !cid.IsChanPoint(&outPoint) {
10,403✔
1137
                                continue
5,151✔
1138
                        }
1139

1140
                        // Deserialize the close summary and return.
1141
                        r := bytes.NewReader(c)
101✔
1142
                        chanSummary, err = deserializeCloseChannelSummary(r)
101✔
1143
                        if err != nil {
101✔
1144
                                return err
×
1145
                        }
×
1146

1147
                        return nil
101✔
1148
                }
1149
                return ErrClosedChannelNotFound
1✔
1150
        }, func() {
102✔
1151
                chanSummary = nil
102✔
1152
        }); err != nil {
103✔
1153
                return nil, err
1✔
1154
        }
1✔
1155

1156
        return chanSummary, nil
101✔
1157
}
1158

1159
// MarkChanFullyClosed marks a channel as fully closed within the database. A
1160
// channel should be marked as fully closed if the channel was initially
1161
// cooperatively closed and it's reached a single confirmation, or after all
1162
// the pending funds in a channel that has been forcibly closed have been
1163
// swept.
1164
func (c *ChannelStateDB) MarkChanFullyClosed(chanPoint *wire.OutPoint) error {
7✔
1165
        var (
7✔
1166
                openChannels  []*OpenChannel
7✔
1167
                pruneLinkNode *btcec.PublicKey
7✔
1168
        )
7✔
1169
        err := kvdb.Update(c.backend, func(tx kvdb.RwTx) error {
14✔
1170
                var b bytes.Buffer
7✔
1171
                if err := graphdb.WriteOutpoint(&b, chanPoint); err != nil {
7✔
1172
                        return err
×
1173
                }
×
1174

1175
                chanID := b.Bytes()
7✔
1176

7✔
1177
                closedChanBucket, err := tx.CreateTopLevelBucket(
7✔
1178
                        closedChannelBucket,
7✔
1179
                )
7✔
1180
                if err != nil {
7✔
1181
                        return err
×
1182
                }
×
1183

1184
                chanSummaryBytes := closedChanBucket.Get(chanID)
7✔
1185
                if chanSummaryBytes == nil {
7✔
1186
                        return fmt.Errorf("no closed channel for "+
×
1187
                                "chan_point=%v found", chanPoint)
×
1188
                }
×
1189

1190
                chanSummaryReader := bytes.NewReader(chanSummaryBytes)
7✔
1191
                chanSummary, err := deserializeCloseChannelSummary(
7✔
1192
                        chanSummaryReader,
7✔
1193
                )
7✔
1194
                if err != nil {
7✔
1195
                        return err
×
1196
                }
×
1197

1198
                chanSummary.IsPending = false
7✔
1199

7✔
1200
                var newSummary bytes.Buffer
7✔
1201
                err = serializeChannelCloseSummary(&newSummary, chanSummary)
7✔
1202
                if err != nil {
7✔
1203
                        return err
×
1204
                }
×
1205

1206
                err = closedChanBucket.Put(chanID, newSummary.Bytes())
7✔
1207
                if err != nil {
7✔
1208
                        return err
×
1209
                }
×
1210

1211
                // Now that the channel is closed, we'll check if we have any
1212
                // other open channels with this peer. If we don't we'll
1213
                // garbage collect it to ensure we don't establish persistent
1214
                // connections to peers without open channels.
1215
                pruneLinkNode = chanSummary.RemotePub
7✔
1216
                openChannels, err = c.fetchOpenChannels(
7✔
1217
                        tx, pruneLinkNode,
7✔
1218
                )
7✔
1219
                if err != nil {
7✔
1220
                        return fmt.Errorf("unable to fetch open channels for "+
×
1221
                                "peer %x: %v",
×
1222
                                pruneLinkNode.SerializeCompressed(), err)
×
1223
                }
×
1224

1225
                return nil
7✔
1226
        }, func() {
7✔
1227
                openChannels = nil
7✔
1228
                pruneLinkNode = nil
7✔
1229
        })
7✔
1230
        if err != nil {
7✔
1231
                return err
×
1232
        }
×
1233

1234
        // Decide whether we want to remove the link node, based upon the number
1235
        // of still open channels.
1236
        return c.pruneLinkNode(openChannels, pruneLinkNode)
7✔
1237
}
1238

1239
// pruneLinkNode determines whether we should garbage collect a link node from
1240
// the database due to no longer having any open channels with it. If there are
1241
// any left, then this acts as a no-op.
1242
func (c *ChannelStateDB) pruneLinkNode(openChannels []*OpenChannel,
1243
        remotePub *btcec.PublicKey) error {
7✔
1244

7✔
1245
        if len(openChannels) > 0 {
7✔
UNCOV
1246
                return nil
×
UNCOV
1247
        }
×
1248

1249
        log.Infof("Pruning link node %x with zero open channels from database",
7✔
1250
                remotePub.SerializeCompressed())
7✔
1251

7✔
1252
        return c.linkNodeDB.DeleteLinkNode(remotePub)
7✔
1253
}
1254

1255
// PruneLinkNodes attempts to prune all link nodes found within the database
1256
// with whom we no longer have any open channels with.
UNCOV
1257
func (c *ChannelStateDB) PruneLinkNodes() error {
×
UNCOV
1258
        allLinkNodes, err := c.linkNodeDB.FetchAllLinkNodes()
×
UNCOV
1259
        if err != nil {
×
1260
                return err
×
1261
        }
×
1262

UNCOV
1263
        for _, linkNode := range allLinkNodes {
×
UNCOV
1264
                var (
×
UNCOV
1265
                        openChannels []*OpenChannel
×
UNCOV
1266
                        linkNode     = linkNode
×
UNCOV
1267
                )
×
UNCOV
1268
                err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
×
UNCOV
1269
                        var err error
×
UNCOV
1270
                        openChannels, err = c.fetchOpenChannels(
×
UNCOV
1271
                                tx, linkNode.IdentityPub,
×
UNCOV
1272
                        )
×
UNCOV
1273
                        return err
×
UNCOV
1274
                }, func() {
×
UNCOV
1275
                        openChannels = nil
×
UNCOV
1276
                })
×
UNCOV
1277
                if err != nil {
×
1278
                        return err
×
1279
                }
×
1280

UNCOV
1281
                err = c.pruneLinkNode(openChannels, linkNode.IdentityPub)
×
UNCOV
1282
                if err != nil {
×
1283
                        return err
×
1284
                }
×
1285
        }
1286

UNCOV
1287
        return nil
×
1288
}
1289

1290
// ChannelShell is a shell of a channel that is meant to be used for channel
1291
// recovery purposes. It contains a minimal OpenChannel instance along with
1292
// addresses for that target node.
1293
type ChannelShell struct {
1294
        // NodeAddrs the set of addresses that this node has known to be
1295
        // reachable at in the past.
1296
        NodeAddrs []net.Addr
1297

1298
        // Chan is a shell of an OpenChannel, it contains only the items
1299
        // required to restore the channel on disk.
1300
        Chan *OpenChannel
1301
}
1302

1303
// RestoreChannelShells is a method that allows the caller to reconstruct the
1304
// state of an OpenChannel from the ChannelShell. We'll attempt to write the
1305
// new channel to disk, create a LinkNode instance with the passed node
1306
// addresses, and finally create an edge within the graph for the channel as
1307
// well. This method is idempotent, so repeated calls with the same set of
1308
// channel shells won't modify the database after the initial call.
1309
func (c *ChannelStateDB) RestoreChannelShells(channelShells ...*ChannelShell) error {
1✔
1310
        err := kvdb.Update(c.backend, func(tx kvdb.RwTx) error {
2✔
1311
                for _, channelShell := range channelShells {
2✔
1312
                        channel := channelShell.Chan
1✔
1313

1✔
1314
                        // When we make a channel, we mark that the channel has
1✔
1315
                        // been restored, this will signal to other sub-systems
1✔
1316
                        // to not attempt to use the channel as if it was a
1✔
1317
                        // regular one.
1✔
1318
                        channel.chanStatus |= ChanStatusRestored
1✔
1319

1✔
1320
                        // First, we'll attempt to create a new open channel
1✔
1321
                        // and link node for this channel. If the channel
1✔
1322
                        // already exists, then in order to ensure this method
1✔
1323
                        // is idempotent, we'll continue to the next step.
1✔
1324
                        channel.Db = c
1✔
1325
                        err := syncNewChannel(
1✔
1326
                                tx, channel, channelShell.NodeAddrs,
1✔
1327
                        )
1✔
1328
                        if err != nil {
1✔
UNCOV
1329
                                return err
×
UNCOV
1330
                        }
×
1331
                }
1332

1333
                return nil
1✔
1334
        }, func() {})
1✔
1335
        if err != nil {
1✔
UNCOV
1336
                return err
×
UNCOV
1337
        }
×
1338

1339
        return nil
1✔
1340
}
1341

1342
// AddrsForNode consults the channel database for all addresses known to the
1343
// passed node public key. The returned boolean indicates if the given node is
1344
// unknown to the channel DB or not.
1345
//
1346
// NOTE: this is part of the AddrSource interface.
1347
func (d *DB) AddrsForNode(nodePub *btcec.PublicKey) (bool, []net.Addr, error) {
1✔
1348
        linkNode, err := d.channelStateDB.linkNodeDB.FetchLinkNode(nodePub)
1✔
1349
        // Only if the error is something other than ErrNodeNotFound do we
1✔
1350
        // return it.
1✔
1351
        switch {
1✔
1352
        case err != nil && !errors.Is(err, ErrNodeNotFound):
×
1353
                return false, nil, err
×
1354

1355
        case errors.Is(err, ErrNodeNotFound):
×
1356
                return false, nil, nil
×
1357
        }
1358

1359
        return true, linkNode.Addresses, nil
1✔
1360
}
1361

1362
// AbandonChannel attempts to remove the target channel from the open channel
1363
// database. If the channel was already removed (has a closed channel entry),
1364
// then we'll return a nil error. Otherwise, we'll insert a new close summary
1365
// into the database.
1366
func (c *ChannelStateDB) AbandonChannel(chanPoint *wire.OutPoint,
1367
        bestHeight uint32) error {
4✔
1368

4✔
1369
        // With the chanPoint constructed, we'll attempt to find the target
4✔
1370
        // channel in the database. If we can't find the channel, then we'll
4✔
1371
        // return the error back to the caller.
4✔
1372
        dbChan, err := c.FetchChannel(*chanPoint)
4✔
1373
        switch {
4✔
1374
        // If the channel wasn't found, then it's possible that it was already
1375
        // abandoned from the database.
1376
        case err == ErrChannelNotFound:
2✔
1377
                _, closedErr := c.FetchClosedChannel(chanPoint)
2✔
1378
                if closedErr != nil {
3✔
1379
                        return closedErr
1✔
1380
                }
1✔
1381

1382
                // If the channel was already closed, then we don't return an
1383
                // error as we'd like this step to be repeatable.
1384
                return nil
1✔
1385
        case err != nil:
×
1386
                return err
×
1387
        }
1388

1389
        // Now that we've found the channel, we'll populate a close summary for
1390
        // the channel, so we can store as much information for this abounded
1391
        // channel as possible. We also ensure that we set Pending to false, to
1392
        // indicate that this channel has been "fully" closed.
1393
        summary := &ChannelCloseSummary{
2✔
1394
                CloseType:               Abandoned,
2✔
1395
                ChanPoint:               *chanPoint,
2✔
1396
                ChainHash:               dbChan.ChainHash,
2✔
1397
                CloseHeight:             bestHeight,
2✔
1398
                RemotePub:               dbChan.IdentityPub,
2✔
1399
                Capacity:                dbChan.Capacity,
2✔
1400
                SettledBalance:          dbChan.LocalCommitment.LocalBalance.ToSatoshis(),
2✔
1401
                ShortChanID:             dbChan.ShortChanID(),
2✔
1402
                RemoteCurrentRevocation: dbChan.RemoteCurrentRevocation,
2✔
1403
                RemoteNextRevocation:    dbChan.RemoteNextRevocation,
2✔
1404
                LocalChanConfig:         dbChan.LocalChanCfg,
2✔
1405
        }
2✔
1406

2✔
1407
        // Finally, we'll close the channel in the DB, and return back to the
2✔
1408
        // caller. We set ourselves as the close initiator because we abandoned
2✔
1409
        // the channel.
2✔
1410
        return dbChan.CloseChannel(summary, ChanStatusLocalCloseInitiator)
2✔
1411
}
1412

1413
// SaveChannelOpeningState saves the serialized channel state for the provided
1414
// chanPoint to the channelOpeningStateBucket.
1415
func (c *ChannelStateDB) SaveChannelOpeningState(outPoint,
1416
        serializedState []byte) error {
92✔
1417

92✔
1418
        return kvdb.Update(c.backend, func(tx kvdb.RwTx) error {
184✔
1419
                bucket, err := tx.CreateTopLevelBucket(channelOpeningStateBucket)
92✔
1420
                if err != nil {
92✔
1421
                        return err
×
1422
                }
×
1423

1424
                return bucket.Put(outPoint, serializedState)
92✔
1425
        }, func() {})
92✔
1426
}
1427

1428
// GetChannelOpeningState fetches the serialized channel state for the provided
1429
// outPoint from the database, or returns ErrChannelNotFound if the channel
1430
// is not found.
1431
func (c *ChannelStateDB) GetChannelOpeningState(outPoint []byte) ([]byte,
1432
        error) {
252✔
1433

252✔
1434
        var serializedState []byte
252✔
1435
        err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
504✔
1436
                bucket := tx.ReadBucket(channelOpeningStateBucket)
252✔
1437
                if bucket == nil {
252✔
UNCOV
1438
                        // If the bucket does not exist, it means we never added
×
UNCOV
1439
                        //  a channel to the db, so return ErrChannelNotFound.
×
UNCOV
1440
                        return ErrChannelNotFound
×
UNCOV
1441
                }
×
1442

1443
                stateBytes := bucket.Get(outPoint)
252✔
1444
                if stateBytes == nil {
299✔
1445
                        return ErrChannelNotFound
47✔
1446
                }
47✔
1447

1448
                serializedState = append(serializedState, stateBytes...)
205✔
1449

205✔
1450
                return nil
205✔
1451
        }, func() {
252✔
1452
                serializedState = nil
252✔
1453
        })
252✔
1454
        return serializedState, err
252✔
1455
}
1456

1457
// DeleteChannelOpeningState removes any state for outPoint from the database.
1458
func (c *ChannelStateDB) DeleteChannelOpeningState(outPoint []byte) error {
24✔
1459
        return kvdb.Update(c.backend, func(tx kvdb.RwTx) error {
48✔
1460
                bucket := tx.ReadWriteBucket(channelOpeningStateBucket)
24✔
1461
                if bucket == nil {
24✔
1462
                        return ErrChannelNotFound
×
1463
                }
×
1464

1465
                return bucket.Delete(outPoint)
24✔
1466
        }, func() {})
24✔
1467
}
1468

1469
// syncVersions function is used for safe db version synchronization. It
1470
// applies migration functions to the current database and recovers the
1471
// previous state of db if at least one error/panic appeared during migration.
1472
func (d *DB) syncVersions(versions []mandatoryVersion) error {
1,750✔
1473
        meta, err := d.FetchMeta()
1,750✔
1474
        if err != nil {
1,750✔
1475
                if err == ErrMetaNotFound {
×
1476
                        meta = &Meta{}
×
1477
                } else {
×
1478
                        return err
×
1479
                }
×
1480
        }
1481

1482
        latestVersion := getLatestDBVersion(versions)
1,750✔
1483
        log.Infof("Checking for schema update: latest_version=%v, "+
1,750✔
1484
                "db_version=%v", latestVersion, meta.DbVersionNumber)
1,750✔
1485

1,750✔
1486
        switch {
1,750✔
1487

1488
        // If the database reports a higher version that we are aware of, the
1489
        // user is probably trying to revert to a prior version of lnd. We fail
1490
        // here to prevent reversions and unintended corruption.
1491
        case meta.DbVersionNumber > latestVersion:
1✔
1492
                log.Errorf("Refusing to revert from db_version=%d to "+
1✔
1493
                        "lower version=%d", meta.DbVersionNumber,
1✔
1494
                        latestVersion)
1✔
1495
                return ErrDBReversion
1✔
1496

1497
        // If the current database version matches the latest version number,
1498
        // then we don't need to perform any migrations.
1499
        case meta.DbVersionNumber == latestVersion:
1,745✔
1500
                return nil
1,745✔
1501
        }
1502

1503
        log.Infof("Performing database schema migration")
4✔
1504

4✔
1505
        // Otherwise, we fetch the migrations which need to applied, and
4✔
1506
        // execute them serially within a single database transaction to ensure
4✔
1507
        // the migration is atomic.
4✔
1508
        migrations, migrationVersions := getMigrationsToApply(
4✔
1509
                versions, meta.DbVersionNumber,
4✔
1510
        )
4✔
1511
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
8✔
1512
                for i, migration := range migrations {
8✔
1513
                        if migration == nil {
4✔
1514
                                continue
×
1515
                        }
1516

1517
                        log.Infof("Applying migration #%v",
4✔
1518
                                migrationVersions[i])
4✔
1519

4✔
1520
                        if err := migration(tx); err != nil {
5✔
1521
                                log.Infof("Unable to apply migration #%v",
1✔
1522
                                        migrationVersions[i])
1✔
1523
                                return err
1✔
1524
                        }
1✔
1525
                }
1526

1527
                meta.DbVersionNumber = latestVersion
2✔
1528
                err := putMeta(meta, tx)
2✔
1529
                if err != nil {
2✔
1530
                        return err
×
1531
                }
×
1532

1533
                // In dry-run mode, return an error to prevent the transaction
1534
                // from committing.
1535
                if d.dryRun {
3✔
1536
                        return ErrDryRunMigrationOK
1✔
1537
                }
1✔
1538

1539
                return nil
1✔
1540
        }, func() {})
4✔
1541
}
1542

1543
// applyOptionalVersions takes a config to determine whether the optional
1544
// migrations will be applied.
1545
//
1546
// NOTE: only support the prune_revocation_log optional migration atm.
1547
func (d *DB) applyOptionalVersions(cfg OptionalMiragtionConfig) error {
1,748✔
1548
        // TODO(yy): need to design the db to support dry run for optional
1,748✔
1549
        // migrations.
1,748✔
1550
        if d.dryRun {
1,749✔
1551
                log.Info("Skipped optional migrations as dry run mode is not " +
1✔
1552
                        "supported yet")
1✔
1553
                return nil
1✔
1554
        }
1✔
1555

1556
        om, err := d.fetchOptionalMeta()
1,747✔
1557
        if err != nil {
1,747✔
1558
                if err == ErrMetaNotFound {
×
1559
                        om = &OptionalMeta{
×
1560
                                Versions: make(map[uint64]string),
×
1561
                        }
×
1562
                } else {
×
1563
                        return err
×
1564
                }
×
1565
        }
1566

1567
        log.Infof("Checking for optional update: prune_revocation_log=%v, "+
1,747✔
1568
                "db_version=%s", cfg.PruneRevocationLog, om)
1,747✔
1569

1,747✔
1570
        // Exit early if the optional migration is not specified.
1,747✔
1571
        if !cfg.PruneRevocationLog {
3,492✔
1572
                return nil
1,745✔
1573
        }
1,745✔
1574

1575
        // Exit early if the optional migration has already been applied.
1576
        if _, ok := om.Versions[0]; ok {
3✔
1577
                return nil
1✔
1578
        }
1✔
1579

1580
        // Get the optional version.
1581
        version := optionalVersions[0]
1✔
1582
        log.Infof("Performing database optional migration: %s", version.name)
1✔
1583

1✔
1584
        migrationCfg := &MigrationConfigImpl{
1✔
1585
                migration30.MigrateRevLogConfigImpl{
1✔
1586
                        NoAmountData: d.noRevLogAmtData,
1✔
1587
                },
1✔
1588
        }
1✔
1589

1✔
1590
        // Migrate the data.
1✔
1591
        if err := version.migration(d, migrationCfg); err != nil {
1✔
1592
                log.Errorf("Unable to apply optional migration: %s, error: %v",
×
1593
                        version.name, err)
×
1594
                return err
×
1595
        }
×
1596

1597
        // Update the optional meta. Notice that unlike the mandatory db
1598
        // migrations where we perform the migration and updating meta in a
1599
        // single db transaction, we use different transactions here. Even when
1600
        // the following update is failed, we should be fine here as we would
1601
        // re-run the optional migration again, which is a noop, during next
1602
        // startup.
1603
        om.Versions[0] = version.name
1✔
1604
        if err := d.putOptionalMeta(om); err != nil {
1✔
1605
                log.Errorf("Unable to update optional meta: %v", err)
×
1606
                return err
×
1607
        }
×
1608

1609
        return nil
1✔
1610
}
1611

1612
// ChannelStateDB returns the sub database that is concerned with the channel
1613
// state.
1614
func (d *DB) ChannelStateDB() *ChannelStateDB {
2,167✔
1615
        return d.channelStateDB
2,167✔
1616
}
2,167✔
1617

1618
// LatestDBVersion returns the number of the latest database version currently
1619
// known to the channel DB.
1620
func LatestDBVersion() uint32 {
1✔
1621
        return getLatestDBVersion(dbVersions)
1✔
1622
}
1✔
1623

1624
func getLatestDBVersion(versions []mandatoryVersion) uint32 {
3,470✔
1625
        return versions[len(versions)-1].number
3,470✔
1626
}
3,470✔
1627

1628
// getMigrationsToApply retrieves the migration function that should be
1629
// applied to the database.
1630
func getMigrationsToApply(versions []mandatoryVersion,
1631
        version uint32) ([]migration, []uint32) {
5✔
1632

5✔
1633
        migrations := make([]migration, 0, len(versions))
5✔
1634
        migrationVersions := make([]uint32, 0, len(versions))
5✔
1635

5✔
1636
        for _, v := range versions {
17✔
1637
                if v.number > version {
18✔
1638
                        migrations = append(migrations, v.migration)
6✔
1639
                        migrationVersions = append(migrationVersions, v.number)
6✔
1640
                }
6✔
1641
        }
1642

1643
        return migrations, migrationVersions
5✔
1644
}
1645

1646
// fetchHistoricalChanBucket returns a the channel bucket for a given outpoint
1647
// from the historical channel bucket. If the bucket does not exist,
1648
// ErrNoHistoricalBucket is returned.
1649
func fetchHistoricalChanBucket(tx kvdb.RTx,
1650
        outPoint *wire.OutPoint) (kvdb.RBucket, error) {
4✔
1651

4✔
1652
        // First fetch the top level bucket which stores all data related to
4✔
1653
        // historically stored channels.
4✔
1654
        historicalChanBucket := tx.ReadBucket(historicalChannelBucket)
4✔
1655
        if historicalChanBucket == nil {
4✔
1656
                return nil, ErrNoHistoricalBucket
×
1657
        }
×
1658

1659
        // With the bucket for the node and chain fetched, we can now go down
1660
        // another level, for the channel itself.
1661
        var chanPointBuf bytes.Buffer
4✔
1662
        if err := graphdb.WriteOutpoint(&chanPointBuf, outPoint); err != nil {
4✔
1663
                return nil, err
×
1664
        }
×
1665
        chanBucket := historicalChanBucket.NestedReadBucket(
4✔
1666
                chanPointBuf.Bytes(),
4✔
1667
        )
4✔
1668
        if chanBucket == nil {
6✔
1669
                return nil, ErrChannelNotFound
2✔
1670
        }
2✔
1671

1672
        return chanBucket, nil
2✔
1673
}
1674

1675
// FetchHistoricalChannel fetches open channel data from the historical channel
1676
// bucket.
1677
func (c *ChannelStateDB) FetchHistoricalChannel(outPoint *wire.OutPoint) (
1678
        *OpenChannel, error) {
4✔
1679

4✔
1680
        var channel *OpenChannel
4✔
1681
        err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
8✔
1682
                chanBucket, err := fetchHistoricalChanBucket(tx, outPoint)
4✔
1683
                if err != nil {
6✔
1684
                        return err
2✔
1685
                }
2✔
1686

1687
                channel, err = fetchOpenChannel(chanBucket, outPoint)
2✔
1688
                if err != nil {
2✔
1689
                        return err
×
1690
                }
×
1691

1692
                channel.Db = c
2✔
1693
                return nil
2✔
1694
        }, func() {
4✔
1695
                channel = nil
4✔
1696
        })
4✔
1697
        if err != nil {
6✔
1698
                return nil, err
2✔
1699
        }
2✔
1700

1701
        return channel, nil
2✔
1702
}
1703

1704
func fetchFinalHtlcsBucket(tx kvdb.RTx,
1705
        chanID lnwire.ShortChannelID) (kvdb.RBucket, error) {
10✔
1706

10✔
1707
        finalHtlcsBucket := tx.ReadBucket(finalHtlcsBucket)
10✔
1708
        if finalHtlcsBucket == nil {
16✔
1709
                return nil, ErrFinalHtlcsBucketNotFound
6✔
1710
        }
6✔
1711

1712
        var chanIDBytes [8]byte
4✔
1713
        byteOrder.PutUint64(chanIDBytes[:], chanID.ToUint64())
4✔
1714

4✔
1715
        chanBucket := finalHtlcsBucket.NestedReadBucket(chanIDBytes[:])
4✔
1716
        if chanBucket == nil {
4✔
1717
                return nil, ErrFinalChannelBucketNotFound
×
1718
        }
×
1719

1720
        return chanBucket, nil
4✔
1721
}
1722

1723
var ErrHtlcUnknown = errors.New("htlc unknown")
1724

1725
// LookupFinalHtlc retrieves a final htlc resolution from the database. If the
1726
// htlc has no final resolution yet, ErrHtlcUnknown is returned.
1727
func (c *ChannelStateDB) LookupFinalHtlc(chanID lnwire.ShortChannelID,
1728
        htlcIndex uint64) (*FinalHtlcInfo, error) {
10✔
1729

10✔
1730
        var idBytes [8]byte
10✔
1731
        byteOrder.PutUint64(idBytes[:], htlcIndex)
10✔
1732

10✔
1733
        var settledByte byte
10✔
1734

10✔
1735
        err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
20✔
1736
                finalHtlcsBucket, err := fetchFinalHtlcsBucket(
10✔
1737
                        tx, chanID,
10✔
1738
                )
10✔
1739
                switch {
10✔
1740
                case errors.Is(err, ErrFinalHtlcsBucketNotFound):
6✔
1741
                        fallthrough
6✔
1742

1743
                case errors.Is(err, ErrFinalChannelBucketNotFound):
6✔
1744
                        return ErrHtlcUnknown
6✔
1745

1746
                case err != nil:
×
1747
                        return fmt.Errorf("cannot fetch final htlcs bucket: %w",
×
1748
                                err)
×
1749
                }
1750

1751
                value := finalHtlcsBucket.Get(idBytes[:])
4✔
1752
                if value == nil {
5✔
1753
                        return ErrHtlcUnknown
1✔
1754
                }
1✔
1755

1756
                if len(value) != 1 {
3✔
1757
                        return errors.New("unexpected final htlc value length")
×
1758
                }
×
1759

1760
                settledByte = value[0]
3✔
1761

3✔
1762
                return nil
3✔
1763
        }, func() {
10✔
1764
                settledByte = 0
10✔
1765
        })
10✔
1766
        if err != nil {
17✔
1767
                return nil, err
7✔
1768
        }
7✔
1769

1770
        info := FinalHtlcInfo{
3✔
1771
                Settled:  settledByte&byte(FinalHtlcSettledBit) != 0,
3✔
1772
                Offchain: settledByte&byte(FinalHtlcOffchainBit) != 0,
3✔
1773
        }
3✔
1774

3✔
1775
        return &info, nil
3✔
1776
}
1777

1778
// PutOnchainFinalHtlcOutcome stores the final on-chain outcome of an htlc in
1779
// the database.
1780
func (c *ChannelStateDB) PutOnchainFinalHtlcOutcome(
1781
        chanID lnwire.ShortChannelID, htlcID uint64, settled bool) error {
1✔
1782

1✔
1783
        // Skip if the user did not opt in to storing final resolutions.
1✔
1784
        if !c.parent.storeFinalHtlcResolutions {
1✔
UNCOV
1785
                return nil
×
UNCOV
1786
        }
×
1787

1788
        return kvdb.Update(c.backend, func(tx kvdb.RwTx) error {
2✔
1789
                finalHtlcsBucket, err := fetchFinalHtlcsBucketRw(tx, chanID)
1✔
1790
                if err != nil {
1✔
1791
                        return err
×
1792
                }
×
1793

1794
                return putFinalHtlc(
1✔
1795
                        finalHtlcsBucket, htlcID,
1✔
1796
                        FinalHtlcInfo{
1✔
1797
                                Settled:  settled,
1✔
1798
                                Offchain: false,
1✔
1799
                        },
1✔
1800
                )
1✔
1801
        }, func() {})
1✔
1802
}
1803

1804
// MakeTestInvoiceDB is used to create a test invoice database for testing
1805
// purposes. It simply calls into MakeTestDB so the same modifiers can be used.
1806
func MakeTestInvoiceDB(t *testing.T, modifiers ...OptionModifier) (
1807
        invoices.InvoiceDB, error) {
154✔
1808

154✔
1809
        return MakeTestDB(t, modifiers...)
154✔
1810
}
154✔
1811

1812
// MakeTestDB creates a new instance of the ChannelDB for testing purposes.
1813
// A callback which cleans up the created temporary directories is also
1814
// returned and intended to be executed after the test completes.
1815
func MakeTestDB(t *testing.T, modifiers ...OptionModifier) (*DB, error) {
287✔
1816
        // First, create a temporary directory to be used for the duration of
287✔
1817
        // this test.
287✔
1818
        tempDirName := t.TempDir()
287✔
1819

287✔
1820
        // Next, create channeldb for the first time.
287✔
1821
        backend, backendCleanup, err := kvdb.GetTestBackend(tempDirName, "cdb")
287✔
1822
        if err != nil {
287✔
1823
                backendCleanup()
×
1824
                return nil, err
×
1825
        }
×
1826

1827
        cdb, err := CreateWithBackend(backend, modifiers...)
287✔
1828
        if err != nil {
287✔
1829
                backendCleanup()
×
1830
                return nil, err
×
1831
        }
×
1832

1833
        t.Cleanup(func() {
574✔
1834
                cdb.Close()
287✔
1835
                backendCleanup()
287✔
1836
        })
287✔
1837

1838
        return cdb, nil
287✔
1839
}
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