mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-23 14:40:30 +01:00
- Add instance constructor `NewExtraOpaqueDataFromTlvTypeMap`. - Add method `Copy`. - Add method `RecordProducers`.
196 lines
5.6 KiB
Go
196 lines
5.6 KiB
Go
package lnwire
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/lightningnetwork/lnd/tlv"
|
|
)
|
|
|
|
// ExtraOpaqueData is the set of data that was appended to this message, some
|
|
// of which we may not actually know how to iterate or parse. By holding onto
|
|
// this data, we ensure that we're able to properly validate the set of
|
|
// signatures that cover these new fields, and ensure we're able to make
|
|
// upgrades to the network in a forwards compatible manner.
|
|
type ExtraOpaqueData []byte
|
|
|
|
// NewExtraOpaqueDataFromTlvTypeMap creates a new ExtraOpaqueData instance from
|
|
// a tlv.TypeMap.
|
|
func NewExtraOpaqueDataFromTlvTypeMap(tlvMap tlv.TypeMap) (ExtraOpaqueData,
|
|
error) {
|
|
|
|
extraData := ExtraOpaqueData{}
|
|
|
|
// If the tlv map is empty, return an empty extra data instance.
|
|
if len(tlvMap) == 0 {
|
|
return extraData, nil
|
|
}
|
|
|
|
// Convert the tlv map to a generic type map.
|
|
tlvMapGeneric := make(map[uint64][]byte)
|
|
for k, v := range tlvMap {
|
|
tlvMapGeneric[uint64(k)] = v
|
|
}
|
|
|
|
// Convert the generic type map to a slice of records.
|
|
records := tlv.MapToRecords(tlvMapGeneric)
|
|
|
|
// Encode the records into the extra data byte slice.
|
|
tlvStream, err := tlv.NewStream(records...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var bytesWriter bytes.Buffer
|
|
if err := tlvStream.Encode(&bytesWriter); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bytesWriter.Bytes(), nil
|
|
}
|
|
|
|
// Encode attempts to encode the raw extra bytes into the passed io.Writer.
|
|
func (e *ExtraOpaqueData) Encode(w *bytes.Buffer) error {
|
|
eBytes := []byte((*e)[:])
|
|
if err := WriteBytes(w, eBytes); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Decode attempts to unpack the raw bytes encoded in the passed io.Reader as a
|
|
// set of extra opaque data.
|
|
func (e *ExtraOpaqueData) Decode(r io.Reader) error {
|
|
// First, we'll attempt to read a set of bytes contained within the
|
|
// passed io.Reader (if any exist).
|
|
rawBytes, err := io.ReadAll(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If we _do_ have some bytes, then we'll swap out our backing pointer.
|
|
// This ensures that any struct that embeds this type will properly
|
|
// store the bytes once this method exits.
|
|
if len(rawBytes) > 0 {
|
|
*e = ExtraOpaqueData(rawBytes)
|
|
} else {
|
|
*e = make([]byte, 0)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PackRecords attempts to encode the set of tlv records into the target
|
|
// ExtraOpaqueData instance. The records will be encoded as a raw TLV stream
|
|
// and stored within the backing slice pointer.
|
|
func (e *ExtraOpaqueData) PackRecords(recordProducers ...tlv.RecordProducer) error {
|
|
// First, assemble all the records passed in in series.
|
|
records := make([]tlv.Record, 0, len(recordProducers))
|
|
for _, producer := range recordProducers {
|
|
records = append(records, producer.Record())
|
|
}
|
|
|
|
// Ensure that the set of records are sorted before we encode them into
|
|
// the stream, to ensure they're canonical.
|
|
tlv.SortRecords(records)
|
|
|
|
tlvStream, err := tlv.NewStream(records...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var extraBytesWriter bytes.Buffer
|
|
if err := tlvStream.Encode(&extraBytesWriter); err != nil {
|
|
return err
|
|
}
|
|
|
|
*e = ExtraOpaqueData(extraBytesWriter.Bytes())
|
|
|
|
return nil
|
|
}
|
|
|
|
// ExtractRecords attempts to decode any types in the internal raw bytes as if
|
|
// it were a tlv stream. The set of raw parsed types is returned, and any
|
|
// passed records (if found in the stream) will be parsed into the proper
|
|
// tlv.Record.
|
|
func (e *ExtraOpaqueData) ExtractRecords(recordProducers ...tlv.RecordProducer) (
|
|
tlv.TypeMap, error) {
|
|
|
|
// First, assemble all the records passed in in series.
|
|
records := make([]tlv.Record, 0, len(recordProducers))
|
|
for _, producer := range recordProducers {
|
|
records = append(records, producer.Record())
|
|
}
|
|
|
|
// Ensure that the set of records are sorted before we attempt to
|
|
// decode from the stream, to ensure they're canonical.
|
|
tlv.SortRecords(records)
|
|
|
|
extraBytesReader := bytes.NewReader(*e)
|
|
|
|
tlvStream, err := tlv.NewStream(records...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Since ExtraOpaqueData is provided by a potentially malicious peer,
|
|
// pass it into the P2P decoding variant.
|
|
return tlvStream.DecodeWithParsedTypesP2P(extraBytesReader)
|
|
}
|
|
|
|
// RecordProducers parses ExtraOpaqueData into a slice of TLV record producers
|
|
// by interpreting it as a TLV map.
|
|
func (e *ExtraOpaqueData) RecordProducers() ([]tlv.RecordProducer, error) {
|
|
var recordProducers []tlv.RecordProducer
|
|
|
|
// If the instance is nil or empty, return an empty slice.
|
|
if e == nil || len(*e) == 0 {
|
|
return recordProducers, nil
|
|
}
|
|
|
|
// Parse the extra opaque data as a TLV map.
|
|
tlvMap, err := e.ExtractRecords()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Convert the TLV map into a slice of records.
|
|
tlvMapGeneric := make(map[uint64][]byte)
|
|
for k, v := range tlvMap {
|
|
tlvMapGeneric[uint64(k)] = v
|
|
}
|
|
records := tlv.MapToRecords(tlvMapGeneric)
|
|
|
|
// Convert the records to record producers.
|
|
recordProducers = make([]tlv.RecordProducer, len(records))
|
|
for i, record := range records {
|
|
recordProducers[i] = &recordProducer{record}
|
|
}
|
|
|
|
return recordProducers, nil
|
|
}
|
|
|
|
// Copy returns a copy of the target ExtraOpaqueData instance.
|
|
func (e *ExtraOpaqueData) Copy() ExtraOpaqueData {
|
|
copyData := make([]byte, len(*e))
|
|
copy(copyData, *e)
|
|
return copyData
|
|
}
|
|
|
|
// EncodeMessageExtraData encodes the given recordProducers into the given
|
|
// extraData.
|
|
func EncodeMessageExtraData(extraData *ExtraOpaqueData,
|
|
recordProducers ...tlv.RecordProducer) error {
|
|
|
|
// Treat extraData as a mutable reference.
|
|
if extraData == nil {
|
|
return fmt.Errorf("extra data cannot be nil")
|
|
}
|
|
|
|
// Pack in the series of TLV records into this message. The order we
|
|
// pass them in doesn't matter, as the method will ensure that things
|
|
// are all properly sorted.
|
|
return extraData.PackRecords(recordProducers...)
|
|
}
|