Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

routing: add build route functionality #3440

Merged
merged 6 commits into from
Sep 25, 2019

Conversation

joostjager
Copy link
Contributor

@joostjager joostjager commented Aug 30, 2019

This PR adds a new rpc for advanced users that allows them to build a fully specified route from a list of pubkeys. The call looks up the relevant channel policies from the graph and calculates the required fees and time lock values.

Route building is exposed via lncli. Example command to build a two hop route:

lncli buildroute --hops <pubkey1>,<pubkey2> --amt 500

It is also possible to leave out the --amt parameter. In that case, a route is built for the minimum amount that it can carry. This option is useful to execute probe payments for which the primary objective is to pick up black holes (stuck htlc). If the payment gets stuck, the time-locked amount is minimal.

@joostjager joostjager force-pushed the buildroute branch 2 times, most recently from 1158ce8 to 038d33b Compare August 30, 2019 08:55
@wpaulino wpaulino added enhancement Improvements to existing features / behaviour routing rpc Related to the RPC interface v0.8.0 labels Aug 30, 2019
@wpaulino wpaulino added this to the 0.8.0 milestone Aug 30, 2019
lnrpc/routerrpc/router.proto Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
cmd/lncli/routerrpc_active.go Outdated Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
cmd/lncli/commands.go Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
lnrpc/routerrpc/router.proto Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
Copy link
Contributor

@halseth halseth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good to me :)

channeldb/graph.go Outdated Show resolved Hide resolved
lnrpc/routerrpc/router_server.go Outdated Show resolved Hide resolved
lnrpc/routerrpc/router_server.go Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
routing/router.go Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
routing/router.go Show resolved Hide resolved
lnrpc/routerrpc/router_server.go Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
routing/pathfind.go Show resolved Hide resolved
_, err = ctx.router.BuildRoute(
nil, hops, nil, 40,
)
if _, ok := err.(ErrNoChannel); !ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asserts fields of error as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

lnrpc/routerrpc/router.proto Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
routing/router.go Show resolved Hide resolved
@joostjager joostjager force-pushed the buildroute branch 2 times, most recently from 8e8fa7c to 8712e88 Compare September 18, 2019 06:46
@joostjager
Copy link
Contributor Author

Also extended the test so that it also exercises selecting the best channel when there are multiple channels between a pair of nodes.

lnrpc/routerrpc/router.proto Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
routing/router.go Outdated Show resolved Hide resolved
routing/router.go Show resolved Hide resolved
routing/router_test.go Show resolved Hide resolved

// Determine max htlc value.
maxHtlc := lnwire.NewMSatFromSatoshis(capacity)
if policy.MaxHTLC != 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use HasMaxHtlc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed. This is the confusion that I think we shouldn't need to have.

Copy link
Contributor Author

@joostjager joostjager Sep 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, looking in the link, it also checks for MaxHTLC == 0. I left the check identical to what happens in the link.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because the link uses ForwardingPolicy, which doesn't contain information about whether the bit is set or not. I'm in favor of adding the check for the bit being set here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it is indeed a good idea, because this function is also used for non-local channels. Changed

routing/router.go Show resolved Hide resolved
// incompatible channel policies.
//
// There is possibility with pubkey addressing that we should
// have selected a different channel downstream, but we don't
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we fix that? Should this be a TODO, or is there another reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would complicate path finding while we expect this situation to be rare. The spec recommends to keep all policies towards a peer identical. If that is the case, there isn't a better channel that we should have selected.

Added this as a comment

cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
Copy link
Contributor

@halseth halseth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only a few nits and suggestions from me, LGTM 🏆

lnrpc/routerrpc/router.proto Show resolved Hide resolved
// If we have a specific amount for which we are building the route,
// validate it against the channel constraints and return the new
// running amount.
if !canIncreaseAmt {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this had would been less confusing if the param was called findMinAmt or similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I purposely renamed it to canIncreaseAmt because I realized that it is a more generic function here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it work to not pass this bool to the method, and instead let the caller check the returned amount? So if the amount has increased, but we cannot increase it, it will take action.

It should be equivalent, I just find the current branching within the prependChannel a bit confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your suggestion, but I think it may be confusing in another way. The current code has already seen a few earlier iterations. If there isn't a way that very clearly improves the readability, I'd rather leave it as is.

routing/router.go Outdated Show resolved Hide resolved
)
if err != nil {
log.Tracef("Skipping chan %v: %v",
inEdge.ChannelID, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an expected error? If not maybe log as Warn

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it can happen if channels have distinct policies. If all of them do not match, we return a proper error. Warn is definitely too heavy in this situation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just that if this is expected to happen, and it leads us to not be able to build a route, the reason will likely never surface, as it's on trace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trace log is intended for use during development. The problem with reporting the exact reason to the caller is similar to the situation that we have in path finding. There may be multiple reasons. I am afraid that just exposing a random one of them will lead to the same incorrect assumptions that we currently have for path finding.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, even if the call succeeds in building a route, some channels may have been skipped. We can't log it on Warn, because it may really not be a warning.

cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
cmd/lncli/cmd_build_route.go Outdated Show resolved Hide resolved
Copy link
Contributor

@wpaulino wpaulino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 💫

if hasAmt {
amtMsat = ctx.Int64("amt") * 1000
if amtMsat == 0 {
return fmt.Errorf("non-zero amount required")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last nit: do we still need all of this? Seems like AmtMsat can just be ctx.Uint64("amt") * 1000.

@Roasbeef Roasbeef merged commit 18f88cb into lightningnetwork:master Sep 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improvements to existing features / behaviour routing rpc Related to the RPC interface
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants