mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
8df03de3e9
If multiple blinded paths are provided, they will each have a different pub key for the destination node. This makes using our existing pathfinding logic tricky since it depends on having a single destination node (characterised by a single pub key). We want to re-use this logic. So what we do is swap out the pub keys of the destinaion hop with a pseudo target pub key. This will then be used during pathfinding. Later on once a path is found, we will swap the real destination keys back in so that onion creation can be done.
221 lines
5.2 KiB
Go
221 lines
5.2 KiB
Go
package routing
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
|
"github.com/lightningnetwork/lnd/fn"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestBlindedPathValidation tests validation of blinded paths.
|
|
func TestBlindedPathValidation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
payment *BlindedPayment
|
|
err error
|
|
}{
|
|
{
|
|
name: "no path",
|
|
payment: &BlindedPayment{},
|
|
err: ErrNoBlindedPath,
|
|
},
|
|
{
|
|
name: "insufficient hops",
|
|
payment: &BlindedPayment{
|
|
BlindedPath: &sphinx.BlindedPath{
|
|
BlindedHops: []*sphinx.BlindedHopInfo{},
|
|
},
|
|
},
|
|
err: ErrInsufficientBlindedHops,
|
|
},
|
|
{
|
|
name: "maximum < minimum",
|
|
payment: &BlindedPayment{
|
|
BlindedPath: &sphinx.BlindedPath{
|
|
BlindedHops: []*sphinx.BlindedHopInfo{
|
|
{},
|
|
},
|
|
},
|
|
HtlcMaximum: 10,
|
|
HtlcMinimum: 20,
|
|
},
|
|
err: ErrHTLCRestrictions,
|
|
},
|
|
{
|
|
name: "valid",
|
|
payment: &BlindedPayment{
|
|
BlindedPath: &sphinx.BlindedPath{
|
|
BlindedHops: []*sphinx.BlindedHopInfo{
|
|
{},
|
|
},
|
|
},
|
|
HtlcMaximum: 15,
|
|
HtlcMinimum: 5,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range tests {
|
|
testCase := testCase
|
|
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
err := testCase.payment.Validate()
|
|
require.ErrorIs(t, err, testCase.err)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestBlindedPaymentToHints tests conversion of a blinded path to a chain of
|
|
// route hints. As our function assumes that the blinded payment has already
|
|
// been validated.
|
|
func TestBlindedPaymentToHints(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
_, pk1 = btcec.PrivKeyFromBytes([]byte{1})
|
|
_, pkb1 = btcec.PrivKeyFromBytes([]byte{2})
|
|
_, pkb2 = btcec.PrivKeyFromBytes([]byte{3})
|
|
_, pkb3 = btcec.PrivKeyFromBytes([]byte{4})
|
|
|
|
v1 = route.NewVertex(pk1)
|
|
vb2 = route.NewVertex(pkb2)
|
|
vb3 = route.NewVertex(pkb3)
|
|
|
|
baseFee uint32 = 1000
|
|
ppmFee uint32 = 500
|
|
cltvDelta uint16 = 140
|
|
htlcMin uint64 = 100
|
|
htlcMax uint64 = 100_000_000
|
|
|
|
sizeEncryptedData = 100
|
|
cipherText = bytes.Repeat(
|
|
[]byte{1}, sizeEncryptedData,
|
|
)
|
|
_, blindedPoint = btcec.PrivKeyFromBytes([]byte{5})
|
|
|
|
rawFeatures = lnwire.NewRawFeatureVector(
|
|
lnwire.AMPOptional,
|
|
)
|
|
|
|
features = lnwire.NewFeatureVector(
|
|
rawFeatures, lnwire.Features,
|
|
)
|
|
)
|
|
|
|
// Create a blinded payment that's just to the introduction node and
|
|
// assert that we get nil hints.
|
|
blindedPayment := &BlindedPayment{
|
|
BlindedPath: &sphinx.BlindedPath{
|
|
IntroductionPoint: pk1,
|
|
BlindingPoint: blindedPoint,
|
|
BlindedHops: []*sphinx.BlindedHopInfo{
|
|
{},
|
|
},
|
|
},
|
|
BaseFee: baseFee,
|
|
ProportionalFeeRate: ppmFee,
|
|
CltvExpiryDelta: cltvDelta,
|
|
HtlcMinimum: htlcMin,
|
|
HtlcMaximum: htlcMax,
|
|
Features: features,
|
|
}
|
|
hints, err := blindedPayment.toRouteHints(fn.None[*btcec.PublicKey]())
|
|
require.NoError(t, err)
|
|
require.Nil(t, hints)
|
|
|
|
// Populate the blinded payment with hops.
|
|
blindedPayment.BlindedPath.BlindedHops = []*sphinx.BlindedHopInfo{
|
|
{
|
|
BlindedNodePub: pkb1,
|
|
CipherText: cipherText,
|
|
},
|
|
{
|
|
BlindedNodePub: pkb2,
|
|
CipherText: cipherText,
|
|
},
|
|
{
|
|
BlindedNodePub: pkb3,
|
|
CipherText: cipherText,
|
|
},
|
|
}
|
|
|
|
policy1 := &models.CachedEdgePolicy{
|
|
TimeLockDelta: cltvDelta,
|
|
MinHTLC: lnwire.MilliSatoshi(htlcMin),
|
|
MaxHTLC: lnwire.MilliSatoshi(htlcMax),
|
|
FeeBaseMSat: lnwire.MilliSatoshi(baseFee),
|
|
FeeProportionalMillionths: lnwire.MilliSatoshi(
|
|
ppmFee,
|
|
),
|
|
ToNodePubKey: func() route.Vertex {
|
|
return vb2
|
|
},
|
|
ToNodeFeatures: features,
|
|
}
|
|
policy2 := &models.CachedEdgePolicy{
|
|
ToNodePubKey: func() route.Vertex {
|
|
return vb3
|
|
},
|
|
ToNodeFeatures: features,
|
|
}
|
|
|
|
blindedEdge1, err := NewBlindedEdge(policy1, blindedPayment, 0)
|
|
require.NoError(t, err)
|
|
|
|
blindedEdge2, err := NewBlindedEdge(policy2, blindedPayment, 1)
|
|
require.NoError(t, err)
|
|
|
|
expected := RouteHints{
|
|
v1: {
|
|
blindedEdge1,
|
|
},
|
|
vb2: {
|
|
blindedEdge2,
|
|
},
|
|
}
|
|
|
|
actual, err := blindedPayment.toRouteHints(fn.None[*btcec.PublicKey]())
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, len(expected), len(actual))
|
|
for vertex, expectedHint := range expected {
|
|
actualHint, ok := actual[vertex]
|
|
require.True(t, ok, "node not found: %v", vertex)
|
|
|
|
require.Len(t, expectedHint, 1)
|
|
require.Len(t, actualHint, 1)
|
|
|
|
// We can't assert that our functions are equal, so we check
|
|
// their output and then mark them as nil so that we can use
|
|
// require.Equal for all our other fields.
|
|
require.Equal(t, expectedHint[0].EdgePolicy().ToNodePubKey(),
|
|
actualHint[0].EdgePolicy().ToNodePubKey())
|
|
|
|
actualHint[0].EdgePolicy().ToNodePubKey = nil
|
|
expectedHint[0].EdgePolicy().ToNodePubKey = nil
|
|
|
|
// The arguments we use for the payload do not matter as long as
|
|
// both functions return the same payload.
|
|
expectedPayloadSize := expectedHint[0].IntermediatePayloadSize(
|
|
0, 0, 0,
|
|
)
|
|
actualPayloadSize := actualHint[0].IntermediatePayloadSize(
|
|
0, 0, 0,
|
|
)
|
|
|
|
require.Equal(t, expectedPayloadSize, actualPayloadSize)
|
|
|
|
require.Equal(t, expectedHint[0], actualHint[0])
|
|
}
|
|
}
|