-
Notifications
You must be signed in to change notification settings - Fork 37.7k
rpc: p2p_v2 rpc argument for addnode #23900
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
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
Shouldn't handshaking be independent of advertised services? Otherwise attackers could simply advertise the wrong services for a peer when forwarding along addr messages, or mitm'ing dns seeds? I thought the idea for p2p v2 was to attempt an encrypted handshake, and if that failed, retry without encryption, and you'd just do that for all connection attempts. |
I might be missing something but don't we already use data from the advertised services in changing the nature of the connection? For example, (I'm not certain, but expect that), we do not ask pruned nodes for older blocks when in IBD.
The current plan is to initiate v2 connections to peers advertising
The MITM attacker could make a v2 node look like a v1 node which would result in a v1 connection, or a v1 node look like v2 which would result still in a v1 connection. Downgrade attacks are a risk mentioned in BIP324:
|
addrman will OR the nServices together when there is conflicting information (and overwrite when getting it from a connection to the node itself), so it should be far easier for an attacker to falsely "upgrade" the rumoured information than to downgrade it. So I think there is relatively little danger in only trying v2 with peers for which we believe they may support it. |
src/rpc/net.cpp
Outdated
@@ -300,6 +301,9 @@ static RPCHelpMan addnode() | |||
if (strCommand == "onetry") | |||
{ | |||
CAddress addr; | |||
if (!request.params[2].isNull()) { | |||
addr.nServices = ServiceFlags{static_cast<uint64_t>(request.params[2].get_int64())}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only for onetry connections?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just happened to be using it like that, but, you're right - it should work for the "add" sub-command as well. Fixed.
We use that to decide not to connect to some peers, but when we do connect to them we treat them the same?
I guess I'm thinking about it backwards: the whole point of p2p v2 is to have an incompatible handshake compared to current p2p, so if the goal is to pass around some advisory data on who supports the new handshake that helps people avoid wasting bandwidth, then using a service bit makes sense for connecting to random peers. But if all that's only for saving bandwidth, why apply it to addnode at all? Just have addnode always try p2pv2 first and fallback -- a redundant connection attempt for addnode nodes only shouldn't be much extra bandwidth? Just feels like this is something that's only relevant for p2pv2, not a reason for addnode to know about service bits in general. (Another alternative would be to have "p2pv2" as an alternative to "onetry" to enable to p2pv2 handshake) |
43dc78e
to
609ca6d
Compare
Changed the code to be (a slightly modified version of) the last approach suggested by @ajtowns. Instead of accepting Ready for further review. |
@@ -45,7 +45,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman) | |||
random_string = fuzzed_data_provider.ConsumeRandomLengthString(64); | |||
}, | |||
[&] { | |||
connman.AddNode(random_string); | |||
connman.AddNode(random_string, fuzzed_data_provider.ConsumeBool()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will invalidate existing fuzz seeds (I think).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One complication is that addnode
is both a bitcoind startup option and a RPC command - so one problem of adding options to the RPC command is that the bitcoind command line option can't easily mimic them (without introducing some string parsing logic as for -bind
or -whitebind
), and it could lead to confusion for users if options were added to -addnode that are only configurable via the addnode RPC but not e.g. in bitcoin.conf
setups.
I had thought about implementing the suggestion in #23763 with a very similar approach, which runs into the same issue.
if (service.IsValid()) { | ||
// strAddNode is an IP:port | ||
auto it = mapConnected.find(service); | ||
if (it != mapConnected.end()) { | ||
addedNode.resolvedAddress = service; | ||
addedNode.fConnected = true; | ||
addedNode.fInbound = it->second; | ||
addedNode.use_p2p_v2 = use_p2p_v2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line (+ the one in the else branch) is unnecessary, use_p2p_v2
is already set in the initialization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Done in #24545.
std::vector<std::string> m_added_nodes GUARDED_BY(m_added_nodes_mutex); | ||
|
||
// connection string and whether to use v2 p2p | ||
std::vector<std::pair<std::string, bool>> m_added_nodes GUARDED_BY(m_added_nodes_mutex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally think adding a small struct with two vars is superior to this std::pair/tie stuff where you have to look up/remember what the components are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in #24545.
@@ -92,6 +93,7 @@ struct AddedNodeInfo | |||
CService resolvedAddress; | |||
bool fConnected; | |||
bool fInbound; | |||
bool use_p2p_v2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Current convention is to prefix with m_
so m_use_p2p_v2
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in #24545.
@@ -2977,7 +2988,8 @@ ServiceFlags CConnman::GetLocalServices() const | |||
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; } | |||
|
|||
CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion) | |||
: m_connected{GetTime<std::chrono::seconds>()}, | |||
: nServices(addrIn.nServices), | |||
m_connected(GetTimeSeconds()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with the change but wondering why it is happening here/now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC, there was a compiler warning(maybe error actually) about the order in the initializer list needing to be the same as that in the class member declaration.
@@ -2146,7 +2146,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const | |||
{ | |||
std::vector<AddedNodeInfo> ret; | |||
|
|||
std::list<std::string> lAddresses(0); | |||
std::list<std::pair<std::string, bool>> lAddresses(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See personal opinion on using structs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in #24545
Approach ACK, but if it's specific to P2P v2 now, doesn't seem like it needs to be broken out into its own PR prior to P2P v2? |
🐙 This pull request conflicts with the target branch and needs rebase. Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft". |
Thanks, @ajtowns @mzumsande @kallewoof. As @ajtowns has pointed out, this is now just specific to BIP324. The code has been rolled into #24545 so let's continue there. Closing this PR. To address @mzumsande's point about RPC vs CLI I will address other comments from here in that code this week. |
This PR adds the ability for a node operator to signal that a peer added via
addnode
supports the BIP324 transport protocol (aka v2).Use case: Working on BIP324, I needed to be able to add a v2 peer and test the handshake. This required allowing the operator to inform the node that the manual connection supports the v2 protocol (on the network, the signaling is done via a service bit,
NODE_P2P_V2
in addr relay).