Files
lnd/lnwire/test_utils.go
Olaoluwa Osuntokun eb877db2ff lnwire: add new TestMessage interface for property tests
In this commit, we add a new `TestMessage` interface for use in property
tests. With this, we'll be able to generate a random instance of a given
message, using the rapid byte stream. This can also eventually be useful
for fuzzing.
2025-03-20 18:28:07 -07:00

361 lines
8.7 KiB
Go

package lnwire
import (
"crypto/sha256"
"fmt"
"net"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"
)
// RandChannelUpdate generates a random ChannelUpdate message using rapid's
// generators.
func RandPartialSig(t *rapid.T) *PartialSig {
// Generate random private key bytes
sigBytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "privKeyBytes")
var s btcec.ModNScalar
s.SetByteSlice(sigBytes)
return &PartialSig{
Sig: s,
}
}
// RandPartialSigWithNonce generates a random PartialSigWithNonce using rapid
// generators.
func RandPartialSigWithNonce(t *rapid.T) *PartialSigWithNonce {
sigLen := rapid.IntRange(1, 65).Draw(t, "partialSigLen")
sigBytes := rapid.SliceOfN(
rapid.Byte(), sigLen, sigLen,
).Draw(t, "partialSig")
sigScalar := new(btcec.ModNScalar)
sigScalar.SetByteSlice(sigBytes)
return NewPartialSigWithNonce(
RandMusig2Nonce(t), *sigScalar,
)
}
// RandPubKey generates a random public key using rapid's generators.
func RandPubKey(t *rapid.T) *btcec.PublicKey {
privKeyBytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(
t, "privKeyBytes",
)
_, pub := btcec.PrivKeyFromBytes(privKeyBytes)
return pub
}
// RandChannelID generates a random channel ID.
func RandChannelID(t *rapid.T) ChannelID {
var c ChannelID
bytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "channelID")
copy(c[:], bytes)
return c
}
// RandShortChannelID generates a random short channel ID.
func RandShortChannelID(t *rapid.T) ShortChannelID {
return NewShortChanIDFromInt(
uint64(rapid.IntRange(1, 100000).Draw(t, "shortChanID")),
)
}
// RandFeatureVector generates a random feature vector.
func RandFeatureVector(t *rapid.T) *RawFeatureVector {
featureVec := NewRawFeatureVector()
// Add a random number of random feature bits
numFeatures := rapid.IntRange(0, 20).Draw(t, "numFeatures")
for i := 0; i < numFeatures; i++ {
bit := FeatureBit(rapid.IntRange(0, 100).Draw(
t, fmt.Sprintf("featureBit-%d", i)),
)
featureVec.Set(bit)
}
return featureVec
}
// RandSignature generates a signature for testing.
func RandSignature(t *rapid.T) Sig {
testRScalar := new(btcec.ModNScalar)
testSScalar := new(btcec.ModNScalar)
// Generate random bytes for R and S
rBytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "rBytes")
sBytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "sBytes")
_ = testRScalar.SetByteSlice(rBytes)
_ = testSScalar.SetByteSlice(sBytes)
testSig := ecdsa.NewSignature(testRScalar, testSScalar)
sig, err := NewSigFromSignature(testSig)
if err != nil {
panic(fmt.Sprintf("unable to create signature: %v", err))
}
return sig
}
// RandPaymentHash generates a random payment hash.
func RandPaymentHash(t *rapid.T) [32]byte {
var hash [32]byte
bytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "paymentHash")
copy(hash[:], bytes)
return hash
}
// RandPaymentPreimage generates a random payment preimage.
func RandPaymentPreimage(t *rapid.T) [32]byte {
var preimage [32]byte
bytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "preimage")
copy(preimage[:], bytes)
return preimage
}
// RandChainHash generates a random chain hash.
func RandChainHash(t *rapid.T) chainhash.Hash {
var hash [32]byte
bytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "chainHash")
copy(hash[:], bytes)
return hash
}
// RandNodeAlias generates a random node alias.
func RandNodeAlias(t *rapid.T) NodeAlias {
var alias NodeAlias
aliasLength := rapid.IntRange(0, 32).Draw(t, "aliasLength")
aliasBytes := rapid.StringN(
0, aliasLength, aliasLength,
).Draw(t, "alias")
copy(alias[:], aliasBytes)
return alias
}
// RandNetAddrs generates random network addresses.
func RandNetAddrs(t *rapid.T) []net.Addr {
numAddresses := rapid.IntRange(0, 5).Draw(t, "numAddresses")
if numAddresses == 0 {
return nil
}
addresses := make([]net.Addr, numAddresses)
for i := 0; i < numAddresses; i++ {
addressType := rapid.IntRange(0, 1).Draw(
t, fmt.Sprintf("addressType-%d", i),
)
switch addressType {
// IPv4.
case 0:
ipBytes := rapid.SliceOfN(rapid.Byte(), 4, 4).Draw(
t, fmt.Sprintf("ipv4-%d", i),
)
port := rapid.IntRange(1, 65535).Draw(
t, fmt.Sprintf("port-%d", i),
)
addresses[i] = &net.TCPAddr{
IP: ipBytes,
Port: port,
}
// IPv6.
case 1:
ipBytes := rapid.SliceOfN(rapid.Byte(), 16, 16).Draw(
t, fmt.Sprintf("ipv6-%d", i),
)
port := rapid.IntRange(1, 65535).Draw(
t, fmt.Sprintf("port-%d", i),
)
addresses[i] = &net.TCPAddr{
IP: ipBytes,
Port: port,
}
}
}
return addresses
}
// RandCustomRecords generates random custom TLV records.
func RandCustomRecords(t *rapid.T,
ignoreRecords fn.Set[uint64],
custom bool) (CustomRecords, fn.Set[uint64]) {
numRecords := rapid.IntRange(0, 5).Draw(t, "numCustomRecords")
customRecords := make(CustomRecords)
if numRecords == 0 {
return nil, nil
}
rangeStart := 0
rangeStop := int(CustomTypeStart)
if custom {
rangeStart = 70_000
rangeStop = 100_000
}
ignoreSet := fn.NewSet[uint64]()
for i := 0; i < numRecords; i++ {
recordType := uint64(
rapid.IntRange(rangeStart, rangeStop).
Filter(func(i int) bool {
return !ignoreRecords.Contains(
uint64(i),
)
}).
Draw(
t, fmt.Sprintf("recordType-%d", i),
),
)
recordLen := rapid.IntRange(4, 64).Draw(
t, fmt.Sprintf("recordLen-%d", i),
)
record := rapid.SliceOfN(
rapid.Byte(), recordLen, recordLen,
).Draw(t, fmt.Sprintf("record-%d", i))
customRecords[recordType] = record
ignoreSet.Add(recordType)
}
return customRecords, ignoreSet
}
// RandMusig2Nonce generates a random musig2 nonce.
func RandMusig2Nonce(t *rapid.T) Musig2Nonce {
var nonce Musig2Nonce
bytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "nonce")
copy(nonce[:], bytes)
return nonce
}
// RandExtraOpaqueData generates random extra opaque data.
func RandExtraOpaqueData(t *rapid.T,
ignoreRecords fn.Set[uint64]) ExtraOpaqueData {
// Make some random records.
cRecords, _ := RandCustomRecords(t, ignoreRecords, false)
if cRecords == nil {
return ExtraOpaqueData{}
}
// Encode those records as opaque data.
recordBytes, err := cRecords.Serialize()
require.NoError(t, err)
return ExtraOpaqueData(recordBytes)
}
// RandOpaqueReason generates a random opaque reason for HTLC failures.
func RandOpaqueReason(t *rapid.T) OpaqueReason {
reasonLen := rapid.IntRange(32, 300).Draw(t, "reasonLen")
return rapid.SliceOfN(rapid.Byte(), reasonLen, reasonLen).Draw(
t, "opaqueReason",
)
}
// RandFailCode generates a random HTLC failure code.
func RandFailCode(t *rapid.T) FailCode {
// List of known failure codes to choose from Using only the documented
// codes.
validCodes := []FailCode{
CodeInvalidRealm,
CodeTemporaryNodeFailure,
CodePermanentNodeFailure,
CodeRequiredNodeFeatureMissing,
CodePermanentChannelFailure,
CodeRequiredChannelFeatureMissing,
CodeUnknownNextPeer,
CodeIncorrectOrUnknownPaymentDetails,
CodeIncorrectPaymentAmount,
CodeFinalExpiryTooSoon,
CodeInvalidOnionVersion,
CodeInvalidOnionHmac,
CodeInvalidOnionKey,
CodeTemporaryChannelFailure,
CodeChannelDisabled,
CodeExpiryTooSoon,
CodeMPPTimeout,
CodeInvalidOnionPayload,
CodeFeeInsufficient,
}
// Choose a random code from the list.
idx := rapid.IntRange(0, len(validCodes)-1).Draw(t, "failCodeIndex")
return validCodes[idx]
}
// RandSHA256Hash generates a random SHA256 hash.
func RandSHA256Hash(t *rapid.T) [sha256.Size]byte {
var hash [sha256.Size]byte
bytes := rapid.SliceOfN(rapid.Byte(), sha256.Size, sha256.Size).Draw(
t, "sha256Hash",
)
copy(hash[:], bytes)
return hash
}
// RandDeliveryAddress generates a random delivery address (script).
func RandDeliveryAddress(t *rapid.T) DeliveryAddress {
addrLen := rapid.IntRange(1, 34).Draw(t, "addrLen")
return rapid.SliceOfN(rapid.Byte(), addrLen, addrLen).Draw(
t, "deliveryAddress",
)
}
// RandChannelType generates a random channel type.
func RandChannelType(t *rapid.T) *ChannelType {
vec := RandFeatureVector(t)
chanType := ChannelType(*vec)
return &chanType
}
// RandLeaseExpiry generates a random lease expiry.
func RandLeaseExpiry(t *rapid.T) *LeaseExpiry {
exp := LeaseExpiry(
uint32(rapid.IntRange(1000, 1000000).Draw(t, "leaseExpiry")),
)
return &exp
}
// RandOutPoint generates a random transaction outpoint.
func RandOutPoint(t *rapid.T) wire.OutPoint {
// Generate a random transaction ID
var txid chainhash.Hash
txidBytes := rapid.SliceOfN(rapid.Byte(), 32, 32).Draw(t, "txid")
copy(txid[:], txidBytes)
// Generate a random output index
vout := uint32(rapid.IntRange(0, 10).Draw(t, "vout"))
return wire.OutPoint{
Hash: txid,
Index: vout,
}
}