lnd/htlcswitch/hop/fuzz_test.go
Carla Kirk-Cohen 585f28c5f5
multi: explicitly signal final hop in pack hop payload
Previously, we'd use the value of nextChanID to infer whether a payload
was for the final hop in a route. This commit updates our packing logic
to explicitly signal to account for blinded routes, which allow zero
value nextChanID in intermediate hops. This is a preparatory commit
that allows us to more thoroughly validate payloads.
2023-12-18 11:27:52 -05:00

140 lines
3.2 KiB
Go

package hop
import (
"bytes"
"errors"
"testing"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/routing/route"
"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)
})
}
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,
CustomRecords: p.customRecords,
}, p.FwdInfo.NextHop.ToUint64()
}
// 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).
func FuzzPayloadFinal(f *testing.F) {
fuzzPayload(f, true)
}
// 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).
func FuzzPayloadIntermediate(f *testing.F) {
fuzzPayload(f, false)
}
func fuzzPayload(f *testing.F, finalPayload bool) {
f.Fuzz(func(t *testing.T, data []byte) {
if len(data) > sphinx.MaxPayloadSize {
return
}
r := bytes.NewReader(data)
payload1, err := NewPayloadFromReader(r, finalPayload)
if err != nil {
return
}
var b bytes.Buffer
hop, nextChanID := hopFromPayload(payload1)
err = hop.PackHopPayload(&b, nextChanID, finalPayload)
if errors.Is(err, route.ErrAMPMissingMPP) {
// PackHopPayload refuses to encode an AMP record
// without an MPP record. However, NewPayloadFromReader
// 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.
return
}
require.NoError(t, err)
payload2, err := NewPayloadFromReader(&b, finalPayload)
require.NoError(t, err)
require.Equal(t, payload1, payload2)
})
}