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

lightningnetwork / lnd / 17027244024

17 Aug 2025 11:32PM UTC coverage: 57.287% (-9.5%) from 66.765%
17027244024

Pull #10167

github

web-flow
Merge fcb4f4303 into fb1adfc21
Pull Request #10167: multi: bump Go to 1.24.6

3 of 18 new or added lines in 6 files covered. (16.67%)

28537 existing lines in 457 files now uncovered.

99094 of 172978 relevant lines covered (57.29%)

1.78 hits per line

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

45.1
/channeldb/meta.go
1
package channeldb
2

3
import (
4
        "bytes"
5
        "errors"
6
        "fmt"
7
        "slices"
8
        "strings"
9

10
        "github.com/lightningnetwork/lnd/kvdb"
11
        "github.com/lightningnetwork/lnd/tlv"
12
)
13

14
var (
15
        // metaBucket stores all the meta information concerning the state of
16
        // the database.
17
        metaBucket = []byte("metadata")
18

19
        // dbVersionKey is a boltdb key and it's used for storing/retrieving
20
        // current database version.
21
        dbVersionKey = []byte("dbp")
22

23
        // dbVersionKey is a boltdb key and it's used for storing/retrieving
24
        // a list of optional migrations that have been applied.
25
        optionalVersionKey = []byte("ovk")
26

27
        // TombstoneKey is the key under which we add a tag in the source DB
28
        // after we've successfully and completely migrated it to the target/
29
        // destination DB.
30
        TombstoneKey = []byte("data-migration-tombstone")
31

32
        // ErrMarkerNotPresent is the error that is returned if the queried
33
        // marker is not present in the given database.
34
        ErrMarkerNotPresent = errors.New("marker not present")
35

36
        // ErrInvalidOptionalVersion is the error that is returned if the
37
        // optional version persisted in the database is invalid.
38
        ErrInvalidOptionalVersion = errors.New("invalid optional version")
39
)
40

41
// Meta structure holds the database meta information.
42
type Meta struct {
43
        // DbVersionNumber is the current schema version of the database.
44
        DbVersionNumber uint32
45
}
46

47
// FetchMeta fetches the metadata from boltdb and returns filled meta structure.
48
func (d *DB) FetchMeta() (*Meta, error) {
3✔
49
        var meta *Meta
3✔
50

3✔
51
        err := kvdb.View(d, func(tx kvdb.RTx) error {
6✔
52
                return FetchMeta(meta, tx)
3✔
53
        }, func() {
6✔
54
                meta = &Meta{}
3✔
55
        })
3✔
56
        if err != nil {
3✔
57
                return nil, err
×
58
        }
×
59

60
        return meta, nil
3✔
61
}
62

63
// FetchMeta is a helper function used in order to allow callers to re-use a
64
// database transaction.
65
func FetchMeta(meta *Meta, tx kvdb.RTx) error {
3✔
66
        metaBucket := tx.ReadBucket(metaBucket)
3✔
67
        if metaBucket == nil {
3✔
68
                return ErrMetaNotFound
×
69
        }
×
70

71
        data := metaBucket.Get(dbVersionKey)
3✔
72
        if data == nil {
6✔
73
                meta.DbVersionNumber = getLatestDBVersion(dbVersions)
3✔
74
        } else {
3✔
UNCOV
75
                meta.DbVersionNumber = byteOrder.Uint32(data)
×
UNCOV
76
        }
×
77

78
        return nil
3✔
79
}
80

81
// PutMeta writes the passed instance of the database met-data struct to disk.
UNCOV
82
func (d *DB) PutMeta(meta *Meta) error {
×
UNCOV
83
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
×
UNCOV
84
                return putMeta(meta, tx)
×
UNCOV
85
        }, func() {})
×
86
}
87

88
// putMeta is an internal helper function used in order to allow callers to
89
// re-use a database transaction. See the publicly exported PutMeta method for
90
// more information.
UNCOV
91
func putMeta(meta *Meta, tx kvdb.RwTx) error {
×
UNCOV
92
        metaBucket, err := tx.CreateTopLevelBucket(metaBucket)
×
UNCOV
93
        if err != nil {
×
94
                return err
×
95
        }
×
96

UNCOV
97
        return putDbVersion(metaBucket, meta)
×
98
}
99

UNCOV
100
func putDbVersion(metaBucket kvdb.RwBucket, meta *Meta) error {
×
UNCOV
101
        scratch := make([]byte, 4)
×
UNCOV
102
        byteOrder.PutUint32(scratch, meta.DbVersionNumber)
×
UNCOV
103
        return metaBucket.Put(dbVersionKey, scratch)
×
UNCOV
104
}
×
105

106
// OptionalMeta structure holds the database optional migration information.
107
type OptionalMeta struct {
108
        // Versions is a set that contains the versions that have been applied.
109
        // When saved to disk, only the indexes are stored.
110
        Versions map[uint64]string
111
}
112

113
// String returns a string representation of the optional meta.
UNCOV
114
func (om *OptionalMeta) String() string {
×
UNCOV
115
        if len(om.Versions) == 0 {
×
116
                return "empty"
×
117
        }
×
118

119
        // Create a slice of indices to sort
UNCOV
120
        indices := make([]uint64, 0, len(om.Versions))
×
UNCOV
121
        for index := range om.Versions {
×
UNCOV
122
                indices = append(indices, index)
×
UNCOV
123
        }
×
124

125
        // Sort the indices in ascending order.
UNCOV
126
        slices.Sort(indices)
×
UNCOV
127

×
UNCOV
128
        // Create the string parts in sorted order.
×
UNCOV
129
        parts := make([]string, len(indices))
×
UNCOV
130
        for i, index := range indices {
×
UNCOV
131
                parts[i] = fmt.Sprintf("%d: %s", index, om.Versions[index])
×
UNCOV
132
        }
×
133

UNCOV
134
        return strings.Join(parts, ", ")
×
135
}
136

137
// fetchOptionalMeta reads the optional meta from the database.
138
func (d *DB) fetchOptionalMeta() (*OptionalMeta, error) {
3✔
139
        om := &OptionalMeta{
3✔
140
                Versions: make(map[uint64]string),
3✔
141
        }
3✔
142

3✔
143
        err := kvdb.View(d, func(tx kvdb.RTx) error {
6✔
144
                metaBucket := tx.ReadBucket(metaBucket)
3✔
145
                if metaBucket == nil {
3✔
146
                        return ErrMetaNotFound
×
147
                }
×
148

149
                vBytes := metaBucket.Get(optionalVersionKey)
3✔
150
                // Exit early if nothing found.
3✔
151
                if vBytes == nil {
6✔
152
                        return nil
3✔
153
                }
3✔
154

155
                // Read the versions' length.
156
                r := bytes.NewReader(vBytes)
3✔
157
                vLen, err := tlv.ReadVarInt(r, &[8]byte{})
3✔
158
                if err != nil {
3✔
159
                        return err
×
160
                }
×
161

162
                // Write the version index.
163
                for i := uint64(0); i < vLen; i++ {
6✔
164
                        version, err := tlv.ReadVarInt(r, &[8]byte{})
3✔
165
                        if err != nil {
3✔
166
                                return err
×
167
                        }
×
168

169
                        // This check would not allow to downgrade LND software
170
                        // to a version with an optional migration when an
171
                        // optional migration not known to the current version
172
                        // has already been applied.
173
                        if version >= uint64(len(optionalVersions)) {
3✔
174
                                return fmt.Errorf("optional version read "+
×
175
                                        "from db is %d, but only optional "+
×
176
                                        "migrations up to %d are known: %w",
×
177
                                        version, len(optionalVersions)-1,
×
178
                                        ErrInvalidOptionalVersion)
×
179
                        }
×
180

181
                        om.Versions[version] = optionalVersions[version].name
3✔
182
                }
183

184
                return nil
3✔
185
        }, func() {})
3✔
186
        if err != nil {
3✔
187
                return nil, err
×
188
        }
×
189

190
        return om, nil
3✔
191
}
192

193
// putOptionalMeta writes an optional meta to the database.
194
func (d *DB) putOptionalMeta(om *OptionalMeta) error {
3✔
195
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
6✔
196
                metaBucket, err := tx.CreateTopLevelBucket(metaBucket)
3✔
197
                if err != nil {
3✔
198
                        return err
×
199
                }
×
200

201
                var b bytes.Buffer
3✔
202

3✔
203
                // Write the total length.
3✔
204
                err = tlv.WriteVarInt(&b, uint64(len(om.Versions)), &[8]byte{})
3✔
205
                if err != nil {
3✔
206
                        return err
×
207
                }
×
208

209
                // Write the version indexes of the single migrations.
210
                for v := range om.Versions {
6✔
211
                        if v >= uint64(len(optionalVersions)) {
3✔
212
                                return ErrInvalidOptionalVersion
×
213
                        }
×
214

215
                        err := tlv.WriteVarInt(&b, v, &[8]byte{})
3✔
216
                        if err != nil {
3✔
217
                                return err
×
218
                        }
×
219
                }
220

221
                return metaBucket.Put(optionalVersionKey, b.Bytes())
3✔
222
        }, func() {})
3✔
223
}
224

225
// CheckMarkerPresent returns the marker under the requested key or
226
// ErrMarkerNotFound if either the root bucket or the marker key within that
227
// bucket does not exist.
228
func CheckMarkerPresent(tx kvdb.RTx, markerKey []byte) ([]byte, error) {
3✔
229
        markerBucket := tx.ReadBucket(markerKey)
3✔
230
        if markerBucket == nil {
6✔
231
                return nil, ErrMarkerNotPresent
3✔
232
        }
3✔
233

UNCOV
234
        val := markerBucket.Get(markerKey)
×
UNCOV
235

×
UNCOV
236
        // If we wrote the marker correctly, we created a bucket _and_ created a
×
UNCOV
237
        // key with a non-empty value. It doesn't matter to us whether the key
×
UNCOV
238
        // exists or whether its value is empty, to us, it just means the marker
×
UNCOV
239
        // isn't there.
×
UNCOV
240
        if len(val) == 0 {
×
UNCOV
241
                return nil, ErrMarkerNotPresent
×
UNCOV
242
        }
×
243

UNCOV
244
        return val, nil
×
245
}
246

247
// EnsureNoTombstone returns an error if there is a tombstone marker in the DB
248
// of the given transaction.
249
func EnsureNoTombstone(tx kvdb.RTx) error {
3✔
250
        marker, err := CheckMarkerPresent(tx, TombstoneKey)
3✔
251
        if err == ErrMarkerNotPresent {
6✔
252
                // No marker present, so no tombstone. The DB is still alive.
3✔
253
                return nil
3✔
254
        }
3✔
UNCOV
255
        if err != nil {
×
256
                return err
×
257
        }
×
258

259
        // There was no error so there is a tombstone marker/tag. We cannot use
260
        // this DB anymore.
UNCOV
261
        return fmt.Errorf("refusing to use db, it was marked with a tombstone "+
×
UNCOV
262
                "after successful data migration; tombstone reads: %s",
×
UNCOV
263
                string(marker))
×
264
}
265

266
// AddMarker adds the marker with the given key into a top level bucket with the
267
// same name. So the structure will look like:
268
//
269
//        marker-key (top level bucket)
270
//            |->   marker-key:marker-value (key/value pair)
UNCOV
271
func AddMarker(tx kvdb.RwTx, markerKey, markerValue []byte) error {
×
UNCOV
272
        if len(markerValue) == 0 {
×
273
                return fmt.Errorf("marker value cannot be empty")
×
274
        }
×
275

UNCOV
276
        markerBucket, err := tx.CreateTopLevelBucket(markerKey)
×
UNCOV
277
        if err != nil {
×
278
                return err
×
279
        }
×
280

UNCOV
281
        return markerBucket.Put(markerKey, markerValue)
×
282
}
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