From 988d01de0df2994d278faa81306322bb6c8da459 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 3 Mar 2021 19:32:14 -0800 Subject: [PATCH] lnwire: introduce new explicit ChannelType TLV record In this commit, we add a new TLV record that's intended to be used as an explicit channel commitment type for a new form of funding negotiation, and later on a dynamic commitment upgrade protocol. As defined, we have 3 channel types: base (the OG), tweakless, and anchors w/ zero fee HTLCs. We omit the original variant of anchors as it was never truly deployed from the PoV of lnd. --- lnwire/channel_type.go | 58 +++++++++++++++++++++++++++++++++++++ lnwire/channel_type_test.go | 28 ++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 lnwire/channel_type.go create mode 100644 lnwire/channel_type_test.go diff --git a/lnwire/channel_type.go b/lnwire/channel_type.go new file mode 100644 index 000000000..a0696048b --- /dev/null +++ b/lnwire/channel_type.go @@ -0,0 +1,58 @@ +package lnwire + +import ( + "io" + + "github.com/lightningnetwork/lnd/tlv" +) + +const ( + // ChannelTypeRecordType is the type of the experimental record used + // to denote which channel type is being negotiated. + ChannelTypeRecordType tlv.Type = 1 +) + +// ChannelType represents a specific channel type as a set of feature bits that +// comprise it. +type ChannelType RawFeatureVector + +// featureBitLen returns the length in bytes of the encoded feature bits. +func (c ChannelType) featureBitLen() uint64 { + fv := RawFeatureVector(c) + return uint64(fv.SerializeSize()) +} + +// Record returns a TLV record that can be used to encode/decode the channel +// type from a given TLV stream. +func (c *ChannelType) Record() tlv.Record { + return tlv.MakeDynamicRecord( + ChannelTypeRecordType, c, c.featureBitLen, channelTypeEncoder, + channelTypeDecoder, + ) +} + +// channelTypeEncoder is a custom TLV encoder for the ChannelType record. +func channelTypeEncoder(w io.Writer, val interface{}, buf *[8]byte) error { + if v, ok := val.(*ChannelType); ok { + // Encode the feature bits as a byte slice without its length + // prepended, as that's already taken care of by the TLV record. + fv := RawFeatureVector(*v) + return fv.encode(w, fv.SerializeSize(), 8) + } + + return tlv.NewTypeForEncodingErr(val, "lnwire.ChannelType") +} + +// channelTypeDecoder is a custom TLV decoder for the ChannelType record. +func channelTypeDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { + if v, ok := val.(*ChannelType); ok { + fv := NewRawFeatureVector() + if err := fv.decode(r, int(l), 8); err != nil { + return err + } + *v = ChannelType(*fv) + return nil + } + + return tlv.NewTypeForEncodingErr(val, "lnwire.ChannelType") +} diff --git a/lnwire/channel_type_test.go b/lnwire/channel_type_test.go new file mode 100644 index 000000000..0edef0779 --- /dev/null +++ b/lnwire/channel_type_test.go @@ -0,0 +1,28 @@ +package lnwire + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestChannelTypeEncodeDecode tests that we're able to properly encode and +// decode channel types within TLV streams. +func TestChannelTypeEncodeDecode(t *testing.T) { + t.Parallel() + + chanType := ChannelType(*NewRawFeatureVector( + StaticRemoteKeyRequired, + AnchorsZeroFeeHtlcTxRequired, + )) + + var extraData ExtraOpaqueData + require.NoError(t, extraData.PackRecords(chanType.Record())) + + var chanType2 ChannelType + tlvs, err := extraData.ExtractRecords(chanType2.Record()) + require.NoError(t, err) + + require.Contains(t, tlvs, ChannelTypeRecordType) + require.Equal(t, chanType, chanType2) +}