• 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

0.0
/channeldb/migration/lnwire21/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) {
×
26
        // Make comparisons in unit tests easy by returning nil if the map is
×
27
        // empty.
×
28
        if len(tlvMap) == 0 {
×
29
                return nil, nil
×
30
        }
×
31

32
        customRecords := make(CustomRecords, len(tlvMap))
×
33
        for k, v := range tlvMap {
×
34
                customRecords[uint64(k)] = v
×
35
        }
×
36

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

44
        return customRecords, nil
×
45
}
46

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

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

59
        return NewCustomRecords(typeMap)
×
60
}
61

62
// Validate checks that all custom records are in the custom type range.
UNCOV
63
func (c CustomRecords) Validate() error {
×
UNCOV
64
        if c == nil {
×
UNCOV
65
                return nil
×
UNCOV
66
        }
×
67

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

UNCOV
75
        return nil
×
76
}
77

78
// Copy returns a copy of the custom records.
79
func (c CustomRecords) Copy() CustomRecords {
×
80
        if c == nil {
×
81
                return nil
×
82
        }
×
83

84
        customRecords := make(CustomRecords, len(c))
×
85
        for k, v := range c {
×
86
                customRecords[k] = v
×
87
        }
×
88

89
        return customRecords
×
90
}
91

92
// ExtendRecordProducers extends the given records slice with the custom
93
// records. The resultant records slice will be sorted if the given records
94
// slice contains TLV types greater than or equal to MinCustomRecordsTlvType.
95
func (c CustomRecords) ExtendRecordProducers(
96
        producers []tlv.RecordProducer) ([]tlv.RecordProducer, error) {
×
97

×
98
        // If the custom records are nil or empty, there is nothing to do.
×
99
        if len(c) == 0 {
×
100
                return producers, nil
×
101
        }
×
102

103
        // Validate the custom records.
104
        err := c.Validate()
×
105
        if err != nil {
×
106
                return nil, err
×
107
        }
×
108

109
        // Ensure that the existing records slice TLV types are not also present
110
        // in the custom records. If they are, the resultant extended records
111
        // slice would erroneously contain duplicate TLV types.
112
        for _, rp := range producers {
×
113
                record := rp.Record()
×
114
                recordTlvType := uint64(record.Type())
×
115

×
116
                _, foundDuplicateTlvType := c[recordTlvType]
×
117
                if foundDuplicateTlvType {
×
118
                        return nil, fmt.Errorf("custom records contains a TLV "+
×
119
                                "type that is already present in the "+
×
120
                                "existing records: %d", recordTlvType)
×
121
                }
×
122
        }
123

124
        // Convert the custom records map to a TLV record producer slice and
125
        // append them to the exiting records slice.
126
        customRecordProducers := RecordsAsProducers(tlv.MapToRecords(c))
×
127
        producers = append(producers, customRecordProducers...)
×
128

×
129
        // If the records slice which was given as an argument included TLV
×
130
        // values greater than or equal to the minimum custom records TLV type
×
131
        // we will sort the extended records slice to ensure that it is ordered
×
132
        // correctly.
×
133
        SortProducers(producers)
×
134

×
135
        return producers, nil
×
136
}
137

138
// RecordProducers returns a slice of record producers for the custom records.
139
func (c CustomRecords) RecordProducers() []tlv.RecordProducer {
×
140
        // If the custom records are nil or empty, return an empty slice.
×
141
        if len(c) == 0 {
×
142
                return nil
×
143
        }
×
144

145
        // Convert the custom records map to a TLV record producer slice.
146
        records := tlv.MapToRecords(c)
×
147

×
148
        return RecordsAsProducers(records)
×
149
}
150

151
// Serialize serializes the custom records into a byte slice.
152
func (c CustomRecords) Serialize() ([]byte, error) {
×
153
        records := tlv.MapToRecords(c)
×
154
        return EncodeRecords(records)
×
155
}
×
156

157
// SerializeTo serializes the custom records into the given writer.
158
func (c CustomRecords) SerializeTo(w io.Writer) error {
×
159
        records := tlv.MapToRecords(c)
×
160
        return EncodeRecordsTo(w, records)
×
161
}
×
162

163
// ProduceRecordsSorted converts a slice of record producers into a slice of
164
// records and then sorts it by type.
UNCOV
165
func ProduceRecordsSorted(recordProducers ...tlv.RecordProducer) []tlv.Record {
×
UNCOV
166
        records := fn.Map(
×
UNCOV
167
                recordProducers,
×
UNCOV
168
                func(producer tlv.RecordProducer) tlv.Record {
×
UNCOV
169
                        return producer.Record()
×
UNCOV
170
                },
×
171
        )
172

173
        // Ensure that the set of records are sorted before we attempt to
174
        // decode from the stream, to ensure they're canonical.
UNCOV
175
        tlv.SortRecords(records)
×
UNCOV
176

×
UNCOV
177
        return records
×
178
}
179

180
// SortProducers sorts the given record producers by their type.
181
func SortProducers(producers []tlv.RecordProducer) {
×
182
        sort.Slice(producers, func(i, j int) bool {
×
183
                recordI := producers[i].Record()
×
184
                recordJ := producers[j].Record()
×
185
                return recordI.Type() < recordJ.Type()
×
186
        })
×
187
}
188

189
// TlvMapToRecords converts a TLV map into a slice of records.
190
func TlvMapToRecords(tlvMap tlv.TypeMap) []tlv.Record {
×
191
        tlvMapGeneric := make(map[uint64][]byte)
×
192
        for k, v := range tlvMap {
×
193
                tlvMapGeneric[uint64(k)] = v
×
194
        }
×
195

196
        return tlv.MapToRecords(tlvMapGeneric)
×
197
}
198

199
// RecordsAsProducers converts a slice of records into a slice of record
200
// producers.
201
func RecordsAsProducers(records []tlv.Record) []tlv.RecordProducer {
×
202
        return fn.Map(records, func(record tlv.Record) tlv.RecordProducer {
×
203
                return &record
×
204
        })
×
205
}
206

207
// EncodeRecords encodes the given records into a byte slice.
208
func EncodeRecords(records []tlv.Record) ([]byte, error) {
×
209
        var buf bytes.Buffer
×
210
        if err := EncodeRecordsTo(&buf, records); err != nil {
×
211
                return nil, err
×
212
        }
×
213

214
        return buf.Bytes(), nil
×
215
}
216

217
// EncodeRecordsTo encodes the given records into the given writer.
UNCOV
218
func EncodeRecordsTo(w io.Writer, records []tlv.Record) error {
×
UNCOV
219
        tlvStream, err := tlv.NewStream(records...)
×
UNCOV
220
        if err != nil {
×
221
                return err
×
222
        }
×
223

UNCOV
224
        return tlvStream.Encode(w)
×
225
}
226

227
// DecodeRecords decodes the given byte slice into the given records and returns
228
// the rest as a TLV type map.
229
func DecodeRecords(r io.Reader,
230
        records ...tlv.Record) (tlv.TypeMap, error) {
×
231

×
232
        tlvStream, err := tlv.NewStream(records...)
×
233
        if err != nil {
×
234
                return nil, err
×
235
        }
×
236

237
        return tlvStream.DecodeWithParsedTypes(r)
×
238
}
239

240
// DecodeRecordsP2P decodes the given byte slice into the given records and
241
// returns the rest as a TLV type map. This function is identical to
242
// DecodeRecords except that the record size is capped at 65535.
243
func DecodeRecordsP2P(r *bytes.Reader,
244
        records ...tlv.Record) (tlv.TypeMap, error) {
×
245

×
246
        tlvStream, err := tlv.NewStream(records...)
×
247
        if err != nil {
×
248
                return nil, err
×
249
        }
×
250

251
        return tlvStream.DecodeWithParsedTypesP2P(r)
×
252
}
253

254
// AssertUniqueTypes asserts that the given records have unique types.
255
func AssertUniqueTypes(r []tlv.Record) error {
×
256
        seen := make(fn.Set[tlv.Type], len(r))
×
257
        for _, record := range r {
×
258
                t := record.Type()
×
259
                if seen.Contains(t) {
×
260
                        return fmt.Errorf("duplicate record type: %d", t)
×
261
                }
×
262
                seen.Add(t)
×
263
        }
264

265
        return nil
×
266
}
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