From 6252563bc5cd55d2562e343e2cebba71ca5c3798 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 28 Jul 2021 15:15:42 -0700 Subject: [PATCH] lnwire: add LeaseExpiry custom record for Open+AcceptChannel --- lnwire/accept_channel.go | 19 +++++++++-- lnwire/lnwire_test.go | 10 ++++-- lnwire/open_channel.go | 19 +++++++++-- lnwire/typed_lease_expiry.go | 52 +++++++++++++++++++++++++++++++ lnwire/typed_lease_expiry_test.go | 25 +++++++++++++++ 5 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 lnwire/typed_lease_expiry.go create mode 100644 lnwire/typed_lease_expiry_test.go diff --git a/lnwire/accept_channel.go b/lnwire/accept_channel.go index 2f7957ff6..4c7aedb9d 100644 --- a/lnwire/accept_channel.go +++ b/lnwire/accept_channel.go @@ -99,6 +99,12 @@ type AcceptChannel struct { // open. ChannelType *ChannelType + // LeaseExpiry represents the absolute expiration height of a channel + // lease. This is a custom TLV record that will only apply when a leased + // channel is being opened using the script enforced lease commitment + // type. + LeaseExpiry *LeaseExpiry + // 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. @@ -125,6 +131,9 @@ func (a *AcceptChannel) Encode(w *bytes.Buffer, pver uint32) error { if a.ChannelType != nil { recordProducers = append(recordProducers, a.ChannelType) } + if a.LeaseExpiry != nil { + recordProducers = append(recordProducers, a.LeaseExpiry) + } err := EncodeMessageExtraData(&a.ExtraData, recordProducers...) if err != nil { return err @@ -226,9 +235,12 @@ func (a *AcceptChannel) Decode(r io.Reader, pver uint32) error { // Next we'll parse out the set of known records, keeping the raw tlv // bytes untouched to ensure we don't drop any bytes erroneously. - var chanType ChannelType + var ( + chanType ChannelType + leaseExpiry LeaseExpiry + ) typeMap, err := tlvRecords.ExtractRecords( - &a.UpfrontShutdownScript, &chanType, + &a.UpfrontShutdownScript, &chanType, &leaseExpiry, ) if err != nil { return err @@ -238,6 +250,9 @@ func (a *AcceptChannel) Decode(r io.Reader, pver uint32) error { if val, ok := typeMap[ChannelTypeRecordType]; ok && val == nil { a.ChannelType = &chanType } + if val, ok := typeMap[LeaseExpiryRecordType]; ok && val == nil { + a.LeaseExpiry = &leaseExpiry + } a.ExtraData = tlvRecords diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 6569c3a5b..0743c1704 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -360,7 +360,7 @@ func TestLightningWireProtocol(t *testing.T) { return } - // 1/2 chance empty upfront shutdown script. + // 1/2 chance empty TLV records. if r.Intn(2) == 0 { req.UpfrontShutdownScript, err = randDeliveryAddress(r) if err != nil { @@ -370,6 +370,9 @@ func TestLightningWireProtocol(t *testing.T) { req.ChannelType = new(ChannelType) *req.ChannelType = ChannelType(*randRawFeatureVector(r)) + + req.LeaseExpiry = new(LeaseExpiry) + *req.LeaseExpiry = LeaseExpiry(1337) } else { req.UpfrontShutdownScript = []byte{} } @@ -429,7 +432,7 @@ func TestLightningWireProtocol(t *testing.T) { return } - // 1/2 chance empty upfront shutdown script. + // 1/2 chance empty TLV records. if r.Intn(2) == 0 { req.UpfrontShutdownScript, err = randDeliveryAddress(r) if err != nil { @@ -439,6 +442,9 @@ func TestLightningWireProtocol(t *testing.T) { req.ChannelType = new(ChannelType) *req.ChannelType = ChannelType(*randRawFeatureVector(r)) + + req.LeaseExpiry = new(LeaseExpiry) + *req.LeaseExpiry = LeaseExpiry(1337) } else { req.UpfrontShutdownScript = []byte{} } diff --git a/lnwire/open_channel.go b/lnwire/open_channel.go index 534f17ee9..273129ad2 100644 --- a/lnwire/open_channel.go +++ b/lnwire/open_channel.go @@ -135,6 +135,12 @@ type OpenChannel struct { // open. ChannelType *ChannelType + // LeaseExpiry represents the absolute expiration height of a channel + // lease. This is a custom TLV record that will only apply when a leased + // channel is being opened using the script enforced lease commitment + // type. + LeaseExpiry *LeaseExpiry + // 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. @@ -160,6 +166,9 @@ func (o *OpenChannel) Encode(w *bytes.Buffer, pver uint32) error { if o.ChannelType != nil { recordProducers = append(recordProducers, o.ChannelType) } + if o.LeaseExpiry != nil { + recordProducers = append(recordProducers, o.LeaseExpiry) + } err := EncodeMessageExtraData(&o.ExtraData, recordProducers...) if err != nil { return err @@ -282,9 +291,12 @@ func (o *OpenChannel) Decode(r io.Reader, pver uint32) error { // Next we'll parse out the set of known records, keeping the raw tlv // bytes untouched to ensure we don't drop any bytes erroneously. - var chanType ChannelType + var ( + chanType ChannelType + leaseExpiry LeaseExpiry + ) typeMap, err := tlvRecords.ExtractRecords( - &o.UpfrontShutdownScript, &chanType, + &o.UpfrontShutdownScript, &chanType, &leaseExpiry, ) if err != nil { return err @@ -294,6 +306,9 @@ func (o *OpenChannel) Decode(r io.Reader, pver uint32) error { if val, ok := typeMap[ChannelTypeRecordType]; ok && val == nil { o.ChannelType = &chanType } + if val, ok := typeMap[LeaseExpiryRecordType]; ok && val == nil { + o.LeaseExpiry = &leaseExpiry + } o.ExtraData = tlvRecords diff --git a/lnwire/typed_lease_expiry.go b/lnwire/typed_lease_expiry.go new file mode 100644 index 000000000..d1a5609c0 --- /dev/null +++ b/lnwire/typed_lease_expiry.go @@ -0,0 +1,52 @@ +package lnwire + +import ( + "io" + + "github.com/lightningnetwork/lnd/tlv" +) + +const ( + // LeaseExpiryType is the type of the experimental record used to + // communicate the expiration of a channel lease throughout the channel + // funding process. + // + // TODO: Decide on actual TLV type. Custom records start at 2^16. + LeaseExpiryRecordType tlv.Type = 1 << 16 +) + +// LeaseExpiry represents the absolute expiration height of a channel lease. All +// outputs that pay directly to the channel initiator are locked until this +// height is reached. +type LeaseExpiry uint32 + +// Record returns a TLV record that can be used to encode/decode the LeaseExpiry +// type from a given TLV stream. +func (l *LeaseExpiry) Record() tlv.Record { + return tlv.MakeStaticRecord( + LeaseExpiryRecordType, l, 4, leaseExpiryEncoder, leaseExpiryDecoder, + ) +} + +// leaseExpiryEncoder is a custom TLV encoder for the LeaseExpiry record. +func leaseExpiryEncoder(w io.Writer, val interface{}, buf *[8]byte) error { + if v, ok := val.(*LeaseExpiry); ok { + return tlv.EUint32T(w, uint32(*v), buf) + } + + return tlv.NewTypeForEncodingErr(val, "lnwire.LeaseExpiry") +} + +// leaseExpiryDecoder is a custom TLV decoder for the LeaseExpiry record. +func leaseExpiryDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { + if v, ok := val.(*LeaseExpiry); ok { + var leaseExpiry uint32 + if err := tlv.DUint32(r, &leaseExpiry, buf, l); err != nil { + return err + } + *v = LeaseExpiry(leaseExpiry) + return nil + } + + return tlv.NewTypeForEncodingErr(val, "lnwire.LeaseExpiry") +} diff --git a/lnwire/typed_lease_expiry_test.go b/lnwire/typed_lease_expiry_test.go new file mode 100644 index 000000000..d5f797b20 --- /dev/null +++ b/lnwire/typed_lease_expiry_test.go @@ -0,0 +1,25 @@ +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 TestLeaseExpiryEncodeDecode(t *testing.T) { + t.Parallel() + + leaseExpiry := LeaseExpiry(1337) + + var extraData ExtraOpaqueData + require.NoError(t, extraData.PackRecords(&leaseExpiry)) + + var leaseExpiry2 LeaseExpiry + tlvs, err := extraData.ExtractRecords(&leaseExpiry2) + require.NoError(t, err) + + require.Contains(t, tlvs, LeaseExpiryRecordType) + require.Equal(t, leaseExpiry, leaseExpiry2) +}