/ lnwire / extra_bytes.go
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) {
 21  	// If the tlv map is empty, we'll want to mirror the behavior of
 22  	// decoding an empty extra opaque data field (see Decode method).
 23  	if len(tlvMap) == 0 {
 24  		return make([]byte, 0), nil
 25  	}
 26  
 27  	// Convert the TLV map into a slice of records.
 28  	records := TlvMapToRecords(tlvMap)
 29  
 30  	// Encode the records into the extra data byte slice.
 31  	return EncodeRecords(records)
 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 {
 36  	eBytes := []byte((*e)[:])
 37  	if err := WriteBytes(w, eBytes); err != nil {
 38  		return err
 39  	}
 40  
 41  	return nil
 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 {
 47  	// First, we'll attempt to read a set of bytes contained within the
 48  	// passed io.Reader (if any exist).
 49  	rawBytes, err := io.ReadAll(r)
 50  	if err != nil {
 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 {
 58  		*e = rawBytes
 59  	} else {
 60  		*e = make([]byte, 0)
 61  	}
 62  
 63  	return nil
 64  }
 65  
 66  // ValidateTLV checks that the raw bytes that make up the ExtraOpaqueData
 67  // instance are a valid TLV stream.
 68  func (e *ExtraOpaqueData) ValidateTLV() error {
 69  	// There is nothing to validate if the ExtraOpaqueData is nil or empty.
 70  	if e == nil || len(*e) == 0 {
 71  		return nil
 72  	}
 73  
 74  	tlvStream, err := tlv.NewStream()
 75  	if err != nil {
 76  		return err
 77  	}
 78  
 79  	// Ensure that the TLV stream is valid by attempting to decode it.
 80  	_, err = tlvStream.DecodeWithParsedTypesP2P(bytes.NewReader(*e))
 81  	if err != nil {
 82  		return fmt.Errorf("invalid TLV stream: %w: %v", err, *e)
 83  	}
 84  
 85  	return nil
 86  }
 87  
 88  // PackRecords attempts to encode the set of tlv records into the target
 89  // ExtraOpaqueData instance. The records will be encoded as a raw TLV stream
 90  // and stored within the backing slice pointer.
 91  func (e *ExtraOpaqueData) PackRecords(
 92  	recordProducers ...tlv.RecordProducer) error {
 93  
 94  	// Assemble all the records passed in series, then encode them.
 95  	records := ProduceRecordsSorted(recordProducers...)
 96  	encoded, err := EncodeRecords(records)
 97  	if err != nil {
 98  		return err
 99  	}
100  
101  	*e = encoded
102  
103  	return nil
104  }
105  
106  // ExtractRecords attempts to decode any types in the internal raw bytes as if
107  // it were a tlv stream. The set of raw parsed types is returned, and any
108  // passed records (if found in the stream) will be parsed into the proper
109  // tlv.Record.
110  func (e *ExtraOpaqueData) ExtractRecords(
111  	recordProducers ...tlv.RecordProducer) (tlv.TypeMap, error) {
112  
113  	// First, assemble all the records passed in series.
114  	records := ProduceRecordsSorted(recordProducers...)
115  	extraBytesReader := bytes.NewReader(*e)
116  
117  	// Since ExtraOpaqueData is provided by a potentially malicious peer,
118  	// pass it into the P2P decoding variant.
119  	return DecodeRecordsP2P(extraBytesReader, records...)
120  }
121  
122  // RecordProducers parses ExtraOpaqueData into a slice of TLV record producers
123  // by interpreting it as a TLV map.
124  func (e *ExtraOpaqueData) RecordProducers() ([]tlv.RecordProducer, error) {
125  	var recordProducers []tlv.RecordProducer
126  
127  	// If the instance is nil or empty, return an empty slice.
128  	if e == nil || len(*e) == 0 {
129  		return recordProducers, nil
130  	}
131  
132  	// Parse the extra opaque data as a TLV map.
133  	tlvMap, err := e.ExtractRecords()
134  	if err != nil {
135  		return nil, err
136  	}
137  
138  	// Convert the TLV map into a slice of record producers.
139  	records := TlvMapToRecords(tlvMap)
140  
141  	return RecordsAsProducers(records), nil
142  }
143  
144  // EncodeMessageExtraData encodes the given recordProducers into the given
145  // extraData.
146  func EncodeMessageExtraData(extraData *ExtraOpaqueData,
147  	recordProducers ...tlv.RecordProducer) error {
148  
149  	// Treat extraData as a mutable reference.
150  	if extraData == nil {
151  		return fmt.Errorf("extra data cannot be nil")
152  	}
153  
154  	// Pack in the series of TLV records into this message. The order we
155  	// pass them in doesn't matter, as the method will ensure that things
156  	// are all properly sorted.
157  	return extraData.PackRecords(recordProducers...)
158  }
159  
160  // ParseAndExtractCustomRecords parses the given extra data into the passed-in
161  // records, then returns any remaining records split into custom records and
162  // extra data.
163  func ParseAndExtractCustomRecords(allExtraData ExtraOpaqueData,
164  	knownRecords ...tlv.RecordProducer) (CustomRecords,
165  	fn.Set[tlv.Type], ExtraOpaqueData, error) {
166  
167  	extraDataTlvMap, err := allExtraData.ExtractRecords(knownRecords...)
168  	if err != nil {
169  		return nil, nil, nil, err
170  	}
171  
172  	// Remove the known and now extracted records from the leftover extra
173  	// data map.
174  	parsedKnownRecords := make(fn.Set[tlv.Type], len(knownRecords))
175  	for _, producer := range knownRecords {
176  		r := producer.Record()
177  
178  		// Only remove the records if it was parsed (remainder is nil).
179  		// We'll just store the type so we can tell the caller which
180  		// records were actually parsed fully.
181  		val, ok := extraDataTlvMap[r.Type()]
182  		if ok && val == nil {
183  			parsedKnownRecords.Add(r.Type())
184  			delete(extraDataTlvMap, r.Type())
185  		}
186  	}
187  
188  	// Any records from the extra data TLV map which are in the custom
189  	// records TLV type range will be included in the custom records field
190  	// and removed from the extra data field.
191  	customRecordsTlvMap := make(tlv.TypeMap, len(extraDataTlvMap))
192  	for k, v := range extraDataTlvMap {
193  		// Skip records that are not in the custom records TLV type
194  		// range.
195  		if k < MinCustomRecordsTlvType {
196  			continue
197  		}
198  
199  		// Include the record in the custom records map.
200  		customRecordsTlvMap[k] = v
201  
202  		// Now that the record is included in the custom records map,
203  		// we can remove it from the extra data TLV map.
204  		delete(extraDataTlvMap, k)
205  	}
206  
207  	// Set the custom records field to the custom records specific TLV
208  	// record map.
209  	customRecords, err := NewCustomRecords(customRecordsTlvMap)
210  	if err != nil {
211  		return nil, nil, nil, err
212  	}
213  
214  	// Encode the remaining records back into the extra data field. These
215  	// records are not in the custom records TLV type range and do not
216  	// have associated fields in the struct that produced the records.
217  	extraData, err := NewExtraOpaqueData(extraDataTlvMap)
218  	if err != nil {
219  		return nil, nil, nil, err
220  	}
221  
222  	// Help with unit testing where we might have the empty value (nil) for
223  	// the extra data instead of the default that's returned by the
224  	// constructor (empty slice).
225  	if len(extraData) == 0 {
226  		extraData = nil
227  	}
228  
229  	return customRecords, parsedKnownRecords, extraData, nil
230  }
231  
232  // MergeAndEncode merges the known records with the extra data and custom
233  // records, then encodes the merged records into raw bytes.
234  func MergeAndEncode(knownRecords []tlv.RecordProducer,
235  	extraData ExtraOpaqueData, customRecords CustomRecords) ([]byte,
236  	error) {
237  
238  	// Construct a slice of all the records that we should include in the
239  	// message extra data field. We will start by including any records from
240  	// the extra data field.
241  	mergedRecords, err := extraData.RecordProducers()
242  	if err != nil {
243  		return nil, err
244  	}
245  
246  	// Merge the known and extra data records.
247  	mergedRecords = append(mergedRecords, knownRecords...)
248  
249  	// Include custom records in the extra data wire field if they are
250  	// present. Ensure that the custom records are validated before encoding
251  	// them.
252  	if err := customRecords.Validate(); err != nil {
253  		return nil, fmt.Errorf("custom records validation error: %w",
254  			err)
255  	}
256  
257  	// Extend the message extra data records slice with TLV records from the
258  	// custom records field.
259  	mergedRecords = append(
260  		mergedRecords, customRecords.RecordProducers()...,
261  	)
262  
263  	// Now we can sort the records and make sure there are no records with
264  	// the same type that would collide when encoding.
265  	sortedRecords := ProduceRecordsSorted(mergedRecords...)
266  	if err := AssertUniqueTypes(sortedRecords); err != nil {
267  		return nil, err
268  	}
269  
270  	return EncodeRecords(sortedRecords)
271  }
272  
273  // ParseAndExtractExtraData parses the given extra data into the passed-in
274  // records, then returns any remaining records as extra data.
275  func ParseAndExtractExtraData(allTlvData ExtraOpaqueData,
276  	knownRecords ...tlv.RecordProducer) (fn.Set[tlv.Type],
277  	ExtraOpaqueData, error) {
278  
279  	extraDataTlvMap, err := allTlvData.ExtractRecords(knownRecords...)
280  	if err != nil {
281  		return nil, nil, err
282  	}
283  
284  	// Remove the known and now extracted records from the leftover extra
285  	// data map.
286  	parsedKnownRecords := make(fn.Set[tlv.Type], len(knownRecords))
287  	for _, producer := range knownRecords {
288  		r := producer.Record()
289  
290  		// Only remove the records if it was parsed (remainder is nil).
291  		// We'll just store the type so we can tell the caller which
292  		// records were actually parsed fully.
293  		val, ok := extraDataTlvMap[r.Type()]
294  		if ok && val == nil {
295  			parsedKnownRecords.Add(r.Type())
296  			delete(extraDataTlvMap, r.Type())
297  		}
298  	}
299  
300  	// Encode the remaining records back into the extra data field. These
301  	// records are not in the custom records TLV type range and do not
302  	// have associated fields in the struct that produced the records.
303  	extraData, err := NewExtraOpaqueData(extraDataTlvMap)
304  	if err != nil {
305  		return nil, nil, err
306  	}
307  
308  	return parsedKnownRecords, extraData, nil
309  }