Files
lnd/lnwire/pure_tlv.go
Elle Mouton 4addfd1d1f lnwire: introduce PureTLVMessage
PureTLVMessage describes an LN message that is a pure TLV stream. If the
message includes a signature, it will sign all the TLV records in the
inclusive ranges: 0 to 159 and 1000000000 to 2999999999.

A comprehensive test is added that shows how two versions of the same
message remain forward compatible.
2025-09-01 11:04:47 +02:00

106 lines
3.8 KiB
Go

package lnwire
import (
"bytes"
"github.com/lightningnetwork/lnd/tlv"
)
const (
// pureTLVUnsignedRangeOneStart defines the start of the first unsigned
// TLV range used for pure TLV messages. The range is inclusive of this
// number.
pureTLVUnsignedRangeOneStart = 160
// pureTLVSignedSecondRangeStart defines the start of the second signed
// TLV range used for pure TLV messages. The range is inclusive of this
// number. Note that the first range is the inclusive range of 0-159.
pureTLVSignedSecondRangeStart = 1000000000
// pureTLVUnsignedRangeTwoStart defines the start of the second unsigned
// TLV range used for pure TLV message.
pureTLVUnsignedRangeTwoStart = 3000000000
)
// PureTLVMessage describes an LN message that is a pure TLV stream. If the
// message includes a signature, it will sign all the TLV records in the
// inclusive ranges: 0 to 159 and 1000000000 to 2999999999.
type PureTLVMessage interface {
// AllRecords returns all the TLV records for the message. This will
// include all the records we know about along with any that we don't
// know about but that fall in the signed TLV range.
AllRecords() []tlv.Record
}
// EncodePureTLVMessage encodes the given PureTLVMessage to the given buffer.
func EncodePureTLVMessage(msg PureTLVMessage, buf *bytes.Buffer) error {
return EncodeRecordsTo(buf, msg.AllRecords())
}
// SerialiseFieldsToSign serialises all the records from the given
// PureTLVMessage that fall within the signed TLV range.
func SerialiseFieldsToSign(msg PureTLVMessage) ([]byte, error) {
// Filter out all the fields not in the signed ranges.
var signedRecords []tlv.Record
for _, record := range msg.AllRecords() {
if InUnsignedRange(record.Type()) {
continue
}
signedRecords = append(signedRecords, record)
}
var buf bytes.Buffer
if err := EncodeRecordsTo(&buf, signedRecords); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// InUnsignedRange returns true if the given TLV type falls outside the TLV
// ranges that the signature of a pure TLV message will cover.
func InUnsignedRange(t tlv.Type) bool {
return (t >= pureTLVUnsignedRangeOneStart &&
t < pureTLVSignedSecondRangeStart) ||
t >= pureTLVUnsignedRangeTwoStart
}
// ExtraSignedFields is a type that stores a map from TLV types in the signed
// range (for PureMessages) to their corresponding serialised values. This type
// can be used to keep around data that we don't yet understand but that we need
// for re-composing the wire message since the signature covers these fields.
type ExtraSignedFields map[uint64][]byte
// ExtraSignedFieldsFromTypeMap is a helper that can be used alongside calls to
// the tlv.Stream DecodeWithParsedTypesP2P or DecodeWithParsedTypes methods to
// extract the tlv type and value pairs in the defined PureTLVMessage signed
// range which we have not handled with any of our defined Records. These
// methods will return a tlv.TypeMap containing the records that were extracted
// from an io.Reader. If the record was know and handled by a defined record,
// then the value accompanying the record's type in the map will be nil.
// Otherwise, if the record was unhandled, it will be non-nil.
func ExtraSignedFieldsFromTypeMap(m tlv.TypeMap) ExtraSignedFields {
extraFields := make(ExtraSignedFields)
for t, v := range m {
// If the value in the type map is nil, then it indicates that
// we know this type, and it was handled by one of the records
// we passed to the decode function vai the TLV stream.
if v == nil {
continue
}
// No need to keep this field if it is unknown to us and is not
// in the sign range.
if InUnsignedRange(t) {
continue
}
// Otherwise, this is an un-handled type, so we keep track of
// it for signature validation and re-encoding later on.
extraFields[uint64(t)] = v
}
return extraFields
}