Skip to content

Commit

Permalink
route: add custom onion blob to sendtoroute
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikEk committed Oct 31, 2022
1 parent 84401f6 commit 6abf4a6
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 39 deletions.
11 changes: 11 additions & 0 deletions cmd/lncli/cmd_payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -975,10 +975,21 @@ func sendToRoute(ctx *cli.Context) error {
route = routes.Route
}

var onionBlob []byte

if ctx.IsSet("onion_blob") {
onionBlob, err = hex.DecodeString(ctx.String("onion_blob"))

if err != nil {
return err
}
}

req := &routerrpc.SendToRouteRequest{
PaymentHash: rHash,
Route: route,
SkipTempErr: ctx.Bool("skip_temp_err"),
OnionBlob: onionBlob,
}

return sendToRouteRequest(ctx, req)
Expand Down
3 changes: 3 additions & 0 deletions docs/release-notes/release-notes-0.16.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
that were signed by our wallet. Prior to this change `SignPsbt` didn't
indicate whether the Psbt held any inputs for our wallet to sign.

* Allowing users to parse a [custom onion blob though the
sendtoroute interface](https://github.com/lightningnetwork/lnd/pull/6750).

* [Add list addresses RPC](https://github.com/lightningnetwork/lnd/pull/6596).

* Add [TrackPayments](https://github.com/lightningnetwork/lnd/pull/6335)
Expand Down
30 changes: 21 additions & 9 deletions lnrpc/routerrpc/router.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions lnrpc/routerrpc/router.proto
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,12 @@ message SendToRouteRequest {
routes, incorrect payment details, or insufficient funds.
*/
bool skip_temp_err = 3;

/*
Custom onion blob. Limited to 1366 bytes. This will be used instead of the
standard generated sphinx package.
*/
bytes onion_blob = 4;
}

message SendToRouteResponse {
Expand Down
5 changes: 5 additions & 0 deletions lnrpc/routerrpc/router.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1726,6 +1726,11 @@
"skip_temp_err": {
"type": "boolean",
"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."
},
"onion_blob": {
"type": "string",
"format": "byte",
"description": "Custom onion blob. Limited to 1366 bytes. This will be used instead of the\nstandard generated sphinx package."
}
}
},
Expand Down
12 changes: 10 additions & 2 deletions lnrpc/routerrpc/router_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,10 @@ func (s *Server) SendToRouteV2(ctx context.Context,
if req.Route == nil {
return nil, fmt.Errorf("unable to send, no routes provided")
}
if len(req.OnionBlob) > lnwire.OnionPacketSize {
return nil, fmt.Errorf("the provided onion blob is to large, "+
"size is limited to %d bytes", lnwire.OnionPacketSize)
}

route, err := s.cfg.RouterBackend.UnmarshallRoute(req.Route)
if err != nil {
Expand All @@ -412,9 +416,13 @@ func (s *Server) SendToRouteV2(ctx context.Context,
// case, we give precedence to the attempt information as stored in the
// db.
if req.SkipTempErr {
attempt, err = s.cfg.Router.SendToRouteSkipTempErr(hash, route)
attempt, err = s.cfg.Router.SendToRouteSkipTempErr(
hash, route, req.OnionBlob,
)
} else {
attempt, err = s.cfg.Router.SendToRoute(hash, route)
attempt, err = s.cfg.Router.SendToRoute(
hash, route, req.OnionBlob,
)
}
if attempt != nil {
rpcAttempt, err := s.cfg.RouterBackend.MarshalHTLCAttempt(
Expand Down
35 changes: 23 additions & 12 deletions routing/payment_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,9 @@ lifecycle:
lastShard := rt.ReceiverAmt() == currentState.remainingAmt

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

// Using the route received from the payment session, create a new
// shard to send.
firstHop, htlcAdd, attempt, err := p.createNewPaymentAttempt(
rt, lastShard,
rt, lastShard, customOnionBlob,
)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -658,7 +661,8 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
}

// createNewPaymentAttempt creates a new payment attempt from the given route.
func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool) (
func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool,
customOninonBlob []byte) (
lnwire.ShortChannelID, *lnwire.UpdateAddHTLC,
*channeldb.HTLCAttemptInfo, error) {

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

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

// Craft an HTLC packet to send to the layer 2 switch. The
// metadata within this packet will be used to route the
Expand All @@ -713,7 +710,21 @@ func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool)
Expiry: rt.TotalTimeLock,
PaymentHash: hash,
}
copy(htlcAdd.OnionBlob[:], onionBlob)

// Use the custom onion blob if it is provided. Otherwise, we will
// generate the raw encoded sphinx packet to be included along
// with the htlcAdd message that we send directly to the
// switch.
if customOninonBlob != nil {
copy(htlcAdd.OnionBlob[:], customOninonBlob)
} else {
onionBlob, _, err := generateSphinxPacket(rt, hash[:],
sessionKey)
if err != nil {
return lnwire.ShortChannelID{}, nil, nil, err
}
copy(htlcAdd.OnionBlob[:], onionBlob)
}

// Attempt to send this payment through the network to complete
// the payment. If this attempt fails, then we'll continue on
Expand Down
16 changes: 9 additions & 7 deletions routing/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -2157,18 +2157,19 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) (

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

return r.sendToRoute(htlcHash, rt, false)
return r.sendToRoute(htlcHash, rt, false, customOnionBlob)
}

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

return r.sendToRoute(htlcHash, rt, true)
return r.sendToRoute(htlcHash, rt, true, customOnionBlob)
}

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

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

var shardError error
attempt, outcome, err := sh.launchShard(rt, false)
attempt, outcome, err := sh.launchShard(rt, false, customOnionBlob)

// With SendToRoute, it can happen that the route exceeds protocol
// constraints. Mark the payment as failed with an internal error.
Expand Down
16 changes: 8 additions & 8 deletions routing/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ func TestChannelUpdateValidation(t *testing.T) {
// Send off the payment request to the router. The specified route
// should be attempted and the channel update should be received by
// router and ignored because it is missing a valid signature.
_, err = ctx.router.SendToRoute(payment, rt)
_, err = ctx.router.SendToRoute(payment, rt, nil)
require.Error(t, err, "expected route to fail with channel update")

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

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

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

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

// Expect a successful send to route.
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
require.NoError(t, err)
require.Equal(t, testAttempt, attempt)

Expand Down Expand Up @@ -4283,7 +4283,7 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) {
).Return(nil, nil)

// Expect a failed send to route.
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
require.Equal(t, tempErr, err)
require.Equal(t, testAttempt, attempt)

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

// Expect a failed send to route.
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
require.Equal(t, permErr, err)
require.Equal(t, testAttempt, attempt)

Expand Down Expand Up @@ -4461,7 +4461,7 @@ func TestSendToRouteTempFailure(t *testing.T) {
).Return(nil, nil)

// Expect a failed send to route.
attempt, err := router.SendToRoute(payHash, rt)
attempt, err := router.SendToRoute(payHash, rt, nil)
require.Equal(t, tempErr, err)
require.Equal(t, testAttempt, attempt)

Expand Down
2 changes: 1 addition & 1 deletion rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5057,7 +5057,7 @@ func (r *rpcServer) dispatchPaymentIntent(
} else {
var attempt *channeldb.HTLCAttempt
attempt, routerErr = r.server.chanRouter.SendToRoute(
payIntent.rHash, payIntent.route,
payIntent.rHash, payIntent.route, nil,
)

if routerErr == nil {
Expand Down

0 comments on commit 6abf4a6

Please sign in to comment.