2023-09-07 15:23:34 -05:00
|
|
|
package hop
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2023-09-07 15:26:22 -05:00
|
|
|
"errors"
|
2023-09-07 15:23:34 -05:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
2023-09-07 15:26:22 -05:00
|
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
2023-09-07 15:23:34 -05:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
LegacyPayloadSize = sphinx.LegacyHopDataSize - sphinx.HMACSize
|
|
|
|
MaxOnionPacketSize = 1366
|
|
|
|
)
|
|
|
|
|
|
|
|
func FuzzHopData(f *testing.F) {
|
|
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
|
|
if len(data) > LegacyPayloadSize {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
|
|
|
var hopData1, hopData2 sphinx.HopData
|
|
|
|
|
|
|
|
if err := hopData1.Decode(r); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
require.NoError(t, hopData1.Encode(&b))
|
|
|
|
require.NoError(t, hopData2.Decode(&b))
|
|
|
|
|
|
|
|
require.Equal(t, hopData1, hopData2)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func FuzzHopPayload(f *testing.F) {
|
|
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
|
|
if len(data) > sphinx.MaxPayloadSize {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
|
|
|
var hopPayload1, hopPayload2 sphinx.HopPayload
|
|
|
|
|
|
|
|
if err := hopPayload1.Decode(r); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
require.NoError(t, hopPayload1.Encode(&b))
|
|
|
|
require.NoError(t, hopPayload2.Decode(&b))
|
|
|
|
|
|
|
|
require.Equal(t, hopPayload1, hopPayload2)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func FuzzOnionPacket(f *testing.F) {
|
|
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
|
|
if len(data) > MaxOnionPacketSize {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
|
|
|
var pkt1, pkt2 sphinx.OnionPacket
|
|
|
|
|
|
|
|
if err := pkt1.Decode(r); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
require.NoError(t, pkt1.Encode(&b))
|
|
|
|
require.NoError(t, pkt2.Decode(&b))
|
|
|
|
|
|
|
|
require.Equal(t, pkt1, pkt2)
|
|
|
|
})
|
|
|
|
}
|
2023-09-07 15:26:22 -05:00
|
|
|
|
|
|
|
func hopFromPayload(p *Payload) (*route.Hop, uint64) {
|
|
|
|
return &route.Hop{
|
|
|
|
AmtToForward: p.FwdInfo.AmountToForward,
|
|
|
|
OutgoingTimeLock: p.FwdInfo.OutgoingCTLV,
|
|
|
|
MPP: p.MPP,
|
|
|
|
AMP: p.AMP,
|
|
|
|
Metadata: p.metadata,
|
2024-01-16 11:12:11 -06:00
|
|
|
EncryptedData: p.encryptedData,
|
|
|
|
BlindingPoint: p.blindingPoint,
|
2023-09-07 15:26:22 -05:00
|
|
|
CustomRecords: p.customRecords,
|
2024-01-16 11:12:11 -06:00
|
|
|
TotalAmtMsat: p.totalAmtMsat,
|
2023-09-07 15:26:22 -05:00
|
|
|
}, p.FwdInfo.NextHop.ToUint64()
|
|
|
|
}
|
|
|
|
|
2023-11-01 10:39:33 -04:00
|
|
|
// FuzzPayloadFinal fuzzes final hop payloads, providing the additional context
|
|
|
|
// that the hop should be final (which is usually obtained by the structure
|
2024-04-22 14:06:17 -04:00
|
|
|
// of the sphinx packet) for the case where a blinding point was provided in
|
|
|
|
// UpdateAddHtlc.
|
|
|
|
func FuzzPayloadFinalBlinding(f *testing.F) {
|
|
|
|
fuzzPayload(f, true, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FuzzPayloadFinal fuzzes final hop payloads, providing the additional context
|
|
|
|
// that the hop should be final (which is usually obtained by the structure
|
|
|
|
// of the sphinx packet) for the case where no blinding point was provided in
|
|
|
|
// UpdateAddHtlc.
|
|
|
|
func FuzzPayloadFinalNoBlinding(f *testing.F) {
|
|
|
|
fuzzPayload(f, true, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the
|
|
|
|
// additional context that a hop should be intermediate (which is usually
|
|
|
|
// obtained by the structure of the sphinx packet) for the case where a
|
|
|
|
// blinding point was provided in UpdateAddHtlc.
|
|
|
|
func FuzzPayloadIntermediateBlinding(f *testing.F) {
|
|
|
|
fuzzPayload(f, false, true)
|
2023-11-01 10:39:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the
|
|
|
|
// additional context that a hop should be intermediate (which is usually
|
2024-04-22 14:06:17 -04:00
|
|
|
// obtained by the structure of the sphinx packet) for the case where no
|
|
|
|
// blinding point was provided in UpdateAddHtlc.
|
|
|
|
func FuzzPayloadIntermediateNoBlinding(f *testing.F) {
|
|
|
|
fuzzPayload(f, false, false)
|
2023-11-01 10:39:33 -04:00
|
|
|
}
|
|
|
|
|
2024-04-22 14:06:17 -04:00
|
|
|
func fuzzPayload(f *testing.F, finalPayload, updateAddBlinded bool) {
|
2023-09-07 15:26:22 -05:00
|
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
|
|
if len(data) > sphinx.MaxPayloadSize {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
2024-04-23 11:27:14 -04:00
|
|
|
payload1, parsed, err := ParseTLVPayload(r)
|
2023-09-07 15:26:22 -05:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-23 11:27:14 -04:00
|
|
|
if err = ValidateParsedPayloadTypes(
|
|
|
|
parsed, finalPayload, updateAddBlinded,
|
|
|
|
); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-07 15:26:22 -05:00
|
|
|
var b bytes.Buffer
|
|
|
|
hop, nextChanID := hopFromPayload(payload1)
|
2023-11-01 10:46:48 -04:00
|
|
|
err = hop.PackHopPayload(&b, nextChanID, finalPayload)
|
2023-11-03 14:24:52 -04:00
|
|
|
switch {
|
|
|
|
// PackHopPayload refuses to encode an AMP record
|
2024-04-23 11:27:14 -04:00
|
|
|
// without an MPP record. However, ValidateParsedPayloadTypes
|
2023-11-03 14:24:52 -04:00
|
|
|
// does allow decoding an AMP record without an MPP
|
|
|
|
// record, since validation is done at a later stage. Do
|
|
|
|
// not report a bug for this case.
|
|
|
|
case errors.Is(err, route.ErrAMPMissingMPP):
|
2023-09-07 15:26:22 -05:00
|
|
|
return
|
2023-11-03 14:24:52 -04:00
|
|
|
|
|
|
|
// PackHopPayload will not encode regular payloads or final
|
|
|
|
// hops in blinded routes that do not have an amount or expiry
|
2024-04-23 11:27:14 -04:00
|
|
|
// TLV set. However, ValidateParsedPayloadTypes will allow
|
|
|
|
// creation of payloads where these TLVs are present, but they
|
|
|
|
// have zero values because validation is done at a later stage.
|
2023-11-03 14:24:52 -04:00
|
|
|
case errors.Is(err, route.ErrMissingField):
|
|
|
|
return
|
|
|
|
|
|
|
|
default:
|
|
|
|
require.NoError(t, err)
|
2023-09-07 15:26:22 -05:00
|
|
|
}
|
|
|
|
|
2024-04-23 11:27:14 -04:00
|
|
|
payload2, parsed, err := ParseTLVPayload(&b)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = ValidateParsedPayloadTypes(
|
|
|
|
parsed, finalPayload, updateAddBlinded,
|
2024-04-22 14:06:17 -04:00
|
|
|
)
|
2023-09-07 15:26:22 -05:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.Equal(t, payload1, payload2)
|
|
|
|
})
|
|
|
|
}
|