routing: extend path finding to be TLV-EOB aware, allow dest TLV records
In this commit, we extend the path finding to be able to recognize when a node needs the new TLV format, or the legacy format based on the feature bits they expose. We also extend the `LightningPayment` struct to allow the caller to specify an arbitrary set of TLV records which can be used for a number of use-cases including various variants of spontaneous payments.
This commit is contained in:
parent
5b4c8ac232
commit
4697cfde30
@ -11,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ var (
|
|||||||
ChannelID: 12345,
|
ChannelID: 12345,
|
||||||
OutgoingTimeLock: 111,
|
OutgoingTimeLock: 111,
|
||||||
AmtToForward: 555,
|
AmtToForward: 555,
|
||||||
|
LegacyPayload: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
testRoute = route.Route{
|
testRoute = route.Route{
|
||||||
@ -144,7 +146,9 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(result.Route, &attempt.Route) {
|
if !reflect.DeepEqual(result.Route, &attempt.Route) {
|
||||||
t.Fatal("unexpected route")
|
t.Fatalf("unexpected route: %v vs %v",
|
||||||
|
spew.Sdump(result.Route),
|
||||||
|
spew.Sdump(attempt.Route))
|
||||||
}
|
}
|
||||||
|
|
||||||
// After the final event, we expect the channel to be closed.
|
// After the final event, we expect the channel to be closed.
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
|
||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
@ -50,7 +51,8 @@ func TestMissionControlStore(t *testing.T) {
|
|||||||
SourcePubKey: route.Vertex{1},
|
SourcePubKey: route.Vertex{1},
|
||||||
Hops: []*route.Hop{
|
Hops: []*route.Hop{
|
||||||
{
|
{
|
||||||
PubKeyBytes: route.Vertex{2},
|
PubKeyBytes: route.Vertex{2},
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -99,10 +101,12 @@ func TestMissionControlStore(t *testing.T) {
|
|||||||
|
|
||||||
// Check that results are stored in chronological order.
|
// Check that results are stored in chronological order.
|
||||||
if !reflect.DeepEqual(&result1, results[0]) {
|
if !reflect.DeepEqual(&result1, results[0]) {
|
||||||
t.Fatal()
|
t.Fatalf("the results differ: %v vs %v", spew.Sdump(&result1),
|
||||||
|
spew.Sdump(results[0]))
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(&result2, results[1]) {
|
if !reflect.DeepEqual(&result2, results[1]) {
|
||||||
t.Fatal()
|
t.Fatalf("the results differ: %v vs %v", spew.Sdump(&result2),
|
||||||
|
spew.Sdump(results[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recreate store to test pruning.
|
// Recreate store to test pruning.
|
||||||
@ -132,9 +136,11 @@ func TestMissionControlStore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(&result2, results[0]) {
|
if !reflect.DeepEqual(&result2, results[0]) {
|
||||||
t.Fatal()
|
t.Fatalf("the results differ: %v vs %v", spew.Sdump(&result2),
|
||||||
|
spew.Sdump(results[0]))
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(&result3, results[1]) {
|
if !reflect.DeepEqual(&result3, results[1]) {
|
||||||
t.Fatal()
|
t.Fatalf("the results differ: %v vs %v", spew.Sdump(&result3),
|
||||||
|
spew.Sdump(results[1]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,15 @@ var (
|
|||||||
SourcePubKey: route.Vertex{10},
|
SourcePubKey: route.Vertex{10},
|
||||||
Hops: []*route.Hop{
|
Hops: []*route.Hop{
|
||||||
{
|
{
|
||||||
ChannelID: 1,
|
ChannelID: 1,
|
||||||
PubKeyBytes: route.Vertex{11},
|
PubKeyBytes: route.Vertex{11},
|
||||||
AmtToForward: 1000,
|
AmtToForward: 1000,
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ChannelID: 2,
|
ChannelID: 2,
|
||||||
PubKeyBytes: route.Vertex{12},
|
PubKeyBytes: route.Vertex{12},
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -167,7 +169,8 @@ func TestMissionControl(t *testing.T) {
|
|||||||
// Check whether history snapshot looks sane.
|
// Check whether history snapshot looks sane.
|
||||||
history := ctx.mc.GetHistorySnapshot()
|
history := ctx.mc.GetHistorySnapshot()
|
||||||
if len(history.Nodes) != 1 {
|
if len(history.Nodes) != 1 {
|
||||||
t.Fatal("unexpected number of nodes")
|
t.Fatalf("unexpected number of nodes: expected 1 got %v",
|
||||||
|
len(history.Nodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(history.Pairs) != 1 {
|
if len(history.Pairs) != 1 {
|
||||||
|
@ -2,6 +2,7 @@ package routing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
@ -9,6 +10,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -98,7 +100,8 @@ func isSamePath(path1, path2 []*channeldb.ChannelEdgePolicy) bool {
|
|||||||
// the source to the target node of the path finding attempt.
|
// the source to the target node of the path finding attempt.
|
||||||
func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex,
|
func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex,
|
||||||
pathEdges []*channeldb.ChannelEdgePolicy, currentHeight uint32,
|
pathEdges []*channeldb.ChannelEdgePolicy, currentHeight uint32,
|
||||||
finalCLTVDelta uint16) (*route.Route, error) {
|
finalCLTVDelta uint16,
|
||||||
|
finalDestRecords []tlv.Record) (*route.Route, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hops []*route.Hop
|
hops []*route.Hop
|
||||||
@ -179,7 +182,27 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex,
|
|||||||
ChannelID: edge.ChannelID,
|
ChannelID: edge.ChannelID,
|
||||||
AmtToForward: amtToForward,
|
AmtToForward: amtToForward,
|
||||||
OutgoingTimeLock: outgoingTimeLock,
|
OutgoingTimeLock: outgoingTimeLock,
|
||||||
|
LegacyPayload: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We start out above by assuming that this node needs the
|
||||||
|
// legacy payload, as if we don't have the full
|
||||||
|
// NodeAnnouncement information for this node, then we can't
|
||||||
|
// assume it knows the latest features. If we do have a feature
|
||||||
|
// vector for this node, then we'll update the info now.
|
||||||
|
if edge.Node.Features != nil {
|
||||||
|
features := edge.Node.Features
|
||||||
|
currentHop.LegacyPayload = !features.HasFeature(
|
||||||
|
lnwire.TLVOnionPayloadOptional,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the last hop, then we'll populate any TLV records
|
||||||
|
// destined for it.
|
||||||
|
if i == len(pathEdges)-1 && len(finalDestRecords) != 0 {
|
||||||
|
currentHop.TLVRecords = finalDestRecords
|
||||||
|
}
|
||||||
|
|
||||||
hops = append([]*route.Hop{currentHop}, hops...)
|
hops = append([]*route.Hop{currentHop}, hops...)
|
||||||
|
|
||||||
// Finally, we update the amount that needs to flow into the
|
// Finally, we update the amount that needs to flow into the
|
||||||
@ -190,7 +213,8 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex,
|
|||||||
|
|
||||||
// With the base routing data expressed as hops, build the full route
|
// With the base routing data expressed as hops, build the full route
|
||||||
newRoute, err := route.NewRouteFromHops(
|
newRoute, err := route.NewRouteFromHops(
|
||||||
nextIncomingAmount, totalTimeLock, route.Vertex(sourceVertex), hops,
|
nextIncomingAmount, totalTimeLock, route.Vertex(sourceVertex),
|
||||||
|
hops,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -261,6 +285,11 @@ type RestrictParams struct {
|
|||||||
// ctlv. After path finding is complete, the caller needs to increase
|
// ctlv. After path finding is complete, the caller needs to increase
|
||||||
// all cltv expiry heights with the required final cltv delta.
|
// all cltv expiry heights with the required final cltv delta.
|
||||||
CltvLimit *uint32
|
CltvLimit *uint32
|
||||||
|
|
||||||
|
// DestPayloadTLV should be set to true if we need to drop off a TLV
|
||||||
|
// payload at the final hop in order to properly complete this payment
|
||||||
|
// attempt.
|
||||||
|
DestPayloadTLV bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathFindingConfig defines global parameters that control the trade-off in
|
// PathFindingConfig defines global parameters that control the trade-off in
|
||||||
@ -316,10 +345,34 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
|||||||
node *channeldb.LightningNode) error {
|
node *channeldb.LightningNode) error {
|
||||||
// TODO(roasbeef): with larger graph can just use disk seeks
|
// TODO(roasbeef): with larger graph can just use disk seeks
|
||||||
// with a visited map
|
// with a visited map
|
||||||
distance[route.Vertex(node.PubKeyBytes)] = nodeWithDist{
|
vertex := route.Vertex(node.PubKeyBytes)
|
||||||
|
distance[vertex] = nodeWithDist{
|
||||||
dist: infinity,
|
dist: infinity,
|
||||||
node: route.Vertex(node.PubKeyBytes),
|
node: route.Vertex(node.PubKeyBytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we don't have any features for this node, then we can
|
||||||
|
// stop here.
|
||||||
|
if node.Features == nil || !r.DestPayloadTLV {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only need to perform this check for the final node, so we
|
||||||
|
// can exit here if this isn't them.
|
||||||
|
if vertex != target {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have any records for the final hop, then we'll check
|
||||||
|
// not to ensure that they are actually able to interpret them.
|
||||||
|
supportsTLV := node.Features.HasFeature(
|
||||||
|
lnwire.TLVOnionPayloadOptional,
|
||||||
|
)
|
||||||
|
if !supportsTLV {
|
||||||
|
return fmt.Errorf("destination hop doesn't " +
|
||||||
|
"understand new TLV paylods")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -670,7 +670,8 @@ func TestFindLowestFeePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
route, err := newRoute(
|
route, err := newRoute(
|
||||||
paymentAmt, sourceVertex, path, startingHeight,
|
paymentAmt, sourceVertex, path, startingHeight,
|
||||||
finalHopCLTV)
|
finalHopCLTV, nil,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create path: %v", err)
|
t.Fatalf("unable to create path: %v", err)
|
||||||
}
|
}
|
||||||
@ -819,7 +820,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
|
|||||||
|
|
||||||
route, err := newRoute(
|
route, err := newRoute(
|
||||||
paymentAmt, sourceVertex, path, startingHeight,
|
paymentAmt, sourceVertex, path, startingHeight,
|
||||||
finalHopCLTV,
|
finalHopCLTV, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create path: %v", err)
|
t.Fatalf("unable to create path: %v", err)
|
||||||
@ -857,9 +858,15 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
|
|||||||
for i := 0; i < len(expectedHops)-1; i++ {
|
for i := 0; i < len(expectedHops)-1; i++ {
|
||||||
var expectedHop [8]byte
|
var expectedHop [8]byte
|
||||||
binary.BigEndian.PutUint64(expectedHop[:], route.Hops[i+1].ChannelID)
|
binary.BigEndian.PutUint64(expectedHop[:], route.Hops[i+1].ChannelID)
|
||||||
if !bytes.Equal(sphinxPath[i].HopData.NextAddress[:], expectedHop[:]) {
|
|
||||||
|
hopData, err := sphinxPath[i].HopPayload.HopData()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to make hop data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(hopData.NextAddress[:], expectedHop[:]) {
|
||||||
t.Fatalf("first hop has incorrect next hop: expected %x, got %x",
|
t.Fatalf("first hop has incorrect next hop: expected %x, got %x",
|
||||||
expectedHop[:], sphinxPath[i].HopData.NextAddress)
|
expectedHop[:], hopData.NextAddress[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,9 +874,15 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
|
|||||||
// to indicate it's the exit hop.
|
// to indicate it's the exit hop.
|
||||||
var exitHop [8]byte
|
var exitHop [8]byte
|
||||||
lastHopIndex := len(expectedHops) - 1
|
lastHopIndex := len(expectedHops) - 1
|
||||||
if !bytes.Equal(sphinxPath[lastHopIndex].HopData.NextAddress[:], exitHop[:]) {
|
|
||||||
|
hopData, err := sphinxPath[lastHopIndex].HopPayload.HopData()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create hop data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(hopData.NextAddress[:], exitHop[:]) {
|
||||||
t.Fatalf("first hop has incorrect next hop: expected %x, got %x",
|
t.Fatalf("first hop has incorrect next hop: expected %x, got %x",
|
||||||
exitHop[:], sphinxPath[lastHopIndex].HopData.NextAddress)
|
exitHop[:], hopData.NextAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedTotalFee lnwire.MilliSatoshi
|
var expectedTotalFee lnwire.MilliSatoshi
|
||||||
@ -1001,7 +1014,11 @@ func TestNewRoute(t *testing.T) {
|
|||||||
timeLockDelta uint16) *channeldb.ChannelEdgePolicy {
|
timeLockDelta uint16) *channeldb.ChannelEdgePolicy {
|
||||||
|
|
||||||
return &channeldb.ChannelEdgePolicy{
|
return &channeldb.ChannelEdgePolicy{
|
||||||
Node: &channeldb.LightningNode{},
|
Node: &channeldb.LightningNode{
|
||||||
|
Features: lnwire.NewFeatureVector(
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
},
|
||||||
FeeProportionalMillionths: feeRate,
|
FeeProportionalMillionths: feeRate,
|
||||||
FeeBaseMSat: baseFee,
|
FeeBaseMSat: baseFee,
|
||||||
TimeLockDelta: timeLockDelta,
|
TimeLockDelta: timeLockDelta,
|
||||||
@ -1176,9 +1193,11 @@ func TestNewRoute(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
route, err := newRoute(testCase.paymentAmount,
|
route, err := newRoute(
|
||||||
sourceVertex, testCase.hops, startingHeight,
|
testCase.paymentAmount, sourceVertex,
|
||||||
finalHopCLTV)
|
testCase.hops, startingHeight, finalHopCLTV,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
if testCase.expectError {
|
if testCase.expectError {
|
||||||
expectedCode := testCase.expectedErrorCode
|
expectedCode := testCase.expectedErrorCode
|
||||||
@ -1683,7 +1702,7 @@ func TestPathFindSpecExample(t *testing.T) {
|
|||||||
carol := ctx.aliases["C"]
|
carol := ctx.aliases["C"]
|
||||||
const amt lnwire.MilliSatoshi = 4999999
|
const amt lnwire.MilliSatoshi = 4999999
|
||||||
route, err := ctx.router.FindRoute(
|
route, err := ctx.router.FindRoute(
|
||||||
bobNode.PubKeyBytes, carol, amt, noRestrictions,
|
bobNode.PubKeyBytes, carol, amt, noRestrictions, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find route: %v", err)
|
t.Fatalf("unable to find route: %v", err)
|
||||||
@ -1742,7 +1761,7 @@ func TestPathFindSpecExample(t *testing.T) {
|
|||||||
|
|
||||||
// We'll now request a route from A -> B -> C.
|
// We'll now request a route from A -> B -> C.
|
||||||
route, err = ctx.router.FindRoute(
|
route, err = ctx.router.FindRoute(
|
||||||
source.PubKeyBytes, carol, amt, noRestrictions,
|
source.PubKeyBytes, carol, amt, noRestrictions, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find routes: %v", err)
|
t.Fatalf("unable to find routes: %v", err)
|
||||||
@ -1925,7 +1944,7 @@ func TestRestrictOutgoingChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
route, err := newRoute(
|
route, err := newRoute(
|
||||||
paymentAmt, sourceVertex, path, startingHeight,
|
paymentAmt, sourceVertex, path, startingHeight,
|
||||||
finalHopCLTV,
|
finalHopCLTV, nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create path: %v", err)
|
t.Fatalf("unable to create path: %v", err)
|
||||||
@ -2033,6 +2052,7 @@ func testCltvLimit(t *testing.T, limit uint32, expectedChannel uint64) {
|
|||||||
)
|
)
|
||||||
route, err := newRoute(
|
route, err := newRoute(
|
||||||
paymentAmt, sourceVertex, path, startingHeight, finalHopCLTV,
|
paymentAmt, sourceVertex, path, startingHeight, finalHopCLTV,
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create path: %v", err)
|
t.Fatalf("unable to create path: %v", err)
|
||||||
|
@ -127,6 +127,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
|||||||
sourceVertex := route.Vertex(ss.SelfNode.PubKeyBytes)
|
sourceVertex := route.Vertex(ss.SelfNode.PubKeyBytes)
|
||||||
route, err := newRoute(
|
route, err := newRoute(
|
||||||
payment.Amount, sourceVertex, path, height, finalCltvDelta,
|
payment.Amount, sourceVertex, path, height, finalCltvDelta,
|
||||||
|
payment.FinalDestRecords,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(roasbeef): return which edge/vertex didn't work
|
// TODO(roasbeef): return which edge/vertex didn't work
|
||||||
|
@ -26,7 +26,11 @@ func TestRequestRoute(t *testing.T) {
|
|||||||
|
|
||||||
path := []*channeldb.ChannelEdgePolicy{
|
path := []*channeldb.ChannelEdgePolicy{
|
||||||
{
|
{
|
||||||
Node: &channeldb.LightningNode{},
|
Node: &channeldb.LightningNode{
|
||||||
|
Features: lnwire.NewFeatureVector(
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/routing/chainview"
|
"github.com/lightningnetwork/lnd/routing/chainview"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/ticker"
|
"github.com/lightningnetwork/lnd/ticker"
|
||||||
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1429,6 +1430,7 @@ type routingMsg struct {
|
|||||||
// factoring in channel capacities and cumulative fees along the route.
|
// factoring in channel capacities and cumulative fees along the route.
|
||||||
func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
||||||
amt lnwire.MilliSatoshi, restrictions *RestrictParams,
|
amt lnwire.MilliSatoshi, restrictions *RestrictParams,
|
||||||
|
destTlvRecords []tlv.Record,
|
||||||
finalExpiry ...uint16) (*route.Route, error) {
|
finalExpiry ...uint16) (*route.Route, error) {
|
||||||
|
|
||||||
var finalCLTVDelta uint16
|
var finalCLTVDelta uint16
|
||||||
@ -1482,6 +1484,7 @@ func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
|||||||
// Create the route with absolute time lock values.
|
// Create the route with absolute time lock values.
|
||||||
route, err := newRoute(
|
route, err := newRoute(
|
||||||
amt, source, path, uint32(currentHeight), finalCLTVDelta,
|
amt, source, path, uint32(currentHeight), finalCLTVDelta,
|
||||||
|
destTlvRecords,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1630,7 +1633,11 @@ type LightningPayment struct {
|
|||||||
// attempting to complete.
|
// attempting to complete.
|
||||||
PaymentRequest []byte
|
PaymentRequest []byte
|
||||||
|
|
||||||
// TODO(roasbeef): add e2e message?
|
// FinalDestRecords are TLV records that are to be sent to the final
|
||||||
|
// hop in the new onion payload format. If the destination does not
|
||||||
|
// understand this new onion payload format, then the payment will
|
||||||
|
// fail.
|
||||||
|
FinalDestRecords []tlv.Record
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendPayment attempts to send a payment as described within the passed
|
// SendPayment attempts to send a payment as described within the passed
|
||||||
@ -1694,6 +1701,8 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) (
|
|||||||
|
|
||||||
// Record this payment hash with the ControlTower, ensuring it is not
|
// Record this payment hash with the ControlTower, ensuring it is not
|
||||||
// already in-flight.
|
// already in-flight.
|
||||||
|
//
|
||||||
|
// TODO(roasbeef): store records as part of creation info?
|
||||||
info := &channeldb.PaymentCreationInfo{
|
info := &channeldb.PaymentCreationInfo{
|
||||||
PaymentHash: payment.PaymentHash,
|
PaymentHash: payment.PaymentHash,
|
||||||
Value: payment.Amount,
|
Value: payment.Amount,
|
||||||
|
@ -231,7 +231,7 @@ func TestFindRoutesWithFeeLimit(t *testing.T) {
|
|||||||
|
|
||||||
route, err := ctx.router.FindRoute(
|
route, err := ctx.router.FindRoute(
|
||||||
ctx.router.selfNode.PubKeyBytes,
|
ctx.router.selfNode.PubKeyBytes,
|
||||||
target, paymentAmt, restrictions,
|
target, paymentAmt, restrictions, nil,
|
||||||
zpay32.DefaultFinalCLTVDelta,
|
zpay32.DefaultFinalCLTVDelta,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -390,12 +390,14 @@ func TestChannelUpdateValidation(t *testing.T) {
|
|||||||
|
|
||||||
hops := []*route.Hop{
|
hops := []*route.Hop{
|
||||||
{
|
{
|
||||||
ChannelID: 1,
|
ChannelID: 1,
|
||||||
PubKeyBytes: hop1,
|
PubKeyBytes: hop1,
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ChannelID: 2,
|
ChannelID: 2,
|
||||||
PubKeyBytes: hop2,
|
PubKeyBytes: hop2,
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1074,8 +1076,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const startingBlockHeight = 101
|
const startingBlockHeight = 101
|
||||||
ctx, cleanUp, err := createTestCtxFromFile(startingBlockHeight,
|
ctx, cleanUp, err := createTestCtxFromFile(
|
||||||
basicGraphFilePath)
|
startingBlockHeight, basicGraphFilePath,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create router: %v", err)
|
t.Fatalf("unable to create router: %v", err)
|
||||||
}
|
}
|
||||||
@ -1108,7 +1111,8 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
fundingTx, _, chanID, err := createChannelEdge(ctx,
|
fundingTx, _, chanID, err := createChannelEdge(ctx,
|
||||||
bitcoinKey1.SerializeCompressed(),
|
bitcoinKey1.SerializeCompressed(),
|
||||||
bitcoinKey2.SerializeCompressed(),
|
bitcoinKey2.SerializeCompressed(),
|
||||||
10000, 500)
|
10000, 500,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create channel edge: %v", err)
|
t.Fatalf("unable to create channel edge: %v", err)
|
||||||
}
|
}
|
||||||
@ -1266,7 +1270,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
copy(targetPubKeyBytes[:], targetNode.SerializeCompressed())
|
copy(targetPubKeyBytes[:], targetNode.SerializeCompressed())
|
||||||
_, err = ctx.router.FindRoute(
|
_, err = ctx.router.FindRoute(
|
||||||
ctx.router.selfNode.PubKeyBytes,
|
ctx.router.selfNode.PubKeyBytes,
|
||||||
targetPubKeyBytes, paymentAmt, noRestrictions,
|
targetPubKeyBytes, paymentAmt, noRestrictions, nil,
|
||||||
zpay32.DefaultFinalCLTVDelta,
|
zpay32.DefaultFinalCLTVDelta,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1309,7 +1313,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
|||||||
// updated.
|
// updated.
|
||||||
_, err = ctx.router.FindRoute(
|
_, err = ctx.router.FindRoute(
|
||||||
ctx.router.selfNode.PubKeyBytes,
|
ctx.router.selfNode.PubKeyBytes,
|
||||||
targetPubKeyBytes, paymentAmt, noRestrictions,
|
targetPubKeyBytes, paymentAmt, noRestrictions, nil,
|
||||||
zpay32.DefaultFinalCLTVDelta,
|
zpay32.DefaultFinalCLTVDelta,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2632,12 +2636,14 @@ func TestRouterPaymentStateMachine(t *testing.T) {
|
|||||||
hop2 := testGraph.aliasMap["c"]
|
hop2 := testGraph.aliasMap["c"]
|
||||||
hops := []*route.Hop{
|
hops := []*route.Hop{
|
||||||
{
|
{
|
||||||
ChannelID: 1,
|
ChannelID: 1,
|
||||||
PubKeyBytes: hop1,
|
PubKeyBytes: hop1,
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ChannelID: 2,
|
ChannelID: 2,
|
||||||
PubKeyBytes: hop2,
|
PubKeyBytes: hop2,
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3270,14 +3276,16 @@ func TestSendToRouteStructuredError(t *testing.T) {
|
|||||||
hop2 := ctx.aliases["c"]
|
hop2 := ctx.aliases["c"]
|
||||||
hops := []*route.Hop{
|
hops := []*route.Hop{
|
||||||
{
|
{
|
||||||
ChannelID: 1,
|
ChannelID: 1,
|
||||||
PubKeyBytes: hop1,
|
PubKeyBytes: hop1,
|
||||||
AmtToForward: payAmt,
|
AmtToForward: payAmt,
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ChannelID: 2,
|
ChannelID: 2,
|
||||||
PubKeyBytes: hop2,
|
PubKeyBytes: hop2,
|
||||||
AmtToForward: payAmt,
|
AmtToForward: payAmt,
|
||||||
|
LegacyPayload: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user