From 77862e45af7aa257c114fae29656a419ebd96474 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 11 Dec 2020 14:36:07 +0800 Subject: [PATCH] lnwire: add benchmark tests to ReadMessage and WriteMessage --- lnwire/message_test.go | 867 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 867 insertions(+) create mode 100644 lnwire/message_test.go diff --git a/lnwire/message_test.go b/lnwire/message_test.go new file mode 100644 index 000000000..fbade5082 --- /dev/null +++ b/lnwire/message_test.go @@ -0,0 +1,867 @@ +package lnwire_test + +import ( + "bytes" + "encoding/binary" + "image/color" + "io" + "math" + "math/big" + "math/rand" + "net" + "sync" + "testing" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tor" + "github.com/stretchr/testify/require" +) + +const deliveryAddressMaxSize = 34 +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +var ( + testSig = &btcec.Signature{ + R: new(big.Int), + S: new(big.Int), + } + testNodeSig, _ = lnwire.NewSigFromSignature(testSig) + + testNumExtraBytes = 1000 + testNumSigs = 100 + testNumChanIDs = 1000 + buffer = make([]byte, 0, lnwire.MaxSliceLength) + + bufPool = sync.Pool{ + New: func() interface{} { + return bytes.NewBuffer(buffer) + }, + } +) + +// BenchmarkWriteMessage benchmarks the performance of lnwire.WriteMessage. It +// generates a test message for each of the lnwire.Message, calls the +// WriteMessage method and benchmark it. +func BenchmarkWriteMessage(b *testing.B) { + // Create testing messages. We will use a constant seed to make sure + // the benchmark uses the same data every time. + r := rand.New(rand.NewSource(42)) + + msgAll := makeAllMessages(b, r) + + // Iterate all messages and write each once. + for _, msg := range msgAll { + m := msg + // Run each message as a sub benchmark test. + b.Run(msg.MsgType().String(), func(b *testing.B) { + + b.ResetTimer() + for i := 0; i < b.N; i++ { + // Fetch a buffer from the pool and reset it. + buf := bufPool.Get().(*bytes.Buffer) + buf.Reset() + + _, err := lnwire.WriteMessage(buf, m, 0) + require.NoError(b, err, "unable to write msg") + + // Put the buffer back when done. + bufPool.Put(buf) + } + }) + } +} + +// BenchmarkReadMessage benchmarks the performance of lnwire.ReadMessage. It +// first creates a test message for each of the lnwire.Message, writes it to +// the buffer, then later reads it from the buffer. +func BenchmarkReadMessage(b *testing.B) { + // Create testing messages. We will use a constant seed to make sure + // the benchmark uses the same data every time. + r := rand.New(rand.NewSource(42)) + msgAll := makeAllMessages(b, r) + + // Write all the messages to the buffer. + for _, msg := range msgAll { + // Fetch a buffer from the pool and reset it. + buf := bufPool.Get().(*bytes.Buffer) + buf.Reset() + + _, err := lnwire.WriteMessage(buf, msg, 0) + require.NoError(b, err, "unable to write msg") + + // Run each message as a sub benchmark test. + m := msg + b.Run(m.MsgType().String(), func(b *testing.B) { + b.ResetTimer() + + for i := 0; i < b.N; i++ { + r := bytes.NewBuffer(buf.Bytes()) + + // Read the message from the buffer. + _, err := lnwire.ReadMessage(r, 0) + require.NoError(b, err, "unable to read msg") + } + }) + + // Put the buffer back when done. + bufPool.Put(buf) + } +} + +// makeAllMessages is used to create testing messages for each lnwire message +// type. +// +// TODO(yy): the following testing messages are created somewhat arbitrary. We +// should standardlize each of the testing messages so that a better baseline +// can be used. +func makeAllMessages(t testing.TB, r *rand.Rand) []lnwire.Message { + msgAll := []lnwire.Message{} + + msgAll = append(msgAll, newMsgInit(t, r)) + msgAll = append(msgAll, newMsgError(t, r)) + msgAll = append(msgAll, newMsgPing(t, r)) + msgAll = append(msgAll, newMsgPong(t, r)) + msgAll = append(msgAll, newMsgOpenChannel(t, r)) + msgAll = append(msgAll, newMsgAcceptChannel(t, r)) + msgAll = append(msgAll, newMsgFundingCreated(t, r)) + msgAll = append(msgAll, newMsgFundingSigned(t, r)) + msgAll = append(msgAll, newMsgFundingLocked(t, r)) + msgAll = append(msgAll, newMsgShutdown(t, r)) + msgAll = append(msgAll, newMsgClosingSigned(t, r)) + msgAll = append(msgAll, newMsgUpdateAddHTLC(t, r)) + msgAll = append(msgAll, newMsgUpdateFulfillHTLC(t, r)) + msgAll = append(msgAll, newMsgUpdateFailHTLC(t, r)) + msgAll = append(msgAll, newMsgCommitSig(t, r)) + msgAll = append(msgAll, newMsgRevokeAndAck(t, r)) + msgAll = append(msgAll, newMsgUpdateFee(t, r)) + msgAll = append(msgAll, newMsgUpdateFailMalformedHTLC(t, r)) + msgAll = append(msgAll, newMsgChannelReestablish(t, r)) + msgAll = append(msgAll, newMsgChannelAnnouncement(t, r)) + msgAll = append(msgAll, newMsgNodeAnnouncement(t, r)) + msgAll = append(msgAll, newMsgChannelUpdate(t, r)) + msgAll = append(msgAll, newMsgAnnounceSignatures(t, r)) + msgAll = append(msgAll, newMsgQueryShortChanIDs(t, r)) + msgAll = append(msgAll, newMsgReplyShortChanIDsEnd(t, r)) + msgAll = append(msgAll, newMsgQueryChannelRange(t, r)) + msgAll = append(msgAll, newMsgReplyChannelRange(t, r)) + msgAll = append(msgAll, newMsgGossipTimestampRange(t, r)) + msgAll = append(msgAll, newMsgQueryShortChanIDsZlib(t, r)) + msgAll = append(msgAll, newMsgReplyChannelRangeZlib(t, r)) + + return msgAll +} + +func newMsgInit(t testing.TB, r io.Reader) *lnwire.Init { + t.Helper() + + return &lnwire.Init{ + GlobalFeatures: rawFeatureVector(), + Features: rawFeatureVector(), + ExtraData: createExtraData(t, r), + } +} + +// newMsgOpenChannel creates a testing OpenChannel message. +func newMsgOpenChannel(t testing.TB, r *rand.Rand) *lnwire.OpenChannel { + t.Helper() + + msg := &lnwire.OpenChannel{ + FundingAmount: btcutil.Amount(r.Int63()), + PushAmount: lnwire.MilliSatoshi(r.Int63()), + DustLimit: btcutil.Amount(r.Int63()), + MaxValueInFlight: lnwire.MilliSatoshi(r.Int63()), + ChannelReserve: btcutil.Amount(r.Int63()), + HtlcMinimum: lnwire.MilliSatoshi(r.Int63()), + FeePerKiloWeight: uint32(r.Int31()), + CsvDelay: uint16(r.Intn(1 << 16)), + MaxAcceptedHTLCs: uint16(r.Intn(1 << 16)), + ChannelFlags: lnwire.FundingFlag(uint8(r.Intn(1 << 8))), + FundingKey: randPubKey(t), + RevocationPoint: randPubKey(t), + PaymentPoint: randPubKey(t), + DelayedPaymentPoint: randPubKey(t), + HtlcPoint: randPubKey(t), + FirstCommitmentPoint: randPubKey(t), + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to read bytes for ChainHash") + + _, err = r.Read(msg.PendingChannelID[:]) + require.NoError(t, err, "unable to read bytes for PendingChannelID") + + return msg +} + +func newMsgAcceptChannel(t testing.TB, r *rand.Rand) *lnwire.AcceptChannel { + t.Helper() + + msg := &lnwire.AcceptChannel{ + DustLimit: btcutil.Amount(r.Int63()), + MaxValueInFlight: lnwire.MilliSatoshi(r.Int63()), + ChannelReserve: btcutil.Amount(r.Int63()), + MinAcceptDepth: uint32(r.Int31()), + HtlcMinimum: lnwire.MilliSatoshi(r.Int63()), + CsvDelay: uint16(r.Intn(1 << 16)), + MaxAcceptedHTLCs: uint16(r.Intn(1 << 16)), + FundingKey: randPubKey(t), + RevocationPoint: randPubKey(t), + PaymentPoint: randPubKey(t), + DelayedPaymentPoint: randPubKey(t), + HtlcPoint: randPubKey(t), + FirstCommitmentPoint: randPubKey(t), + UpfrontShutdownScript: randDeliveryAddress(t, r), + ExtraData: createExtraData(t, r), + } + _, err := r.Read(msg.PendingChannelID[:]) + require.NoError(t, err, "unable to generate pending chan id") + + return msg +} + +func newMsgError(t testing.TB, r io.Reader) *lnwire.Error { + t.Helper() + + msg := lnwire.NewError() + + _, err := r.Read(msg.ChanID[:]) + require.NoError(t, err, "unable to generate chan id") + + msg.Data = createExtraData(t, r) + + return msg +} + +func newMsgPing(t testing.TB, r *rand.Rand) *lnwire.Ping { + t.Helper() + + return &lnwire.Ping{ + NumPongBytes: uint16(r.Intn(1 << 16)), + PaddingBytes: createExtraData(t, r), + } +} + +func newMsgPong(t testing.TB, r io.Reader) *lnwire.Pong { + t.Helper() + + return &lnwire.Pong{ + PongBytes: createExtraData(t, r), + } +} + +func newMsgFundingCreated(t testing.TB, r *rand.Rand) *lnwire.FundingCreated { + t.Helper() + + msg := &lnwire.FundingCreated{ + CommitSig: testNodeSig, + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.PendingChannelID[:]) + require.NoError(t, err, "unable to generate pending chan id") + + _, err = r.Read(msg.FundingPoint.Hash[:]) + require.NoError(t, err, "unable to generate hash") + + msg.FundingPoint.Index = uint32(r.Int31()) % math.MaxUint16 + + return msg +} + +func newMsgFundingSigned(t testing.TB, r io.Reader) *lnwire.FundingSigned { + t.Helper() + + var c [32]byte + + _, err := r.Read(c[:]) + require.NoError(t, err, "unable to generate chan id") + + msg := &lnwire.FundingSigned{ + ChanID: lnwire.ChannelID(c), + CommitSig: testNodeSig, + ExtraData: createExtraData(t, r), + } + + return msg +} + +func newMsgFundingLocked(t testing.TB, r io.Reader) *lnwire.FundingLocked { + t.Helper() + + var c [32]byte + + _, err := r.Read(c[:]) + require.NoError(t, err, "unable to generate chan id") + + pubKey := randPubKey(t) + + msg := lnwire.NewFundingLocked(lnwire.ChannelID(c), pubKey) + msg.ExtraData = createExtraData(t, r) + + return msg +} + +func newMsgShutdown(t testing.TB, r *rand.Rand) *lnwire.Shutdown { + t.Helper() + + msg := &lnwire.Shutdown{ + Address: randDeliveryAddress(t, r), + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.ChannelID[:]) + require.NoError(t, err, "unable to generate channel id") + + return msg +} + +func newMsgClosingSigned(t testing.TB, r *rand.Rand) *lnwire.ClosingSigned { + t.Helper() + + msg := &lnwire.ClosingSigned{ + FeeSatoshis: btcutil.Amount(r.Int63()), + Signature: testNodeSig, + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.ChannelID[:]) + require.NoError(t, err, "unable to generate chan id") + + return msg +} + +func newMsgUpdateAddHTLC(t testing.TB, r *rand.Rand) *lnwire.UpdateAddHTLC { + t.Helper() + + msg := &lnwire.UpdateAddHTLC{ + ID: r.Uint64(), + Amount: lnwire.MilliSatoshi(r.Int63()), + Expiry: r.Uint32(), + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.ChanID[:]) + require.NoError(t, err, "unable to generate chan id") + + _, err = r.Read(msg.PaymentHash[:]) + require.NoError(t, err, "unable to generate paymenthash") + + _, err = r.Read(msg.OnionBlob[:]) + require.NoError(t, err, "unable to generate onion blob") + + return msg +} + +func newMsgUpdateFulfillHTLC(t testing.TB, + r *rand.Rand) *lnwire.UpdateFulfillHTLC { + + t.Helper() + + msg := &lnwire.UpdateFulfillHTLC{ + ID: r.Uint64(), + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.ChanID[:]) + require.NoError(t, err, "unable to generate chan id") + + _, err = r.Read(msg.PaymentPreimage[:]) + require.NoError(t, err, "unable to generate payment preimage") + + return msg +} + +func newMsgUpdateFailHTLC(t testing.TB, r *rand.Rand) *lnwire.UpdateFailHTLC { + t.Helper() + + msg := &lnwire.UpdateFailHTLC{ + ID: r.Uint64(), + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.ChanID[:]) + require.NoError(t, err, "unable to generate chan id") + + return msg +} + +func newMsgCommitSig(t testing.TB, r io.Reader) *lnwire.CommitSig { + t.Helper() + + msg := lnwire.NewCommitSig() + + _, err := r.Read(msg.ChanID[:]) + require.NoError(t, err, "unable to generate chan id") + + msg.CommitSig = testNodeSig + msg.ExtraData = createExtraData(t, r) + + msg.HtlcSigs = make([]lnwire.Sig, testNumSigs) + for i := 0; i < testNumSigs; i++ { + msg.HtlcSigs[i] = testNodeSig + } + + return msg +} + +func newMsgRevokeAndAck(t testing.TB, r io.Reader) *lnwire.RevokeAndAck { + t.Helper() + + msg := lnwire.NewRevokeAndAck() + + _, err := r.Read(msg.ChanID[:]) + require.NoError(t, err, "unable to generate chan id") + + _, err = r.Read(msg.Revocation[:]) + require.NoError(t, err, "unable to generate bytes") + + msg.NextRevocationKey = randPubKey(t) + require.NoError(t, err, "unable to generate key") + + msg.ExtraData = createExtraData(t, r) + + return msg +} + +func newMsgUpdateFee(t testing.TB, r *rand.Rand) *lnwire.UpdateFee { + t.Helper() + + msg := &lnwire.UpdateFee{ + FeePerKw: uint32(r.Int31()), + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.ChanID[:]) + require.NoError(t, err, "unable to generate chan id") + + return msg +} + +func newMsgUpdateFailMalformedHTLC(t testing.TB, + r *rand.Rand) *lnwire.UpdateFailMalformedHTLC { + + t.Helper() + + msg := &lnwire.UpdateFailMalformedHTLC{ + ID: r.Uint64(), + FailureCode: lnwire.FailCode(r.Intn(1 << 16)), + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.ChanID[:]) + require.NoError(t, err, "unable to generate chan id") + + _, err = r.Read(msg.ShaOnionBlob[:]) + require.NoError(t, err, "unable to generate sha256 onion blob") + + return msg +} + +func newMsgChannelReestablish(t testing.TB, + r *rand.Rand) *lnwire.ChannelReestablish { + + t.Helper() + + msg := &lnwire.ChannelReestablish{ + NextLocalCommitHeight: uint64(r.Int63()), + RemoteCommitTailHeight: uint64(r.Int63()), + LocalUnrevokedCommitPoint: randPubKey(t), + ExtraData: createExtraData(t, r), + } + + _, err := r.Read(msg.LastRemoteCommitSecret[:]) + require.NoError(t, err, "unable to read commit secret") + + return msg +} + +func newMsgChannelAnnouncement(t testing.TB, + r *rand.Rand) *lnwire.ChannelAnnouncement { + + t.Helper() + + msg := &lnwire.ChannelAnnouncement{ + ShortChannelID: lnwire.NewShortChanIDFromInt(uint64(r.Int63())), + Features: rawFeatureVector(), + NodeID1: randRawKey(t), + NodeID2: randRawKey(t), + BitcoinKey1: randRawKey(t), + BitcoinKey2: randRawKey(t), + ExtraOpaqueData: createExtraData(t, r), + NodeSig1: testNodeSig, + NodeSig2: testNodeSig, + BitcoinSig1: testNodeSig, + BitcoinSig2: testNodeSig, + } + + _, err := r.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to generate chain hash") + + return msg +} + +func newMsgNodeAnnouncement(t testing.TB, + r *rand.Rand) *lnwire.NodeAnnouncement { + + t.Helper() + + msg := &lnwire.NodeAnnouncement{ + Features: rawFeatureVector(), + Timestamp: uint32(r.Int31()), + Alias: randAlias(r), + RGBColor: color.RGBA{ + R: uint8(r.Intn(1 << 8)), + G: uint8(r.Intn(1 << 8)), + B: uint8(r.Intn(1 << 8)), + }, + NodeID: randRawKey(t), + Addresses: randAddrs(t, r), + ExtraOpaqueData: createExtraData(t, r), + Signature: testNodeSig, + } + + return msg +} + +func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate { + t.Helper() + + msgFlags := lnwire.ChanUpdateMsgFlags(r.Int31()) + maxHtlc := lnwire.MilliSatoshi(r.Int63()) + + // We make the max_htlc field zero if it is not flagged + // as being part of the ChannelUpdate, to pass + // serialization tests, as it will be ignored if the bit + // is not set. + if msgFlags&lnwire.ChanUpdateOptionMaxHtlc == 0 { + maxHtlc = 0 + } + + msg := &lnwire.ChannelUpdate{ + ShortChannelID: lnwire.NewShortChanIDFromInt(r.Uint64()), + Timestamp: uint32(r.Int31()), + MessageFlags: msgFlags, + ChannelFlags: lnwire.ChanUpdateChanFlags(r.Int31()), + TimeLockDelta: uint16(r.Int31()), + HtlcMinimumMsat: lnwire.MilliSatoshi(r.Int63()), + HtlcMaximumMsat: maxHtlc, + BaseFee: uint32(r.Int31()), + FeeRate: uint32(r.Int31()), + ExtraOpaqueData: createExtraData(t, r), + Signature: testNodeSig, + } + + _, err := r.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to generate chain hash") + + return msg +} + +func newMsgAnnounceSignatures(t testing.TB, + r *rand.Rand) *lnwire.AnnounceSignatures { + + t.Helper() + + msg := &lnwire.AnnounceSignatures{ + ShortChannelID: lnwire.NewShortChanIDFromInt( + uint64(r.Int63()), + ), + ExtraOpaqueData: createExtraData(t, r), + NodeSignature: testNodeSig, + BitcoinSignature: testNodeSig, + } + + _, err := r.Read(msg.ChannelID[:]) + require.NoError(t, err, "unable to generate chan id") + + return msg +} + +func newMsgQueryShortChanIDs(t testing.TB, + r *rand.Rand) *lnwire.QueryShortChanIDs { + + t.Helper() + + msg := &lnwire.QueryShortChanIDs{ + EncodingType: lnwire.EncodingSortedPlain, + ExtraData: createExtraData(t, r), + } + + _, err := rand.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to read chain hash") + + for i := 0; i < testNumChanIDs; i++ { + msg.ShortChanIDs = append(msg.ShortChanIDs, + lnwire.NewShortChanIDFromInt(uint64(r.Int63()))) + } + + return msg +} + +func newMsgQueryShortChanIDsZlib(t testing.TB, + r *rand.Rand) *lnwire.QueryShortChanIDs { + + t.Helper() + + msg := &lnwire.QueryShortChanIDs{ + EncodingType: lnwire.EncodingSortedZlib, + ExtraData: createExtraData(t, r), + } + + _, err := rand.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to read chain hash") + + for i := 0; i < testNumChanIDs; i++ { + msg.ShortChanIDs = append(msg.ShortChanIDs, + lnwire.NewShortChanIDFromInt(uint64(r.Int63()))) + } + + return msg +} + +func newMsgReplyShortChanIDsEnd(t testing.TB, + r *rand.Rand) *lnwire.ReplyShortChanIDsEnd { + + t.Helper() + + msg := lnwire.NewReplyShortChanIDsEnd() + + _, err := rand.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to read chain hash") + + msg.Complete = uint8(r.Int31n(2)) + msg.ExtraData = createExtraData(t, r) + + return msg +} + +func newMsgQueryChannelRange(t testing.TB, + r *rand.Rand) *lnwire.QueryChannelRange { + + t.Helper() + + msg := lnwire.NewQueryChannelRange() + + _, err := rand.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to read chain hash") + + msg.FirstBlockHeight = r.Uint32() + msg.NumBlocks = r.Uint32() + msg.ExtraData = createExtraData(t, r) + + return msg +} + +func newMsgReplyChannelRange(t testing.TB, + r *rand.Rand) *lnwire.ReplyChannelRange { + + t.Helper() + + msg := &lnwire.ReplyChannelRange{ + EncodingType: lnwire.EncodingSortedPlain, + ExtraData: createExtraData(t, r), + } + + _, err := rand.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to read chain hash") + + msg.Complete = uint8(r.Int31n(2)) + + for i := 0; i < testNumChanIDs; i++ { + msg.ShortChanIDs = append(msg.ShortChanIDs, + lnwire.NewShortChanIDFromInt(uint64(r.Int63()))) + } + + return msg +} + +func newMsgReplyChannelRangeZlib(t testing.TB, + r *rand.Rand) *lnwire.ReplyChannelRange { + + t.Helper() + + msg := &lnwire.ReplyChannelRange{ + EncodingType: lnwire.EncodingSortedZlib, + ExtraData: createExtraData(t, r), + } + + _, err := rand.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to read chain hash") + + msg.Complete = uint8(r.Int31n(2)) + + for i := 0; i < testNumChanIDs; i++ { + msg.ShortChanIDs = append(msg.ShortChanIDs, + lnwire.NewShortChanIDFromInt(uint64(r.Int63()))) + } + + return msg +} + +func newMsgGossipTimestampRange(t testing.TB, + r *rand.Rand) *lnwire.GossipTimestampRange { + + t.Helper() + + msg := lnwire.NewGossipTimestampRange() + msg.FirstTimestamp = r.Uint32() + msg.TimestampRange = r.Uint32() + msg.ExtraData = createExtraData(t, r) + + _, err := r.Read(msg.ChainHash[:]) + require.NoError(t, err, "unable to read chain hash") + + return msg +} + +func randRawKey(t testing.TB) [33]byte { + t.Helper() + + var n [33]byte + + priv, err := btcec.NewPrivateKey(btcec.S256()) + require.NoError(t, err, "failed to create privKey") + + copy(n[:], priv.PubKey().SerializeCompressed()) + + return n +} + +func randPubKey(t testing.TB) *btcec.PublicKey { + t.Helper() + + priv, err := btcec.NewPrivateKey(btcec.S256()) + require.NoError(t, err, "failed to create pubkey") + + return priv.PubKey() +} + +func rawFeatureVector() *lnwire.RawFeatureVector { + // Get a slice of known feature bits. + featureBits := make([]lnwire.FeatureBit, 0, len(lnwire.Features)) + for fb := range lnwire.Features { + featureBits = append(featureBits, fb) + } + + featureVec := lnwire.NewRawFeatureVector(featureBits...) + + return featureVec +} + +func randDeliveryAddress(t testing.TB, r *rand.Rand) lnwire.DeliveryAddress { + t.Helper() + + // Generate a max sized address. + size := r.Intn(deliveryAddressMaxSize) + 1 + da := lnwire.DeliveryAddress(make([]byte, size)) + + _, err := r.Read(da) + require.NoError(t, err, "unable to read address") + return da +} + +func randTCP4Addr(t testing.TB, r *rand.Rand) *net.TCPAddr { + t.Helper() + + var ip [4]byte + _, err := r.Read(ip[:]) + require.NoError(t, err, "unable to read ip") + + var port [2]byte + _, err = r.Read(port[:]) + require.NoError(t, err, "unable to read port") + + addrIP := net.IP(ip[:]) + addrPort := int(binary.BigEndian.Uint16(port[:])) + + return &net.TCPAddr{IP: addrIP, Port: addrPort} +} + +func randTCP6Addr(t testing.TB, r *rand.Rand) *net.TCPAddr { + t.Helper() + + var ip [16]byte + + _, err := r.Read(ip[:]) + require.NoError(t, err, "unable to read ip") + + var port [2]byte + _, err = r.Read(port[:]) + require.NoError(t, err, "unable to read port") + + addrIP := net.IP(ip[:]) + addrPort := int(binary.BigEndian.Uint16(port[:])) + + return &net.TCPAddr{IP: addrIP, Port: addrPort} +} + +func randV2OnionAddr(t testing.TB, r *rand.Rand) *tor.OnionAddr { + t.Helper() + + var serviceID [tor.V2DecodedLen]byte + _, err := r.Read(serviceID[:]) + require.NoError(t, err, "unable to read serviceID") + + var port [2]byte + _, err = r.Read(port[:]) + require.NoError(t, err, "unable to read port") + + onionService := tor.Base32Encoding.EncodeToString(serviceID[:]) + onionService += tor.OnionSuffix + addrPort := int(binary.BigEndian.Uint16(port[:])) + + return &tor.OnionAddr{OnionService: onionService, Port: addrPort} +} + +func randV3OnionAddr(t testing.TB, r *rand.Rand) *tor.OnionAddr { + t.Helper() + + var serviceID [tor.V3DecodedLen]byte + _, err := r.Read(serviceID[:]) + require.NoError(t, err, "unable to read serviceID") + + var port [2]byte + _, err = r.Read(port[:]) + require.NoError(t, err, "unable to read port") + + onionService := tor.Base32Encoding.EncodeToString(serviceID[:]) + onionService += tor.OnionSuffix + addrPort := int(binary.BigEndian.Uint16(port[:])) + + return &tor.OnionAddr{OnionService: onionService, Port: addrPort} +} + +func randAddrs(t testing.TB, r *rand.Rand) []net.Addr { + tcp4Addr := randTCP4Addr(t, r) + tcp6Addr := randTCP6Addr(t, r) + v2OnionAddr := randV2OnionAddr(t, r) + v3OnionAddr := randV3OnionAddr(t, r) + + return []net.Addr{tcp4Addr, tcp6Addr, v2OnionAddr, v3OnionAddr} +} + +func randAlias(r *rand.Rand) lnwire.NodeAlias { + var a lnwire.NodeAlias + for i := range a { + a[i] = letterBytes[r.Intn(len(letterBytes))] + } + + return a +} + +func createExtraData(t testing.TB, r io.Reader) []byte { + t.Helper() + + // Read random bytes. + extraData := make([]byte, testNumExtraBytes) + _, err := r.Read(extraData) + require.NoError(t, err, "unable to generate extra data") + + // Encode the data length. + binary.BigEndian.PutUint16(extraData[:2], uint16(len(extraData[2:]))) + + return extraData +}