lnwire: preserve unknown odd zero-length final hop TLVs

When decoding an onion message payload, the loop that forwards
unrecognized final hop TLVs to higher layers skipped any entry with a
zero-length value. DecodeWithParsedTypesP2P marks a recognized type with
a nil map entry but records the raw bytes for an unknown type, and an
unknown odd TLV with an empty value is valid. Keying the skip off a
length check therefore dropped such a TLV instead of passing it through.

Test the recognized-type skip against a nil entry so a valid unknown odd
zero-length TLV is preserved.
This commit is contained in:
bitromortac 2026-06-11 16:10:31 +02:00
parent 31168557c3
commit bc5cfb5adc
No known key found for this signature in database
GPG key ID: 1965063FC13BEBE2
2 changed files with 30 additions and 4 deletions

View file

@ -179,9 +179,13 @@ func (o *OnionMessagePayload) Decode(r io.Reader) (map[tlv.Type][]byte, error) {
continue
}
// Skip any tlvs that have been recognized in our decoding (a
// zero entry means that we recognized the entry).
if len(tlvBytes) == 0 {
// Skip any tlvs that have been recognized in our decoding.
// DecodeWithParsedTypesP2P stores a nil entry for known types
// that it decoded into a dedicated field above, and the raw
// bytes for unknown types. A nil check (rather than a length
// check) is required so that a valid unknown odd tlv with a
// zero-length value is not mistaken for a recognized type.
if tlvBytes == nil {
continue
}
@ -199,7 +203,7 @@ func (o *OnionMessagePayload) Decode(r io.Reader) (map[tlv.Type][]byte, error) {
// If we read out an invoice, invoice error or invoice request tlv
// sub-namespace, add it to our set of final payloads. This value won't
// have been added in the loop above, because we recognized the TLV so
// len(tlvMap[invoiceType].tlvBytes) will be zero (thus, skipped above).
// tlvMap[invoiceType].tlvBytes will be nil (thus, skipped above).
if _, ok := tlvMap[InvoiceNamespaceType]; ok {
o.FinalHopTLVs = append(
o.FinalHopTLVs, invoicePayload,

View file

@ -294,6 +294,28 @@ func TestOnionMessagePayloadRoundTrip(t *testing.T) {
decoded.FinalHopTLVs[0].Value,
)
})
t.Run("odd unknown zero-length final hop TLV", func(t *testing.T) {
t.Parallel()
// A valid unknown odd tlv with a zero-length value must be
// preserved rather than mistaken for a recognized type, which
// is why decode keys off a nil entry instead of an empty one.
original := &OnionMessagePayload{
FinalHopTLVs: []*FinalHopTLV{
{
TLVType: 65,
Value: []byte{},
},
},
}
decoded := encodeAndDecode(t, original)
require.Len(t, decoded.FinalHopTLVs, 1)
require.Equal(t, tlv.Type(65), decoded.FinalHopTLVs[0].TLVType)
require.Empty(t, decoded.FinalHopTLVs[0].Value)
})
}
// TestFinalHopTLVValidate tests that FinalHopTLV.Validate correctly rejects