Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
Already on GitHub? Sign in to your account
[net] Allow disconnectnode RPC to be called with node id #10143
Conversation
|
Concept ACK. Though if we're going to be exposing this value to our apis, we need to define it sanely first. ATM it's just an int, so it's tricky encode/decode it safely. I'd prefer change it to a uint32_t first, so that we can use ParseUInt32 here. |
|
Concept ACK, I've wanted this many times before... but I'm concerned about the nodeid--- right now if it wraps bad things happen, and it will be harder to fix if we've made it a part of the API. |
|
edit: Decided I didn't like this thought and bailed. Meant to cancel, closed instead. Sorry! |
theuni
closed this
Apr 3, 2017
theuni
reopened this
Apr 3, 2017
|
I suppose it would be prudent to
The last one is what happens all over the place, and is fine as long as we ensure that the values really are unique. To do so, we can either:
Obviously the first is cleaner, and would probably end up being simpler too. |
fanquake
added
the
RPC/REST/ZMQ
label
Apr 3, 2017
|
See theuni/bitcoin@57ea0cc for the wrapping/defining changes. Still need to verify the first two points above, though. @jnewbery If you don't object, I'll go ahead and PR that as a prerequisite for this one. |
| - bool ret = g_connman->DisconnectNode(request.params[0].get_str()); | ||
| + std::string node = (request.params[0].get_str()); | ||
| + bool ret; | ||
| + if (std::all_of(node.begin(), node.end(), ::isdigit)) { |
laanwj
Apr 4, 2017
Owner
Concept ACK, but please make this API explicit.
-
APIs that switch based on magic heuristics on how a value 'looks'. Initially this seems user friendly, but it will quickly grow to maintainable, insecure monsters of obscure rules. It should be made completely explicit.
-
Also: do not encode integers as strings and use functions like std::stoi. Just use .get_int() if you need an int value and leave the encoding of values up to the RPC mechanism.
The most straightforward way would be to just add a function disconnectnodebyid.
jnewbery
Apr 4, 2017
Member
Yes, I agree that APIs shouldn't be magic. I thought it wouldn't be too problematic here because there can't be any ambiguity at all between nodeIds and IP addresses (and disconnectnode() is already a bit magic - it can take either IPv4 or IPv6 addresses).
I don't like the proliferation of additional RPCs that perform a very small function if we can avoid it.
How about we add a new argument to this RPC called "nodeId"? The RPC can be called with strictly one of the arguments. There's no magic or ambiguity, and the node disconnection logic is contained within one RPC.
laanwj
Apr 5, 2017
•
Owner
How about we add a new argument to this RPC called "nodeId"? The RPC can be called with strictly one of the arguments. There's no magic or ambiguity, and the node disconnection logic is contained within one RPC.
Sounds reasonable to me. To be clear: anything that doesn't encode the integer as a string is more reasonable to me than this (another reason: because it allows passing the 'id' value literally without having to string-encode it in the client). I don't have a strong opinion whether it should be one or multiple RPCs.
This value is already exposed in APIs since |
|
Found myself wanting this for disconnect after reading debug.log many times, annoying to have to getpeerinfo for it. |
|
@theuni - of course, no objection at all! |
Well you can write an IPv4 address as a decimal integer... I guess we're excluding that (very uncommon) usage? |
+1. Exactly. This is exactly why these things get ugly. There's always overlaps in representations that you may not be thinking about in the initial design, then get realized later. Let's just take the sane route here and make the ambiguity impossible. |
Yep, fine. |
|
ok, another idea. I think it's reasonable that people might want to update node by id in other ways (ie switching whitelisting on and off, changing ban score, etc). How would you feel about an updatenode RPC to do those things? To begin with it will allow disconnecting and turning whitelisting on/off, and it can be added to later if people think further functionality is useful. I already have a branch with an updatenode RPC here: jnewbery/bitcoin@5bbe24f . Adding a 'disconnect' argument to it is trivial. We should keep this RPC hidden in order to not commit ourselves to a public API that we might want to change later (eg if we break out whitelisting into more granular behaviour at some point). Thoughts? |
|
I've added an |
|
I don't think 'updatepeer X disconnect' is clearer than 'disconnectnode'. Having the RPC call as the verb is easier to use and remember. We have an example of a very bad multiplexed call in the API: |
|
Yes, I'll reimplement this as an additional argument to disconnectnode. Longer term, my thinking around updatepeer is that it'd be handy as a swiss army knife for peer interop. We may wish to disconnect/ban/whitelist/change other attributes/etc for peers, and having a single RPC that does all of that would be a sensible API, and certainly better than a different RPC for each attribute. On the other hand, I do agree that 'disconnect' is a verb, so naturally |
|
I've pushed a version where disconnectnode now takes two arguments: address and nodeid. Strictly one argument must be given. (note: with named arguments, you can just supply a single argument, with positional arguments you need to set 'address' to the empty string if you want to use 'nodeid'). I changed the name of the existing argument from 'node' to 'address', since it only takes address. I think that's the right thing to do since the current name is misleading, but we'll need release notes to document the API change. This should only be merged after @theuni's branch here: theuni/bitcoin@57ea0cc. |
| @@ -234,12 +234,15 @@ UniValue addnode(const JSONRPCRequest& request) | ||
| UniValue disconnectnode(const JSONRPCRequest& request) | ||
| { | ||
| - if (request.fHelp || request.params.size() != 1) | ||
| + if (request.fHelp || request.params.size() >= 3) | ||
| throw std::runtime_error( | ||
| "disconnectnode \"node\" \n" |
jnewbery
Apr 10, 2017
Member
Thanks. Help text now fixed. I'll squash the commit when this is ready for merge.
|
While you can write an IP as a decimal number, you can't specify an IP plus port in such a manner. Makes perfect sense to say String = IP and Number = nodeid IMO. Don't care too strongly if the current (two arguments) approach is used, but I wouldn't call it sane. :p |
|
@luke-jr thanks for the review. I agree that the two arguments approach looks a bit odd when used as positional arguments, but it makes perfect sense when using named arguments: call the RPC with address= if you want to disconnect by address, or call with nodeid= if you want to disconnect by nodeid. The fact that it works at all with positional arguments is more a historical quirk of bitcoin's RPC rather than a feature.I think any of the three approaches I've implemented are fine (overloading one argument, having a second argument or having a separate 'update peer' RPC). I really just want the functionality somewhere, and I'm happy to go along with the consensus view for where the best place to put it is. |
theuni
referenced this pull request
Apr 10, 2017
Merged
net: gracefully handle NodeId wrapping #10176
|
(aside, the addnode thing isn't that nuts if you thing of it as a shortening of "addednodelist ", which is what it actually is :) ) |
Yes, it makes sense if you're a scholar of Bitcoin Core code archaeology :) It'd be nice if |
jnewbery
referenced this pull request
Apr 12, 2017
Merged
[tests] Remove is_network_split from functional test framework #10198
| @@ -607,7 +629,7 @@ static const CRPCCommand commands[] = | ||
| { "network", "ping", &ping, true, {} }, | ||
| { "network", "getpeerinfo", &getpeerinfo, true, {} }, | ||
| { "network", "addnode", &addnode, true, {"node","command"} }, | ||
| - { "network", "disconnectnode", &disconnectnode, true, {"node"} }, | ||
| + { "network", "disconnectnode", &disconnectnode, true, {"address", "nodeid"} }, |
MarcoFalke
Apr 13, 2017
Member
This is yet another breaking change. Needs rationale and mention in the release notes. Maybe backport.
JSONRPCException: Unknown named parameter address (-8)
|
@MarcoFalke indeed. See my earlier comment:
The fact that this argument is currently called 'node' and the help text say |
|
Rebased and squashed, with a couple of code cleanups. #10176 is merged so this is now ready for review. |
|
@jnewbery Yeah sorry for missing that. During my review flow I look at the code first, then read the comments before sending the ACK. Generally I think it makes sense to keep breaking changes at a minimum or at least don't splatter them across consecutive releases. As we already have such a breaking change in 0.14.1 it makes sense to bundle this one in as well. Would you mind to create a minimal fix (similar to #10084) and tag that for backport? Not sure if it can into 0.14.1 at this stage, but having the pull open can not hurt. |
jnewbery
referenced this pull request
Apr 13, 2017
Merged
[rpc] rename disconnectnode argument #10204
|
rebased |
| "\nImmediately disconnects from the specified node.\n" | ||
| "\nArguments:\n" | ||
| "1. \"address\" (string, required) The IP address/port of the node\n" | ||
| + "2. \"nodeid\" (string, required) The node ID(see getpeerinfo for nodes)\n" |
laanwj
Apr 18, 2017
•
Owner
Please mention in the documentation that only one of either can be provided, and the other one needs to be null (or missing, in case of the second argument, I guess).
Also I'd prefer a space after ID before ( otherwise it looks like a parametrized something
|
fixed @laanwj's review comment. |
|
ACK 1871884 |
| + address = request.params[0].get_str(); | ||
| + } | ||
| + | ||
| + if (request.params.size() == 1) { |
laanwj
Apr 19, 2017
•
Owner
Right, I also wondered about that.
My recommendation, also for readability, would be to structure this symmetrically e.g.
const UniValue &address_arg = request.params[0];
const UniValue &id_arg = request.params.size() < 2 ? NullUniValue : request.params[1];
...
if (!address_arg.IsNull() && id_arg.IsNull()) {
/* handle kick-by-address */
std::string address = address_arg.get_str();
} else if (!id_arg.IsNull() && address_arg.IsNull()) {
/* handle kick-by-id */
NodeId nodeid = (NodeId) id_arg.get_int64();
} else {
throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of adress and nodeid should be provided.");
}
laanwj
Apr 19, 2017
Owner
General advice when designing new RPC APIs: please try to treat IsNull arguments the same as missing arguments, both in the middle as at the end. I know a lot of the current RPCs don't heed that advice, but that's something that needs to be improved to prevent unexpected behavior when switching to using named arguments.
jnewbery
Apr 19, 2017
Member
Thanks @laanwj. That's good advice. I've rewritten this function based on your suggested structure.
| + } | ||
| + | ||
| + if (request.params.size() == 1) { | ||
| + ret = g_connman->DisconnectNode(request.params[0].get_str()); |
| + throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of adress and nodeid should be provided."); | ||
| + } | ||
| + | ||
| + NodeId nodeid = (NodeId) request.params[1].get_int(); |
|
@jnewbery I don't consider positional arguments to be merely historical. |
|
Rewritten using @laanwj's suggested structure. |
| throw std::runtime_error( | ||
| - "disconnectnode \"address\" \n" | ||
| - "\nImmediately disconnects from the specified node.\n" | ||
| + "disconnectnode \"address\" [nodeid]\n" |
| "\nArguments:\n" | ||
| - "1. \"address\" (string, required) The IP address/port of the node\n" | ||
| + "1. \"address\" (string, optional) The IP address/port of the node\n" | ||
| + "2. \"nodeid\" (int, optional) The node ID (see getpeerinfo for node IDs)\n" |
jnewbery
added some commits
Apr 3, 2017
|
Thanks @luke-jr . Nits addressed. I've added tests for the new functionality in the nodehandling.py test script (and renamed it to disconnect_ban.py). Whilst I was there I tidied up the test script, reduced the dependencies on standard libraries, and reduced the runtime from 20s to 9s on my pc. Happy to split the test script changes out into a new PR if that's preferable for people. |
|
utACK d54297f |
laanwj
merged commit d54297f
into
bitcoin:master
Apr 20, 2017
1 check passed
added a commit
that referenced
this pull request
Apr 20, 2017
|
@laanwj thanks for merging (and thanks for the suggestion of structuring the disconnectnode code differently). In future, feel free to squash my test commits before merging, or ask me to squash. I feel like micro commits are helpful for reviewers, but don't necessarily need to be in the history. |
jnewbery
referenced this pull request
Apr 20, 2017
Merged
[rpc] listsinceblock should include lost transactions when parameter is a reorg'd block #9622
|
@jnewbery Yes, that would have made sense. I try to keep some pace in merging things once they're ready to merge, as there are so many PRs held up on one thing or another. |
jnewbery commentedApr 3, 2017
The disconnectnode RPC can currently only be called with the IP address/port
of the node the user wishes to connect. This commit allows the node to
be disconnected using the nodeid returned by getpeerinfo().