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/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, } require.Nil(t, blindedPayment.toRouteHints()) // Populate the blinded payment with hops. blindedPayment.BlindedPath.BlindedHops = []*sphinx.BlindedHopInfo{ { BlindedNodePub: pkb1, CipherText: cipherText, }, { BlindedNodePub: pkb2, CipherText: cipherText, }, { BlindedNodePub: pkb3, CipherText: cipherText, }, } expected := RouteHints{ v1: { //nolint:lll &BlindedEdge{ policy: &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, }, blindingPoint: blindedPoint, cipherText: cipherText, }, }, vb2: { &BlindedEdge{ policy: &models.CachedEdgePolicy{ ToNodePubKey: func() route.Vertex { return vb3 }, ToNodeFeatures: features, }, cipherText: cipherText, }, }, } 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 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, false, 0, ) actualPayloadSize := actualHint[0].IntermediatePayloadSize( 0, 0, false, 0, ) require.Equal(t, expectedPayloadSize, actualPayloadSize) require.Equal(t, expectedHint[0], actualHint[0]) } }