• 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

88.65
/lnwire/extra_bytes.go
1
package lnwire
2

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

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

12
// ExtraOpaqueData is the set of data that was appended to this message, some
13
// of which we may not actually know how to iterate or parse. By holding onto
14
// this data, we ensure that we're able to properly validate the set of
15
// signatures that cover these new fields, and ensure we're able to make
16
// upgrades to the network in a forwards compatible manner.
17
type ExtraOpaqueData []byte
18

19
// NewExtraOpaqueData creates a new ExtraOpaqueData instance from a tlv.TypeMap.
20
func NewExtraOpaqueData(tlvMap tlv.TypeMap) (ExtraOpaqueData, error) {
6,360✔
21
        // If the tlv map is empty, we'll want to mirror the behavior of
6,360✔
22
        // decoding an empty extra opaque data field (see Decode method).
6,360✔
23
        if len(tlvMap) == 0 {
12,531✔
24
                return make([]byte, 0), nil
6,171✔
25
        }
6,171✔
26

27
        // Convert the TLV map into a slice of records.
28
        records := TlvMapToRecords(tlvMap)
189✔
29

189✔
30
        // Encode the records into the extra data byte slice.
189✔
31
        return EncodeRecords(records)
189✔
32
}
33

34
// Encode attempts to encode the raw extra bytes into the passed io.Writer.
35
func (e *ExtraOpaqueData) Encode(w *bytes.Buffer) error {
205✔
36
        eBytes := []byte((*e)[:])
205✔
37
        if err := WriteBytes(w, eBytes); err != nil {
205✔
38
                return err
×
39
        }
×
40

41
        return nil
205✔
42
}
43

44
// Decode attempts to unpack the raw bytes encoded in the passed-in io.Reader as
45
// a set of extra opaque data.
46
func (e *ExtraOpaqueData) Decode(r io.Reader) error {
13,442✔
47
        // First, we'll attempt to read a set of bytes contained within the
13,442✔
48
        // passed io.Reader (if any exist).
13,442✔
49
        rawBytes, err := io.ReadAll(r)
13,442✔
50
        if err != nil {
13,442✔
51
                return err
×
52
        }
×
53

54
        // If we _do_ have some bytes, then we'll swap out our backing pointer.
55
        // This ensures that any struct that embeds this type will properly
56
        // store the bytes once this method exits.
57
        if len(rawBytes) > 0 {
20,365✔
58
                *e = rawBytes
6,923✔
59
        } else {
13,445✔
60
                *e = make([]byte, 0)
6,522✔
61
        }
6,522✔
62

63
        return nil
13,442✔
64
}
65

66
// PackRecords attempts to encode the set of tlv records into the target
67
// ExtraOpaqueData instance. The records will be encoded as a raw TLV stream
68
// and stored within the backing slice pointer.
69
func (e *ExtraOpaqueData) PackRecords(
70
        recordProducers ...tlv.RecordProducer) error {
34,923✔
71

34,923✔
72
        // Assemble all the records passed in series, then encode them.
34,923✔
73
        records := ProduceRecordsSorted(recordProducers...)
34,923✔
74
        encoded, err := EncodeRecords(records)
34,923✔
75
        if err != nil {
34,923✔
76
                return err
×
77
        }
×
78

79
        *e = encoded
34,923✔
80

34,923✔
81
        return nil
34,923✔
82
}
83

84
// ExtractRecords attempts to decode any types in the internal raw bytes as if
85
// it were a tlv stream. The set of raw parsed types is returned, and any
86
// passed records (if found in the stream) will be parsed into the proper
87
// tlv.Record.
88
func (e *ExtraOpaqueData) ExtractRecords(
89
        recordProducers ...tlv.RecordProducer) (tlv.TypeMap, error) {
15,308✔
90

15,308✔
91
        // First, assemble all the records passed in series.
15,308✔
92
        records := ProduceRecordsSorted(recordProducers...)
15,308✔
93
        extraBytesReader := bytes.NewReader(*e)
15,308✔
94

15,308✔
95
        // Since ExtraOpaqueData is provided by a potentially malicious peer,
15,308✔
96
        // pass it into the P2P decoding variant.
15,308✔
97
        return DecodeRecordsP2P(extraBytesReader, records...)
15,308✔
98
}
15,308✔
99

100
// RecordProducers parses ExtraOpaqueData into a slice of TLV record producers
101
// by interpreting it as a TLV map.
102
func (e *ExtraOpaqueData) RecordProducers() ([]tlv.RecordProducer, error) {
9,307✔
103
        var recordProducers []tlv.RecordProducer
9,307✔
104

9,307✔
105
        // If the instance is nil or empty, return an empty slice.
9,307✔
106
        if e == nil || len(*e) == 0 {
18,491✔
107
                return recordProducers, nil
9,184✔
108
        }
9,184✔
109

110
        // Parse the extra opaque data as a TLV map.
111
        tlvMap, err := e.ExtractRecords()
123✔
112
        if err != nil {
123✔
113
                return nil, err
×
114
        }
×
115

116
        // Convert the TLV map into a slice of record producers.
117
        records := TlvMapToRecords(tlvMap)
123✔
118

123✔
119
        return RecordsAsProducers(records), nil
123✔
120
}
121

122
// EncodeMessageExtraData encodes the given recordProducers into the given
123
// extraData.
124
func EncodeMessageExtraData(extraData *ExtraOpaqueData,
125
        recordProducers ...tlv.RecordProducer) error {
2,086✔
126

2,086✔
127
        // Treat extraData as a mutable reference.
2,086✔
128
        if extraData == nil {
2,086✔
129
                return fmt.Errorf("extra data cannot be nil")
×
130
        }
×
131

132
        // Pack in the series of TLV records into this message. The order we
133
        // pass them in doesn't matter, as the method will ensure that things
134
        // are all properly sorted.
135
        return extraData.PackRecords(recordProducers...)
2,086✔
136
}
137

138
// ParseAndExtractCustomRecords parses the given extra data into the passed-in
139
// records, then returns any remaining records split into custom records and
140
// extra data.
141
func ParseAndExtractCustomRecords(allExtraData ExtraOpaqueData,
142
        knownRecords ...tlv.RecordProducer) (CustomRecords,
143
        fn.Set[tlv.Type], ExtraOpaqueData, error) {
6,499✔
144

6,499✔
145
        extraDataTlvMap, err := allExtraData.ExtractRecords(knownRecords...)
6,499✔
146
        if err != nil {
6,641✔
147
                return nil, nil, nil, err
142✔
148
        }
142✔
149

150
        // Remove the known and now extracted records from the leftover extra
151
        // data map.
152
        parsedKnownRecords := make(fn.Set[tlv.Type], len(knownRecords))
6,357✔
153
        for _, producer := range knownRecords {
11,670✔
154
                r := producer.Record()
5,313✔
155

5,313✔
156
                // Only remove the records if it was parsed (remainder is nil).
5,313✔
157
                // We'll just store the type so we can tell the caller which
5,313✔
158
                // records were actually parsed fully.
5,313✔
159
                val, ok := extraDataTlvMap[r.Type()]
5,313✔
160
                if ok && val == nil {
6,178✔
161
                        parsedKnownRecords.Add(r.Type())
865✔
162
                        delete(extraDataTlvMap, r.Type())
865✔
163
                }
865✔
164
        }
165

166
        // Any records from the extra data TLV map which are in the custom
167
        // records TLV type range will be included in the custom records field
168
        // and removed from the extra data field.
169
        customRecordsTlvMap := make(tlv.TypeMap, len(extraDataTlvMap))
6,357✔
170
        for k, v := range extraDataTlvMap {
11,295✔
171
                // Skip records that are not in the custom records TLV type
4,938✔
172
                // range.
4,938✔
173
                if k < MinCustomRecordsTlvType {
6,220✔
174
                        continue
1,282✔
175
                }
176

177
                // Include the record in the custom records map.
178
                customRecordsTlvMap[k] = v
3,656✔
179

3,656✔
180
                // Now that the record is included in the custom records map,
3,656✔
181
                // we can remove it from the extra data TLV map.
3,656✔
182
                delete(extraDataTlvMap, k)
3,656✔
183
        }
184

185
        // Set the custom records field to the custom records specific TLV
186
        // record map.
187
        customRecords, err := NewCustomRecords(customRecordsTlvMap)
6,357✔
188
        if err != nil {
6,357✔
189
                return nil, nil, nil, err
×
190
        }
×
191

192
        // Encode the remaining records back into the extra data field. These
193
        // records are not in the custom records TLV type range and do not
194
        // have associated fields in the struct that produced the records.
195
        extraData, err := NewExtraOpaqueData(extraDataTlvMap)
6,357✔
196
        if err != nil {
6,357✔
197
                return nil, nil, nil, err
×
198
        }
×
199

200
        // Help with unit testing where we might have the empty value (nil) for
201
        // the extra data instead of the default that's returned by the
202
        // constructor (empty slice).
203
        if len(extraData) == 0 {
12,527✔
204
                extraData = nil
6,170✔
205
        }
6,170✔
206

207
        return customRecords, parsedKnownRecords, extraData, nil
6,357✔
208
}
209

210
// MergeAndEncode merges the known records with the extra data and custom
211
// records, then encodes the merged records into raw bytes.
212
func MergeAndEncode(knownRecords []tlv.RecordProducer,
213
        extraData ExtraOpaqueData, customRecords CustomRecords) ([]byte,
214
        error) {
9,304✔
215

9,304✔
216
        // Construct a slice of all the records that we should include in the
9,304✔
217
        // message extra data field. We will start by including any records from
9,304✔
218
        // the extra data field.
9,304✔
219
        mergedRecords, err := extraData.RecordProducers()
9,304✔
220
        if err != nil {
9,304✔
221
                return nil, err
×
222
        }
×
223

224
        // Merge the known and extra data records.
225
        mergedRecords = append(mergedRecords, knownRecords...)
9,304✔
226

9,304✔
227
        // Include custom records in the extra data wire field if they are
9,304✔
228
        // present. Ensure that the custom records are validated before encoding
9,304✔
229
        // them.
9,304✔
230
        if err := customRecords.Validate(); err != nil {
9,307✔
231
                return nil, fmt.Errorf("custom records validation error: %w",
3✔
232
                        err)
3✔
233
        }
3✔
234

235
        // Extend the message extra data records slice with TLV records from the
236
        // custom records field.
237
        mergedRecords = append(
9,301✔
238
                mergedRecords, customRecords.RecordProducers()...,
9,301✔
239
        )
9,301✔
240

9,301✔
241
        // Now we can sort the records and make sure there are no records with
9,301✔
242
        // the same type that would collide when encoding.
9,301✔
243
        sortedRecords := ProduceRecordsSorted(mergedRecords...)
9,301✔
244
        if err := AssertUniqueTypes(sortedRecords); err != nil {
9,302✔
245
                return nil, err
1✔
246
        }
1✔
247

248
        return EncodeRecords(sortedRecords)
9,300✔
249
}
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