lnrpc+routing: Only accept a single route for SendToRoute
This commit is contained in:
10 files changed
+693
-736
No files matched your search
+10
-1
@@ -2476,9 +2476,18 @@ func sendToRoute(ctx *cli.Context) error {
|
|||||||
"from incoming array of routes: %v", err)
|
"from incoming array of routes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(routes.Routes) == 0 {
|
||||||
|
return fmt.Errorf("no routes provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(routes.Routes) != 1 {
|
||||||
|
return fmt.Errorf("expected a single route, but got %v",
|
||||||
|
len(routes.Routes))
|
||||||
|
}
|
||||||
|
|
||||||
req := &lnrpc.SendToRouteRequest{
|
req := &lnrpc.SendToRouteRequest{
|
||||||
PaymentHash: rHash,
|
PaymentHash: rHash,
|
||||||
Routes: routes.Routes,
|
Route: routes.Routes[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendToRouteRequest(ctx, req)
|
return sendToRouteRequest(ctx, req)
|
||||||
|
|||||||
+5
-5
@@ -1491,7 +1491,7 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
}
|
}
|
||||||
sendReq := &lnrpc.SendToRouteRequest{
|
sendReq := &lnrpc.SendToRouteRequest{
|
||||||
PaymentHash: resp.RHash,
|
PaymentHash: resp.RHash,
|
||||||
Routes: routes.Routes,
|
Route: routes.Routes[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
err = alicePayStream.Send(sendReq)
|
err = alicePayStream.Send(sendReq)
|
||||||
@@ -1529,7 +1529,7 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
|
|
||||||
sendReq = &lnrpc.SendToRouteRequest{
|
sendReq = &lnrpc.SendToRouteRequest{
|
||||||
PaymentHash: resp.RHash,
|
PaymentHash: resp.RHash,
|
||||||
Routes: routes.Routes,
|
Route: routes.Routes[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
err = alicePayStream.Send(sendReq)
|
err = alicePayStream.Send(sendReq)
|
||||||
@@ -4308,7 +4308,7 @@ func testSingleHopSendToRoute(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
for _, rHash := range rHashes {
|
for _, rHash := range rHashes {
|
||||||
sendReq := &lnrpc.SendToRouteRequest{
|
sendReq := &lnrpc.SendToRouteRequest{
|
||||||
PaymentHash: rHash,
|
PaymentHash: rHash,
|
||||||
Routes: routes.Routes,
|
Route: routes.Routes[0],
|
||||||
}
|
}
|
||||||
err := alicePayStream.Send(sendReq)
|
err := alicePayStream.Send(sendReq)
|
||||||
|
|
||||||
@@ -4479,7 +4479,7 @@ func testMultiHopSendToRoute(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
for _, rHash := range rHashes {
|
for _, rHash := range rHashes {
|
||||||
sendReq := &lnrpc.SendToRouteRequest{
|
sendReq := &lnrpc.SendToRouteRequest{
|
||||||
PaymentHash: rHash,
|
PaymentHash: rHash,
|
||||||
Routes: routes.Routes,
|
Route: routes.Routes[0],
|
||||||
}
|
}
|
||||||
err := alicePayStream.Send(sendReq)
|
err := alicePayStream.Send(sendReq)
|
||||||
|
|
||||||
@@ -4634,7 +4634,7 @@ func testSendToRouteErrorPropagation(net *lntest.NetworkHarness, t *harnessTest)
|
|||||||
|
|
||||||
sendReq := &lnrpc.SendToRouteRequest{
|
sendReq := &lnrpc.SendToRouteRequest{
|
||||||
PaymentHash: rHash,
|
PaymentHash: rHash,
|
||||||
Routes: fakeRoute.Routes,
|
Route: fakeRoute.Routes[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := alicePayStream.Send(sendReq); err != nil {
|
if err := alicePayStream.Send(sendReq); err != nil {
|
||||||
|
|||||||
+623
-637
File diff suppressed because it is too large.
Load diff
+1
-7
@@ -896,13 +896,7 @@ message SendToRouteRequest {
|
|||||||
/// An optional hex-encoded payment hash to be used for the HTLC.
|
/// An optional hex-encoded payment hash to be used for the HTLC.
|
||||||
string payment_hash_string = 2;
|
string payment_hash_string = 2;
|
||||||
|
|
||||||
/**
|
reserved 3;
|
||||||
Deprecated. The set of routes that should be used to attempt to complete the
|
|
||||||
payment. The possibility to pass in multiple routes is deprecated and
|
|
||||||
instead the single route field below should be used in combination with the
|
|
||||||
streaming variant of SendToRoute.
|
|
||||||
*/
|
|
||||||
repeated Route routes = 3 [deprecated = true];
|
|
||||||
|
|
||||||
/// Route that should be used to attempt to complete the payment.
|
/// Route that should be used to attempt to complete the payment.
|
||||||
Route route = 4;
|
Route route = 4;
|
||||||
|
|||||||
@@ -3216,13 +3216,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "/ An optional hex-encoded payment hash to be used for the HTLC."
|
"description": "/ An optional hex-encoded payment hash to be used for the HTLC."
|
||||||
},
|
},
|
||||||
"routes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/lnrpcRoute"
|
|
||||||
},
|
|
||||||
"description": "*\nDeprecated. The set of routes that should be used to attempt to complete the\npayment. The possibility to pass in multiple routes is deprecated and \ninstead the single route field below should be used in combination with the \nstreaming variant of SendToRoute."
|
|
||||||
},
|
|
||||||
"route": {
|
"route": {
|
||||||
"$ref": "#/definitions/lnrpcRoute",
|
"$ref": "#/definitions/lnrpcRoute",
|
||||||
"description": "/ Route that should be used to attempt to complete the payment."
|
"description": "/ Route that should be used to attempt to complete the payment."
|
||||||
|
|||||||
@@ -231,18 +231,14 @@ func (m *missionControl) NewPaymentSession(routeHints [][]zpay32.HopHint,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPaymentSessionFromRoutes creates a new paymentSession instance that will
|
// NewPaymentSessionForRoute creates a new paymentSession instance that is just
|
||||||
// skip all path finding, and will instead utilize a set of pre-built routes.
|
// used for failure reporting to missioncontrol.
|
||||||
// This constructor allows callers to specify their own routes which can be
|
func (m *missionControl) NewPaymentSessionForRoute(preBuiltRoute *route.Route) *paymentSession {
|
||||||
// used for things like channel rebalancing, and swaps.
|
|
||||||
func (m *missionControl) NewPaymentSessionFromRoutes(routes []*route.Route) *paymentSession {
|
|
||||||
return &paymentSession{
|
return &paymentSession{
|
||||||
pruneViewSnapshot: m.GraphPruneView(),
|
pruneViewSnapshot: m.GraphPruneView(),
|
||||||
haveRoutes: true,
|
|
||||||
preBuiltRoutes: routes,
|
|
||||||
errFailedPolicyChans: make(map[EdgeLocator]struct{}),
|
errFailedPolicyChans: make(map[EdgeLocator]struct{}),
|
||||||
mc: m,
|
mc: m,
|
||||||
pathFinder: findPath,
|
preBuiltRoute: preBuiltRoute,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-13
@@ -32,8 +32,8 @@ type paymentSession struct {
|
|||||||
|
|
||||||
mc *missionControl
|
mc *missionControl
|
||||||
|
|
||||||
haveRoutes bool
|
preBuiltRoute *route.Route
|
||||||
preBuiltRoutes []*route.Route
|
preBuiltRouteTried bool
|
||||||
|
|
||||||
pathFinder pathFinder
|
pathFinder pathFinder
|
||||||
}
|
}
|
||||||
@@ -115,19 +115,17 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
|||||||
height uint32, finalCltvDelta uint16) (*route.Route, error) {
|
height uint32, finalCltvDelta uint16) (*route.Route, error) {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
// If we have a set of pre-built routes, then we'll just pop off the
|
|
||||||
// next route from the queue, and use it directly.
|
|
||||||
case p.haveRoutes && len(p.preBuiltRoutes) > 0:
|
|
||||||
nextRoute := p.preBuiltRoutes[0]
|
|
||||||
p.preBuiltRoutes[0] = nil // Set to nil to avoid GC leak.
|
|
||||||
p.preBuiltRoutes = p.preBuiltRoutes[1:]
|
|
||||||
|
|
||||||
return nextRoute, nil
|
// If we have a pre-built route, use that directly.
|
||||||
|
case p.preBuiltRoute != nil && !p.preBuiltRouteTried:
|
||||||
|
p.preBuiltRouteTried = true
|
||||||
|
|
||||||
// If we were instantiated with a set of pre-built routes, and we've
|
return p.preBuiltRoute, nil
|
||||||
// run out, then we'll return a terminal error.
|
|
||||||
case p.haveRoutes && len(p.preBuiltRoutes) == 0:
|
// If the pre-built route has been tried already, the payment session is
|
||||||
return nil, fmt.Errorf("pre-built routes exhausted")
|
// over.
|
||||||
|
case p.preBuiltRoute != nil:
|
||||||
|
return nil, fmt.Errorf("pre-built route already tried")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise we actually need to perform path finding, so we'll obtain
|
// Otherwise we actually need to perform path finding, so we'll obtain
|
||||||
|
|||||||
+17
-13
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/multimutex"
|
"github.com/lightningnetwork/lnd/multimutex"
|
||||||
@@ -1523,21 +1524,24 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *route
|
|||||||
return r.sendPayment(payment, paySession)
|
return r.sendPayment(payment, paySession)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendToRoute attempts to send a payment as described within the passed
|
// SendToRoute attempts to send a payment with the given hash through the
|
||||||
// LightningPayment through the provided routes. This function is blocking
|
// provided route. This function is blocking and will return the obtained
|
||||||
// and will return either: when the payment is successful, or all routes
|
// preimage if the payment is successful or the full error in case of a failure.
|
||||||
// have been attempted and resulted in a failed payment. If the payment
|
func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, route *route.Route) (
|
||||||
// succeeds, then a non-nil Route will be returned which describes the
|
lntypes.Preimage, error) {
|
||||||
// path the successful payment traversed within the network to reach the
|
|
||||||
// destination. Additionally, the payment preimage will also be returned.
|
|
||||||
func (r *ChannelRouter) SendToRoute(routes []*route.Route,
|
|
||||||
payment *LightningPayment) ([32]byte, *route.Route, error) {
|
|
||||||
|
|
||||||
paySession := r.missionControl.NewPaymentSessionFromRoutes(
|
// Create a payment session for just this route.
|
||||||
routes,
|
paySession := r.missionControl.NewPaymentSessionForRoute(route)
|
||||||
)
|
|
||||||
|
|
||||||
return r.sendPayment(payment, paySession)
|
// Create a (mostly) dummy payment, as the created payment session is
|
||||||
|
// not going to do path finding.
|
||||||
|
payment := &LightningPayment{
|
||||||
|
PaymentHash: hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
preimage, _, err := r.sendPayment(payment, paySession)
|
||||||
|
|
||||||
|
return preimage, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendPayment attempts to send a payment as described within the passed
|
// sendPayment attempts to send a payment as described within the passed
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"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/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
@@ -407,12 +408,12 @@ func TestChannelUpdateValidation(t *testing.T) {
|
|||||||
|
|
||||||
// The payment parameter is mostly redundant in SendToRoute. Can be left
|
// The payment parameter is mostly redundant in SendToRoute. Can be left
|
||||||
// empty for this test.
|
// empty for this test.
|
||||||
payment := &LightningPayment{}
|
var payment lntypes.Hash
|
||||||
|
|
||||||
// Send off the payment request to the router. The specified route
|
// Send off the payment request to the router. The specified route
|
||||||
// should be attempted and the channel update should be received by
|
// should be attempted and the channel update should be received by
|
||||||
// router and ignored because it is missing a valid signature.
|
// router and ignored because it is missing a valid signature.
|
||||||
_, _, err = ctx.router.SendToRoute([]*route.Route{rt}, payment)
|
_, err = ctx.router.SendToRoute(payment, rt)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected route to fail with channel update")
|
t.Fatalf("expected route to fail with channel update")
|
||||||
}
|
}
|
||||||
@@ -445,7 +446,7 @@ func TestChannelUpdateValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Retry the payment using the same route as before.
|
// Retry the payment using the same route as before.
|
||||||
_, _, err = ctx.router.SendToRoute([]*route.Route{rt}, payment)
|
_, err = ctx.router.SendToRoute(payment, rt)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected route to fail with channel update")
|
t.Fatalf("expected route to fail with channel update")
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-42
@@ -2771,7 +2771,7 @@ type paymentStream struct {
|
|||||||
// lnrpc.SendToRouteRequest can be passed to sendPayment.
|
// lnrpc.SendToRouteRequest can be passed to sendPayment.
|
||||||
type rpcPaymentRequest struct {
|
type rpcPaymentRequest struct {
|
||||||
*lnrpc.SendRequest
|
*lnrpc.SendRequest
|
||||||
routes []*route.Route
|
route *route.Route
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculateFeeLimit returns the fee limit in millisatoshis. If a percentage
|
// calculateFeeLimit returns the fee limit in millisatoshis. If a percentage
|
||||||
@@ -2854,29 +2854,13 @@ func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error
|
|||||||
func unmarshallSendToRouteRequest(req *lnrpc.SendToRouteRequest,
|
func unmarshallSendToRouteRequest(req *lnrpc.SendToRouteRequest,
|
||||||
graph *channeldb.ChannelGraph) (*rpcPaymentRequest, error) {
|
graph *channeldb.ChannelGraph) (*rpcPaymentRequest, error) {
|
||||||
|
|
||||||
switch {
|
if req.Route == nil {
|
||||||
case len(req.Routes) == 0 && req.Route == nil:
|
return nil, fmt.Errorf("unable to send, no route provided")
|
||||||
return nil, fmt.Errorf("unable to send, no routes provided")
|
|
||||||
case len(req.Routes) > 0 && req.Route != nil:
|
|
||||||
return nil, fmt.Errorf("cannot use both route and routes field")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var routes []*route.Route
|
route, err := routerrpc.UnmarshallRoute(req.Route, graph)
|
||||||
if len(req.Routes) > 0 {
|
if err != nil {
|
||||||
routes = make([]*route.Route, len(req.Routes))
|
return nil, err
|
||||||
for i, rpcroute := range req.Routes {
|
|
||||||
route, err := routerrpc.UnmarshallRoute(rpcroute, graph)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routes[i] = route
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rt, err := routerrpc.UnmarshallRoute(req.Route, graph)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routes = []*route.Route{rt}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &rpcPaymentRequest{
|
return &rpcPaymentRequest{
|
||||||
@@ -2884,7 +2868,7 @@ func unmarshallSendToRouteRequest(req *lnrpc.SendToRouteRequest,
|
|||||||
PaymentHash: req.PaymentHash,
|
PaymentHash: req.PaymentHash,
|
||||||
PaymentHashString: req.PaymentHashString,
|
PaymentHashString: req.PaymentHashString,
|
||||||
},
|
},
|
||||||
routes: routes,
|
route: route,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2903,7 +2887,7 @@ type rpcPaymentIntent struct {
|
|||||||
routeHints [][]zpay32.HopHint
|
routeHints [][]zpay32.HopHint
|
||||||
outgoingChannelID *uint64
|
outgoingChannelID *uint64
|
||||||
|
|
||||||
routes []*route.Route
|
route *route.Route
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractPaymentIntent attempts to parse the complete details required to
|
// extractPaymentIntent attempts to parse the complete details required to
|
||||||
@@ -2914,7 +2898,7 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
|
|||||||
payIntent := rpcPaymentIntent{}
|
payIntent := rpcPaymentIntent{}
|
||||||
|
|
||||||
// If a route was specified, then we can use that directly.
|
// If a route was specified, then we can use that directly.
|
||||||
if len(rpcPayReq.routes) != 0 {
|
if rpcPayReq.route != nil {
|
||||||
// If the user is using the REST interface, then they'll be
|
// If the user is using the REST interface, then they'll be
|
||||||
// passing the payment hash as a hex encoded string.
|
// passing the payment hash as a hex encoded string.
|
||||||
if rpcPayReq.PaymentHashString != "" {
|
if rpcPayReq.PaymentHashString != "" {
|
||||||
@@ -2930,7 +2914,7 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
|
|||||||
copy(payIntent.rHash[:], rpcPayReq.PaymentHash)
|
copy(payIntent.rHash[:], rpcPayReq.PaymentHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
payIntent.routes = rpcPayReq.routes
|
payIntent.route = rpcPayReq.route
|
||||||
return payIntent, nil
|
return payIntent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3089,7 +3073,7 @@ func (r *rpcServer) dispatchPaymentIntent(
|
|||||||
|
|
||||||
// If a route was specified, then we'll pass the route directly to the
|
// If a route was specified, then we'll pass the route directly to the
|
||||||
// router, otherwise we'll create a payment session to execute it.
|
// router, otherwise we'll create a payment session to execute it.
|
||||||
if len(payIntent.routes) == 0 {
|
if payIntent.route == nil {
|
||||||
payment := &routing.LightningPayment{
|
payment := &routing.LightningPayment{
|
||||||
Target: payIntent.dest,
|
Target: payIntent.dest,
|
||||||
Amount: payIntent.msat,
|
Amount: payIntent.msat,
|
||||||
@@ -3110,13 +3094,11 @@ func (r *rpcServer) dispatchPaymentIntent(
|
|||||||
payment,
|
payment,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
payment := &routing.LightningPayment{
|
preImage, routerErr = r.server.chanRouter.SendToRoute(
|
||||||
PaymentHash: payIntent.rHash,
|
payIntent.rHash, payIntent.route,
|
||||||
}
|
|
||||||
|
|
||||||
preImage, route, routerErr = r.server.chanRouter.SendToRoute(
|
|
||||||
payIntent.routes, payment,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
route = payIntent.route
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the route failed, then we'll return a nil save err, but a non-nil
|
// If the route failed, then we'll return a nil save err, but a non-nil
|
||||||
@@ -3127,14 +3109,8 @@ func (r *rpcServer) dispatchPaymentIntent(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a route was used to complete this payment, then we'll need to
|
// Calculate amount paid to receiver.
|
||||||
// compute the final amount sent
|
amt := route.TotalAmount - route.TotalFees
|
||||||
var amt lnwire.MilliSatoshi
|
|
||||||
if len(payIntent.routes) > 0 {
|
|
||||||
amt = route.TotalAmount - route.TotalFees
|
|
||||||
} else {
|
|
||||||
amt = payIntent.msat
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the completed payment to the database for record keeping
|
// Save the completed payment to the database for record keeping
|
||||||
// purposes.
|
// purposes.
|
||||||
@@ -3328,7 +3304,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
|
|||||||
func (r *rpcServer) SendToRouteSync(ctx context.Context,
|
func (r *rpcServer) SendToRouteSync(ctx context.Context,
|
||||||
req *lnrpc.SendToRouteRequest) (*lnrpc.SendResponse, error) {
|
req *lnrpc.SendToRouteRequest) (*lnrpc.SendResponse, error) {
|
||||||
|
|
||||||
if len(req.Routes) == 0 {
|
if req.Route == nil {
|
||||||
return nil, fmt.Errorf("unable to send, no routes provided")
|
return nil, fmt.Errorf("unable to send, no routes provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user