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

lnrpc: Add UpdateNodeAnnouncement endpoint #5587

Merged

Conversation

positiveblue
Copy link
Contributor

@positiveblue positiveblue commented Jul 29, 2021

Allow users to change nodeAnnouncement values such Color, Alias, Features or
Addresses and broadcast the update to its peers without having to
restart the node.

Fixes #5466

Copy link
Contributor Author

@positiveblue positiveblue left a comment

Choose a reason for hiding this comment

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

Also, should I add release notes to 0.13.x or to 0.14.x?

@@ -3711,6 +3718,22 @@ message ListPermissionsResponse {
map<string, MacaroonPermissionList> method_permissions = 1;
}

message NodeAnnouncementUpdateRequest {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

features and addresses have the problem that they are sets. I do not have the full context about what kind of features are out there but we need to decided if the request will directly substitute the current features with the ones in the request or if we need a mechanism to perform join/add/subtract of single features

Copy link
Member

Choose a reason for hiding this comment

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

we need to decided if the request will directly substitute the current features with the ones in the request or if we need a mechanism to perform join/add/subtract of single features

Yeah this is what I was hinting at re knowing which fields are actually being updated. Seems we could try to handle the two (?) cases explicitly. So in one mode, it's a wholesale replacement of w/e feature bits we already have advertised. In the other case, a user can specify that it wants a particular bit to be added or removed.

One possible way to do this would be to have something like an "action" defined for each entry, so something like:

enum FeatureBitAction {
    ADD = 0; 
    REMOVE = 1; 
}

map<FeatureBit, FeatureBitActiion> FeatureBitUpdate = 1;

This way we don't force the caller to fully specify all the existing feature bits, instead they say if they want a bit to be removed or added.

string alias = 3;

// Addresses is a slice of all the node's known addresses.
repeated NodeAddress addresses = 4;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similar problem that the one with features. Should we simply substitute the current node addresses with the ones that are provided in the request or we need to keep the ones in the conf file for example.
Also, should we use the default port for those addresses that are only the IP, or we should not support not providing it

Copy link
Member

Choose a reason for hiding this comment

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

I think the same "action" scheme I outlined above could work here, so turning it into a map of actions.

// Addresses is a slice of all the node's known addresses.
repeated NodeAddress addresses = 4;
}
message NodeAnnouncementUpdateResponse {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if it is worth to return all the data that will be broadcasted to other nodes

Copy link
Member

Choose a reason for hiding this comment

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

Could be useful to just send what was actually updated? So like if you try to add a new address, but it's already advertised, then you get back an empty response telling you it was effectively a noop.

Related to this response handling, is that we likely want to return a gRPC status/error code, to indicate if a user tries to do something like a set a feature bit that we actually don't understand. On the other-hand, we could allow such behavior as it may be useful when paired with something like: #5346

lnrpc/lightning.yaml Outdated Show resolved Hide resolved
t.Fatalf("faild to validate an invalid alias for an update node announcement request: %v", err)
}

// TODO(positiveblue): Add checks for features and addresses
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is still not validation for these two parameters in the server implementation

Copy link
Member

Choose a reason for hiding this comment

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

So for feature here, we'll need to make a decision (for the validation logic), if we want to allow them to advertise feature bits unknown to us or not.

For addresses, as is we rely on net ResolveTCPAddr (or the onion counter part) to make sure they're actually a valid address.

@positiveblue positiveblue force-pushed the 5466-update-node-announcement-data branch 3 times, most recently from 41decb4 to 6aa0dc4 Compare July 29, 2021 20:28
@Roasbeef
Copy link
Member

Also, should I add release notes to 0.13.x or to 0.14.x?

0.14.1!

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

Solid first time contribution! Did an initial pass over the diff, and I think the main thing that needs to be sorted out is how we handle partial updates to the set of advertised feature bits and/or addresses. See my comments inline for some suggestions.

lnrpc/lightning.proto Outdated Show resolved Hide resolved
lnrpc/lightning.proto Outdated Show resolved Hide resolved
@@ -3711,6 +3718,22 @@ message ListPermissionsResponse {
map<string, MacaroonPermissionList> method_permissions = 1;
}

message NodeAnnouncementUpdateRequest {
Copy link
Member

Choose a reason for hiding this comment

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

we need to decided if the request will directly substitute the current features with the ones in the request or if we need a mechanism to perform join/add/subtract of single features

Yeah this is what I was hinting at re knowing which fields are actually being updated. Seems we could try to handle the two (?) cases explicitly. So in one mode, it's a wholesale replacement of w/e feature bits we already have advertised. In the other case, a user can specify that it wants a particular bit to be added or removed.

One possible way to do this would be to have something like an "action" defined for each entry, so something like:

enum FeatureBitAction {
    ADD = 0; 
    REMOVE = 1; 
}

map<FeatureBit, FeatureBitActiion> FeatureBitUpdate = 1;

This way we don't force the caller to fully specify all the existing feature bits, instead they say if they want a bit to be removed or added.

string alias = 3;

// Addresses is a slice of all the node's known addresses.
repeated NodeAddress addresses = 4;
Copy link
Member

Choose a reason for hiding this comment

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

I think the same "action" scheme I outlined above could work here, so turning it into a map of actions.

// Addresses is a slice of all the node's known addresses.
repeated NodeAddress addresses = 4;
}
message NodeAnnouncementUpdateResponse {
Copy link
Member

Choose a reason for hiding this comment

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

Could be useful to just send what was actually updated? So like if you try to add a new address, but it's already advertised, then you get back an empty response telling you it was effectively a noop.

Related to this response handling, is that we likely want to return a gRPC status/error code, to indicate if a user tries to do something like a set a feature bit that we actually don't understand. On the other-hand, we could allow such behavior as it may be useful when paired with something like: #5346

rpcserver.go Outdated Show resolved Hide resolved
rpcserver.go Outdated Show resolved Hide resolved
rpcserver.go Outdated Show resolved Hide resolved
t.Fatalf("faild to validate an invalid alias for an update node announcement request: %v", err)
}

// TODO(positiveblue): Add checks for features and addresses
Copy link
Member

Choose a reason for hiding this comment

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

So for feature here, we'll need to make a decision (for the validation logic), if we want to allow them to advertise feature bits unknown to us or not.

For addresses, as is we rely on net ResolveTCPAddr (or the onion counter part) to make sure they're actually a valid address.

lntest/itest/lnd_channel_graph_test.go Outdated Show resolved Hide resolved
@positiveblue
Copy link
Contributor Author

Thank you so much for the feedback @Roasbeef I think it is enough for the next iteration, I will ping you after applying the changes

Copy link
Collaborator

@guggero guggero left a comment

Choose a reason for hiding this comment

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

Gave this PR a quick first-pass review, mostly to answer some of your questions.
Nice work so far, definitely looks like you're on the right track. Looking forward to the next iteration.

lnrpc/lightning.proto Outdated Show resolved Hide resolved
lnrpc/lightning.yaml Outdated Show resolved Hide resolved
rpcserver.go Outdated Show resolved Hide resolved
@positiveblue positiveblue force-pushed the 5466-update-node-announcement-data branch from 6aa0dc4 to cf1e095 Compare August 9, 2021 05:07
@positiveblue positiveblue requested a review from Roasbeef August 9, 2021 05:23
@positiveblue positiveblue force-pushed the 5466-update-node-announcement-data branch from cf1e095 to fca3156 Compare August 9, 2021 05:29
docs/release-notes/release-notes-0.14.0.md Outdated Show resolved Hide resolved
rpcserver.go Outdated
ops := &lnrpc.Op{Entity: "features"}

fsets := make(map[feature.Set]*lnwire.RawFeatureVector)
sets := []feature.Set{feature.SetNodeAnn}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Roasbeef should the endpoint allow the user to update ANY feature set? (I think yes... but I am not sure)
In that case, we should include it in the proto request (set-bit-action)

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think any feature, other than features we have set by default (existing in the feature deps set).

rpcserver.go Outdated
// Ensure that all of our feature sets properly set any
// dependent features.
fv := lnwire.NewFeatureVector(raw, lnwire.Features)
err := feature.ValidateDeps(fv)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we need to validate anything else?

Copy link
Member

Choose a reason for hiding this comment

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

See above comment, this ensures the features are well defined, but can possibly allow a user to over write a feature we set (like anchor channels), which we want to prevent IMO.

rpcserver.go Outdated
removeAddr := make([]net.Addr, 0, len(updates))
addAddr := make([]net.Addr, 0, len(updates))
for _, update := range updates {
addr, err := net.ResolveTCPAddr(update.Address.GetNetwork(), update.Address.GetAddr())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

How should I make this work with tor? Should I instead use parseAddr using the node configuration?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, using parseAddr will ensure that if Tor is active, the DNS request is tunneled over that.

rpcserver.go Outdated Show resolved Hide resolved
rpcserver.go Outdated
LastUpdate: time.Unix(int64(newNodeAnn.Timestamp), 0),
Addresses: newNodeAnn.Addresses,
Alias: newNodeAnn.Alias.String(),
Features: lnwire.NewFeatureVector(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is what causes me a bit of discomfort, the endpoint will broadcast the node announcement changes but it can also be used for updating other feature sets?

Copy link
Member

Choose a reason for hiding this comment

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

What do you mean other feature sets? You mean things that we don't actually understand?

@Roasbeef Roasbeef added the P3 might get fixed, nice to have label Aug 31, 2021
@Roasbeef Roasbeef added this to the v0.14.0 milestone Aug 31, 2021
rpcserver.go Outdated
}

if len(nodeModifiers) == 0 {
return nil, fmt.Errorf("unable detect any new values to update " +
Copy link
Collaborator

Choose a reason for hiding this comment

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

When only updating features, UpdateNodeAnnouncement returns an error (but still updates the featurebit) .

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for noticing @sputn1ck

Actually this can be tricky because even if when we have updated some parts of the node it is not until the end of the function that we update the on-disk version of our announcement so other sub-systems don't become out of sync + announce those changes to our peers.

I will return resp instead of nil so the user always have track of what operations were performed. Even so, if some operations succeed but the endpoint fails before completing the full flow the user will have to call it again until it succeeds to ensure that the copy on disk + our peers have the correct node announcement version

@positiveblue positiveblue mentioned this pull request Oct 13, 2021
@Roasbeef Roasbeef modified the milestones: v0.14.0, v0.15.0 Oct 19, 2021
@positiveblue positiveblue force-pushed the 5466-update-node-announcement-data branch 8 times, most recently from 13d3537 to 2b4bafb Compare January 8, 2022 20:30
@positiveblue positiveblue force-pushed the 5466-update-node-announcement-data branch from c334ca8 to fd32aa8 Compare April 2, 2022 01:13
New endpoint for the `Peers` subrpc.
@positiveblue positiveblue force-pushed the 5466-update-node-announcement-data branch from fd32aa8 to 2ada770 Compare April 2, 2022 01:27
@positiveblue positiveblue requested a review from Roasbeef April 2, 2022 01:28
@positiveblue
Copy link
Contributor Author

@Roasbeef I added the new command for lndcli. For the macaroon, we had some discussion about not having it's own. I think this is ready to go 🚀

What new uses for the sub-server do you have in mind 👀?

Basic logic for the endpoint:

- Get the current nodeAnn information
- Calculate modifications
- Apply modifications
- Return changes
@positiveblue positiveblue force-pushed the 5466-update-node-announcement-data branch 4 times, most recently from 8c9f798 to 78a1a93 Compare April 3, 2022 01:47
@positiveblue positiveblue force-pushed the 5466-update-node-announcement-data branch from 78a1a93 to fdfdd10 Compare April 3, 2022 02:11
@lightninglabs-deploy
Copy link

@Roasbeef: review reminder
@positiveblue, remember to re-request review from reviewers when ready

@Roasbeef
Copy link
Member

Roasbeef commented Apr 12, 2022

What new uses for the sub-server do you have in mind 👀?

See this comment: #6262 (comment). More generically, this sub-server lets us move towards an architectures where a main lnd node doesn't actually have a gossiper included and instead uses this API to push out updates and either obtain updates for the graph, or have those updates applied to a read-only mirror it maintains.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

LGTM 🌾

@Roasbeef Roasbeef merged commit 2fd900b into lightningnetwork:master Apr 12, 2022
@joostjager
Copy link
Contributor

@positiveblue I noticed that changing an alias using UpdateNodeAnnouncement doesn't survive a restart. The node will revert to the configured alias again. Is that intentional?

@Roasbeef
Copy link
Member

The node will revert to the configured alias again. Is that intentional?

As is yeah, and imo that's the correct behavior, as arguably a user shouldn't set the config level param if they want to have it be updated dynamically. There also isn't a great way to distinguish between a particular field of the node announcement that should ignore the static config values.

@joostjager
Copy link
Contributor

If you don't set the config level parameters, the dynamically set alias is still forgotten after a restart. The node will revert to a truncated pubkey as the alias.

How can one use this api in a way that no default alias or other node announcement fields can leak during restarts?

@Roasbeef
Copy link
Member

Roasbeef commented Nov 2, 2022

Ah yeah I think that's an oversight, we should correct that. The gap here is that we didn't add a test case for node restart.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P3 might get fixed, nice to have
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request]: Allow updating node announcement data without restarting the node
8 participants