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

lightningnetwork / lnd / 13586005509

28 Feb 2025 10:14AM UTC coverage: 68.629% (+9.9%) from 58.77%
13586005509

Pull #9521

github

web-flow
Merge 37d3a70a5 into 8532955b3
Pull Request #9521: unit: remove GOACC, use Go 1.20 native coverage functionality

129950 of 189351 relevant lines covered (68.63%)

23726.46 hits per line

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

93.71
/lnwire/custom_records.go
1
package lnwire
2

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

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

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

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

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

32
        customRecords := make(CustomRecords, len(tlvMap))
7,396✔
33
        for k, v := range tlvMap {
16,198✔
34
                customRecords[uint64(k)] = v
8,802✔
35
        }
8,802✔
36

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

44
        return customRecords, nil
7,395✔
45
}
46

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

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

59
        return NewCustomRecords(typeMap)
3,932✔
60
}
61

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

68
        for key := range c {
25,641✔
69
                if key < MinCustomRecordsTlvType {
14,789✔
70
                        return fmt.Errorf("custom records entry with TLV "+
5✔
71
                                "type below min: %d", MinCustomRecordsTlvType)
5✔
72
                }
5✔
73
        }
74

75
        return nil
10,852✔
76
}
77

78
// Copy returns a copy of the custom records.
79
func (c CustomRecords) Copy() CustomRecords {
50,736✔
80
        if c == nil {
100,395✔
81
                return nil
49,659✔
82
        }
49,659✔
83

84
        customRecords := make(CustomRecords, len(c))
1,080✔
85
        for k, v := range c {
2,284✔
86
                customRecords[k] = v
1,204✔
87
        }
1,204✔
88

89
        return customRecords
1,080✔
90
}
91

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

101
        for k, v := range other {
956✔
102
                copiedRecords[k] = v
46✔
103
        }
46✔
104

105
        return copiedRecords
910✔
106
}
107

108
// ExtendRecordProducers extends the given records slice with the custom
109
// records. The resultant records slice will be sorted if the given records
110
// slice contains TLV types greater than or equal to MinCustomRecordsTlvType.
111
func (c CustomRecords) ExtendRecordProducers(
112
        producers []tlv.RecordProducer) ([]tlv.RecordProducer, error) {
29,657✔
113

29,657✔
114
        // If the custom records are nil or empty, there is nothing to do.
29,657✔
115
        if len(c) == 0 {
58,606✔
116
                return producers, nil
28,949✔
117
        }
28,949✔
118

119
        // Validate the custom records.
120
        err := c.Validate()
711✔
121
        if err != nil {
712✔
122
                return nil, err
1✔
123
        }
1✔
124

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

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

140
        // Convert the custom records map to a TLV record producer slice and
141
        // append them to the exiting records slice.
142
        customRecordProducers := RecordsAsProducers(tlv.MapToRecords(c))
709✔
143
        producers = append(producers, customRecordProducers...)
709✔
144

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

709✔
151
        return producers, nil
709✔
152
}
153

154
// RecordProducers returns a slice of record producers for the custom records.
155
func (c CustomRecords) RecordProducers() []tlv.RecordProducer {
9,304✔
156
        // If the custom records are nil or empty, return an empty slice.
9,304✔
157
        if len(c) == 0 {
16,156✔
158
                return nil
6,852✔
159
        }
6,852✔
160

161
        // Convert the custom records map to a TLV record producer slice.
162
        records := tlv.MapToRecords(c)
2,455✔
163

2,455✔
164
        return RecordsAsProducers(records)
2,455✔
165
}
166

167
// Serialize serializes the custom records into a byte slice.
168
func (c CustomRecords) Serialize() ([]byte, error) {
3,362✔
169
        records := tlv.MapToRecords(c)
3,362✔
170
        return EncodeRecords(records)
3,362✔
171
}
3,362✔
172

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

179
// ProduceRecordsSorted converts a slice of record producers into a slice of
180
// records and then sorts it by type.
181
func ProduceRecordsSorted(recordProducers ...tlv.RecordProducer) []tlv.Record {
59,814✔
182
        records := fn.Map(
59,814✔
183
                recordProducers,
59,814✔
184
                func(producer tlv.RecordProducer) tlv.Record {
93,731✔
185
                        return producer.Record()
33,917✔
186
                },
33,917✔
187
        )
188

189
        // Ensure that the set of records are sorted before we attempt to
190
        // decode from the stream, to ensure they're canonical.
191
        tlv.SortRecords(records)
59,814✔
192

59,814✔
193
        return records
59,814✔
194
}
195

196
// SortProducers sorts the given record producers by their type.
197
func SortProducers(producers []tlv.RecordProducer) {
709✔
198
        sort.Slice(producers, func(i, j int) bool {
796✔
199
                recordI := producers[i].Record()
87✔
200
                recordJ := producers[j].Record()
87✔
201
                return recordI.Type() < recordJ.Type()
87✔
202
        })
87✔
203
}
204

205
// TlvMapToRecords converts a TLV map into a slice of records.
206
func TlvMapToRecords(tlvMap tlv.TypeMap) []tlv.Record {
312✔
207
        tlvMapGeneric := make(map[uint64][]byte)
312✔
208
        for k, v := range tlvMap {
2,296✔
209
                tlvMapGeneric[uint64(k)] = v
1,984✔
210
        }
1,984✔
211

212
        return tlv.MapToRecords(tlvMapGeneric)
312✔
213
}
214

215
// RecordsAsProducers converts a slice of records into a slice of record
216
// producers.
217
func RecordsAsProducers(records []tlv.Record) []tlv.RecordProducer {
3,287✔
218
        return fn.Map(records, func(record tlv.Record) tlv.RecordProducer {
8,484✔
219
                return &record
5,197✔
220
        })
5,197✔
221
}
222

223
// EncodeRecords encodes the given records into a byte slice.
224
func EncodeRecords(records []tlv.Record) ([]byte, error) {
47,768✔
225
        var buf bytes.Buffer
47,768✔
226
        if err := EncodeRecordsTo(&buf, records); err != nil {
47,768✔
227
                return nil, err
×
228
        }
×
229

230
        return buf.Bytes(), nil
47,768✔
231
}
232

233
// EncodeRecordsTo encodes the given records into the given writer.
234
func EncodeRecordsTo(w io.Writer, records []tlv.Record) error {
48,092✔
235
        tlvStream, err := tlv.NewStream(records...)
48,092✔
236
        if err != nil {
48,092✔
237
                return err
×
238
        }
×
239

240
        return tlvStream.Encode(w)
48,092✔
241
}
242

243
// DecodeRecords decodes the given byte slice into the given records and returns
244
// the rest as a TLV type map.
245
func DecodeRecords(r io.Reader,
246
        records ...tlv.Record) (tlv.TypeMap, error) {
4,047✔
247

4,047✔
248
        tlvStream, err := tlv.NewStream(records...)
4,047✔
249
        if err != nil {
4,047✔
250
                return nil, err
×
251
        }
×
252

253
        return tlvStream.DecodeWithParsedTypes(r)
4,047✔
254
}
255

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

15,308✔
262
        tlvStream, err := tlv.NewStream(records...)
15,308✔
263
        if err != nil {
15,308✔
264
                return nil, err
×
265
        }
×
266

267
        return tlvStream.DecodeWithParsedTypesP2P(r)
15,308✔
268
}
269

270
// AssertUniqueTypes asserts that the given records have unique types.
271
func AssertUniqueTypes(r []tlv.Record) error {
9,301✔
272
        seen := make(fn.Set[tlv.Type], len(r))
9,301✔
273
        for _, record := range r {
13,922✔
274
                t := record.Type()
4,621✔
275
                if seen.Contains(t) {
4,622✔
276
                        return fmt.Errorf("duplicate record type: %d", t)
1✔
277
                }
1✔
278
                seen.Add(t)
4,620✔
279
        }
280

281
        return nil
9,300✔
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