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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 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