Merge pull request #8791 from ellemouton/assumeTLV

routing: assume TLV onion feature bit
This commit is contained in:
Elle authored and GitHub committed 2024-06-24 11:03:33 -07:00
commit 6b64703db4
8 files changed
+56 -270

No files matched your search

@@ -79,6 +79,11 @@
# Technical and Architectural Updates # Technical and Architectural Updates
## BOLT Spec Updates ## BOLT Spec Updates
* Start assuming that all hops used during path-finding and route construction
[support the TLV onion
format](https://github.com/lightningnetwork/lnd/pull/8791).
## Testing ## Testing
## Database ## Database
## Code Health ## Code Health
@@ -88,5 +93,6 @@
* Andras Banki-Horvath * Andras Banki-Horvath
* Bufo * Bufo
* Elle Mouton
* Matheus Degiovani * Matheus Degiovani
* Slyghtning * Slyghtning
+6 -8
View File
@@ -25,7 +25,7 @@ type AdditionalEdge interface {
// additional edge when being an intermediate hop in a route NOT the // additional edge when being an intermediate hop in a route NOT the
// final hop. // final hop.
IntermediatePayloadSize(amount lnwire.MilliSatoshi, expiry uint32, IntermediatePayloadSize(amount lnwire.MilliSatoshi, expiry uint32,
legacy bool, channelID uint64) uint64 channelID uint64) uint64
// EdgePolicy returns the policy of the additional edge. // EdgePolicy returns the policy of the additional edge.
EdgePolicy() *models.CachedEdgePolicy EdgePolicy() *models.CachedEdgePolicy
@@ -33,7 +33,7 @@ type AdditionalEdge interface {
// PayloadSizeFunc defines the interface for the payload size function. // PayloadSizeFunc defines the interface for the payload size function.
type PayloadSizeFunc func(amount lnwire.MilliSatoshi, expiry uint32, type PayloadSizeFunc func(amount lnwire.MilliSatoshi, expiry uint32,
legacy bool, channelID uint64) uint64 channelID uint64) uint64
// PrivateEdge implements the AdditionalEdge interface. As the name implies it // PrivateEdge implements the AdditionalEdge interface. As the name implies it
// is used for private route hints that the receiver adds for example to an // is used for private route hints that the receiver adds for example to an
@@ -50,12 +50,11 @@ func (p *PrivateEdge) EdgePolicy() *models.CachedEdgePolicy {
// IntermediatePayloadSize returns the sphinx payload size defined in BOLT04 if // IntermediatePayloadSize returns the sphinx payload size defined in BOLT04 if
// this edge were to be included in a route. // this edge were to be included in a route.
func (p *PrivateEdge) IntermediatePayloadSize(amount lnwire.MilliSatoshi, func (p *PrivateEdge) IntermediatePayloadSize(amount lnwire.MilliSatoshi,
expiry uint32, legacy bool, channelID uint64) uint64 { expiry uint32, channelID uint64) uint64 {
hop := route.Hop{ hop := route.Hop{
AmtToForward: amount, AmtToForward: amount,
OutgoingTimeLock: expiry, OutgoingTimeLock: expiry,
LegacyPayload: legacy,
} }
return hop.PayloadSize(channelID) return hop.PayloadSize(channelID)
@@ -77,11 +76,10 @@ func (b *BlindedEdge) EdgePolicy() *models.CachedEdgePolicy {
// IntermediatePayloadSize returns the sphinx payload size defined in BOLT04 if // IntermediatePayloadSize returns the sphinx payload size defined in BOLT04 if
// this edge were to be included in a route. // this edge were to be included in a route.
func (b *BlindedEdge) IntermediatePayloadSize(_ lnwire.MilliSatoshi, _ uint32, func (b *BlindedEdge) IntermediatePayloadSize(_ lnwire.MilliSatoshi, _ uint32,
_ bool, _ uint64) uint64 { _ uint64) uint64 {
hop := route.Hop{ hop := route.Hop{
BlindingPoint: b.blindingPoint, BlindingPoint: b.blindingPoint,
LegacyPayload: false,
EncryptedData: b.cipherText, EncryptedData: b.cipherText,
} }
@@ -97,11 +95,11 @@ var _ AdditionalEdge = (*BlindedEdge)(nil)
// defaultHopPayloadSize is the default payload size of a normal (not-blinded) // defaultHopPayloadSize is the default payload size of a normal (not-blinded)
// hop in the route. // hop in the route.
func defaultHopPayloadSize(amount lnwire.MilliSatoshi, expiry uint32, func defaultHopPayloadSize(amount lnwire.MilliSatoshi, expiry uint32,
legacy bool, channelID uint64) uint64 { channelID uint64) uint64 {
// The payload size of a cleartext intermediate hop is equal to the // The payload size of a cleartext intermediate hop is equal to the
// payload size of a private edge therefore we reuse its size function. // payload size of a private edge therefore we reuse its size function.
edge := PrivateEdge{} edge := PrivateEdge{}
return edge.IntermediatePayloadSize(amount, expiry, legacy, channelID) return edge.IntermediatePayloadSize(amount, expiry, channelID)
} }
-13
View File
@@ -27,24 +27,12 @@ func TestIntermediatePayloadSize(t *testing.T) {
nextHop uint64 nextHop uint64
edge AdditionalEdge edge AdditionalEdge
}{ }{
{
name: "Legacy payload private edge",
hop: route.Hop{
AmtToForward: 1000,
OutgoingTimeLock: 600000,
ChannelID: 3432483437438,
LegacyPayload: true,
},
nextHop: 1,
edge: &PrivateEdge{},
},
{ {
name: "Tlv payload private edge", name: "Tlv payload private edge",
hop: route.Hop{ hop: route.Hop{
AmtToForward: 1000, AmtToForward: 1000,
OutgoingTimeLock: 600000, OutgoingTimeLock: 600000,
ChannelID: 3432483437438, ChannelID: 3432483437438,
LegacyPayload: false,
}, },
nextHop: 1, nextHop: 1,
edge: &PrivateEdge{}, edge: &PrivateEdge{},
@@ -86,7 +74,6 @@ func TestIntermediatePayloadSize(t *testing.T) {
IntermediatePayloadSize( IntermediatePayloadSize(
testCase.hop.AmtToForward, testCase.hop.AmtToForward,
testCase.hop.OutgoingTimeLock, testCase.hop.OutgoingTimeLock,
testCase.hop.LegacyPayload,
testCase.nextHop, testCase.nextHop,
) )
+2 -2
View File
@@ -202,10 +202,10 @@ func TestBlindedPaymentToHints(t *testing.T) {
// The arguments we use for the payload do not matter as long as // The arguments we use for the payload do not matter as long as
// both functions return the same payload. // both functions return the same payload.
expectedPayloadSize := expectedHint[0].IntermediatePayloadSize( expectedPayloadSize := expectedHint[0].IntermediatePayloadSize(
0, 0, false, 0, 0, 0, 0,
) )
actualPayloadSize := actualHint[0].IntermediatePayloadSize( actualPayloadSize := actualHint[0].IntermediatePayloadSize(
0, 0, false, 0, 0, 0, 0,
) )
require.Equal(t, expectedPayloadSize, actualPayloadSize) require.Equal(t, expectedPayloadSize, actualPayloadSize)
-32
View File
@@ -1,32 +0,0 @@
package routing
import (
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/mock"
)
// mockAdditionalEdge is a mock of the AdditionalEdge interface.
type mockAdditionalEdge struct{ mock.Mock }
// IntermediatePayloadSize returns the sphinx payload size defined in BOLT04 if
// this edge were to be included in a route.
func (m *mockAdditionalEdge) IntermediatePayloadSize(amount lnwire.MilliSatoshi,
expiry uint32, legacy bool, channelID uint64) uint64 {
args := m.Called(amount, expiry, legacy, channelID)
return args.Get(0).(uint64)
}
// EdgePolicy return the policy of the mockAdditionalEdge.
func (m *mockAdditionalEdge) EdgePolicy() *models.CachedEdgePolicy {
args := m.Called()
edgePolicy := args.Get(0)
if edgePolicy == nil {
return nil
}
return edgePolicy.(*models.CachedEdgePolicy)
}
+4 -25
View File
@@ -161,7 +161,6 @@ func newRoute(sourceVertex route.Vertex,
fee int64 fee int64
totalAmtMsatBlinded lnwire.MilliSatoshi totalAmtMsatBlinded lnwire.MilliSatoshi
outgoingTimeLock uint32 outgoingTimeLock uint32
tlvPayload bool
customRecords record.CustomSet customRecords record.CustomSet
mpp *record.MPP mpp *record.MPP
metadata []byte metadata []byte
@@ -180,13 +179,6 @@ func newRoute(sourceVertex route.Vertex,
return edge.ToNodeFeatures.HasFeature(feature) return edge.ToNodeFeatures.HasFeature(feature)
} }
// We start by assuming the node doesn't support TLV. We'll now
// inspect the node's feature vector to see if we can promote
// the hop. We assume already that the feature vector's
// transitive dependencies have already been validated by path
// finding or some other means.
tlvPayload = supports(lnwire.TLVOnionPayloadOptional)
if i == len(pathEdges)-1 { if i == len(pathEdges)-1 {
// If this is the last hop, then the hop payload will // If this is the last hop, then the hop payload will
// contain the exact amount. In BOLT #4: Onion Routing // contain the exact amount. In BOLT #4: Onion Routing
@@ -204,12 +196,7 @@ func newRoute(sourceVertex route.Vertex,
totalTimeLock += uint32(finalHop.cltvDelta) totalTimeLock += uint32(finalHop.cltvDelta)
outgoingTimeLock = totalTimeLock outgoingTimeLock = totalTimeLock
// Attach any custom records to the final hop if the // Attach any custom records to the final hop.
// receiver supports TLV.
if !tlvPayload && finalHop.records != nil {
return nil, errors.New("cannot attach " +
"custom records")
}
customRecords = finalHop.records customRecords = finalHop.records
// If we're attaching a payment addr but the receiver // If we're attaching a payment addr but the receiver
@@ -275,7 +262,6 @@ func newRoute(sourceVertex route.Vertex,
ChannelID: edge.ChannelID, ChannelID: edge.ChannelID,
AmtToForward: amtToForward, AmtToForward: amtToForward,
OutgoingTimeLock: outgoingTimeLock, OutgoingTimeLock: outgoingTimeLock,
LegacyPayload: !tlvPayload,
CustomRecords: customRecords, CustomRecords: customRecords,
MPP: mpp, MPP: mpp,
Metadata: metadata, Metadata: metadata,
@@ -659,8 +645,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// The payload size of the final hop differ from intermediate hops // The payload size of the final hop differ from intermediate hops
// and depends on whether the destination is blinded or not. // and depends on whether the destination is blinded or not.
lastHopPayloadSize := lastHopPayloadSize(r, finalHtlcExpiry, amt, lastHopPayloadSize := lastHopPayloadSize(r, finalHtlcExpiry, amt)
!features.HasFeature(lnwire.TLVOnionPayloadOptional))
// We can't always assume that the end destination is publicly // We can't always assume that the end destination is publicly
// advertised to the network so we'll manually include the target node. // advertised to the network so we'll manually include the target node.
@@ -882,14 +867,10 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
return return
} }
supportsTlv := fromFeatures.HasFeature(
lnwire.TLVOnionPayloadOptional,
)
payloadSize = edge.hopPayloadSizeFn( payloadSize = edge.hopPayloadSizeFn(
amountToSend, amountToSend,
uint32(toNodeDist.incomingCltv), uint32(toNodeDist.incomingCltv),
!supportsTlv, edge.policy.ChannelID, edge.policy.ChannelID,
) )
} }
@@ -1176,7 +1157,7 @@ func getProbabilityBasedDist(weight int64, probability float64,
// It depends on the tlv types which are present and also whether the hop is // It depends on the tlv types which are present and also whether the hop is
// part of a blinded route or not. // part of a blinded route or not.
func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32, func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32,
amount lnwire.MilliSatoshi, legacy bool) uint64 { amount lnwire.MilliSatoshi) uint64 {
if r.BlindedPayment != nil { if r.BlindedPayment != nil {
blindedPath := r.BlindedPayment.BlindedPath.BlindedHops blindedPath := r.BlindedPayment.BlindedPath.BlindedHops
@@ -1186,7 +1167,6 @@ func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32,
finalHop := route.Hop{ finalHop := route.Hop{
AmtToForward: amount, AmtToForward: amount,
OutgoingTimeLock: uint32(finalHtlcExpiry), OutgoingTimeLock: uint32(finalHtlcExpiry),
LegacyPayload: false,
EncryptedData: encryptedData, EncryptedData: encryptedData,
} }
if len(blindedPath) == 1 { if len(blindedPath) == 1 {
@@ -1214,7 +1194,6 @@ func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32,
AmtToForward: amount, AmtToForward: amount,
OutgoingTimeLock: uint32(finalHtlcExpiry), OutgoingTimeLock: uint32(finalHtlcExpiry),
CustomRecords: r.DestCustomRecords, CustomRecords: r.DestCustomRecords,
LegacyPayload: legacy,
MPP: mpp, MPP: mpp,
AMP: amp, AMP: amp,
Metadata: r.Metadata, Metadata: r.Metadata,
+30 -187
View File
@@ -3,7 +3,6 @@ package routing
import ( import (
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
@@ -25,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
switchhop "github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/record"
@@ -763,9 +763,6 @@ func TestPathFinding(t *testing.T) {
}, { }, {
name: "path finding with additional edges", name: "path finding with additional edges",
fn: runPathFindingWithAdditionalEdges, fn: runPathFindingWithAdditionalEdges,
}, {
name: "path finding max payload restriction",
fn: runPathFindingMaxPayloadRestriction,
}, { }, {
name: "path finding with redundant additional edges", name: "path finding with redundant additional edges",
fn: runPathFindingWithRedundantAdditionalEdges, fn: runPathFindingWithRedundantAdditionalEdges,
@@ -1033,7 +1030,8 @@ var basicGraphPathFindingTests = []basicGraphPathFindingTestCase{
// Basic route with fee limit. // Basic route with fee limit.
{target: "sophon", paymentAmt: 100, feeLimit: 50, {target: "sophon", paymentAmt: 100, feeLimit: 50,
expectFailureNoPath: true, expectFailureNoPath: true,
}} },
}
func runBasicGraphPathFinding(t *testing.T, useCache bool) { func runBasicGraphPathFinding(t *testing.T, useCache bool) {
testGraphInstance, err := parseTestGraph(t, useCache, basicGraphFilePath) testGraphInstance, err := parseTestGraph(t, useCache, basicGraphFilePath)
@@ -1126,32 +1124,27 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
// Hops should point to the next hop // Hops should point to the next hop
for i := 0; i < len(expectedHops)-1; i++ { for i := 0; i < len(expectedHops)-1; i++ {
var expectedHop [8]byte payload, _, err := switchhop.ParseTLVPayload(
binary.BigEndian.PutUint64(expectedHop[:], route.Hops[i+1].ChannelID) bytes.NewReader(sphinxPath[i].HopPayload.Payload),
)
require.NoError(t, err)
hopData, err := sphinxPath[i].HopPayload.HopData() require.Equal(
if err != nil { t, route.Hops[i+1].ChannelID,
t.Fatalf("unable to make hop data: %v", err) payload.FwdInfo.NextHop.ToUint64(),
} )
if !bytes.Equal(hopData.NextAddress[:], expectedHop[:]) {
t.Fatalf("first hop has incorrect next hop: expected %x, got %x",
expectedHop[:], hopData.NextAddress[:])
}
} }
lastHopIndex := len(expectedHops) - 1
payload, _, err := switchhop.ParseTLVPayload(
bytes.NewReader(sphinxPath[lastHopIndex].HopPayload.Payload),
)
require.NoError(t, err)
// The final hop should have a next hop value of all zeroes in order // The final hop should have a next hop value of all zeroes in order
// to indicate it's the exit hop. // to indicate it's the exit hop.
var exitHop [8]byte require.Zero(t, payload.FwdInfo.NextHop.ToUint64())
lastHopIndex := len(expectedHops) - 1
hopData, err := sphinxPath[lastHopIndex].HopPayload.HopData()
require.NoError(t, err, "unable to create hop data")
if !bytes.Equal(hopData.NextAddress[:], exitHop[:]) {
t.Fatalf("first hop has incorrect next hop: expected %x, got %x",
exitHop[:], hopData.NextAddress)
}
var expectedTotalFee lnwire.MilliSatoshi var expectedTotalFee lnwire.MilliSatoshi
for i := 0; i < expectedHopCount; i++ { for i := 0; i < expectedHopCount; i++ {
@@ -1291,122 +1284,6 @@ func runPathFindingWithAdditionalEdges(t *testing.T, useCache bool) {
assertExpectedPath(t, graph.aliasMap, path, "songoku", "doge") assertExpectedPath(t, graph.aliasMap, path, "songoku", "doge")
} }
// runPathFindingMaxPayloadRestriction tests the maximum size of a sphinx
// package when creating a route. So we make sure the pathfinder does not return
// a route which is greater than the maximum sphinx package size of 1300 bytes
// defined in BOLT04.
func runPathFindingMaxPayloadRestriction(t *testing.T, useCache bool) {
graph, err := parseTestGraph(t, useCache, basicGraphFilePath)
require.NoError(t, err, "unable to create graph")
sourceNode, err := graph.graph.SourceNode()
require.NoError(t, err, "unable to fetch source node")
paymentAmt := lnwire.NewMSatFromSatoshis(100)
// Create a node doge which is not visible in the graph.
dogePubKeyHex := "03dd46ff29a6941b4a2607525b043ec9b020b3f318a1bf281" +
"536fd7011ec59c882"
dogePubKeyBytes, err := hex.DecodeString(dogePubKeyHex)
require.NoError(t, err, "unable to decode public key")
dogePubKey, err := btcec.ParsePubKey(dogePubKeyBytes)
require.NoError(t, err, "unable to parse public key from bytes")
doge := &channeldb.LightningNode{}
doge.AddPubKey(dogePubKey)
doge.Alias = "doge"
copy(doge.PubKeyBytes[:], dogePubKeyBytes)
graph.aliasMap["doge"] = doge.PubKeyBytes
const (
chanID uint64 = 1337
finalHtlcExpiry int32 = 0
)
// Create the channel edge going from songoku to doge and later add it
// with the mocked size function to the graph data.
songokuToDogePolicy := &models.CachedEdgePolicy{
ToNodePubKey: func() route.Vertex {
return doge.PubKeyBytes
},
ToNodeFeatures: lnwire.EmptyFeatureVector(),
ChannelID: chanID,
FeeBaseMSat: 1,
FeeProportionalMillionths: 1000,
TimeLockDelta: 9,
}
// The route has 2 hops. The exit hop (doge) and the hop
// (songoku -> doge). The desired path looks like this:
// source -> songoku -> doge
tests := []struct {
name string
mockedPayloadSize uint64
err error
}{
{
// The final hop payload size needs to be considered
// as well and because it's treated differently than the
// intermediate hops the following tests choose to use
// the legacy payload format to have a constant final
// hop payload size.
name: "route max payload size (1300)",
mockedPayloadSize: 1300 - sphinx.LegacyHopDataSize,
},
{
// We increase the enrypted data size by one byte.
name: "route 1 bytes bigger than max " +
"payload",
mockedPayloadSize: 1300 - sphinx.LegacyHopDataSize + 1,
err: errNoPathFound,
},
}
for _, testCase := range tests {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
restrictions := *noRestrictions
// No tlv payload, this makes sure the final hop uses
// the legacy payload.
restrictions.DestFeatures = lnwire.EmptyFeatureVector()
// Create the mocked AdditionalEdge and mock the
// corresponding calls.
mockedEdge := &mockAdditionalEdge{}
mockedEdge.On("EdgePolicy").Return(songokuToDogePolicy)
mockedEdge.On("IntermediatePayloadSize",
paymentAmt, uint32(finalHtlcExpiry), true,
chanID).Once().
Return(testCase.mockedPayloadSize)
additionalEdges := map[route.Vertex][]AdditionalEdge{
graph.aliasMap["songoku"]: {mockedEdge},
}
path, err := dbFindPath(
graph.graph, additionalEdges,
&mockBandwidthHints{}, &restrictions,
testPathFindingConfig, sourceNode.PubKeyBytes,
doge.PubKeyBytes, paymentAmt, 0,
finalHtlcExpiry,
)
require.ErrorIs(t, err, testCase.err)
if err == nil {
assertExpectedPath(t, graph.aliasMap, path,
"songoku", "doge")
}
mockedEdge.AssertExpectations(t)
})
}
}
// runPathFindingWithRedundantAdditionalEdges asserts that we are able to find // runPathFindingWithRedundantAdditionalEdges asserts that we are able to find
// paths to nodes ignoring additional edges that are already known by self node. // paths to nodes ignoring additional edges that are already known by self node.
func runPathFindingWithRedundantAdditionalEdges(t *testing.T, useCache bool) { func runPathFindingWithRedundantAdditionalEdges(t *testing.T, useCache bool) {
@@ -1464,7 +1341,6 @@ func runPathFindingWithRedundantAdditionalEdges(t *testing.T, useCache bool) {
// TestNewRoute tests whether the construction of hop payloads by newRoute // TestNewRoute tests whether the construction of hop payloads by newRoute
// is executed correctly. // is executed correctly.
func TestNewRoute(t *testing.T) { func TestNewRoute(t *testing.T) {
var sourceKey [33]byte var sourceKey [33]byte
sourceVertex := route.Vertex(sourceKey) sourceVertex := route.Vertex(sourceKey)
@@ -1541,8 +1417,6 @@ func TestNewRoute(t *testing.T) {
// expectError is true. // expectError is true.
expectedErrorCode errorCode expectedErrorCode errorCode
expectedTLVPayload bool
expectedMPP *record.MPP expectedMPP *record.MPP
}{ }{
{ {
@@ -1586,7 +1460,6 @@ func TestNewRoute(t *testing.T) {
expectedTimeLocks: []uint32{1, 1}, expectedTimeLocks: []uint32{1, 1},
expectedTotalAmount: 100130, expectedTotalAmount: 100130,
expectedTotalTimeLock: 6, expectedTotalTimeLock: 6,
expectedTLVPayload: true,
}, { }, {
// For a two hop payment, only the fee for the first hop // For a two hop payment, only the fee for the first hop
// needs to be paid. The destination hop does not require // needs to be paid. The destination hop does not require
@@ -1603,7 +1476,6 @@ func TestNewRoute(t *testing.T) {
expectedTimeLocks: []uint32{1, 1}, expectedTimeLocks: []uint32{1, 1},
expectedTotalAmount: 100130, expectedTotalAmount: 100130,
expectedTotalTimeLock: 6, expectedTotalTimeLock: 6,
expectedTLVPayload: true,
expectedMPP: record.NewMPP( expectedMPP: record.NewMPP(
100000, testPaymentAddr, 100000, testPaymentAddr,
), ),
@@ -1717,14 +1589,6 @@ func TestNewRoute(t *testing.T) {
} }
finalHop := route.Hops[len(route.Hops)-1] finalHop := route.Hops[len(route.Hops)-1]
if !finalHop.LegacyPayload !=
testCase.expectedTLVPayload {
t.Errorf("Expected final hop tlv payload: %t, "+
"but got: %t instead",
testCase.expectedTLVPayload,
!finalHop.LegacyPayload)
}
if !reflect.DeepEqual( if !reflect.DeepEqual(
finalHop.MPP, testCase.expectedMPP, finalHop.MPP, testCase.expectedMPP,
@@ -1787,9 +1651,9 @@ func TestNewRoute(t *testing.T) {
func runNewRoutePathTooLong(t *testing.T, useCache bool) { func runNewRoutePathTooLong(t *testing.T, useCache bool) {
var testChannels []*testChannel var testChannels []*testChannel
// Setup a linear network of 21 hops. // Setup a linear network of 26 hops.
fromNode := "start" fromNode := "start"
for i := 0; i < 21; i++ { for i := 0; i < 26; i++ {
toNode := fmt.Sprintf("node-%v", i+1) toNode := fmt.Sprintf("node-%v", i+1)
c := symmetricTestChannel(fromNode, toNode, 100000, &testChannelPolicy{ c := symmetricTestChannel(fromNode, toNode, 100000, &testChannelPolicy{
Expiry: 144, Expiry: 144,
@@ -1804,18 +1668,16 @@ func runNewRoutePathTooLong(t *testing.T, useCache bool) {
ctx := newPathFindingTestContext(t, useCache, testChannels, "start") ctx := newPathFindingTestContext(t, useCache, testChannels, "start")
// Assert that we can find 20 hop routes. // Assert that we can find 25 hop routes.
node20 := ctx.keyFromAlias("node-20") node25 := ctx.keyFromAlias("node-25")
payAmt := lnwire.MilliSatoshi(100001) payAmt := lnwire.MilliSatoshi(100001)
_, err := ctx.findPath(node20, payAmt) _, err := ctx.findPath(node25, payAmt)
require.NoError(t, err, "unexpected pathfinding failure") require.NoError(t, err, "unexpected pathfinding failure")
// Assert that finding a 21 hop route fails. // Assert that finding a 26 hop route fails.
node21 := ctx.keyFromAlias("node-21") node26 := ctx.keyFromAlias("node-26")
_, err = ctx.findPath(node21, payAmt) _, err = ctx.findPath(node26, payAmt)
if err != errNoPathFound { require.ErrorIs(t, err, errNoPathFound)
t.Fatalf("not route error expected, but got %v", err)
}
// Assert that we can't find a 20 hop route if custom records make it // Assert that we can't find a 20 hop route if custom records make it
// exceed the maximum payload size. // exceed the maximum payload size.
@@ -1823,7 +1685,7 @@ func runNewRoutePathTooLong(t *testing.T, useCache bool) {
ctx.restrictParams.DestCustomRecords = map[uint64][]byte{ ctx.restrictParams.DestCustomRecords = map[uint64][]byte{
100000: bytes.Repeat([]byte{1}, 100), 100000: bytes.Repeat([]byte{1}, 100),
} }
_, err = ctx.findPath(node20, payAmt) _, err = ctx.findPath(node25, payAmt)
if err != errNoPathFound { if err != errNoPathFound {
t.Fatalf("not route error expected, but got %v", err) t.Fatalf("not route error expected, but got %v", err)
} }
@@ -3675,7 +3537,6 @@ func TestLastHopPayloadSize(t *testing.T) {
restrictions *RestrictParams restrictions *RestrictParams
finalHopExpiry int32 finalHopExpiry int32
amount lnwire.MilliSatoshi amount lnwire.MilliSatoshi
legacy bool
}{ }{
{ {
name: "Non blinded final hop", name: "Non blinded final hop",
@@ -3687,22 +3548,6 @@ func TestLastHopPayloadSize(t *testing.T) {
}, },
amount: amtToForward, amount: amtToForward,
finalHopExpiry: finalHopExpiry, finalHopExpiry: finalHopExpiry,
legacy: false,
},
{
name: "Non blinded final hop legacy",
restrictions: &RestrictParams{
// The legacy encoding has no ability to include
// those extra data we expect that this data is
// ignored.
PaymentAddr: paymentAddr,
DestCustomRecords: customRecords,
Metadata: metadata,
Amp: ampOptions,
},
amount: amtToForward,
finalHopExpiry: finalHopExpiry,
legacy: true,
}, },
{ {
name: "Blinded final hop introduction point", name: "Blinded final hop introduction point",
@@ -3754,7 +3599,6 @@ func TestLastHopPayloadSize(t *testing.T) {
finalHop = route.Hop{ finalHop = route.Hop{
AmtToForward: tc.amount, AmtToForward: tc.amount,
OutgoingTimeLock: uint32(tc.finalHopExpiry), OutgoingTimeLock: uint32(tc.finalHopExpiry),
LegacyPayload: false,
EncryptedData: blindedPath[len(blindedPath)-1].CipherText, EncryptedData: blindedPath[len(blindedPath)-1].CipherText,
} }
if len(blindedPath) == 1 { if len(blindedPath) == 1 {
@@ -3763,7 +3607,6 @@ func TestLastHopPayloadSize(t *testing.T) {
} else { } else {
//nolint:lll //nolint:lll
finalHop = route.Hop{ finalHop = route.Hop{
LegacyPayload: tc.legacy,
AmtToForward: tc.amount, AmtToForward: tc.amount,
OutgoingTimeLock: uint32(tc.finalHopExpiry), OutgoingTimeLock: uint32(tc.finalHopExpiry),
Metadata: tc.restrictions.Metadata, Metadata: tc.restrictions.Metadata,
@@ -3778,7 +3621,7 @@ func TestLastHopPayloadSize(t *testing.T) {
expectedPayloadSize := lastHopPayloadSize( expectedPayloadSize := lastHopPayloadSize(
tc.restrictions, tc.finalHopExpiry, tc.restrictions, tc.finalHopExpiry,
tc.amount, tc.legacy, tc.amount,
) )
require.Equal( require.Equal(
+8 -3
View File
@@ -133,6 +133,11 @@ type Hop struct {
// LegacyPayload if true, then this signals that this node doesn't // LegacyPayload if true, then this signals that this node doesn't
// understand the new TLV payload, so we must instead use the legacy // understand the new TLV payload, so we must instead use the legacy
// payload. // payload.
//
// NOTE: we should no longer ever create a Hop with Legacy set to true.
// The only reason we are keeping this member is that it could be the
// case that we have serialised hops persisted to disk where
// LegacyPayload is true.
LegacyPayload bool LegacyPayload bool
// Metadata is additional data that is sent along with the payment to // Metadata is additional data that is sent along with the payment to
@@ -190,7 +195,7 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64,
// If this is a legacy payload, then we'll exit here as this method // If this is a legacy payload, then we'll exit here as this method
// shouldn't be called. // shouldn't be called.
if h.LegacyPayload == true { if h.LegacyPayload {
return fmt.Errorf("cannot pack hop payloads for legacy " + return fmt.Errorf("cannot pack hop payloads for legacy " +
"payloads") "payloads")
} }
@@ -370,8 +375,8 @@ func validateNextChanID(nextChanIDIsSet, isBlinded, finalHop bool) error {
} }
} }
// Size returns the total size this hop's payload would take up in the onion // PayloadSize returns the total size this hop's payload would take up in the
// packet. // onion packet.
func (h *Hop) PayloadSize(nextChanID uint64) uint64 { func (h *Hop) PayloadSize(nextChanID uint64) uint64 {
if h.LegacyPayload { if h.LegacyPayload {
return sphinx.LegacyHopDataSize return sphinx.LegacyHopDataSize