mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
75bdf2d252
Simplifies the code slightly and improves the error message printed if the original and deserialized messages do not match.
780 lines
20 KiB
Go
780 lines
20 KiB
Go
package lnwire
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/zlib"
|
|
"encoding/binary"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// prefixWithMsgType takes []byte and adds a wire protocol prefix
|
|
// to make the []byte into an actual message to be used in fuzzing.
|
|
func prefixWithMsgType(data []byte, prefix MessageType) []byte {
|
|
var prefixBytes [2]byte
|
|
binary.BigEndian.PutUint16(prefixBytes[:], uint16(prefix))
|
|
data = append(prefixBytes[:], data...)
|
|
|
|
return data
|
|
}
|
|
|
|
// assertEqualFunc is a function used to assert that two deserialized messages
|
|
// are equivalent.
|
|
type assertEqualFunc func(t *testing.T, x, y any)
|
|
|
|
// wireMsgHarnessCustom performs the actual fuzz testing of the appropriate wire
|
|
// message. This function will check that the passed-in message passes wire
|
|
// length checks, is a valid message once deserialized, and passes a sequence of
|
|
// serialization and deserialization checks.
|
|
func wireMsgHarnessCustom(t *testing.T, data []byte, msgType MessageType,
|
|
assertEqual assertEqualFunc) {
|
|
|
|
data = prefixWithMsgType(data, msgType)
|
|
|
|
// Create a reader with the byte array.
|
|
r := bytes.NewReader(data)
|
|
|
|
// Check that the created message is not greater than the maximum
|
|
// message size.
|
|
if len(data) > MaxSliceLength {
|
|
return
|
|
}
|
|
|
|
msg, err := ReadMessage(r, 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// We will serialize the message into a new bytes buffer.
|
|
var b bytes.Buffer
|
|
_, err = WriteMessage(&b, msg, 0)
|
|
require.NoError(t, err)
|
|
|
|
// Deserialize the message from the serialized bytes buffer, and then
|
|
// assert that the original message is equal to the newly deserialized
|
|
// message.
|
|
newMsg, err := ReadMessage(&b, 0)
|
|
require.NoError(t, err)
|
|
|
|
assertEqual(t, msg, newMsg)
|
|
}
|
|
|
|
func wireMsgHarness(t *testing.T, data []byte, msgType MessageType) {
|
|
t.Helper()
|
|
assertEq := func(t *testing.T, x, y any) {
|
|
require.Equal(t, x, y)
|
|
}
|
|
wireMsgHarnessCustom(t, data, msgType, assertEq)
|
|
}
|
|
|
|
func FuzzAcceptChannel(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
// We can't use require.Equal for UpfrontShutdownScript, since
|
|
// we consider the empty slice and nil to be equivalent.
|
|
assertEq := func(t *testing.T, x, y any) {
|
|
require.IsType(t, &AcceptChannel{}, x)
|
|
first, _ := x.(*AcceptChannel)
|
|
require.IsType(t, &AcceptChannel{}, y)
|
|
second, _ := y.(*AcceptChannel)
|
|
|
|
require.True(
|
|
t, bytes.Equal(
|
|
first.UpfrontShutdownScript,
|
|
second.UpfrontShutdownScript,
|
|
),
|
|
)
|
|
first.UpfrontShutdownScript = nil
|
|
second.UpfrontShutdownScript = nil
|
|
|
|
require.Equal(t, first, second)
|
|
}
|
|
|
|
wireMsgHarnessCustom(t, data, MsgAcceptChannel, assertEq)
|
|
})
|
|
}
|
|
|
|
func FuzzAnnounceSignatures(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgAnnounceSignatures)
|
|
})
|
|
}
|
|
|
|
func FuzzAnnounceSignatures2(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgAnnounceSignatures2)
|
|
})
|
|
}
|
|
|
|
func FuzzChannelAnnouncement(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgChannelAnnouncement)
|
|
})
|
|
}
|
|
|
|
func FuzzChannelAnnouncement2(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
// We can't use require.Equal for Features, since we consider
|
|
// the empty map and nil to be equivalent.
|
|
assertEq := func(t *testing.T, x, y any) {
|
|
require.IsType(t, &ChannelAnnouncement2{}, x)
|
|
first, _ := x.(*ChannelAnnouncement2)
|
|
require.IsType(t, &ChannelAnnouncement2{}, y)
|
|
second, _ := y.(*ChannelAnnouncement2)
|
|
|
|
require.True(
|
|
t,
|
|
first.Features.Val.Equals(&second.Features.Val),
|
|
)
|
|
first.Features.Val = *NewRawFeatureVector()
|
|
second.Features.Val = *NewRawFeatureVector()
|
|
|
|
require.Equal(t, first, second)
|
|
}
|
|
|
|
wireMsgHarnessCustom(t, data, MsgChannelAnnouncement2, assertEq)
|
|
})
|
|
}
|
|
|
|
func FuzzChannelReestablish(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgChannelReestablish)
|
|
})
|
|
}
|
|
|
|
func FuzzChannelUpdate(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgChannelUpdate)
|
|
})
|
|
}
|
|
|
|
func FuzzChannelUpdate2(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgChannelUpdate2)
|
|
})
|
|
}
|
|
|
|
func FuzzClosingSigned(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgClosingSigned)
|
|
})
|
|
}
|
|
|
|
func FuzzCommitSig(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgCommitSig)
|
|
})
|
|
}
|
|
|
|
func FuzzError(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgError)
|
|
})
|
|
}
|
|
|
|
func FuzzWarning(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgWarning)
|
|
})
|
|
}
|
|
|
|
func FuzzStfu(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgStfu)
|
|
})
|
|
}
|
|
|
|
func FuzzFundingCreated(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgFundingCreated)
|
|
})
|
|
}
|
|
|
|
func FuzzChannelReady(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgChannelReady)
|
|
})
|
|
}
|
|
|
|
func FuzzFundingSigned(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgFundingSigned)
|
|
})
|
|
}
|
|
|
|
func FuzzGossipTimestampRange(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgGossipTimestampRange)
|
|
})
|
|
}
|
|
|
|
func FuzzInit(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgInit)
|
|
})
|
|
}
|
|
|
|
func FuzzNodeAnnouncement(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
// We can't use require.Equal for Addresses, since the same IP
|
|
// can be represented by different underlying bytes. Instead, we
|
|
// compare the normalized string representation of each address.
|
|
assertEq := func(t *testing.T, x, y any) {
|
|
require.IsType(t, &NodeAnnouncement{}, x)
|
|
first, _ := x.(*NodeAnnouncement)
|
|
require.IsType(t, &NodeAnnouncement{}, y)
|
|
second, _ := y.(*NodeAnnouncement)
|
|
|
|
require.Equal(
|
|
t, len(first.Addresses), len(second.Addresses),
|
|
)
|
|
for i := range first.Addresses {
|
|
require.Equal(
|
|
t, first.Addresses[i].String(),
|
|
second.Addresses[i].String(),
|
|
)
|
|
}
|
|
first.Addresses = nil
|
|
second.Addresses = nil
|
|
|
|
require.Equal(t, first, second)
|
|
}
|
|
|
|
wireMsgHarnessCustom(t, data, MsgNodeAnnouncement, assertEq)
|
|
})
|
|
}
|
|
|
|
func FuzzOpenChannel(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
// We can't use require.Equal for UpfrontShutdownScript, since
|
|
// we consider the empty slice and nil to be equivalent.
|
|
assertEq := func(t *testing.T, x, y any) {
|
|
require.IsType(t, &OpenChannel{}, x)
|
|
first, _ := x.(*OpenChannel)
|
|
require.IsType(t, &OpenChannel{}, y)
|
|
second, _ := y.(*OpenChannel)
|
|
|
|
require.True(
|
|
t, bytes.Equal(
|
|
first.UpfrontShutdownScript,
|
|
second.UpfrontShutdownScript,
|
|
),
|
|
)
|
|
first.UpfrontShutdownScript = nil
|
|
second.UpfrontShutdownScript = nil
|
|
|
|
require.Equal(t, first, second)
|
|
}
|
|
|
|
wireMsgHarnessCustom(t, data, MsgOpenChannel, assertEq)
|
|
})
|
|
}
|
|
|
|
func FuzzPing(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgPing)
|
|
})
|
|
}
|
|
|
|
func FuzzPong(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgPong)
|
|
})
|
|
}
|
|
|
|
func FuzzQueryChannelRange(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgQueryChannelRange)
|
|
})
|
|
}
|
|
|
|
func FuzzZlibQueryShortChanIDs(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
var buf bytes.Buffer
|
|
zlibWriter := zlib.NewWriter(&buf)
|
|
_, err := zlibWriter.Write(data)
|
|
require.NoError(t, err) // Zlib bug?
|
|
|
|
err = zlibWriter.Close()
|
|
require.NoError(t, err) // Zlib bug?
|
|
|
|
compressedPayload := buf.Bytes()
|
|
|
|
chainhash := []byte("00000000000000000000000000000000")
|
|
numBytesInBody := len(compressedPayload) + 1
|
|
zlibByte := []byte("\x01")
|
|
|
|
bodyBytes := make([]byte, 2)
|
|
binary.BigEndian.PutUint16(bodyBytes, uint16(numBytesInBody))
|
|
|
|
payload := chainhash
|
|
payload = append(payload, bodyBytes...)
|
|
payload = append(payload, zlibByte...)
|
|
payload = append(payload, compressedPayload...)
|
|
|
|
wireMsgHarness(t, payload, MsgQueryShortChanIDs)
|
|
})
|
|
}
|
|
|
|
func FuzzQueryShortChanIDs(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgQueryShortChanIDs)
|
|
})
|
|
}
|
|
|
|
func FuzzZlibReplyChannelRange(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
var buf bytes.Buffer
|
|
zlibWriter := zlib.NewWriter(&buf)
|
|
_, err := zlibWriter.Write(data)
|
|
require.NoError(t, err) // Zlib bug?
|
|
|
|
err = zlibWriter.Close()
|
|
require.NoError(t, err) // Zlib bug?
|
|
|
|
compressedPayload := buf.Bytes()
|
|
|
|
// Initialize some []byte vars which will prefix our payload
|
|
chainhash := []byte("00000000000000000000000000000000")
|
|
firstBlockHeight := []byte("\x00\x00\x00\x00")
|
|
numBlocks := []byte("\x00\x00\x00\x00")
|
|
completeByte := []byte("\x00")
|
|
|
|
numBytesInBody := len(compressedPayload) + 1
|
|
zlibByte := []byte("\x01")
|
|
|
|
bodyBytes := make([]byte, 2)
|
|
binary.BigEndian.PutUint16(bodyBytes, uint16(numBytesInBody))
|
|
|
|
payload := chainhash
|
|
payload = append(payload, firstBlockHeight...)
|
|
payload = append(payload, numBlocks...)
|
|
payload = append(payload, completeByte...)
|
|
payload = append(payload, bodyBytes...)
|
|
payload = append(payload, zlibByte...)
|
|
payload = append(payload, compressedPayload...)
|
|
|
|
wireMsgHarness(t, payload, MsgReplyChannelRange)
|
|
})
|
|
}
|
|
|
|
func FuzzReplyChannelRange(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
// We can't use require.Equal for Timestamps, since we consider
|
|
// the empty slice and nil to be equivalent.
|
|
assertEq := func(t *testing.T, x, y any) {
|
|
require.IsType(t, &ReplyChannelRange{}, x)
|
|
first, _ := x.(*ReplyChannelRange)
|
|
require.IsType(t, &ReplyChannelRange{}, y)
|
|
second, _ := y.(*ReplyChannelRange)
|
|
|
|
require.Equal(
|
|
t, len(first.Timestamps),
|
|
len(second.Timestamps),
|
|
)
|
|
for i, ts1 := range first.Timestamps {
|
|
ts2 := second.Timestamps[i]
|
|
require.Equal(t, ts1, ts2)
|
|
}
|
|
first.Timestamps = nil
|
|
second.Timestamps = nil
|
|
|
|
require.Equal(t, first, second)
|
|
}
|
|
|
|
wireMsgHarnessCustom(t, data, MsgReplyChannelRange, assertEq)
|
|
})
|
|
}
|
|
|
|
func FuzzReplyShortChanIDsEnd(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgReplyShortChanIDsEnd)
|
|
})
|
|
}
|
|
|
|
func FuzzRevokeAndAck(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgRevokeAndAck)
|
|
})
|
|
}
|
|
|
|
func FuzzShutdown(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgShutdown)
|
|
})
|
|
}
|
|
|
|
func FuzzUpdateAddHTLC(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgUpdateAddHTLC)
|
|
})
|
|
}
|
|
|
|
func FuzzUpdateFailHTLC(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgUpdateFailHTLC)
|
|
})
|
|
}
|
|
|
|
func FuzzUpdateFailMalformedHTLC(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgUpdateFailMalformedHTLC)
|
|
})
|
|
}
|
|
|
|
func FuzzUpdateFee(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgUpdateFee)
|
|
})
|
|
}
|
|
|
|
func FuzzUpdateFulfillHTLC(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgUpdateFulfillHTLC)
|
|
})
|
|
}
|
|
|
|
func FuzzDynPropose(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgDynPropose)
|
|
})
|
|
}
|
|
|
|
func FuzzDynReject(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgDynReject)
|
|
})
|
|
}
|
|
|
|
func FuzzDynAck(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgDynAck)
|
|
})
|
|
}
|
|
|
|
func FuzzKickoffSig(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgKickoffSig)
|
|
})
|
|
}
|
|
|
|
func FuzzCustomMessage(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte, customMessageType uint16) {
|
|
if customMessageType < uint16(CustomTypeStart) {
|
|
customMessageType += uint16(CustomTypeStart)
|
|
}
|
|
|
|
wireMsgHarness(t, data, MessageType(customMessageType))
|
|
})
|
|
}
|
|
|
|
// FuzzParseRawSignature tests that our DER-encoded signature parsing does not
|
|
// panic for arbitrary inputs and that serializing and reparsing the signatures
|
|
// does not mutate them.
|
|
func FuzzParseRawSignature(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
sig, err := NewSigFromECDSARawSignature(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sig2, err := NewSigFromECDSARawSignature(sig.ToSignatureBytes())
|
|
require.NoError(t, err, "failed to reparse signature")
|
|
|
|
require.Equal(t, sig, sig2, "signature mismatch")
|
|
})
|
|
}
|
|
|
|
// FuzzConvertFixedSignature tests that conversion of fixed 64-byte signatures
|
|
// to DER-encoded signatures does not panic and that parsing and reconverting
|
|
// the signatures does not mutate them.
|
|
func FuzzConvertFixedSignature(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
var sig Sig
|
|
if len(data) > len(sig.bytes[:]) {
|
|
return
|
|
}
|
|
copy(sig.bytes[:], data)
|
|
|
|
derSig, err := sig.ToSignature()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sig2, err := NewSigFromSignature(derSig)
|
|
require.NoError(t, err, "failed to parse signature")
|
|
|
|
derSig2, err := sig2.ToSignature()
|
|
require.NoError(t, err, "failed to reconvert signature to DER")
|
|
|
|
derBytes := derSig.Serialize()
|
|
derBytes2 := derSig2.Serialize()
|
|
require.Equal(t, derBytes, derBytes2, "signature mismatch")
|
|
})
|
|
}
|
|
|
|
// FuzzConvertFixedSchnorrSignature tests that conversion of fixed 64-byte
|
|
// Schnorr signatures to and from the btcec format does not panic or mutate the
|
|
// signatures.
|
|
func FuzzConvertFixedSchnorrSignature(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
var sig Sig
|
|
if len(data) > len(sig.bytes[:]) {
|
|
return
|
|
}
|
|
copy(sig.bytes[:], data)
|
|
sig.ForceSchnorr()
|
|
|
|
btcecSig, err := sig.ToSignature()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sig2, err := NewSigFromSignature(btcecSig)
|
|
require.NoError(t, err, "failed to parse signature")
|
|
|
|
btcecSig2, err := sig2.ToSignature()
|
|
require.NoError(
|
|
t, err, "failed to reconvert signature to btcec format",
|
|
)
|
|
|
|
btcecBytes := btcecSig.Serialize()
|
|
btcecBytes2 := btcecSig2.Serialize()
|
|
require.Equal(t, btcecBytes, btcecBytes2, "signature mismatch")
|
|
})
|
|
}
|
|
|
|
// prefixWithFailCode adds a failure code prefix to data.
|
|
func prefixWithFailCode(data []byte, code FailCode) []byte {
|
|
var codeBytes [2]byte
|
|
binary.BigEndian.PutUint16(codeBytes[:], uint16(code))
|
|
data = append(codeBytes[:], data...)
|
|
|
|
return data
|
|
}
|
|
|
|
// onionFailureHarnessCustom performs the actual fuzz testing of the appropriate
|
|
// onion failure message. This function will check that the passed-in message
|
|
// passes wire length checks, is a valid message once deserialized, and passes a
|
|
// sequence of serialization and deserialization checks.
|
|
func onionFailureHarnessCustom(t *testing.T, data []byte, code FailCode,
|
|
assertEqual assertEqualFunc) {
|
|
|
|
data = prefixWithFailCode(data, code)
|
|
|
|
// Don't waste time fuzzing messages larger than we'll ever accept.
|
|
if len(data) > MaxSliceLength {
|
|
return
|
|
}
|
|
|
|
// First check whether the failure message can be decoded.
|
|
r := bytes.NewReader(data)
|
|
msg, err := DecodeFailureMessage(r, 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// We now have a valid decoded message. Verify that encoding and
|
|
// decoding the message does not mutate it.
|
|
|
|
var b bytes.Buffer
|
|
err = EncodeFailureMessage(&b, msg, 0)
|
|
require.NoError(t, err, "failed to encode failure message")
|
|
|
|
newMsg, err := DecodeFailureMessage(&b, 0)
|
|
require.NoError(t, err, "failed to decode serialized failure message")
|
|
|
|
assertEqual(t, msg, newMsg)
|
|
|
|
// Now verify that encoding/decoding full packets works as expected.
|
|
|
|
var pktBuf bytes.Buffer
|
|
if err := EncodeFailure(&pktBuf, msg, 0); err != nil {
|
|
// EncodeFailure returns an error if the encoded message would
|
|
// exceed FailureMessageLength bytes, as LND always encodes
|
|
// fixed-size packets for privacy. But it is valid to decode
|
|
// messages longer than this, so we should not report an error
|
|
// if the original message was longer.
|
|
//
|
|
// We add 2 to the length of the original message since it may
|
|
// have omitted a channel_update type prefix of 2 bytes. When
|
|
// we re-encode such a message, we will add the 2-byte prefix
|
|
// as prescribed by the spec.
|
|
if len(data)+2 > FailureMessageLength {
|
|
return
|
|
}
|
|
|
|
t.Fatalf("failed to encode failure packet: %v", err)
|
|
}
|
|
|
|
// We should use FailureMessageLength sized packets plus 2 bytes to
|
|
// encode the message length and 2 bytes to encode the padding length,
|
|
// as recommended by the spec.
|
|
require.Equal(
|
|
t, pktBuf.Len(), FailureMessageLength+4,
|
|
"wrong failure message length",
|
|
)
|
|
|
|
pktMsg, err := DecodeFailure(&pktBuf, 0)
|
|
require.NoError(t, err, "failed to decode failure packet")
|
|
|
|
assertEqual(t, msg, pktMsg)
|
|
}
|
|
|
|
func onionFailureHarness(t *testing.T, data []byte, code FailCode) {
|
|
t.Helper()
|
|
assertEq := func(t *testing.T, x, y any) {
|
|
require.Equal(t, x, y)
|
|
}
|
|
onionFailureHarnessCustom(t, data, code, assertEq)
|
|
}
|
|
|
|
func FuzzFailIncorrectDetails(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
// Since FailIncorrectDetails.Decode can leave extraOpaqueData
|
|
// as nil while FailIncorrectDetails.Encode writes an empty
|
|
// slice, we need to use a custom equality function.
|
|
assertEq := func(t *testing.T, x, y any) {
|
|
msg1, ok := x.(*FailIncorrectDetails)
|
|
require.True(
|
|
t, ok, "msg1 was not FailIncorrectDetails",
|
|
)
|
|
|
|
msg2, ok := y.(*FailIncorrectDetails)
|
|
require.True(
|
|
t, ok, "msg2 was not FailIncorrectDetails",
|
|
)
|
|
|
|
require.Equal(t, msg1.amount, msg2.amount)
|
|
require.Equal(t, msg1.height, msg2.height)
|
|
require.True(
|
|
t, bytes.Equal(
|
|
msg1.extraOpaqueData,
|
|
msg2.extraOpaqueData,
|
|
),
|
|
)
|
|
}
|
|
|
|
onionFailureHarnessCustom(
|
|
t, data, CodeIncorrectOrUnknownPaymentDetails, assertEq,
|
|
)
|
|
})
|
|
}
|
|
|
|
func FuzzFailInvalidOnionVersion(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeInvalidOnionVersion)
|
|
})
|
|
}
|
|
|
|
func FuzzFailInvalidOnionHmac(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeInvalidOnionHmac)
|
|
})
|
|
}
|
|
|
|
func FuzzFailInvalidOnionKey(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeInvalidOnionKey)
|
|
})
|
|
}
|
|
|
|
func FuzzFailTemporaryChannelFailure(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeTemporaryChannelFailure)
|
|
})
|
|
}
|
|
|
|
func FuzzFailAmountBelowMinimum(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeAmountBelowMinimum)
|
|
})
|
|
}
|
|
|
|
func FuzzFailFeeInsufficient(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeFeeInsufficient)
|
|
})
|
|
}
|
|
|
|
func FuzzFailIncorrectCltvExpiry(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeIncorrectCltvExpiry)
|
|
})
|
|
}
|
|
|
|
func FuzzFailExpiryTooSoon(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeExpiryTooSoon)
|
|
})
|
|
}
|
|
|
|
func FuzzFailChannelDisabled(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeChannelDisabled)
|
|
})
|
|
}
|
|
|
|
func FuzzFailFinalIncorrectCltvExpiry(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeFinalIncorrectCltvExpiry)
|
|
})
|
|
}
|
|
|
|
func FuzzFailFinalIncorrectHtlcAmount(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeFinalIncorrectHtlcAmount)
|
|
})
|
|
}
|
|
|
|
func FuzzInvalidOnionPayload(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeInvalidOnionPayload)
|
|
})
|
|
}
|
|
|
|
func FuzzFailInvalidBlinding(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
onionFailureHarness(t, data, CodeInvalidBlinding)
|
|
})
|
|
}
|
|
|
|
func FuzzClosingSig(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgClosingSig)
|
|
})
|
|
}
|
|
|
|
func FuzzClosingComplete(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
wireMsgHarness(t, data, MsgClosingComplete)
|
|
})
|
|
}
|
|
|
|
// FuzzFee tests that decoding and re-encoding a Fee TLV does not mutate it.
|
|
func FuzzFee(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
if len(data) > 8 {
|
|
return
|
|
}
|
|
|
|
var fee Fee
|
|
var buf [8]byte
|
|
r := bytes.NewReader(data)
|
|
|
|
if err := feeDecoder(r, &fee, &buf, 8); err != nil {
|
|
return
|
|
}
|
|
|
|
var b bytes.Buffer
|
|
require.NoError(t, feeEncoder(&b, &fee, &buf))
|
|
|
|
// Use bytes.Equal instead of require.Equal so that nil and
|
|
// empty slices are considered equal.
|
|
require.True(
|
|
t, bytes.Equal(data, b.Bytes()), "%v != %v", data,
|
|
b.Bytes(),
|
|
)
|
|
})
|
|
}
|