multi: add blinded route to route requests expressed as hints

Add the option to include a blinded route in a route request (exclusive
to including hop hints, because it's incongruous to include both), and
express the route as a chain of hop hints.

Using a chain of hints over a single hint to represent the whole path
allows us to re-use our route construction to fill in a lot of the
path on our behalf.
This commit is contained in:
Carla Kirk-Cohen
2022-12-15 16:48:56 -05:00
committed by Olaoluwa Osuntokun
parent 48e36d93d4
commit c9609b8214
7 changed files with 501 additions and 18 deletions

View File

@ -3,7 +3,10 @@ package routing
import (
"testing"
"github.com/btcsuite/btcd/btcec/v2"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/require"
)
@ -68,3 +71,113 @@ func TestBlindedPathValidation(t *testing.T) {
})
}
}
// 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
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,
BlindedHops: []*sphinx.BlindedHopInfo{
{},
},
},
BaseFee: baseFee,
ProportionalFee: ppmFee,
CltvExpiryDelta: cltvDelta,
HtlcMinimum: htlcMin,
HtlcMaximum: htlcMax,
Features: features,
}
require.Nil(t, blindedPayment.toRouteHints())
// Populate the blinded payment with hops.
blindedPayment.BlindedPath.BlindedHops = []*sphinx.BlindedHopInfo{
{
BlindedNodePub: pkb1,
},
{
BlindedNodePub: pkb2,
},
{
BlindedNodePub: pkb3,
},
}
expected := RouteHints{
v1: {
{
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,
},
},
vb2: {
{
ToNodePubKey: func() route.Vertex {
return vb3
},
ToNodeFeatures: features,
},
},
}
actual := blindedPayment.toRouteHints()
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 as nil so that we can use
// require.Equal for all our other fields.
require.Equal(t, expectedHint[0].ToNodePubKey(),
actualHint[0].ToNodePubKey())
actualHint[0].ToNodePubKey = nil
expectedHint[0].ToNodePubKey = nil
require.Equal(t, expectedHint[0], actualHint[0])
}
}