• 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

47.06
/channeldb/meta.go
1
package channeldb
2

3
import (
4
        "bytes"
5
        "errors"
6
        "fmt"
7

8
        "github.com/lightningnetwork/lnd/kvdb"
9
        "github.com/lightningnetwork/lnd/tlv"
10
)
11

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

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

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

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

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

35
// Meta structure holds the database meta information.
36
type Meta struct {
37
        // DbVersionNumber is the current schema version of the database.
38
        DbVersionNumber uint32
39
}
40

41
// FetchMeta fetches the metadata from boltdb and returns filled meta structure.
42
func (d *DB) FetchMeta() (*Meta, error) {
3✔
43
        var meta *Meta
3✔
44

3✔
45
        err := kvdb.View(d, func(tx kvdb.RTx) error {
6✔
46
                return FetchMeta(meta, tx)
3✔
47
        }, func() {
6✔
48
                meta = &Meta{}
3✔
49
        })
3✔
50
        if err != nil {
3✔
51
                return nil, err
×
52
        }
×
53

54
        return meta, nil
3✔
55
}
56

57
// FetchMeta is a helper function used in order to allow callers to re-use a
58
// database transaction.
59
func FetchMeta(meta *Meta, tx kvdb.RTx) error {
3✔
60
        metaBucket := tx.ReadBucket(metaBucket)
3✔
61
        if metaBucket == nil {
6✔
62
                return ErrMetaNotFound
3✔
63
        }
3✔
64

65
        data := metaBucket.Get(dbVersionKey)
3✔
66
        if data == nil {
3✔
67
                meta.DbVersionNumber = getLatestDBVersion(dbVersions)
×
68
        } else {
3✔
69
                meta.DbVersionNumber = byteOrder.Uint32(data)
3✔
70
        }
3✔
71

72
        return nil
3✔
73
}
74

75
// PutMeta writes the passed instance of the database met-data struct to disk.
UNCOV
76
func (d *DB) PutMeta(meta *Meta) error {
×
UNCOV
77
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
×
UNCOV
78
                return putMeta(meta, tx)
×
UNCOV
79
        }, func() {})
×
80
}
81

82
// putMeta is an internal helper function used in order to allow callers to
83
// re-use a database transaction. See the publicly exported PutMeta method for
84
// more information.
85
func putMeta(meta *Meta, tx kvdb.RwTx) error {
3✔
86
        metaBucket, err := tx.CreateTopLevelBucket(metaBucket)
3✔
87
        if err != nil {
3✔
88
                return err
×
89
        }
×
90

91
        return putDbVersion(metaBucket, meta)
3✔
92
}
93

94
func putDbVersion(metaBucket kvdb.RwBucket, meta *Meta) error {
3✔
95
        scratch := make([]byte, 4)
3✔
96
        byteOrder.PutUint32(scratch, meta.DbVersionNumber)
3✔
97
        return metaBucket.Put(dbVersionKey, scratch)
3✔
98
}
3✔
99

100
// OptionalMeta structure holds the database optional migration information.
101
type OptionalMeta struct {
102
        // Versions is a set that contains the versions that have been applied.
103
        // When saved to disk, only the indexes are stored.
104
        Versions map[uint64]string
105
}
106

107
func (om *OptionalMeta) String() string {
3✔
108
        s := ""
3✔
109
        for index, name := range om.Versions {
3✔
UNCOV
110
                s += fmt.Sprintf("%d: %s", index, name)
×
UNCOV
111
        }
×
112
        if s == "" {
6✔
113
                s = "empty"
3✔
114
        }
3✔
115
        return s
3✔
116
}
117

118
// fetchOptionalMeta reads the optional meta from the database.
119
func (d *DB) fetchOptionalMeta() (*OptionalMeta, error) {
3✔
120
        om := &OptionalMeta{
3✔
121
                Versions: make(map[uint64]string),
3✔
122
        }
3✔
123

3✔
124
        err := kvdb.View(d, func(tx kvdb.RTx) error {
6✔
125
                metaBucket := tx.ReadBucket(metaBucket)
3✔
126
                if metaBucket == nil {
3✔
127
                        return ErrMetaNotFound
×
128
                }
×
129

130
                vBytes := metaBucket.Get(optionalVersionKey)
3✔
131
                // Exit early if nothing found.
3✔
132
                if vBytes == nil {
6✔
133
                        return nil
3✔
134
                }
3✔
135

136
                // Read the versions' length.
UNCOV
137
                r := bytes.NewReader(vBytes)
×
UNCOV
138
                vLen, err := tlv.ReadVarInt(r, &[8]byte{})
×
UNCOV
139
                if err != nil {
×
140
                        return err
×
141
                }
×
142

143
                // Write the version index.
UNCOV
144
                for i := uint64(0); i < vLen; i++ {
×
UNCOV
145
                        version, err := tlv.ReadVarInt(r, &[8]byte{})
×
UNCOV
146
                        if err != nil {
×
147
                                return err
×
148
                        }
×
UNCOV
149
                        om.Versions[version] = optionalVersions[i].name
×
150
                }
151

UNCOV
152
                return nil
×
153
        }, func() {})
3✔
154
        if err != nil {
3✔
155
                return nil, err
×
156
        }
×
157

158
        return om, nil
3✔
159
}
160

161
// putOptionalMeta writes an optional meta to the database.
UNCOV
162
func (d *DB) putOptionalMeta(om *OptionalMeta) error {
×
UNCOV
163
        return kvdb.Update(d, func(tx kvdb.RwTx) error {
×
UNCOV
164
                metaBucket, err := tx.CreateTopLevelBucket(metaBucket)
×
UNCOV
165
                if err != nil {
×
166
                        return err
×
167
                }
×
168

UNCOV
169
                var b bytes.Buffer
×
UNCOV
170

×
UNCOV
171
                // Write the total length.
×
UNCOV
172
                err = tlv.WriteVarInt(&b, uint64(len(om.Versions)), &[8]byte{})
×
UNCOV
173
                if err != nil {
×
174
                        return err
×
175
                }
×
176

177
                // Write the version indexes.
UNCOV
178
                for v := range om.Versions {
×
UNCOV
179
                        err := tlv.WriteVarInt(&b, v, &[8]byte{})
×
UNCOV
180
                        if err != nil {
×
181
                                return err
×
182
                        }
×
183
                }
184

UNCOV
185
                return metaBucket.Put(optionalVersionKey, b.Bytes())
×
UNCOV
186
        }, func() {})
×
187
}
188

189
// CheckMarkerPresent returns the marker under the requested key or
190
// ErrMarkerNotFound if either the root bucket or the marker key within that
191
// bucket does not exist.
192
func CheckMarkerPresent(tx kvdb.RTx, markerKey []byte) ([]byte, error) {
3✔
193
        markerBucket := tx.ReadBucket(markerKey)
3✔
194
        if markerBucket == nil {
6✔
195
                return nil, ErrMarkerNotPresent
3✔
196
        }
3✔
197

UNCOV
198
        val := markerBucket.Get(markerKey)
×
UNCOV
199

×
UNCOV
200
        // If we wrote the marker correctly, we created a bucket _and_ created a
×
UNCOV
201
        // key with a non-empty value. It doesn't matter to us whether the key
×
UNCOV
202
        // exists or whether its value is empty, to us, it just means the marker
×
UNCOV
203
        // isn't there.
×
UNCOV
204
        if len(val) == 0 {
×
UNCOV
205
                return nil, ErrMarkerNotPresent
×
UNCOV
206
        }
×
207

UNCOV
208
        return val, nil
×
209
}
210

211
// EnsureNoTombstone returns an error if there is a tombstone marker in the DB
212
// of the given transaction.
213
func EnsureNoTombstone(tx kvdb.RTx) error {
3✔
214
        marker, err := CheckMarkerPresent(tx, TombstoneKey)
3✔
215
        if err == ErrMarkerNotPresent {
6✔
216
                // No marker present, so no tombstone. The DB is still alive.
3✔
217
                return nil
3✔
218
        }
3✔
UNCOV
219
        if err != nil {
×
220
                return err
×
221
        }
×
222

223
        // There was no error so there is a tombstone marker/tag. We cannot use
224
        // this DB anymore.
UNCOV
225
        return fmt.Errorf("refusing to use db, it was marked with a tombstone "+
×
UNCOV
226
                "after successful data migration; tombstone reads: %s",
×
UNCOV
227
                string(marker))
×
228
}
229

230
// AddMarker adds the marker with the given key into a top level bucket with the
231
// same name. So the structure will look like:
232
//
233
//        marker-key (top level bucket)
234
//            |->   marker-key:marker-value (key/value pair)
UNCOV
235
func AddMarker(tx kvdb.RwTx, markerKey, markerValue []byte) error {
×
UNCOV
236
        if len(markerValue) == 0 {
×
237
                return fmt.Errorf("marker value cannot be empty")
×
238
        }
×
239

UNCOV
240
        markerBucket, err := tx.CreateTopLevelBucket(markerKey)
×
UNCOV
241
        if err != nil {
×
242
                return err
×
243
        }
×
244

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