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 }