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

lightningnetwork / lnd / 13566028875

27 Feb 2025 12:09PM UTC coverage: 49.396% (-9.4%) from 58.748%
13566028875

Pull #9555

github

ellemouton
graph/db: populate the graph cache in Start instead of during construction

In this commit, we move the graph cache population logic out of the
ChannelGraph constructor and into its Start method instead.
Pull Request #9555: graph: extract cache from CRUD [6]

34 of 54 new or added lines in 4 files covered. (62.96%)

27464 existing lines in 436 files now uncovered.

101095 of 204664 relevant lines covered (49.4%)

1.54 hits per line

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

54.17
/channeldb/height_hint.go
1
package channeldb
2

3
import (
4
        "bytes"
5

6
        "github.com/lightningnetwork/lnd/chainntnfs"
7
        "github.com/lightningnetwork/lnd/kvdb"
8
)
9

10
var (
11
        // spendHintBucket is the name of the bucket which houses the height
12
        // hint for outpoints. Each height hint represents the earliest height
13
        // at which its corresponding outpoint could have been spent within.
14
        spendHintBucket = []byte("spend-hints")
15

16
        // confirmHintBucket is the name of the bucket which houses the height
17
        // hints for transactions. Each height hint represents the earliest
18
        // height at which its corresponding transaction could have been
19
        // confirmed within.
20
        confirmHintBucket = []byte("confirm-hints")
21
)
22

23
// CacheConfig contains the HeightHintCache configuration.
24
type CacheConfig struct {
25
        // QueryDisable prevents reliance on the Height Hint Cache.  This is
26
        // necessary to recover from an edge case when the height recorded in
27
        // the cache is higher than the actual height of a spend, causing a
28
        // channel to become "stuck" in a pending close state.
29
        QueryDisable bool
30
}
31

32
// HeightHintCache is an implementation of the SpendHintCache and
33
// ConfirmHintCache interfaces backed by a channeldb DB instance where the hints
34
// will be stored.
35
type HeightHintCache struct {
36
        cfg CacheConfig
37
        db  kvdb.Backend
38
}
39

40
// Compile-time checks to ensure HeightHintCache satisfies the SpendHintCache
41
// and ConfirmHintCache interfaces.
42
var _ chainntnfs.SpendHintCache = (*HeightHintCache)(nil)
43
var _ chainntnfs.ConfirmHintCache = (*HeightHintCache)(nil)
44

45
// NewHeightHintCache returns a new height hint cache backed by a database.
46
func NewHeightHintCache(cfg CacheConfig, db kvdb.Backend) (*HeightHintCache,
47
        error) {
3✔
48

3✔
49
        cache := &HeightHintCache{cfg, db}
3✔
50
        if err := cache.initBuckets(); err != nil {
3✔
51
                return nil, err
×
52
        }
×
53

54
        return cache, nil
3✔
55
}
56

57
// initBuckets ensures that the primary buckets used by the circuit are
58
// initialized so that we can assume their existence after startup.
59
func (c *HeightHintCache) initBuckets() error {
3✔
60
        return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
6✔
61
                _, err := tx.CreateTopLevelBucket(spendHintBucket)
3✔
62
                if err != nil {
3✔
63
                        return err
×
64
                }
×
65

66
                _, err = tx.CreateTopLevelBucket(confirmHintBucket)
3✔
67
                return err
3✔
68
        })
69
}
70

71
// CommitSpendHint commits a spend hint for the outpoints to the cache.
72
func (c *HeightHintCache) CommitSpendHint(height uint32,
73
        spendRequests ...chainntnfs.SpendRequest) error {
3✔
74

3✔
75
        if len(spendRequests) == 0 {
6✔
76
                return nil
3✔
77
        }
3✔
78

79
        log.Tracef("Updating spend hint to height %d for %v", height,
3✔
80
                spendRequests)
3✔
81

3✔
82
        return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
6✔
83
                spendHints := tx.ReadWriteBucket(spendHintBucket)
3✔
84
                if spendHints == nil {
3✔
85
                        return chainntnfs.ErrCorruptedHeightHintCache
×
86
                }
×
87

88
                var hint bytes.Buffer
3✔
89
                if err := WriteElement(&hint, height); err != nil {
3✔
90
                        return err
×
91
                }
×
92

93
                for _, spendRequest := range spendRequests {
6✔
94
                        spendRequest := spendRequest
3✔
95
                        spendHintKey, err := spendHintKey(&spendRequest)
3✔
96
                        if err != nil {
3✔
97
                                return err
×
98
                        }
×
99
                        err = spendHints.Put(spendHintKey, hint.Bytes())
3✔
100
                        if err != nil {
3✔
101
                                return err
×
102
                        }
×
103
                }
104

105
                return nil
3✔
106
        })
107
}
108

109
// QuerySpendHint returns the latest spend hint for an outpoint.
110
// ErrSpendHintNotFound is returned if a spend hint does not exist within the
111
// cache for the outpoint.
112
func (c *HeightHintCache) QuerySpendHint(
113
        spendRequest chainntnfs.SpendRequest) (uint32, error) {
3✔
114

3✔
115
        var hint uint32
3✔
116
        if c.cfg.QueryDisable {
3✔
UNCOV
117
                log.Debugf("Ignoring spend height hint for %v (height hint "+
×
UNCOV
118
                        "cache query disabled)", spendRequest)
×
UNCOV
119
                return 0, nil
×
UNCOV
120
        }
×
121
        err := kvdb.View(c.db, func(tx kvdb.RTx) error {
6✔
122
                spendHints := tx.ReadBucket(spendHintBucket)
3✔
123
                if spendHints == nil {
3✔
124
                        return chainntnfs.ErrCorruptedHeightHintCache
×
125
                }
×
126

127
                spendHintKey, err := spendHintKey(&spendRequest)
3✔
128
                if err != nil {
3✔
129
                        return err
×
130
                }
×
131
                spendHint := spendHints.Get(spendHintKey)
3✔
132
                if spendHint == nil {
6✔
133
                        return chainntnfs.ErrSpendHintNotFound
3✔
134
                }
3✔
135

136
                return ReadElement(bytes.NewReader(spendHint), &hint)
3✔
137
        }, func() {
3✔
138
                hint = 0
3✔
139
        })
3✔
140
        if err != nil {
6✔
141
                return 0, err
3✔
142
        }
3✔
143

144
        return hint, nil
3✔
145
}
146

147
// PurgeSpendHint removes the spend hint for the outpoints from the cache.
148
func (c *HeightHintCache) PurgeSpendHint(
UNCOV
149
        spendRequests ...chainntnfs.SpendRequest) error {
×
UNCOV
150

×
UNCOV
151
        if len(spendRequests) == 0 {
×
152
                return nil
×
153
        }
×
154

UNCOV
155
        log.Tracef("Removing spend hints for %v", spendRequests)
×
UNCOV
156

×
UNCOV
157
        return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
×
UNCOV
158
                spendHints := tx.ReadWriteBucket(spendHintBucket)
×
UNCOV
159
                if spendHints == nil {
×
160
                        return chainntnfs.ErrCorruptedHeightHintCache
×
161
                }
×
162

UNCOV
163
                for _, spendRequest := range spendRequests {
×
UNCOV
164
                        spendRequest := spendRequest
×
UNCOV
165
                        spendHintKey, err := spendHintKey(&spendRequest)
×
UNCOV
166
                        if err != nil {
×
167
                                return err
×
168
                        }
×
UNCOV
169
                        if err := spendHints.Delete(spendHintKey); err != nil {
×
170
                                return err
×
171
                        }
×
172
                }
173

UNCOV
174
                return nil
×
175
        })
176
}
177

178
// CommitConfirmHint commits a confirm hint for the transactions to the cache.
179
func (c *HeightHintCache) CommitConfirmHint(height uint32,
180
        confRequests ...chainntnfs.ConfRequest) error {
3✔
181

3✔
182
        if len(confRequests) == 0 {
6✔
183
                return nil
3✔
184
        }
3✔
185

186
        log.Tracef("Updating confirm hints to height %d for %v", height,
3✔
187
                confRequests)
3✔
188

3✔
189
        return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
6✔
190
                confirmHints := tx.ReadWriteBucket(confirmHintBucket)
3✔
191
                if confirmHints == nil {
3✔
192
                        return chainntnfs.ErrCorruptedHeightHintCache
×
193
                }
×
194

195
                var hint bytes.Buffer
3✔
196
                if err := WriteElement(&hint, height); err != nil {
3✔
197
                        return err
×
198
                }
×
199

200
                for _, confRequest := range confRequests {
6✔
201
                        confRequest := confRequest
3✔
202
                        confHintKey, err := confHintKey(&confRequest)
3✔
203
                        if err != nil {
3✔
204
                                return err
×
205
                        }
×
206
                        err = confirmHints.Put(confHintKey, hint.Bytes())
3✔
207
                        if err != nil {
3✔
208
                                return err
×
209
                        }
×
210
                }
211

212
                return nil
3✔
213
        })
214
}
215

216
// QueryConfirmHint returns the latest confirm hint for a transaction hash.
217
// ErrConfirmHintNotFound is returned if a confirm hint does not exist within
218
// the cache for the transaction hash.
219
func (c *HeightHintCache) QueryConfirmHint(
220
        confRequest chainntnfs.ConfRequest) (uint32, error) {
3✔
221

3✔
222
        var hint uint32
3✔
223
        if c.cfg.QueryDisable {
3✔
UNCOV
224
                log.Debugf("Ignoring confirmation height hint for %v (height "+
×
UNCOV
225
                        "hint cache query disabled)", confRequest)
×
UNCOV
226
                return 0, nil
×
UNCOV
227
        }
×
228
        err := kvdb.View(c.db, func(tx kvdb.RTx) error {
6✔
229
                confirmHints := tx.ReadBucket(confirmHintBucket)
3✔
230
                if confirmHints == nil {
3✔
231
                        return chainntnfs.ErrCorruptedHeightHintCache
×
232
                }
×
233

234
                confHintKey, err := confHintKey(&confRequest)
3✔
235
                if err != nil {
3✔
236
                        return err
×
237
                }
×
238
                confirmHint := confirmHints.Get(confHintKey)
3✔
239
                if confirmHint == nil {
6✔
240
                        return chainntnfs.ErrConfirmHintNotFound
3✔
241
                }
3✔
242

243
                return ReadElement(bytes.NewReader(confirmHint), &hint)
3✔
244
        }, func() {
3✔
245
                hint = 0
3✔
246
        })
3✔
247
        if err != nil {
6✔
248
                return 0, err
3✔
249
        }
3✔
250

251
        return hint, nil
3✔
252
}
253

254
// PurgeConfirmHint removes the confirm hint for the transactions from the
255
// cache.
256
func (c *HeightHintCache) PurgeConfirmHint(
UNCOV
257
        confRequests ...chainntnfs.ConfRequest) error {
×
UNCOV
258

×
UNCOV
259
        if len(confRequests) == 0 {
×
260
                return nil
×
261
        }
×
262

UNCOV
263
        log.Tracef("Removing confirm hints for %v", confRequests)
×
UNCOV
264

×
UNCOV
265
        return kvdb.Batch(c.db, func(tx kvdb.RwTx) error {
×
UNCOV
266
                confirmHints := tx.ReadWriteBucket(confirmHintBucket)
×
UNCOV
267
                if confirmHints == nil {
×
268
                        return chainntnfs.ErrCorruptedHeightHintCache
×
269
                }
×
270

UNCOV
271
                for _, confRequest := range confRequests {
×
UNCOV
272
                        confRequest := confRequest
×
UNCOV
273
                        confHintKey, err := confHintKey(&confRequest)
×
UNCOV
274
                        if err != nil {
×
275
                                return err
×
276
                        }
×
UNCOV
277
                        if err := confirmHints.Delete(confHintKey); err != nil {
×
278
                                return err
×
279
                        }
×
280
                }
281

UNCOV
282
                return nil
×
283
        })
284
}
285

286
// confHintKey returns the key that will be used to index the confirmation
287
// request's hint within the height hint cache.
288
func confHintKey(r *chainntnfs.ConfRequest) ([]byte, error) {
3✔
289
        if r.TxID == chainntnfs.ZeroHash {
3✔
UNCOV
290
                return r.PkScript.Script(), nil
×
UNCOV
291
        }
×
292

293
        var txid bytes.Buffer
3✔
294
        if err := WriteElement(&txid, r.TxID); err != nil {
3✔
295
                return nil, err
×
296
        }
×
297

298
        return txid.Bytes(), nil
3✔
299
}
300

301
// spendHintKey returns the key that will be used to index the spend request's
302
// hint within the height hint cache.
303
func spendHintKey(r *chainntnfs.SpendRequest) ([]byte, error) {
3✔
304
        if r.OutPoint == chainntnfs.ZeroOutPoint {
3✔
UNCOV
305
                return r.PkScript.Script(), nil
×
UNCOV
306
        }
×
307

308
        var outpoint bytes.Buffer
3✔
309
        err := WriteElement(&outpoint, r.OutPoint)
3✔
310
        if err != nil {
3✔
311
                return nil, err
×
312
        }
×
313

314
        return outpoint.Bytes(), nil
3✔
315
}
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