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

lightningnetwork / lnd / 14193549836

01 Apr 2025 10:40AM UTC coverage: 69.046% (+0.007%) from 69.039%
14193549836

Pull #9665

github

web-flow
Merge e8825f209 into b01f4e514
Pull Request #9665: kvdb: bump etcd libs to v3.5.12

133439 of 193262 relevant lines covered (69.05%)

22119.45 hits per line

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

75.0
/channeldb/migration16/migration.go
1
package migration16
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "errors"
7
        "fmt"
8

9
        "github.com/btcsuite/btcd/wire"
10
        "github.com/lightningnetwork/lnd/kvdb"
11
)
12

13
var (
14
        paymentsRootBucket = []byte("payments-root-bucket")
15

16
        paymentSequenceKey = []byte("payment-sequence-key")
17

18
        duplicatePaymentsBucket = []byte("payment-duplicate-bucket")
19

20
        paymentsIndexBucket = []byte("payments-index-bucket")
21

22
        byteOrder = binary.BigEndian
23
)
24

25
// paymentIndexType indicates the type of index we have recorded in the payment
26
// indexes bucket.
27
type paymentIndexType uint8
28

29
// paymentIndexTypeHash is a payment index type which indicates that we have
30
// created an index of payment sequence number to payment hash.
31
const paymentIndexTypeHash paymentIndexType = 0
32

33
// paymentIndex stores all the information we require to create an index by
34
// sequence number for a payment.
35
type paymentIndex struct {
36
        // paymentHash is the hash of the payment, which is its key in the
37
        // payment root bucket.
38
        paymentHash []byte
39

40
        // sequenceNumbers is the set of sequence numbers associated with this
41
        // payment hash. There will be more than one sequence number in the
42
        // case where duplicate payments are present.
43
        sequenceNumbers [][]byte
44
}
45

46
// MigrateSequenceIndex migrates the payments db to contain a new bucket which
47
// provides an index from sequence number to payment hash. This is required
48
// for more efficient sequential lookup of payments, which are keyed by payment
49
// hash before this migration.
50
func MigrateSequenceIndex(tx kvdb.RwTx) error {
3✔
51
        log.Infof("Migrating payments to add sequence number index")
3✔
52

3✔
53
        // Get a list of indices we need to write.
3✔
54
        indexList, err := getPaymentIndexList(tx)
3✔
55
        if err != nil {
3✔
56
                return err
×
57
        }
×
58

59
        // Create the top level bucket that we will use to index payments in.
60
        bucket, err := tx.CreateTopLevelBucket(paymentsIndexBucket)
3✔
61
        if err != nil {
3✔
62
                return err
×
63
        }
×
64

65
        // Write an index for each of our payments.
66
        for _, index := range indexList {
6✔
67
                // Write indexes for each of our sequence numbers.
3✔
68
                for _, seqNr := range index.sequenceNumbers {
8✔
69
                        err := putIndex(bucket, seqNr, index.paymentHash)
5✔
70
                        if err != nil {
6✔
71
                                return err
1✔
72
                        }
1✔
73
                }
74
        }
75

76
        return nil
2✔
77
}
78

79
// putIndex performs a sanity check that ensures we are not writing duplicate
80
// indexes to disk then creates the index provided.
81
func putIndex(bucket kvdb.RwBucket, sequenceNr, paymentHash []byte) error {
5✔
82
        // Add a sanity check that we do not already have an entry with
5✔
83
        // this sequence number.
5✔
84
        existingEntry := bucket.Get(sequenceNr)
5✔
85
        if existingEntry != nil {
6✔
86
                return fmt.Errorf("sequence number: %x duplicated",
1✔
87
                        sequenceNr)
1✔
88
        }
1✔
89

90
        bytes, err := serializePaymentIndexEntry(paymentHash)
4✔
91
        if err != nil {
4✔
92
                return err
×
93
        }
×
94

95
        return bucket.Put(sequenceNr, bytes)
4✔
96
}
97

98
// serializePaymentIndexEntry serializes a payment hash typed index. The value
99
// produced contains a payment index type (which can be used in future to
100
// signal different payment index types) and the payment hash.
101
func serializePaymentIndexEntry(hash []byte) ([]byte, error) {
7✔
102
        var b bytes.Buffer
7✔
103

7✔
104
        err := binary.Write(&b, byteOrder, paymentIndexTypeHash)
7✔
105
        if err != nil {
7✔
106
                return nil, err
×
107
        }
×
108

109
        if err := wire.WriteVarBytes(&b, 0, hash); err != nil {
7✔
110
                return nil, err
×
111
        }
×
112

113
        return b.Bytes(), nil
7✔
114
}
115

116
// getPaymentIndexList gets a list of indices we need to write for our current
117
// set of payments.
118
func getPaymentIndexList(tx kvdb.RTx) ([]paymentIndex, error) {
3✔
119
        // Iterate over all payments and store their indexing keys. This is
3✔
120
        // needed, because no modifications are allowed inside a Bucket.ForEach
3✔
121
        // loop.
3✔
122
        paymentsBucket := tx.ReadBucket(paymentsRootBucket)
3✔
123
        if paymentsBucket == nil {
3✔
124
                return nil, nil
×
125
        }
×
126

127
        var indexList []paymentIndex
3✔
128
        err := paymentsBucket.ForEach(func(k, v []byte) error {
6✔
129
                // Get the bucket which contains the payment, fail if the key
3✔
130
                // does not have a bucket.
3✔
131
                bucket := paymentsBucket.NestedReadBucket(k)
3✔
132
                if bucket == nil {
3✔
133
                        return fmt.Errorf("non bucket element in " +
×
134
                                "payments bucket")
×
135
                }
×
136
                seqBytes := bucket.Get(paymentSequenceKey)
3✔
137
                if seqBytes == nil {
3✔
138
                        return fmt.Errorf("nil sequence number bytes")
×
139
                }
×
140

141
                seqNrs, err := fetchSequenceNumbers(bucket)
3✔
142
                if err != nil {
3✔
143
                        return err
×
144
                }
×
145

146
                // Create an index object with our payment hash and sequence
147
                // numbers and append it to our set of indexes.
148
                index := paymentIndex{
3✔
149
                        paymentHash:     k,
3✔
150
                        sequenceNumbers: seqNrs,
3✔
151
                }
3✔
152

3✔
153
                indexList = append(indexList, index)
3✔
154
                return nil
3✔
155
        })
156
        if err != nil {
3✔
157
                return nil, err
×
158
        }
×
159

160
        return indexList, nil
3✔
161
}
162

163
// fetchSequenceNumbers fetches all the sequence numbers associated with a
164
// payment, including those belonging to any duplicate payments.
165
func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) {
3✔
166
        seqNum := paymentBucket.Get(paymentSequenceKey)
3✔
167
        if seqNum == nil {
3✔
168
                return nil, errors.New("expected sequence number")
×
169
        }
×
170

171
        sequenceNumbers := [][]byte{seqNum}
3✔
172

3✔
173
        // Get the duplicate payments bucket, if it has no duplicates, just
3✔
174
        // return early with the payment sequence number.
3✔
175
        duplicates := paymentBucket.NestedReadBucket(duplicatePaymentsBucket)
3✔
176
        if duplicates == nil {
4✔
177
                return sequenceNumbers, nil
1✔
178
        }
1✔
179

180
        // If we do have duplicated, they are keyed by sequence number, so we
181
        // iterate through the duplicates bucket and add them to our set of
182
        // sequence numbers.
183
        if err := duplicates.ForEach(func(k, v []byte) error {
4✔
184
                sequenceNumbers = append(sequenceNumbers, k)
2✔
185
                return nil
2✔
186
        }); err != nil {
2✔
187
                return nil, err
×
188
        }
×
189

190
        return sequenceNumbers, nil
2✔
191
}
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