From 420001a98cf210fa36db9e8ce5a579bad16794a4 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 4 Jun 2025 12:08:19 +0200 Subject: [PATCH] lnwire: add InboundFee TLV record to ChannelUpdate --- lnwire/channel_update.go | 34 +++++++++++++++++++++++++++++-- lnwire/test_message.go | 43 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index 55d9d3181..558d81a9e 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -6,6 +6,7 @@ import ( "io" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightningnetwork/lnd/tlv" ) // ChanUpdateMsgFlags is a bitfield that signals whether optional fields are @@ -114,6 +115,10 @@ type ChannelUpdate1 struct { // HtlcMaximumMsat is the maximum HTLC value which will be accepted. HtlcMaximumMsat MilliSatoshi + // InboundFee is an optional TLV record that contains the fee + // information for incoming HTLCs. + InboundFee tlv.OptionalRecordT[tlv.TlvType55555, Fee] + // ExtraData is the set of data that was appended to this message to // fill out the full maximum transport message size. These fields can // be used to specify optional data such as custom TLV fields. @@ -156,12 +161,27 @@ func (a *ChannelUpdate1) Decode(r io.Reader, _ uint32) error { } } - err = a.ExtraOpaqueData.Decode(r) + var tlvRecords ExtraOpaqueData + if err := ReadElements(r, &tlvRecords); err != nil { + return err + } + + var inboundFee = a.InboundFee.Zero() + typeMap, err := tlvRecords.ExtractRecords(&inboundFee) if err != nil { return err } - return a.ExtraOpaqueData.ValidateTLV() + val, ok := typeMap[a.InboundFee.TlvType()] + if ok && val == nil { + a.InboundFee = tlv.SomeRecordT(inboundFee) + } + + if len(tlvRecords) != 0 { + a.ExtraOpaqueData = tlvRecords + } + + return nil } // Encode serializes the target ChannelUpdate into the passed io.Writer @@ -218,6 +238,16 @@ func (a *ChannelUpdate1) Encode(w *bytes.Buffer, pver uint32) error { } } + recordProducers := make([]tlv.RecordProducer, 0, 1) + a.InboundFee.WhenSome(func(fee tlv.RecordT[tlv.TlvType55555, Fee]) { + recordProducers = append(recordProducers, &fee) + }) + + err := EncodeMessageExtraData(&a.ExtraOpaqueData, recordProducers...) + if err != nil { + return err + } + // Finally, append any extra opaque data. return WriteBytes(w, a.ExtraOpaqueData) } diff --git a/lnwire/test_message.go b/lnwire/test_message.go index 8b3d98400..9d086c868 100644 --- a/lnwire/test_message.go +++ b/lnwire/test_message.go @@ -12,6 +12,7 @@ import ( "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/require" "pgregory.net/rapid" ) @@ -406,6 +407,45 @@ func (a *ChannelUpdate1) RandTestMessage(t *rapid.T) Message { maxHtlc = 0 } + // Randomly decide if an inbound fee should be included. + // By default, our extra opaque data will just be random TLV but if we + // include an inbound fee, then we will also set the record in the + // extra opaque data. + var ( + customRecords, _ = RandCustomRecords(t, nil, false) + inboundFee tlv.OptionalRecordT[tlv.TlvType55555, Fee] + ) + includeInboundFee := rapid.Bool().Draw(t, "includeInboundFee") + if includeInboundFee { + if customRecords == nil { + customRecords = make(CustomRecords) + } + + inFeeBase := int32( + rapid.IntRange(-1000, 1000).Draw(t, "inFeeBase"), + ) + inFeeProp := int32( + rapid.IntRange(-1000, 1000).Draw(t, "inFeeProp"), + ) + fee := Fee{ + BaseFee: inFeeBase, + FeeRate: inFeeProp, + } + inboundFee = tlv.SomeRecordT( + tlv.NewRecordT[tlv.TlvType55555, Fee](fee), + ) + + var b bytes.Buffer + feeRecord := fee.Record() + err := feeRecord.Encode(&b) + require.NoError(t, err) + + customRecords[uint64(FeeRecordType)] = b.Bytes() + } + + extraBytes, err := customRecords.Serialize() + require.NoError(t, err) + return &ChannelUpdate1{ Signature: RandSignature(t), ChainHash: hash, @@ -428,7 +468,8 @@ func (a *ChannelUpdate1) RandTestMessage(t *rapid.T) Message { t, "feeRate"), ), HtlcMaximumMsat: maxHtlc, - ExtraOpaqueData: RandExtraOpaqueData(t, nil), + InboundFee: inboundFee, + ExtraOpaqueData: extraBytes, } }