Merge pull request #9175 from ellemouton/g175UpdateMessageStructure
lnwire+netann: update structure of g175 messages to be pure TLV
This commit is contained in:
18 files changed
+1460
-268
No files matched your search
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -12,9 +12,6 @@ import (
|
||||
// ChannelAnnouncement2 message is used to announce the existence of a taproot
|
||||
// channel between two peers in the network.
|
||||
type ChannelAnnouncement2 struct {
|
||||
// Signature is a Schnorr signature over the TLV stream of the message.
|
||||
Signature Sig
|
||||
|
||||
// ChainHash denotes the target chain that this channel was opened
|
||||
// within. This value should be the genesis hash of the target chain.
|
||||
ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash]
|
||||
@@ -59,74 +56,14 @@ type ChannelAnnouncement2 struct {
|
||||
// the funding output is a pure 2-of-2 MuSig aggregate public key.
|
||||
MerkleRootHash tlv.OptionalRecordT[tlv.TlvType16, [32]byte]
|
||||
|
||||
// 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
|
||||
}
|
||||
// Signature is a Schnorr signature over serialised signed-range TLV
|
||||
// stream of the message.
|
||||
Signature tlv.RecordT[tlv.TlvType160, Sig]
|
||||
|
||||
// Decode deserializes a serialized AnnounceSignatures1 stored in the passed
|
||||
// io.Reader observing the specified protocol version.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (c *ChannelAnnouncement2) Decode(r io.Reader, _ uint32) error {
|
||||
err := ReadElement(r, &c.Signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signature.ForceSchnorr()
|
||||
|
||||
return c.DecodeTLVRecords(r)
|
||||
}
|
||||
|
||||
// DecodeTLVRecords decodes only the TLV section of the message.
|
||||
func (c *ChannelAnnouncement2) DecodeTLVRecords(r io.Reader) error {
|
||||
// First extract into extra opaque data.
|
||||
var tlvRecords ExtraOpaqueData
|
||||
if err := ReadElements(r, &tlvRecords); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
|
||||
btcKey1 = tlv.ZeroRecordT[tlv.TlvType12, [33]byte]()
|
||||
btcKey2 = tlv.ZeroRecordT[tlv.TlvType14, [33]byte]()
|
||||
merkleRootHash = tlv.ZeroRecordT[tlv.TlvType16, [32]byte]()
|
||||
)
|
||||
typeMap, err := tlvRecords.ExtractRecords(
|
||||
&chainHash, &c.Features, &c.ShortChannelID, &c.Capacity,
|
||||
&c.NodeID1, &c.NodeID2, &btcKey1, &btcKey2, &merkleRootHash,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// By default, the chain-hash is the bitcoin mainnet genesis block hash.
|
||||
c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
|
||||
if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
|
||||
c.ChainHash.Val = chainHash.Val
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.BitcoinKey1.TlvType()]; ok {
|
||||
c.BitcoinKey1 = tlv.SomeRecordT(btcKey1)
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.BitcoinKey2.TlvType()]; ok {
|
||||
c.BitcoinKey2 = tlv.SomeRecordT(btcKey2)
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.MerkleRootHash.TlvType()]; ok {
|
||||
c.MerkleRootHash = tlv.SomeRecordT(merkleRootHash)
|
||||
}
|
||||
|
||||
if len(tlvRecords) != 0 {
|
||||
c.ExtraOpaqueData = tlvRecords
|
||||
}
|
||||
|
||||
return c.ExtraOpaqueData.ValidateTLV()
|
||||
// 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
|
||||
}
|
||||
|
||||
// Encode serializes the target AnnounceSignatures1 into the passed io.Writer
|
||||
@@ -134,21 +71,27 @@ func (c *ChannelAnnouncement2) DecodeTLVRecords(r io.Reader) error {
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (c *ChannelAnnouncement2) Encode(w *bytes.Buffer, _ uint32) error {
|
||||
_, err := w.Write(c.Signature.RawBytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = c.DataToSign()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return WriteBytes(w, c.ExtraOpaqueData)
|
||||
return EncodePureTLVMessage(c, w)
|
||||
}
|
||||
|
||||
// DataToSign encodes the data to be signed into the ExtraOpaqueData member and
|
||||
// returns it.
|
||||
func (c *ChannelAnnouncement2) DataToSign() ([]byte, error) {
|
||||
// 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 (c *ChannelAnnouncement2) AllRecords() []tlv.Record {
|
||||
recordProducers := append(
|
||||
c.allNonSignatureRecordProducers(), &c.Signature,
|
||||
)
|
||||
|
||||
return ProduceRecordsSorted(recordProducers...)
|
||||
}
|
||||
|
||||
// allNonSignatureRecordProducers returns all the TLV record producers for the
|
||||
// message except the signature record producer.
|
||||
//
|
||||
//nolint:ll
|
||||
func (c *ChannelAnnouncement2) allNonSignatureRecordProducers() []tlv.RecordProducer {
|
||||
// The chain-hash record is only included if it is _not_ equal to the
|
||||
// bitcoin mainnet genisis block hash.
|
||||
var recordProducers []tlv.RecordProducer
|
||||
@@ -178,12 +121,126 @@ func (c *ChannelAnnouncement2) DataToSign() ([]byte, error) {
|
||||
},
|
||||
)
|
||||
|
||||
err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...)
|
||||
recordProducers = append(recordProducers, RecordsAsProducers(
|
||||
tlv.MapToRecords(c.ExtraSignedFields),
|
||||
)...)
|
||||
|
||||
return recordProducers
|
||||
}
|
||||
|
||||
// Decode deserializes a serialized AnnounceSignatures1 stored in the passed
|
||||
// io.Reader observing the specified protocol version.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (c *ChannelAnnouncement2) Decode(r io.Reader, _ uint32) error {
|
||||
var (
|
||||
chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
|
||||
btcKey1 = tlv.ZeroRecordT[tlv.TlvType12, [33]byte]()
|
||||
btcKey2 = tlv.ZeroRecordT[tlv.TlvType14, [33]byte]()
|
||||
merkleRootHash = tlv.ZeroRecordT[tlv.TlvType16, [32]byte]()
|
||||
)
|
||||
stream, err := tlv.NewStream(ProduceRecordsSorted(
|
||||
&chainHash,
|
||||
&c.Features,
|
||||
&c.ShortChannelID,
|
||||
&c.Capacity,
|
||||
&c.NodeID1,
|
||||
&c.NodeID2,
|
||||
&btcKey1,
|
||||
&btcKey2,
|
||||
&merkleRootHash,
|
||||
&c.Signature,
|
||||
)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
c.Signature.Val.ForceSchnorr()
|
||||
|
||||
typeMap, err := stream.DecodeWithParsedTypesP2P(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.ExtraOpaqueData, nil
|
||||
// By default, the chain-hash is the bitcoin mainnet genesis block hash.
|
||||
c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
|
||||
if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
|
||||
c.ChainHash.Val = chainHash.Val
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.BitcoinKey1.TlvType()]; ok {
|
||||
c.BitcoinKey1 = tlv.SomeRecordT(btcKey1)
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.BitcoinKey2.TlvType()]; ok {
|
||||
c.BitcoinKey2 = tlv.SomeRecordT(btcKey2)
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.MerkleRootHash.TlvType()]; ok {
|
||||
c.MerkleRootHash = tlv.SomeRecordT(merkleRootHash)
|
||||
}
|
||||
|
||||
c.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeNonSigTLVRecords decodes only the TLV section of the message.
|
||||
func (c *ChannelAnnouncement2) DecodeNonSigTLVRecords(r io.Reader) error {
|
||||
var (
|
||||
chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
|
||||
btcKey1 = tlv.ZeroRecordT[tlv.TlvType12, [33]byte]()
|
||||
btcKey2 = tlv.ZeroRecordT[tlv.TlvType14, [33]byte]()
|
||||
merkleRootHash = tlv.ZeroRecordT[tlv.TlvType16, [32]byte]()
|
||||
)
|
||||
stream, err := tlv.NewStream(ProduceRecordsSorted(
|
||||
&chainHash,
|
||||
&c.Features,
|
||||
&c.ShortChannelID,
|
||||
&c.Capacity,
|
||||
&c.NodeID1,
|
||||
&c.NodeID2,
|
||||
&btcKey1,
|
||||
&btcKey2,
|
||||
&merkleRootHash,
|
||||
)...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
typeMap, err := stream.DecodeWithParsedTypesP2P(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// By default, the chain-hash is the bitcoin mainnet genesis block hash.
|
||||
c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
|
||||
if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
|
||||
c.ChainHash.Val = chainHash.Val
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.BitcoinKey1.TlvType()]; ok {
|
||||
c.BitcoinKey1 = tlv.SomeRecordT(btcKey1)
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.BitcoinKey2.TlvType()]; ok {
|
||||
c.BitcoinKey2 = tlv.SomeRecordT(btcKey2)
|
||||
}
|
||||
|
||||
if _, ok := typeMap[c.MerkleRootHash.TlvType()]; ok {
|
||||
c.MerkleRootHash = tlv.SomeRecordT(merkleRootHash)
|
||||
}
|
||||
|
||||
c.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeAllNonSigFields encodes the entire message to the given writer but
|
||||
// excludes the signature field.
|
||||
func (c *ChannelAnnouncement2) EncodeAllNonSigFields(w io.Writer) error {
|
||||
return EncodeRecordsTo(
|
||||
w, ProduceRecordsSorted(c.allNonSignatureRecordProducers()...),
|
||||
)
|
||||
}
|
||||
|
||||
// MsgType returns the integer uniquely identifying this message type on the
|
||||
@@ -209,6 +266,10 @@ var _ Message = (*ChannelAnnouncement2)(nil)
|
||||
// lnwire.SizeableMessage interface.
|
||||
var _ SizeableMessage = (*ChannelAnnouncement2)(nil)
|
||||
|
||||
// A compile time check to ensure ChannelAnnouncement2 implements the
|
||||
// lnwire.PureTLVMessage interface.
|
||||
var _ PureTLVMessage = (*ChannelAnnouncement2)(nil)
|
||||
|
||||
// Node1KeyBytes returns the bytes representing the public key of node 1 in the
|
||||
// channel.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestChanAnn2EncodeDecode tests the encoding and decoding of the
|
||||
// ChannelAnnouncement2 message using hardcoded byte slices.
|
||||
func TestChanAnn2EncodeDecode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// We'll create a raw byte stream that represents a valid
|
||||
// ChannelAnnouncement2 message with various known and unknown fields in
|
||||
// the signed TLV ranges along with the signature in the unsigned range.
|
||||
rawBytes := []byte{
|
||||
// ChainHash record (optional, not mainnet).
|
||||
0x00, // type.
|
||||
0x20, // length.
|
||||
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
|
||||
// Features record.
|
||||
0x02, // type.
|
||||
0x02, // length.
|
||||
0x1, 0x2, // value.
|
||||
|
||||
// ShortChannelID record.
|
||||
0x04, // type.
|
||||
0x08, // length.
|
||||
0x0, 0x0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x3, // value.
|
||||
|
||||
// Unknown TLV record.
|
||||
0x05, // type.
|
||||
0x02, // length.
|
||||
0xab, 0xcd, // value.
|
||||
|
||||
// Capacity record.
|
||||
0x06, // type.
|
||||
0x08, // length.
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x86, 0xa0, // value: 100000.
|
||||
|
||||
// NodeID1 record.
|
||||
0x08, // type.
|
||||
0x21, // length.
|
||||
0x2, 0x28, 0xf2, 0xaf, 0xa, 0xbe, 0x32, 0x24, 0x3, 0x48, 0xf,
|
||||
0xb3, 0xee, 0x17, 0x2f, 0x7f, 0x16, 0x1, 0xe6, 0x7d, 0x1d, 0xa6,
|
||||
0xca, 0xd4, 0xb, 0x54, 0xc4, 0x46, 0x8d, 0x48, 0x23, 0x6c, 0x39,
|
||||
|
||||
// NodeID2 record.
|
||||
0x0a, // type.
|
||||
0x21, // length.
|
||||
0x3, 0x28, 0xf2, 0xaf, 0xa, 0xbe, 0x32, 0x24, 0x3, 0x48, 0xf,
|
||||
0xb3, 0xee, 0x17, 0x2f, 0x7f, 0x16, 0x1, 0xe6, 0x7d, 0x1d, 0xa6,
|
||||
0xca, 0xd4, 0xb, 0x54, 0xc4, 0x46, 0x8d, 0x48, 0x23, 0x6c, 0x39,
|
||||
|
||||
// Unknown TLV record.
|
||||
0x6f, // type.
|
||||
0x2, // length.
|
||||
0x79, 0x79, // value.
|
||||
|
||||
// Signature.
|
||||
0xa0, // type.
|
||||
0x40, // length.
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
|
||||
0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||
0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
|
||||
0x3f, // value.
|
||||
}
|
||||
secondSignedRangeType := new(bytes.Buffer)
|
||||
var buf [8]byte
|
||||
err := tlv.WriteVarInt(
|
||||
secondSignedRangeType, pureTLVSignedSecondRangeStart+1, &buf,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
rawBytes = append(rawBytes, secondSignedRangeType.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 := &ChannelAnnouncement2{}
|
||||
r := bytes.NewReader(rawBytes)
|
||||
err = msg.Decode(r, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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())
|
||||
}
|
||||
@@ -3,10 +3,12 @@ package lnwire
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,6 +38,40 @@ func (c ChannelID) String() string {
|
||||
return hex.EncodeToString(c[:])
|
||||
}
|
||||
|
||||
// Record returns a TLV record that can be used to encode/decode a ChannelID
|
||||
// to/from a TLV stream.
|
||||
func (c *ChannelID) Record() tlv.Record {
|
||||
return tlv.MakeStaticRecord(0, c, 32, encodeChannelID, decodeChannelID)
|
||||
}
|
||||
|
||||
func encodeChannelID(w io.Writer, val interface{}, buf *[8]byte) error {
|
||||
if v, ok := val.(*ChannelID); ok {
|
||||
bigSize := [32]byte(*v)
|
||||
|
||||
return tlv.EBytes32(w, &bigSize, buf)
|
||||
}
|
||||
|
||||
return tlv.NewTypeForEncodingErr(val, "lnwire.ChannelID")
|
||||
}
|
||||
|
||||
func decodeChannelID(r io.Reader, val interface{}, buf *[8]byte,
|
||||
l uint64) error {
|
||||
|
||||
if v, ok := val.(*ChannelID); ok {
|
||||
var id [32]byte
|
||||
err := tlv.DBytes32(r, &id, buf, l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = id
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return tlv.NewTypeForDecodingErr(val, "lnwire.ChannelID", l, l)
|
||||
}
|
||||
|
||||
// NewChanIDFromOutPoint converts a target OutPoint into a ChannelID that is
|
||||
// usable within the network. In order to convert the OutPoint into a ChannelID,
|
||||
// we XOR the lower 2-bytes of the txid within the OutPoint with the big-endian
|
||||
|
||||
+35
-54
@@ -22,10 +22,6 @@ const (
|
||||
// HTLCs and other parameters. This message is also used to redeclare initially
|
||||
// set channel parameters.
|
||||
type ChannelUpdate2 struct {
|
||||
// Signature is used to validate the announced data and prove the
|
||||
// ownership of node id.
|
||||
Signature Sig
|
||||
|
||||
// ChainHash denotes the target chain that this channel was opened
|
||||
// within. This value should be the genesis hash of the target chain.
|
||||
// Along with the short channel ID, this uniquely identifies the
|
||||
@@ -74,10 +70,22 @@ type ChannelUpdate2 struct {
|
||||
// millionth of a satoshi.
|
||||
FeeProportionalMillionths tlv.RecordT[tlv.TlvType18, uint32]
|
||||
|
||||
// ExtraOpaqueData 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.
|
||||
ExtraOpaqueData ExtraOpaqueData
|
||||
// Signature is used to validate the announced data and prove the
|
||||
// ownership of node id.
|
||||
Signature tlv.RecordT[tlv.TlvType160, Sig]
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Encode serializes the target ChannelUpdate2 into the passed io.Writer
|
||||
// observing the protocol version specified.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (c *ChannelUpdate2) Encode(w *bytes.Buffer, _ uint32) error {
|
||||
return EncodePureTLVMessage(c, w)
|
||||
}
|
||||
|
||||
// Decode deserializes a serialized ChannelUpdate2 stored in the passed
|
||||
@@ -85,17 +93,6 @@ type ChannelUpdate2 struct {
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (c *ChannelUpdate2) Decode(r io.Reader, _ uint32) error {
|
||||
err := ReadElement(r, &c.Signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signature.ForceSchnorr()
|
||||
|
||||
return c.DecodeTLVRecords(r)
|
||||
}
|
||||
|
||||
// DecodeTLVRecords decodes only the TLV section of the message.
|
||||
func (c *ChannelUpdate2) DecodeTLVRecords(r io.Reader) error {
|
||||
// First extract into extra opaque data.
|
||||
var tlvRecords ExtraOpaqueData
|
||||
if err := ReadElements(r, &tlvRecords); err != nil {
|
||||
@@ -111,10 +108,12 @@ func (c *ChannelUpdate2) DecodeTLVRecords(r io.Reader) error {
|
||||
&secondPeer, &c.CLTVExpiryDelta, &c.HTLCMinimumMsat,
|
||||
&c.HTLCMaximumMsat, &c.FeeBaseMsat,
|
||||
&c.FeeProportionalMillionths,
|
||||
&c.Signature,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signature.Val.ForceSchnorr()
|
||||
|
||||
// By default, the chain-hash is the bitcoin mainnet genesis block hash.
|
||||
c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
|
||||
@@ -150,38 +149,21 @@ func (c *ChannelUpdate2) DecodeTLVRecords(r io.Reader) error {
|
||||
c.FeeProportionalMillionths.Val = defaultFeeProportionalMillionths //nolint:ll
|
||||
}
|
||||
|
||||
if len(tlvRecords) != 0 {
|
||||
c.ExtraOpaqueData = tlvRecords
|
||||
}
|
||||
c.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
|
||||
|
||||
return c.ExtraOpaqueData.ValidateTLV()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode serializes the target ChannelUpdate2 into the passed io.Writer
|
||||
// observing the protocol version specified.
|
||||
// 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.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (c *ChannelUpdate2) Encode(w *bytes.Buffer, _ uint32) error {
|
||||
_, err := w.Write(c.Signature.RawBytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// NOTE: this is part of the PureTLVMessage interface.
|
||||
func (c *ChannelUpdate2) AllRecords() []tlv.Record {
|
||||
var recordProducers []tlv.RecordProducer
|
||||
|
||||
_, err = c.DataToSign()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return WriteBytes(w, c.ExtraOpaqueData)
|
||||
}
|
||||
|
||||
// DataToSign is used to retrieve part of the announcement message which should
|
||||
// be signed. For the ChannelUpdate2 message, this includes the serialised TLV
|
||||
// records.
|
||||
func (c *ChannelUpdate2) DataToSign() ([]byte, error) {
|
||||
// The chain-hash record is only included if it is _not_ equal to the
|
||||
// bitcoin mainnet genisis block hash.
|
||||
var recordProducers []tlv.RecordProducer
|
||||
if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) {
|
||||
hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
|
||||
hash.Val = c.ChainHash.Val
|
||||
@@ -190,7 +172,7 @@ func (c *ChannelUpdate2) DataToSign() ([]byte, error) {
|
||||
}
|
||||
|
||||
recordProducers = append(recordProducers,
|
||||
&c.ShortChannelID, &c.BlockHeight,
|
||||
&c.ShortChannelID, &c.BlockHeight, &c.Signature,
|
||||
)
|
||||
|
||||
// Only include the disable flags if any bit is set.
|
||||
@@ -225,12 +207,11 @@ func (c *ChannelUpdate2) DataToSign() ([]byte, error) {
|
||||
)
|
||||
}
|
||||
|
||||
err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
recordProducers = append(recordProducers, RecordsAsProducers(
|
||||
tlv.MapToRecords(c.ExtraSignedFields),
|
||||
)...)
|
||||
|
||||
return c.ExtraOpaqueData, nil
|
||||
return ProduceRecordsSorted(recordProducers...)
|
||||
}
|
||||
|
||||
// MsgType returns the integer uniquely identifying this message type on the
|
||||
@@ -248,14 +229,14 @@ func (c *ChannelUpdate2) SerializedSize() (uint32, error) {
|
||||
return MessageSerializedSize(c)
|
||||
}
|
||||
|
||||
func (c *ChannelUpdate2) ExtraData() ExtraOpaqueData {
|
||||
return c.ExtraOpaqueData
|
||||
}
|
||||
|
||||
// A compile time check to ensure ChannelUpdate2 implements the
|
||||
// lnwire.Message interface.
|
||||
var _ Message = (*ChannelUpdate2)(nil)
|
||||
|
||||
// A compile time check to ensure ChannelUpdate2 implements the
|
||||
// lnwire.PureTLVMessage interface.
|
||||
var _ PureTLVMessage = (*ChannelUpdate2)(nil)
|
||||
|
||||
// SCID returns the ShortChannelID of the channel that the update applies to.
|
||||
//
|
||||
// NOTE: this is part of the ChannelUpdate interface.
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestChanUpdate2EncodeDecode tests the encoding and decoding of the
|
||||
// ChannelUpdate2 message using hardcoded byte slices.
|
||||
func TestChanUpdate2EncodeDecode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// We'll create a raw byte stream that represents a valid ChannelUpdate2
|
||||
// message. This includes the signature and a TLV stream with both known
|
||||
// and unknown records.
|
||||
rawBytes := []byte{
|
||||
// ChainHash record (optional, not mainnet).
|
||||
0x0, // type.
|
||||
0x20, // length.
|
||||
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
|
||||
// ShortChannelID record.
|
||||
0x2, // type.
|
||||
0x8, // length.
|
||||
0x0, 0x0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x3, // value.
|
||||
|
||||
// BlockHeight record.
|
||||
0x4, // type.
|
||||
0x4, // length.
|
||||
0x0, 0x0, 0x1, 0x0, // value.
|
||||
|
||||
// DisabledFlags record.
|
||||
0x6, // type.
|
||||
0x1, // length.
|
||||
0x1, // value.
|
||||
|
||||
// SecondPeer record.
|
||||
0x8, // type.
|
||||
0x0, // length.
|
||||
|
||||
// Unknown odd-type TLV record.
|
||||
0x9, // type.
|
||||
0x2, // length.
|
||||
0xab, 0xcd, // value.
|
||||
|
||||
// CLTVExpiryDelta record.
|
||||
0xa, // type.
|
||||
0x2, // length.
|
||||
0x0, 0x10, // value.
|
||||
|
||||
// HTLCMinimumMsat record.
|
||||
0xc, // type.
|
||||
0x5, // length.
|
||||
0xfe, 0x0, 0xf, 0x42, 0x40, // value (BigSize: 1_000_000).
|
||||
|
||||
// HTLCMaximumMsat record.
|
||||
0xe, // type.
|
||||
0x5, // length.
|
||||
0xfe, 0x0, 0xf, 0x42, 0x40, // value (BigSize: 1_000_000).
|
||||
|
||||
// FeeBaseMsat record.
|
||||
0x10, // type.
|
||||
0x4, // length.
|
||||
0x0, 0x0, 0x1, 0x0, // value.
|
||||
|
||||
// FeeProportionalMillionths record.
|
||||
0x12, // type.
|
||||
0x4, // length.
|
||||
0x0, 0x0, 0x1, 0x0, // value.
|
||||
|
||||
// Extra Opaque Data - Unknown Record.
|
||||
0x14, // type.
|
||||
0x2, // length.
|
||||
0x79, 0x79, // value.
|
||||
|
||||
// Signature.
|
||||
0xa0, // type.
|
||||
0x40, // length.
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
|
||||
0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||
0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
|
||||
0x3f, // value
|
||||
}
|
||||
|
||||
secondSignedRangeType := new(bytes.Buffer)
|
||||
var buf [8]byte
|
||||
err := tlv.WriteVarInt(
|
||||
secondSignedRangeType, pureTLVSignedSecondRangeStart+1, &buf,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
rawBytes = append(rawBytes, secondSignedRangeType.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 := &ChannelUpdate2{}
|
||||
r := bytes.NewReader(rawBytes)
|
||||
err = msg.Decode(r, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 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())
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
)
|
||||
|
||||
const (
|
||||
// pureTLVUnsignedRangeOneStart defines the start of the first unsigned
|
||||
// TLV range used for pure TLV messages. The range is inclusive of this
|
||||
// number.
|
||||
pureTLVUnsignedRangeOneStart = 160
|
||||
|
||||
// pureTLVSignedSecondRangeStart defines the start of the second signed
|
||||
// TLV range used for pure TLV messages. The range is inclusive of this
|
||||
// number. Note that the first range is the inclusive range of 0-159.
|
||||
pureTLVSignedSecondRangeStart = 1000000000
|
||||
|
||||
// pureTLVUnsignedRangeTwoStart defines the start of the second unsigned
|
||||
// TLV range used for pure TLV message.
|
||||
pureTLVUnsignedRangeTwoStart = 3000000000
|
||||
)
|
||||
|
||||
// PureTLVMessage describes an LN message that is a pure TLV stream. If the
|
||||
// message includes a signature, it will sign all the TLV records in the
|
||||
// inclusive ranges: 0 to 159 and 1000000000 to 2999999999.
|
||||
type PureTLVMessage interface {
|
||||
// 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.
|
||||
AllRecords() []tlv.Record
|
||||
}
|
||||
|
||||
// EncodePureTLVMessage encodes the given PureTLVMessage to the given buffer.
|
||||
func EncodePureTLVMessage(msg PureTLVMessage, buf *bytes.Buffer) error {
|
||||
return EncodeRecordsTo(buf, msg.AllRecords())
|
||||
}
|
||||
|
||||
// SerialiseFieldsToSign serialises all the records from the given
|
||||
// PureTLVMessage that fall within the signed TLV range.
|
||||
func SerialiseFieldsToSign(msg PureTLVMessage) ([]byte, error) {
|
||||
// Filter out all the fields not in the signed ranges.
|
||||
var signedRecords []tlv.Record
|
||||
for _, record := range msg.AllRecords() {
|
||||
if InUnsignedRange(record.Type()) {
|
||||
continue
|
||||
}
|
||||
|
||||
signedRecords = append(signedRecords, record)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := EncodeRecordsTo(&buf, signedRecords); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// InUnsignedRange returns true if the given TLV type falls outside the TLV
|
||||
// ranges that the signature of a pure TLV message will cover.
|
||||
func InUnsignedRange(t tlv.Type) bool {
|
||||
return (t >= pureTLVUnsignedRangeOneStart &&
|
||||
t < pureTLVSignedSecondRangeStart) ||
|
||||
t >= pureTLVUnsignedRangeTwoStart
|
||||
}
|
||||
|
||||
// ExtraSignedFields is a type that stores a map from TLV types in the signed
|
||||
// range (for PureMessages) to their corresponding serialised values. This type
|
||||
// can be used to keep around data that we don't yet understand but that we need
|
||||
// for re-composing the wire message since the signature covers these fields.
|
||||
type ExtraSignedFields map[uint64][]byte
|
||||
|
||||
// ExtraSignedFieldsFromTypeMap is a helper that can be used alongside calls to
|
||||
// the tlv.Stream DecodeWithParsedTypesP2P or DecodeWithParsedTypes methods to
|
||||
// extract the tlv type and value pairs in the defined PureTLVMessage signed
|
||||
// range which we have not handled with any of our defined Records. These
|
||||
// methods will return a tlv.TypeMap containing the records that were extracted
|
||||
// from an io.Reader. If the record was know and handled by a defined record,
|
||||
// then the value accompanying the record's type in the map will be nil.
|
||||
// Otherwise, if the record was unhandled, it will be non-nil.
|
||||
func ExtraSignedFieldsFromTypeMap(m tlv.TypeMap) ExtraSignedFields {
|
||||
extraFields := make(ExtraSignedFields)
|
||||
for t, v := range m {
|
||||
// If the value in the type map is nil, then it indicates that
|
||||
// we know this type, and it was handled by one of the records
|
||||
// we passed to the decode function vai the TLV stream.
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// No need to keep this field if it is unknown to us and is not
|
||||
// in the sign range.
|
||||
if InUnsignedRange(t) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, this is an un-handled type, so we keep track of
|
||||
// it for signature validation and re-encoding later on.
|
||||
extraFields[uint64(t)] = v
|
||||
}
|
||||
|
||||
return extraFields
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestPureTLVMessages tests the forwards compatibility of two versions of the
|
||||
// same Lightning Network message that uses the Pure TLV format. This in essence
|
||||
// tests that and older client is able to verify the signature over relevant
|
||||
// data in a newer client's message.
|
||||
func TestPureTLVMessage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
_, pkA = btcec.PrivKeyFromBytes([]byte{1})
|
||||
_, pkB = btcec.PrivKeyFromBytes([]byte{2})
|
||||
capacity = MilliSatoshi(100)
|
||||
)
|
||||
|
||||
// Test encode and decode of MsgV1 as is.
|
||||
t.Run("Encode and Decode of MsgV1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
msgOld := newMsgV1(pkA, &capacity)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
require.NoError(t, msgOld.Encode(buf, 0))
|
||||
|
||||
var msgOld2 MsgV1
|
||||
require.NoError(t, msgOld2.Decode(buf, 0))
|
||||
|
||||
require.Equal(t, msgOld, &msgOld2)
|
||||
})
|
||||
|
||||
// Test encode and decode of MsgV2 as is.
|
||||
t.Run("Encode and Decode of MsgV2", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
msgNew := newMsgV2(
|
||||
pkA, &capacity, pkB, []byte{1, 2, 3, 4}, 90, 100, true,
|
||||
)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
require.NoError(t, msgNew.Encode(buf, 0))
|
||||
|
||||
var msgNew2 MsgV2
|
||||
require.NoError(t, msgNew2.Decode(buf, 0))
|
||||
|
||||
require.Equal(t, msgNew, &msgNew2)
|
||||
})
|
||||
|
||||
// Create a MsgV2 and decode it into a MsgV1. Both the new client
|
||||
// (MsgV2) and old client (MsgV1) should be able to generate the same
|
||||
// digest that will be used to create and validate the signture.
|
||||
t.Run("Encode MsgV2 and decode via MsgV1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
buf = bytes.NewBuffer(nil)
|
||||
msgV2 = newMsgV2(
|
||||
pkA, &capacity, pkB, []byte{1, 2, 3, 4}, 100,
|
||||
90, true,
|
||||
)
|
||||
)
|
||||
require.NoError(t, msgV2.Encode(buf, 0))
|
||||
|
||||
// Get the serialised bytes that would be signed for msgV2.
|
||||
signData1, err := SerialiseFieldsToSign(msgV2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Decoding via the old message should store some of the extra
|
||||
// fields.
|
||||
var msgV1 MsgV1
|
||||
require.NoError(t, msgV1.Decode(buf, 0))
|
||||
require.NotEmpty(t, msgV1.ExtraSignedFields)
|
||||
|
||||
// Show that the extra fields map contains unknown fields in the
|
||||
// signed range but not unknown fields in the unsigned range.
|
||||
_, ok := msgV1.ExtraSignedFields[uint64(msgV2.Num.TlvType())] //nolint:ll
|
||||
require.True(t, ok)
|
||||
_, ok = msgV1.ExtraSignedFields[uint64(msgV2.Other.TlvType())] //nolint:ll
|
||||
require.False(t, ok)
|
||||
|
||||
// The serialised bytes to verify the signature against should
|
||||
// be the same though.
|
||||
signData2, err := SerialiseFieldsToSign(&msgV1)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, signData1, signData2)
|
||||
|
||||
// Re-encoding via the old message should keep the extra fields.
|
||||
buf = bytes.NewBuffer(nil)
|
||||
require.NoError(t, msgV1.Encode(buf, 0))
|
||||
|
||||
var msgV1ReEncoded MsgV1
|
||||
require.NoError(t, msgV1ReEncoded.Decode(buf, 0))
|
||||
|
||||
require.Equal(t, &msgV1, &msgV1ReEncoded)
|
||||
})
|
||||
}
|
||||
|
||||
// MsgV1 represents a more minimal, first version of a Lightning Network
|
||||
// message.
|
||||
type MsgV1 struct {
|
||||
// Two known fields in the signed range.
|
||||
NodeKey tlv.RecordT[tlv.TlvType0, *btcec.PublicKey]
|
||||
Capacity tlv.OptionalRecordT[tlv.TlvType1, MilliSatoshi]
|
||||
|
||||
// Signature in the unsigned range.
|
||||
Signature tlv.RecordT[tlv.TlvType160, Sig]
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
var _ Message = (*MsgV1)(nil)
|
||||
var _ PureTLVMessage = (*MsgV1)(nil)
|
||||
|
||||
// newMsgV1 is a constructor for MsgV1.
|
||||
func newMsgV1(nodeKey *btcec.PublicKey, capacity *MilliSatoshi) *MsgV1 {
|
||||
newMsg := &MsgV1{
|
||||
NodeKey: tlv.NewPrimitiveRecord[tlv.TlvType0](
|
||||
nodeKey,
|
||||
),
|
||||
Signature: tlv.NewRecordT[tlv.TlvType160](
|
||||
testSchnorrSig,
|
||||
),
|
||||
ExtraSignedFields: make(ExtraSignedFields),
|
||||
}
|
||||
|
||||
if capacity != nil {
|
||||
newMsg.Capacity = tlv.SomeRecordT(
|
||||
tlv.NewPrimitiveRecord[tlv.TlvType1](*capacity),
|
||||
)
|
||||
}
|
||||
|
||||
return newMsg
|
||||
}
|
||||
|
||||
// Decode deserializes a serialized MsgV1 in the passed io.Reader.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (g *MsgV1) Decode(r io.Reader, _ uint32) error {
|
||||
var capacity = tlv.ZeroRecordT[tlv.TlvType1, MilliSatoshi]()
|
||||
stream, err := tlv.NewStream(
|
||||
ProduceRecordsSorted(
|
||||
&g.NodeKey,
|
||||
&capacity,
|
||||
&g.Signature,
|
||||
)...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Signature.Val.ForceSchnorr()
|
||||
|
||||
typeMap, err := stream.DecodeWithParsedTypesP2P(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := typeMap[g.Capacity.TlvType()]; ok {
|
||||
g.Capacity = tlv.SomeRecordT(capacity)
|
||||
}
|
||||
|
||||
g.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode serializes the target MsgV1 into the passed buffer.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (g *MsgV1) Encode(buf *bytes.Buffer, _ uint32) error {
|
||||
return EncodePureTLVMessage(g, buf)
|
||||
}
|
||||
|
||||
// MsgType returns the integer uniquely identifying this message type on the
|
||||
// wire.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (g *MsgV1) MsgType() MessageType {
|
||||
return 7777
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This is part of the PureTLVMessage interface.
|
||||
func (g *MsgV1) AllRecords() []tlv.Record {
|
||||
recordProducers := []tlv.RecordProducer{
|
||||
&g.NodeKey,
|
||||
&g.Signature,
|
||||
}
|
||||
recordProducers = append(
|
||||
recordProducers,
|
||||
RecordsAsProducers(
|
||||
tlv.MapToRecords(g.ExtraSignedFields),
|
||||
)...,
|
||||
)
|
||||
|
||||
g.Capacity.WhenSome(
|
||||
func(capacity tlv.RecordT[tlv.TlvType1, MilliSatoshi]) {
|
||||
recordProducers = append(recordProducers, &capacity)
|
||||
},
|
||||
)
|
||||
|
||||
return ProduceRecordsSorted(recordProducers...)
|
||||
}
|
||||
|
||||
// MsgV2 represents a newer version of MsgV1 which contains more fields both in
|
||||
// the unsigned and signed TLV ranges.
|
||||
type MsgV2 struct {
|
||||
NodeKey tlv.RecordT[tlv.TlvType0, *btcec.PublicKey]
|
||||
Capacity tlv.OptionalRecordT[tlv.TlvType1, MilliSatoshi]
|
||||
|
||||
// An additional fields (optional) in the signed range.
|
||||
BitcoinKey tlv.OptionalRecordT[tlv.TlvType3, *btcec.PublicKey]
|
||||
|
||||
// A zero length TLV in the signed range.
|
||||
SecondPeer tlv.OptionalRecordT[tlv.TlvType5, TrueBoolean]
|
||||
|
||||
// Signature in the unsigned range.
|
||||
Signature tlv.RecordT[tlv.TlvType160, Sig]
|
||||
|
||||
// Another field in the unsigned range. An older node can throw this
|
||||
// away.
|
||||
SPVProof tlv.RecordT[tlv.TlvType161, []byte]
|
||||
|
||||
// A new field in the second signed range. An older node should keep
|
||||
// this since it is part of the serialised message that is signed.
|
||||
Num tlv.RecordT[tlv.TlvType1000000000, uint8]
|
||||
|
||||
// Another field in the second unsigned-range. Older nodes may throw
|
||||
// this away and it won't affect the digest used for signature creation
|
||||
// and validation.
|
||||
Other tlv.RecordT[tlv.TlvType3000000000, uint8]
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// newMsgV2 is a constructor for MsgV2.
|
||||
func newMsgV2(nodeKey *btcec.PublicKey, capacity *MilliSatoshi,
|
||||
btcKey *btcec.PublicKey, spvProof []byte, num, other uint8,
|
||||
secondPeer bool) *MsgV2 {
|
||||
|
||||
newMsg := &MsgV2{
|
||||
NodeKey: tlv.NewPrimitiveRecord[tlv.TlvType0](nodeKey),
|
||||
SPVProof: tlv.NewPrimitiveRecord[tlv.TlvType161](spvProof),
|
||||
Num: tlv.NewPrimitiveRecord[tlv.TlvType1000000000](num),
|
||||
Other: tlv.NewPrimitiveRecord[tlv.TlvType3000000000](num),
|
||||
Signature: tlv.NewRecordT[tlv.TlvType160](
|
||||
testSchnorrSig,
|
||||
),
|
||||
ExtraSignedFields: make(ExtraSignedFields),
|
||||
}
|
||||
|
||||
if secondPeer {
|
||||
newMsg.SecondPeer = tlv.SomeRecordT(
|
||||
tlv.NewRecordT[tlv.TlvType5](TrueBoolean{}),
|
||||
)
|
||||
}
|
||||
|
||||
if capacity != nil {
|
||||
newMsg.Capacity = tlv.SomeRecordT(
|
||||
tlv.NewPrimitiveRecord[tlv.TlvType1](*capacity),
|
||||
)
|
||||
}
|
||||
|
||||
if btcKey != nil {
|
||||
newMsg.BitcoinKey = tlv.SomeRecordT(
|
||||
tlv.NewPrimitiveRecord[tlv.TlvType3](btcKey),
|
||||
)
|
||||
}
|
||||
|
||||
return newMsg
|
||||
}
|
||||
|
||||
// Decode deserializes a serialized MsgV2 in the passed io.Reader.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (g *MsgV2) Decode(r io.Reader, _ uint32) error {
|
||||
var (
|
||||
capacity = tlv.ZeroRecordT[tlv.TlvType1, MilliSatoshi]()
|
||||
btcKey = tlv.ZeroRecordT[tlv.TlvType3, *btcec.PublicKey]()
|
||||
secondPeer = tlv.ZeroRecordT[tlv.TlvType5, TrueBoolean]()
|
||||
)
|
||||
|
||||
stream, err := tlv.NewStream(
|
||||
ProduceRecordsSorted(
|
||||
&g.NodeKey,
|
||||
&capacity,
|
||||
&btcKey,
|
||||
&secondPeer,
|
||||
&g.Signature,
|
||||
&g.SPVProof,
|
||||
&g.Num,
|
||||
&g.Other,
|
||||
)...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Signature.Val.ForceSchnorr()
|
||||
|
||||
typeMap, err := stream.DecodeWithParsedTypesP2P(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := typeMap[g.Capacity.TlvType()]; ok {
|
||||
g.Capacity = tlv.SomeRecordT(capacity)
|
||||
}
|
||||
|
||||
if _, ok := typeMap[g.SecondPeer.TlvType()]; ok {
|
||||
g.SecondPeer = tlv.SomeRecordT(secondPeer)
|
||||
}
|
||||
|
||||
if _, ok := typeMap[g.BitcoinKey.TlvType()]; ok {
|
||||
g.BitcoinKey = tlv.SomeRecordT(btcKey)
|
||||
}
|
||||
|
||||
g.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode serializes the target MsgV2 into the passed buffer.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (g *MsgV2) Encode(buf *bytes.Buffer, _ uint32) error {
|
||||
return EncodePureTLVMessage(g, buf)
|
||||
}
|
||||
|
||||
// MsgType returns the integer uniquely identifying this message type on the
|
||||
// wire.
|
||||
//
|
||||
// This is part of the lnwire.Message interface.
|
||||
func (g *MsgV2) MsgType() MessageType {
|
||||
return 7779
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This is part of the PureTLVMessage interface.
|
||||
func (g *MsgV2) AllRecords() []tlv.Record {
|
||||
recordProducers := []tlv.RecordProducer{
|
||||
&g.NodeKey,
|
||||
&g.Signature,
|
||||
&g.SPVProof,
|
||||
&g.Num,
|
||||
&g.Other,
|
||||
}
|
||||
recordProducers = append(recordProducers, RecordsAsProducers(
|
||||
tlv.MapToRecords(g.ExtraSignedFields),
|
||||
)...)
|
||||
|
||||
g.Capacity.WhenSome(
|
||||
func(c tlv.RecordT[tlv.TlvType1, MilliSatoshi]) {
|
||||
recordProducers = append(recordProducers, &c)
|
||||
},
|
||||
)
|
||||
g.BitcoinKey.WhenSome(
|
||||
func(key tlv.RecordT[tlv.TlvType3, *btcec.PublicKey]) {
|
||||
recordProducers = append(recordProducers, &key)
|
||||
},
|
||||
)
|
||||
g.SecondPeer.WhenSome(
|
||||
func(second tlv.RecordT[tlv.TlvType5, TrueBoolean]) {
|
||||
recordProducers = append(recordProducers, &second)
|
||||
},
|
||||
)
|
||||
|
||||
return ProduceRecordsSorted(recordProducers...)
|
||||
}
|
||||
+38
-16
@@ -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
|
||||
@@ -213,7 +230,6 @@ func (c *ChannelAnnouncement2) RandTestMessage(t *rapid.T) Message {
|
||||
copy(chainHashObj[:], chainHash[:])
|
||||
|
||||
msg := &ChannelAnnouncement2{
|
||||
Signature: RandSignature(t),
|
||||
ChainHash: tlv.NewPrimitiveRecord[tlv.TlvType0, chainhash.Hash](
|
||||
chainHashObj,
|
||||
),
|
||||
@@ -232,10 +248,16 @@ func (c *ChannelAnnouncement2) RandTestMessage(t *rapid.T) Message {
|
||||
NodeID2: tlv.NewPrimitiveRecord[tlv.TlvType10, [33]byte](
|
||||
nodeID2,
|
||||
),
|
||||
ExtraOpaqueData: RandExtraOpaqueData(t, nil),
|
||||
ExtraSignedFields: make(map[uint64][]byte),
|
||||
}
|
||||
|
||||
msg.Signature.ForceSchnorr()
|
||||
msg.Signature.Val = RandSignature(t)
|
||||
msg.Signature.Val.ForceSchnorr()
|
||||
|
||||
randRecs, _ := RandSignedRangeRecords(t)
|
||||
if len(randRecs) > 0 {
|
||||
msg.ExtraSignedFields = ExtraSignedFields(randRecs)
|
||||
}
|
||||
|
||||
// Randomly include optional fields
|
||||
if rapid.Bool().Draw(t, "includeBitcoinKey1") {
|
||||
@@ -411,7 +433,7 @@ func (a *ChannelUpdate1) RandTestMessage(t *rapid.T) Message {
|
||||
// include an inbound fee, then we will also set the record in the
|
||||
// extra opaque data.
|
||||
var (
|
||||
customRecords, _ = RandCustomRecords(t, nil, false)
|
||||
customRecords, _ = RandCustomRecords(t, nil)
|
||||
inboundFee tlv.OptionalRecordT[tlv.TlvType55555, Fee]
|
||||
)
|
||||
includeInboundFee := rapid.Bool().Draw(t, "includeInboundFee")
|
||||
@@ -513,7 +535,6 @@ func (c *ChannelUpdate2) RandTestMessage(t *rapid.T) Message {
|
||||
|
||||
//nolint:ll
|
||||
msg := &ChannelUpdate2{
|
||||
Signature: RandSignature(t),
|
||||
ChainHash: tlv.NewPrimitiveRecord[tlv.TlvType0, chainhash.Hash](
|
||||
chainHashObj,
|
||||
),
|
||||
@@ -541,10 +562,11 @@ func (c *ChannelUpdate2) RandTestMessage(t *rapid.T) Message {
|
||||
FeeProportionalMillionths: tlv.NewPrimitiveRecord[tlv.TlvType18, uint32](
|
||||
feeProportionalMillionths,
|
||||
),
|
||||
ExtraOpaqueData: RandExtraOpaqueData(t, nil),
|
||||
ExtraSignedFields: make(map[uint64][]byte),
|
||||
}
|
||||
|
||||
msg.Signature.ForceSchnorr()
|
||||
msg.Signature.Val = RandSignature(t)
|
||||
msg.Signature.Val.ForceSchnorr()
|
||||
|
||||
if rapid.Bool().Draw(t, "isSecondPeer") {
|
||||
msg.SecondPeer = tlv.SomeRecordT(
|
||||
@@ -728,7 +750,7 @@ var _ TestMessage = (*CommitSig)(nil)
|
||||
//
|
||||
// This is part of the TestMessage interface.
|
||||
func (c *CommitSig) RandTestMessage(t *rapid.T) Message {
|
||||
cr, _ := RandCustomRecords(t, nil, true)
|
||||
cr, _ := RandCustomRecords(t, nil)
|
||||
sig := &CommitSig{
|
||||
ChanID: RandChannelID(t),
|
||||
CommitSig: RandSignature(t),
|
||||
@@ -1606,7 +1628,7 @@ func (s *Shutdown) RandTestMessage(t *rapid.T) Message {
|
||||
shutdownNonce = SomeShutdownNonce(RandMusig2Nonce(t))
|
||||
}
|
||||
|
||||
cr, _ := RandCustomRecords(t, nil, true)
|
||||
cr, _ := RandCustomRecords(t, nil)
|
||||
|
||||
return &Shutdown{
|
||||
ChannelID: RandChannelID(t),
|
||||
@@ -1663,7 +1685,7 @@ func (c *UpdateAddHTLC) RandTestMessage(t *rapid.T) Message {
|
||||
|
||||
numRecords := rapid.IntRange(0, 5).Draw(t, "numRecords")
|
||||
if numRecords > 0 {
|
||||
msg.CustomRecords, _ = RandCustomRecords(t, nil, true)
|
||||
msg.CustomRecords, _ = RandCustomRecords(t, nil)
|
||||
}
|
||||
|
||||
// 50/50 chance to add a blinding point
|
||||
@@ -1744,7 +1766,7 @@ func (c *UpdateFulfillHTLC) RandTestMessage(t *rapid.T) Message {
|
||||
PaymentPreimage: RandPaymentPreimage(t),
|
||||
}
|
||||
|
||||
cr, ignoreRecords := RandCustomRecords(t, nil, true)
|
||||
cr, ignoreRecords := RandCustomRecords(t, nil)
|
||||
msg.CustomRecords = cr
|
||||
|
||||
randData := RandExtraOpaqueData(t, ignoreRecords)
|
||||
|
||||
+25
-11
@@ -198,23 +198,37 @@ func RandNetAddrs(t *rapid.T) []net.Addr {
|
||||
}
|
||||
|
||||
// RandCustomRecords generates random custom TLV records.
|
||||
func RandCustomRecords(t *rapid.T,
|
||||
ignoreRecords fn.Set[uint64],
|
||||
custom bool) (CustomRecords, fn.Set[uint64]) {
|
||||
func RandCustomRecords(t *rapid.T, ignoreRecords fn.Set[uint64]) (CustomRecords,
|
||||
fn.Set[uint64]) {
|
||||
|
||||
numRecords := rapid.IntRange(0, 5).Draw(t, "numCustomRecords")
|
||||
customRecords, set := RandTLVRecords(
|
||||
t, ignoreRecords, MinCustomRecordsTlvType,
|
||||
)
|
||||
|
||||
// Validate the custom records as a sanity check.
|
||||
require.NoError(t, customRecords.Validate())
|
||||
|
||||
return customRecords, set
|
||||
}
|
||||
|
||||
// RandSignedRangeRecords generates a random set of signed records in the
|
||||
// second "signed" tlv range for pure TLV messages.
|
||||
func RandSignedRangeRecords(t *rapid.T) (CustomRecords, fn.Set[uint64]) {
|
||||
return RandTLVRecords(t, nil, pureTLVSignedSecondRangeStart)
|
||||
}
|
||||
|
||||
// RandTLVRecords generates custom TLV records.
|
||||
func RandTLVRecords(t *rapid.T, ignoreRecords fn.Set[uint64],
|
||||
rangeStart int) (CustomRecords, fn.Set[uint64]) {
|
||||
|
||||
numRecords := rapid.IntRange(0, 5).Draw(t, "numRecords")
|
||||
customRecords := make(CustomRecords)
|
||||
|
||||
if numRecords == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rangeStart := 0
|
||||
rangeStop := int(CustomTypeStart)
|
||||
if custom {
|
||||
rangeStart = 70_000
|
||||
rangeStop = 100_000
|
||||
}
|
||||
rangeStop := rangeStart + 30_000
|
||||
|
||||
ignoreSet := fn.NewSet[uint64]()
|
||||
for i := 0; i < numRecords; i++ {
|
||||
@@ -258,7 +272,7 @@ func RandExtraOpaqueData(t *rapid.T,
|
||||
ignoreRecords fn.Set[uint64]) ExtraOpaqueData {
|
||||
|
||||
// Make some random records.
|
||||
cRecords, _ := RandCustomRecords(t, ignoreRecords, false)
|
||||
cRecords, _ := RandTLVRecords(t, ignoreRecords, 0)
|
||||
if cRecords == nil {
|
||||
return ExtraOpaqueData{}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user