• 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

93.63
/lnwire/custom_records.go
1
package lnwire
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "io"
7
        "maps"
8
        "sort"
9

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

14
const (
15
        // MinCustomRecordsTlvType is the minimum custom records TLV type as
16
        // defined in BOLT 01.
17
        MinCustomRecordsTlvType = 65536
18
)
19

20
// CustomRecords stores a set of custom key/value pairs. Map keys are TLV types
21
// which must be greater than or equal to MinCustomRecordsTlvType.
22
type CustomRecords map[uint64][]byte
23

24
// NewCustomRecords creates a new CustomRecords instance from a
25
// tlv.TypeMap.
26
func NewCustomRecords(tlvMap tlv.TypeMap) (CustomRecords, error) {
11,840✔
27
        // Make comparisons in unit tests easy by returning nil if the map is
11,840✔
28
        // empty.
11,840✔
29
        if len(tlvMap) == 0 {
16,421✔
30
                return nil, nil
4,581✔
31
        }
4,581✔
32

33
        customRecords := make(CustomRecords, len(tlvMap))
7,262✔
34
        for k, v := range tlvMap {
15,223✔
35
                customRecords[uint64(k)] = v
7,961✔
36
        }
7,961✔
37

38
        // Validate the custom records.
39
        err := customRecords.Validate()
7,262✔
40
        if err != nil {
7,263✔
41
                return nil, fmt.Errorf("custom records from tlv map "+
1✔
42
                        "validation error: %w", err)
1✔
43
        }
1✔
44

45
        return customRecords, nil
7,261✔
46
}
47

48
// ParseCustomRecords creates a new CustomRecords instance from a tlv.Blob.
49
func ParseCustomRecords(b tlv.Blob) (CustomRecords, error) {
3,209✔
50
        return ParseCustomRecordsFrom(bytes.NewReader(b))
3,209✔
51
}
3,209✔
52

53
// ParseCustomRecordsFrom creates a new CustomRecords instance from a reader.
54
func ParseCustomRecordsFrom(r io.Reader) (CustomRecords, error) {
3,851✔
55
        typeMap, err := DecodeRecords(r)
3,851✔
56
        if err != nil {
3,851✔
57
                return nil, fmt.Errorf("error decoding HTLC record: %w", err)
×
58
        }
×
59

60
        return NewCustomRecords(typeMap)
3,851✔
61
}
62

63
// Validate checks that all custom records are in the custom type range.
64
func (c CustomRecords) Validate() error {
17,211✔
65
        if c == nil {
24,051✔
66
                return nil
6,840✔
67
        }
6,840✔
68

69
        for key := range c {
22,068✔
70
                if key < MinCustomRecordsTlvType {
11,699✔
71
                        return fmt.Errorf("custom records entry with TLV "+
5✔
72
                                "type below min: %d", MinCustomRecordsTlvType)
5✔
73
                }
5✔
74
        }
75

76
        return nil
10,369✔
77
}
78

79
// Copy returns a copy of the custom records.
80
func (c CustomRecords) Copy() CustomRecords {
48,081✔
81
        if c == nil {
95,087✔
82
                return nil
47,006✔
83
        }
47,006✔
84

85
        customRecords := make(CustomRecords, len(c))
1,078✔
86
        for k, v := range c {
2,280✔
87
                customRecords[k] = v
1,202✔
88
        }
1,202✔
89

90
        return customRecords
1,078✔
91
}
92

93
// MergedCopy creates a copy of the records and merges them with the given
94
// records. If the same key is present in both sets, the value from the other
95
// records will be used.
96
func (c CustomRecords) MergedCopy(other CustomRecords) CustomRecords {
910✔
97
        copiedRecords := make(CustomRecords, len(c))
910✔
98
        maps.Copy(copiedRecords, c)
910✔
99
        maps.Copy(copiedRecords, other)
910✔
100

910✔
101
        return copiedRecords
910✔
102
}
910✔
103

104
// ExtendRecordProducers extends the given records slice with the custom
105
// records. The resultant records slice will be sorted if the given records
106
// slice contains TLV types greater than or equal to MinCustomRecordsTlvType.
107
func (c CustomRecords) ExtendRecordProducers(
108
        producers []tlv.RecordProducer) ([]tlv.RecordProducer, error) {
27,743✔
109

27,743✔
110
        // If the custom records are nil or empty, there is nothing to do.
27,743✔
111
        if len(c) == 0 {
54,780✔
112
                return producers, nil
27,037✔
113
        }
27,037✔
114

115
        // Validate the custom records.
116
        err := c.Validate()
709✔
117
        if err != nil {
710✔
118
                return nil, err
1✔
119
        }
1✔
120

121
        // Ensure that the existing records slice TLV types are not also present
122
        // in the custom records. If they are, the resultant extended records
123
        // slice would erroneously contain duplicate TLV types.
124
        for _, rp := range producers {
718✔
125
                record := rp.Record()
10✔
126
                recordTlvType := uint64(record.Type())
10✔
127

10✔
128
                _, foundDuplicateTlvType := c[recordTlvType]
10✔
129
                if foundDuplicateTlvType {
11✔
130
                        return nil, fmt.Errorf("custom records contains a TLV "+
1✔
131
                                "type that is already present in the "+
1✔
132
                                "existing records: %d", recordTlvType)
1✔
133
                }
1✔
134
        }
135

136
        // Convert the custom records map to a TLV record producer slice and
137
        // append them to the exiting records slice.
138
        customRecordProducers := RecordsAsProducers(tlv.MapToRecords(c))
707✔
139
        producers = append(producers, customRecordProducers...)
707✔
140

707✔
141
        // If the records slice which was given as an argument included TLV
707✔
142
        // values greater than or equal to the minimum custom records TLV type
707✔
143
        // we will sort the extended records slice to ensure that it is ordered
707✔
144
        // correctly.
707✔
145
        SortProducers(producers)
707✔
146

707✔
147
        return producers, nil
707✔
148
}
149

150
// RecordProducers returns a slice of record producers for the custom records.
151
func (c CustomRecords) RecordProducers() []tlv.RecordProducer {
9,197✔
152
        // If the custom records are nil or empty, return an empty slice.
9,197✔
153
        if len(c) == 0 {
15,989✔
154
                return nil
6,792✔
155
        }
6,792✔
156

157
        // Convert the custom records map to a TLV record producer slice.
158
        records := tlv.MapToRecords(c)
2,408✔
159

2,408✔
160
        return RecordsAsProducers(records)
2,408✔
161
}
162

163
// Serialize serializes the custom records into a byte slice.
164
func (c CustomRecords) Serialize() ([]byte, error) {
5,660✔
165
        records := tlv.MapToRecords(c)
5,660✔
166
        return EncodeRecords(records)
5,660✔
167
}
5,660✔
168

169
// SerializeTo serializes the custom records into the given writer.
170
func (c CustomRecords) SerializeTo(w io.Writer) error {
154✔
171
        records := tlv.MapToRecords(c)
154✔
172
        return EncodeRecordsTo(w, records)
154✔
173
}
154✔
174

175
// ProduceRecordsSorted converts a slice of record producers into a slice of
176
// records and then sorts it by type.
177
func ProduceRecordsSorted(recordProducers ...tlv.RecordProducer) []tlv.Record {
57,817✔
178
        records := fn.Map(
57,817✔
179
                recordProducers,
57,817✔
180
                func(producer tlv.RecordProducer) tlv.Record {
91,257✔
181
                        return producer.Record()
33,440✔
182
                },
33,440✔
183
        )
184

185
        // Ensure that the set of records are sorted before we attempt to
186
        // decode from the stream, to ensure they're canonical.
187
        tlv.SortRecords(records)
57,817✔
188

57,817✔
189
        return records
57,817✔
190
}
191

192
// SortProducers sorts the given record producers by their type.
193
func SortProducers(producers []tlv.RecordProducer) {
707✔
194
        sort.Slice(producers, func(i, j int) bool {
794✔
195
                recordI := producers[i].Record()
87✔
196
                recordJ := producers[j].Record()
87✔
197
                return recordI.Type() < recordJ.Type()
87✔
198
        })
87✔
199
}
200

201
// TlvMapToRecords converts a TLV map into a slice of records.
202
func TlvMapToRecords(tlvMap tlv.TypeMap) []tlv.Record {
380✔
203
        tlvMapGeneric := make(map[uint64][]byte)
380✔
204
        for k, v := range tlvMap {
2,652✔
205
                tlvMapGeneric[uint64(k)] = v
2,272✔
206
        }
2,272✔
207

208
        return tlv.MapToRecords(tlvMapGeneric)
380✔
209
}
210

211
// RecordsAsProducers converts a slice of records into a slice of record
212
// producers.
213
func RecordsAsProducers(records []tlv.Record) []tlv.RecordProducer {
3,272✔
214
        return fn.Map(records, func(record tlv.Record) tlv.RecordProducer {
7,857✔
215
                return &record
4,585✔
216
        })
4,585✔
217
}
218

219
// EncodeRecords encodes the given records into a byte slice.
220
func EncodeRecords(records []tlv.Record) ([]byte, error) {
48,207✔
221
        var buf bytes.Buffer
48,207✔
222
        if err := EncodeRecordsTo(&buf, records); err != nil {
48,207✔
223
                return nil, err
×
224
        }
×
225

226
        return buf.Bytes(), nil
48,207✔
227
}
228

229
// EncodeRecordsTo encodes the given records into the given writer.
230
func EncodeRecordsTo(w io.Writer, records []tlv.Record) error {
48,531✔
231
        tlvStream, err := tlv.NewStream(records...)
48,531✔
232
        if err != nil {
48,531✔
233
                return err
×
234
        }
×
235

236
        return tlvStream.Encode(w)
48,531✔
237
}
238

239
// DecodeRecords decodes the given byte slice into the given records and returns
240
// the rest as a TLV type map.
241
func DecodeRecords(r io.Reader,
242
        records ...tlv.Record) (tlv.TypeMap, error) {
3,966✔
243

3,966✔
244
        tlvStream, err := tlv.NewStream(records...)
3,966✔
245
        if err != nil {
3,966✔
246
                return nil, err
×
247
        }
×
248

249
        return tlvStream.DecodeWithParsedTypes(r)
3,966✔
250
}
251

252
// DecodeRecordsP2P decodes the given byte slice into the given records and
253
// returns the rest as a TLV type map. This function is identical to
254
// DecodeRecords except that the record size is capped at 65535.
255
func DecodeRecordsP2P(r *bytes.Reader,
256
        records ...tlv.Record) (tlv.TypeMap, error) {
15,204✔
257

15,204✔
258
        tlvStream, err := tlv.NewStream(records...)
15,204✔
259
        if err != nil {
15,204✔
260
                return nil, err
×
261
        }
×
262

263
        return tlvStream.DecodeWithParsedTypesP2P(r)
15,204✔
264
}
265

266
// AssertUniqueTypes asserts that the given records have unique types.
267
func AssertUniqueTypes(r []tlv.Record) error {
9,194✔
268
        seen := make(fn.Set[tlv.Type], len(r))
9,194✔
269
        for _, record := range r {
13,215✔
270
                t := record.Type()
4,021✔
271
                if seen.Contains(t) {
4,022✔
272
                        return fmt.Errorf("duplicate record type: %d", t)
1✔
273
                }
1✔
274
                seen.Add(t)
4,020✔
275
        }
276

277
        return nil
9,193✔
278
}
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