Skip to content

Commit 6abf4a6

Browse files
committed
route: add custom onion blob to sendtoroute
1 parent 84401f6 commit 6abf4a6

File tree

10 files changed

+97
-39
lines changed

10 files changed

+97
-39
lines changed

cmd/lncli/cmd_payments.go

+11
Original file line numberDiff line numberDiff line change
@@ -975,10 +975,21 @@ func sendToRoute(ctx *cli.Context) error {
975975
route = routes.Route
976976
}
977977

978+
var onionBlob []byte
979+
980+
if ctx.IsSet("onion_blob") {
981+
onionBlob, err = hex.DecodeString(ctx.String("onion_blob"))
982+
983+
if err != nil {
984+
return err
985+
}
986+
}
987+
978988
req := &routerrpc.SendToRouteRequest{
979989
PaymentHash: rHash,
980990
Route: route,
981991
SkipTempErr: ctx.Bool("skip_temp_err"),
992+
OnionBlob: onionBlob,
982993
}
983994

984995
return sendToRouteRequest(ctx, req)

docs/release-notes/release-notes-0.16.0.md

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
that were signed by our wallet. Prior to this change `SignPsbt` didn't
2222
indicate whether the Psbt held any inputs for our wallet to sign.
2323

24+
* Allowing users to parse a [custom onion blob though the
25+
sendtoroute interface](https://github.com/lightningnetwork/lnd/pull/6750).
26+
2427
* [Add list addresses RPC](https://github.com/lightningnetwork/lnd/pull/6596).
2528

2629
* Add [TrackPayments](https://github.com/lightningnetwork/lnd/pull/6335)

lnrpc/routerrpc/router.pb.go

+21-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lnrpc/routerrpc/router.proto

+6
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,12 @@ message SendToRouteRequest {
362362
routes, incorrect payment details, or insufficient funds.
363363
*/
364364
bool skip_temp_err = 3;
365+
366+
/*
367+
Custom onion blob. Limited to 1366 bytes. This will be used instead of the
368+
standard generated sphinx package.
369+
*/
370+
bytes onion_blob = 4;
365371
}
366372

367373
message SendToRouteResponse {

lnrpc/routerrpc/router.swagger.json

+5
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,11 @@
17261726
"skip_temp_err": {
17271727
"type": "boolean",
17281728
"description": "Whether the payment should be marked as failed when a temporary error is\nreturned from the given route. Set it to true so the payment won't be\nfailed unless a terminal error is occurred, such as payment timeout, no\nroutes, incorrect payment details, or insufficient funds."
1729+
},
1730+
"onion_blob": {
1731+
"type": "string",
1732+
"format": "byte",
1733+
"description": "Custom onion blob. Limited to 1366 bytes. This will be used instead of the\nstandard generated sphinx package."
17291734
}
17301735
}
17311736
},

lnrpc/routerrpc/router_server.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,10 @@ func (s *Server) SendToRouteV2(ctx context.Context,
392392
if req.Route == nil {
393393
return nil, fmt.Errorf("unable to send, no routes provided")
394394
}
395+
if len(req.OnionBlob) > lnwire.OnionPacketSize {
396+
return nil, fmt.Errorf("the provided onion blob is to large, "+
397+
"size is limited to %d bytes", lnwire.OnionPacketSize)
398+
}
395399

396400
route, err := s.cfg.RouterBackend.UnmarshallRoute(req.Route)
397401
if err != nil {
@@ -412,9 +416,13 @@ func (s *Server) SendToRouteV2(ctx context.Context,
412416
// case, we give precedence to the attempt information as stored in the
413417
// db.
414418
if req.SkipTempErr {
415-
attempt, err = s.cfg.Router.SendToRouteSkipTempErr(hash, route)
419+
attempt, err = s.cfg.Router.SendToRouteSkipTempErr(
420+
hash, route, req.OnionBlob,
421+
)
416422
} else {
417-
attempt, err = s.cfg.Router.SendToRoute(hash, route)
423+
attempt, err = s.cfg.Router.SendToRoute(
424+
hash, route, req.OnionBlob,
425+
)
418426
}
419427
if attempt != nil {
420428
rpcAttempt, err := s.cfg.RouterBackend.MarshalHTLCAttempt(

routing/payment_lifecycle.go

+23-12
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,9 @@ lifecycle:
296296
lastShard := rt.ReceiverAmt() == currentState.remainingAmt
297297

298298
// We found a route to try, launch a new shard.
299-
attempt, outcome, err := shardHandler.launchShard(rt, lastShard)
299+
attempt, outcome, err := shardHandler.launchShard(
300+
rt, lastShard, nil,
301+
)
300302
switch {
301303
// We may get a terminal error if we've processed a shard with
302304
// a terminal state (settled or permanent failure), while we
@@ -426,12 +428,13 @@ type launchOutcome struct {
426428
// non-nil error, it means that the attempt was not sent onto the network, so
427429
// no result will be available in the future for it.
428430
func (p *shardHandler) launchShard(rt *route.Route,
429-
lastShard bool) (*channeldb.HTLCAttemptInfo, *launchOutcome, error) {
431+
lastShard bool, customOnionBlob []byte) (*channeldb.HTLCAttemptInfo,
432+
*launchOutcome, error) {
430433

431434
// Using the route received from the payment session, create a new
432435
// shard to send.
433436
firstHop, htlcAdd, attempt, err := p.createNewPaymentAttempt(
434-
rt, lastShard,
437+
rt, lastShard, customOnionBlob,
435438
)
436439
if err != nil {
437440
return nil, nil, err
@@ -658,7 +661,8 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
658661
}
659662

660663
// createNewPaymentAttempt creates a new payment attempt from the given route.
661-
func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool) (
664+
func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool,
665+
customOninonBlob []byte) (
662666
lnwire.ShortChannelID, *lnwire.UpdateAddHTLC,
663667
*channeldb.HTLCAttemptInfo, error) {
664668

@@ -696,14 +700,7 @@ func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool)
696700
hop.AMP = shard.AMP()
697701
}
698702

699-
// Generate the raw encoded sphinx packet to be included along
700-
// with the htlcAdd message that we send directly to the
701-
// switch.
702703
hash := shard.Hash()
703-
onionBlob, _, err := generateSphinxPacket(rt, hash[:], sessionKey)
704-
if err != nil {
705-
return lnwire.ShortChannelID{}, nil, nil, err
706-
}
707704

708705
// Craft an HTLC packet to send to the layer 2 switch. The
709706
// metadata within this packet will be used to route the
@@ -713,7 +710,21 @@ func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool)
713710
Expiry: rt.TotalTimeLock,
714711
PaymentHash: hash,
715712
}
716-
copy(htlcAdd.OnionBlob[:], onionBlob)
713+
714+
// Use the custom onion blob if it is provided. Otherwise, we will
715+
// generate the raw encoded sphinx packet to be included along
716+
// with the htlcAdd message that we send directly to the
717+
// switch.
718+
if customOninonBlob != nil {
719+
copy(htlcAdd.OnionBlob[:], customOninonBlob)
720+
} else {
721+
onionBlob, _, err := generateSphinxPacket(rt, hash[:],
722+
sessionKey)
723+
if err != nil {
724+
return lnwire.ShortChannelID{}, nil, nil, err
725+
}
726+
copy(htlcAdd.OnionBlob[:], onionBlob)
727+
}
717728

718729
// Attempt to send this payment through the network to complete
719730
// the payment. If this attempt fails, then we'll continue on

routing/router.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -2157,18 +2157,19 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) (
21572157

21582158
// SendToRoute sends a payment using the provided route and fails the payment
21592159
// when an error is returned from the attempt.
2160-
func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash,
2161-
rt *route.Route) (*channeldb.HTLCAttempt, error) {
2160+
func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, rt *route.Route,
2161+
customOnionBlob []byte) (*channeldb.HTLCAttempt, error) {
21622162

2163-
return r.sendToRoute(htlcHash, rt, false)
2163+
return r.sendToRoute(htlcHash, rt, false, customOnionBlob)
21642164
}
21652165

21662166
// SendToRouteSkipTempErr sends a payment using the provided route and fails
21672167
// the payment ONLY when a terminal error is returned from the attempt.
21682168
func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash,
2169-
rt *route.Route) (*channeldb.HTLCAttempt, error) {
2169+
rt *route.Route, customOnionBlob []byte) (*channeldb.HTLCAttempt,
2170+
error) {
21702171

2171-
return r.sendToRoute(htlcHash, rt, true)
2172+
return r.sendToRoute(htlcHash, rt, true, customOnionBlob)
21722173
}
21732174

21742175
// sendToRoute attempts to send a payment with the given hash through the
@@ -2178,7 +2179,8 @@ func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash,
21782179
// was initiated, both return values will be non-nil. If skipTempErr is true,
21792180
// the payment won't be failed unless a terminal error has occurred.
21802181
func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
2181-
skipTempErr bool) (*channeldb.HTLCAttempt, error) {
2182+
skipTempErr bool, customOnionBlob []byte) (*channeldb.HTLCAttempt,
2183+
error) {
21822184

21832185
// Calculate amount paid to receiver.
21842186
amt := rt.ReceiverAmt()
@@ -2243,7 +2245,7 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
22432245
}
22442246

22452247
var shardError error
2246-
attempt, outcome, err := sh.launchShard(rt, false)
2248+
attempt, outcome, err := sh.launchShard(rt, false, customOnionBlob)
22472249

22482250
// With SendToRoute, it can happen that the route exceeds protocol
22492251
// constraints. Mark the payment as failed with an internal error.

routing/router_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ func TestChannelUpdateValidation(t *testing.T) {
461461
// Send off the payment request to the router. The specified route
462462
// should be attempted and the channel update should be received by
463463
// router and ignored because it is missing a valid signature.
464-
_, err = ctx.router.SendToRoute(payment, rt)
464+
_, err = ctx.router.SendToRoute(payment, rt, nil)
465465
require.Error(t, err, "expected route to fail with channel update")
466466

467467
_, e1, e2, err = ctx.router.GetChannelByID(
@@ -478,7 +478,7 @@ func TestChannelUpdateValidation(t *testing.T) {
478478
signErrChanUpdate(t, testGraph.privKeyMap["b"], &errChanUpdate)
479479

480480
// Retry the payment using the same route as before.
481-
_, err = ctx.router.SendToRoute(payment, rt)
481+
_, err = ctx.router.SendToRoute(payment, rt, nil)
482482
require.Error(t, err, "expected route to fail with channel update")
483483

484484
// This time a valid signature was supplied and the policy change should
@@ -2870,7 +2870,7 @@ func TestSendToRouteStructuredError(t *testing.T) {
28702870
// update should be received by router and ignored
28712871
// because it is missing a valid
28722872
// signature.
2873-
_, err = ctx.router.SendToRoute(payment, rt)
2873+
_, err = ctx.router.SendToRoute(payment, rt, nil)
28742874

28752875
fErr, ok := err.(*htlcswitch.ForwardingError)
28762876
require.True(
@@ -2951,7 +2951,7 @@ func TestSendToRouteMaxHops(t *testing.T) {
29512951
// Send off the payment request to the router. We expect an error back
29522952
// indicating that the route is too long.
29532953
var payment lntypes.Hash
2954-
_, err = ctx.router.SendToRoute(payment, rt)
2954+
_, err = ctx.router.SendToRoute(payment, rt, nil)
29552955
if err != route.ErrMaxRouteHopsExceeded {
29562956
t.Fatalf("expected ErrMaxRouteHopsExceeded, but got %v", err)
29572957
}
@@ -4197,7 +4197,7 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) {
41974197
).Return(nil)
41984198

41994199
// Expect a successful send to route.
4200-
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
4200+
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
42014201
require.NoError(t, err)
42024202
require.Equal(t, testAttempt, attempt)
42034203

@@ -4283,7 +4283,7 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) {
42834283
).Return(nil, nil)
42844284

42854285
// Expect a failed send to route.
4286-
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
4286+
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
42874287
require.Equal(t, tempErr, err)
42884288
require.Equal(t, testAttempt, attempt)
42894289

@@ -4372,7 +4372,7 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) {
43724372
).Return(&failureReason, nil)
43734373

43744374
// Expect a failed send to route.
4375-
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
4375+
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
43764376
require.Equal(t, permErr, err)
43774377
require.Equal(t, testAttempt, attempt)
43784378

@@ -4461,7 +4461,7 @@ func TestSendToRouteTempFailure(t *testing.T) {
44614461
).Return(nil, nil)
44624462

44634463
// Expect a failed send to route.
4464-
attempt, err := router.SendToRoute(payHash, rt)
4464+
attempt, err := router.SendToRoute(payHash, rt, nil)
44654465
require.Equal(t, tempErr, err)
44664466
require.Equal(t, testAttempt, attempt)
44674467

rpcserver.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -5057,7 +5057,7 @@ func (r *rpcServer) dispatchPaymentIntent(
50575057
} else {
50585058
var attempt *channeldb.HTLCAttempt
50595059
attempt, routerErr = r.server.chanRouter.SendToRoute(
5060-
payIntent.rHash, payIntent.route,
5060+
payIntent.rHash, payIntent.route, nil,
50615061
)
50625062

50635063
if routerErr == nil {

0 commit comments

Comments
 (0)