From b77b698b9d34ffe1193fb8ca819e9d8bc751855a Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 11 Oct 2024 16:33:21 +0200 Subject: [PATCH] lnwire: update AnnounceSigs2 to use pure TLV --- lnwire/announcement_signatures_2.go | 96 ++++++++++++++++-------- lnwire/announcement_signatures_2_test.go | 78 +++++++++++++++++++ lnwire/test_message.go | 27 +++++-- 3 files changed, 165 insertions(+), 36 deletions(-) create mode 100644 lnwire/announcement_signatures_2_test.go diff --git a/lnwire/announcement_signatures_2.go b/lnwire/announcement_signatures_2.go index 6e893dafd..04e4c0a9f 100644 --- a/lnwire/announcement_signatures_2.go +++ b/lnwire/announcement_signatures_2.go @@ -3,6 +3,8 @@ package lnwire import ( "bytes" "io" + + "github.com/lightningnetwork/lnd/tlv" ) // AnnounceSignatures2 is a direct message between two endpoints of a @@ -14,27 +16,40 @@ type AnnounceSignatures2 struct { // Channel id is better for users and debugging and short channel id is // used for quick test on existence of the particular utxo inside the // blockchain, because it contains information about block. - ChannelID ChannelID + ChannelID tlv.RecordT[tlv.TlvType0, ChannelID] // ShortChannelID is the unique description of the funding transaction. // It is constructed with the most significant 3 bytes as the block // height, the next 3 bytes indicating the transaction index within the // block, and the least significant two bytes indicating the output // index which pays to the channel. - ShortChannelID ShortChannelID + ShortChannelID tlv.RecordT[tlv.TlvType2, ShortChannelID] // PartialSignature is the combination of the partial Schnorr signature // created for the node's bitcoin key with the partial signature created // for the node's node ID key. - PartialSignature PartialSig + PartialSignature tlv.RecordT[tlv.TlvType4, PartialSig] - // ExtraOpaqueData is the set of data that was appended to this - // message, some of which we may not actually know how to iterate or - // parse. By holding onto this data, we ensure that we're able to - // properly validate the set of signatures that cover these new fields, - // and ensure we're able to make upgrades to the network in a forwards - // compatible manner. - ExtraOpaqueData ExtraOpaqueData + // Any extra fields in the signed range that we do not yet know about, + // but we need to keep them for signature validation and to produce a + // valid message. + ExtraSignedFields +} + +// NewAnnSigs2 is a constructor for AnnounceSignatures2. +func NewAnnSigs2(chanID ChannelID, scid ShortChannelID, + partialSig PartialSig) *AnnounceSignatures2 { + + return &AnnounceSignatures2{ + ChannelID: tlv.NewRecordT[tlv.TlvType0, ChannelID](chanID), + ShortChannelID: tlv.NewRecordT[tlv.TlvType2, ShortChannelID]( + scid, + ), + PartialSignature: tlv.NewRecordT[tlv.TlvType4, PartialSig]( + partialSig, + ), + ExtraSignedFields: make(ExtraSignedFields), + } } // A compile time check to ensure AnnounceSignatures2 implements the @@ -45,17 +60,30 @@ var _ Message = (*AnnounceSignatures2)(nil) // lnwire.SizeableMessage interface. var _ SizeableMessage = (*AnnounceSignatures2)(nil) +// A compile time check to ensure ChannelAnnouncement2 implements the +// lnwire.PureTLVMessage interface. +var _ PureTLVMessage = (*AnnounceSignatures2)(nil) + // Decode deserializes a serialized AnnounceSignatures2 stored in the passed // io.Reader observing the specified protocol version. // // This is part of the lnwire.Message interface. func (a *AnnounceSignatures2) Decode(r io.Reader, _ uint32) error { - return ReadElements(r, - &a.ChannelID, - &a.ShortChannelID, - &a.PartialSignature, - &a.ExtraOpaqueData, - ) + stream, err := tlv.NewStream(ProduceRecordsSorted( + &a.ChannelID, &a.ShortChannelID, &a.PartialSignature, + )...) + if err != nil { + return err + } + + typeMap, err := stream.DecodeWithParsedTypesP2P(r) + if err != nil { + return err + } + + a.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap) + + return nil } // Encode serializes the target AnnounceSignatures2 into the passed io.Writer @@ -63,19 +91,7 @@ func (a *AnnounceSignatures2) Decode(r io.Reader, _ uint32) error { // // This is part of the lnwire.Message interface. func (a *AnnounceSignatures2) Encode(w *bytes.Buffer, _ uint32) error { - if err := WriteChannelID(w, a.ChannelID); err != nil { - return err - } - - if err := WriteShortChannelID(w, a.ShortChannelID); err != nil { - return err - } - - if err := WriteElement(w, a.PartialSignature); err != nil { - return err - } - - return WriteBytes(w, a.ExtraOpaqueData) + return EncodePureTLVMessage(a, w) } // MsgType returns the integer uniquely identifying this message type on the @@ -93,16 +109,34 @@ func (a *AnnounceSignatures2) SerializedSize() (uint32, error) { return MessageSerializedSize(a) } +// 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. +// +// NOTE: this is part of the PureTLVMessage interface. +func (a *AnnounceSignatures2) AllRecords() []tlv.Record { + recordProducers := []tlv.RecordProducer{ + &a.ChannelID, &a.ShortChannelID, + &a.PartialSignature, + } + + recordProducers = append(recordProducers, RecordsAsProducers( + tlv.MapToRecords(a.ExtraSignedFields), + )...) + + return ProduceRecordsSorted(recordProducers...) +} + // SCID returns the ShortChannelID of the channel. // // NOTE: this is part of the AnnounceSignatures interface. func (a *AnnounceSignatures2) SCID() ShortChannelID { - return a.ShortChannelID + return a.ShortChannelID.Val } // ChanID returns the ChannelID identifying the channel. // // NOTE: this is part of the AnnounceSignatures interface. func (a *AnnounceSignatures2) ChanID() ChannelID { - return a.ChannelID + return a.ChannelID.Val } diff --git a/lnwire/announcement_signatures_2_test.go b/lnwire/announcement_signatures_2_test.go new file mode 100644 index 000000000..6b945edcf --- /dev/null +++ b/lnwire/announcement_signatures_2_test.go @@ -0,0 +1,78 @@ +package lnwire + +import ( + "bytes" + "testing" + + "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/require" +) + +// TestAnnSigs2EncodeDecode tests the encoding and decoding of the +// AnnounceSignatures2 message using hardcoded byte slices. +func TestAnnSigs2EncodeDecode(t *testing.T) { + t.Parallel() + + // We'll create a raw byte stream that represents a valid + // AnnounceSignatures2 message with various known and unknown fields in + // the signed TLV ranges. + var rawBytes []byte + + // ChannelID. + rawBytes = append(rawBytes, []byte{ + 0x00, // type + 0x20, // length + }...) + rawBytes = append(rawBytes, make([]byte, 32)...) // value + + // ShortChannelID. + rawBytes = append(rawBytes, []byte{ + 0x02, // type + 0x08, // length + 0, 0, 1, 0, 0, 2, 0, 3, // value + }...) + + // PartialSignature. + rawBytes = append(rawBytes, []byte{ + 0x04, // type + 0x20, // length + }...) + rawBytes = append(rawBytes, make([]byte, 32)...) // value + + // Extra field in the first signed range. + rawBytes = append(rawBytes, []byte{ + 0x30, // type + 0x02, // length + 0xab, 0xcd, // value + }...) + + w := new(bytes.Buffer) + var buf [8]byte + err := tlv.WriteVarInt(w, pureTLVSignedSecondRangeStart+1, &buf) + require.NoError(t, err) + + // Extra field in the second signed range. + rawBytes = append(rawBytes, w.Bytes()...) // type + rawBytes = append(rawBytes, []byte{ + 0x02, // length + 0x79, 0x79, // value + }...) + + // Now, create a new empty message and decode the raw bytes into it. + msg := &AnnounceSignatures2{} + r := bytes.NewReader(rawBytes) + err = msg.Decode(r, 0) + require.NoError(t, err) + + // At this point, we expect 2 extra signed fields. + require.Len(t, msg.ExtraSignedFields, 2) + + // Next, encode the message back into a new byte buffer. + var b bytes.Buffer + err = msg.Encode(&b, 0) + require.NoError(t, err) + + // The re-encoded bytes should be exactly the same as the original raw + // bytes. + require.Equal(t, rawBytes, b.Bytes()) +} diff --git a/lnwire/test_message.go b/lnwire/test_message.go index 77432fd89..8f946f190 100644 --- a/lnwire/test_message.go +++ b/lnwire/test_message.go @@ -129,12 +129,29 @@ var _ TestMessage = (*AnnounceSignatures2)(nil) // // This is part of the TestMessage interface. func (a *AnnounceSignatures2) RandTestMessage(t *rapid.T) Message { - return &AnnounceSignatures2{ - ChannelID: RandChannelID(t), - ShortChannelID: RandShortChannelID(t), - PartialSignature: *RandPartialSig(t), - ExtraOpaqueData: RandExtraOpaqueData(t, nil), + var ( + chanID = RandChannelID(t) + scid = RandShortChannelID(t) + pSig = RandPartialSig(t) + ) + + msg := &AnnounceSignatures2{ + ChannelID: tlv.NewRecordT[tlv.TlvType0, ChannelID]( + chanID, + ), + ShortChannelID: tlv.NewRecordT[tlv.TlvType2](scid), + PartialSignature: tlv.NewRecordT[tlv.TlvType4, PartialSig]( + *pSig, + ), + ExtraSignedFields: make(map[uint64][]byte), } + + randRecs, _ := RandSignedRangeRecords(t) + if len(randRecs) > 0 { + msg.ExtraSignedFields = ExtraSignedFields(randRecs) + } + + return msg } // A compile time check to ensure ChannelAnnouncement1 implements the