-
Notifications
You must be signed in to change notification settings - Fork 37.7k
p2p, rpc: Manual block-relay-only connections with addnode #24170
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
5b8c16d
to
f2fc01f
Compare
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. 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. |
I'd be really interested in opinions whether the use case (as outlined in OP and linked issue) is important enough to justify the added complexity of adding another connection type (and also adding more complexity to |
f2fc01f
to
0a8f4dc
Compare
Rebased and addressed feedback. |
0a8f4dc
to
6196e95
Compare
6196e95
to
99154c5
Compare
src/net.h
Outdated
@@ -1087,7 +1114,7 @@ class CConnman | |||
AddrMan& addrman; | |||
std::deque<std::string> m_addr_fetches GUARDED_BY(m_addr_fetches_mutex); | |||
Mutex m_addr_fetches_mutex; | |||
std::vector<std::string> m_added_nodes GUARDED_BY(m_added_nodes_mutex); | |||
std::vector<AddedNodeEntry> 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.
Why not try std::map<std::string, ConnectionType> m_added_nodes, then we can avoid to define AddedNodeEntry?
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.
Hmm, I'm a bit undecided about that: I think avoiding a struct is not really a good argument for switching form vector to map: We would need to switch to a pair or struct if we'd ever need to add another member, plus there is a case for introducing a struct even if we'd switch to a map, as was recently done in PruneLocks example.
Performance-wise, this doesn't seem important at all since the container cannot have more than 8 entries (MAX_ADDNODE_CONNECTIONS).
src/net_processing.cpp
Outdated
@@ -1230,7 +1230,7 @@ void PeerManagerImpl::InitializeNode(CNode *pnode) | |||
mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(pnode->IsInboundConn())); | |||
assert(m_txrequest.Count(nodeid) == 0); | |||
} | |||
PeerRef peer = std::make_shared<Peer>(nodeid, /*tx_relay=*/ !pnode->IsBlockOnlyConn()); | |||
PeerRef peer = std::make_shared<Peer>(nodeid, /*tx_relay=*/!pnode->IsBlockOnlyConn() && !pnode->IsManualBlockRelayConn()); |
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 not directly change IsBlockOnlyConn() directly as below, then can avoid to define new function IsManualBlockRelayConn()?
bool IsBlockOnlyConn() const {
-
return m_conn_type == ConnectionType::BLOCK_RELAY;
-
}
return m_conn_type == ConnectionType::BLOCK_RELAY || m_conn_type == ConnectionType::MANUAL_BLOCK_RELAY;
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 #24170 (comment) - there are several placed that use IsBlockOnlyConn()
with logic specific to automatic block-relay-only connections that have logic that does not apply to manual ones. So if I did that, we'd need additional logic in these places to distinguish between manual and automatic 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.
That seems preferable?
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 have changed the logic now such that IsBlockRelayConn
is used when both connection types are meant, where as IsManualBlockRelayConn
and IsAutomaticBlockRelayConn
are used in spots where only one is meant.
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.
Concept ACK. This seems like a reasonable feature request.
I've left a few comments, mostly around naming.
I wonder if in future, we could change the net/net_processing responsibilities such that net_processing is unaware of whether a connection is manual or not. The only places that net_processing uses that information are in MaybeDiscourageAndDisconnect()
(before this PR), and EvictExtraOutboundPeers()
(in this PR). If disconnection and eviction logic is moved out of net_processing (cc @cfields), then net_processing won't need to be aware of the difference between manual and automatic connections.
src/net.h
Outdated
ConnectionType conn_type; | ||
}; | ||
|
||
struct AddedNodeEntry { |
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 know that this is maybe a bit of a stretch for this PR, but I really don't like the addnode/addednode terminology, and would much prefer this to be named something like ManualConnectionEntry
, which much more precisely describes the nature of these 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.
The downside of ManualConnectionEntry
is that it is not a connection, just a name of a node we'll try to connect to (which may succeed or not). I didn't really like the name AddedNodeEntry either though, but couldn't think of anything better. Maybe ManualNodeEntry
?
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.
Or maybe ManualConnectionTarget
or DesiredManualConnection
? I'm not sure they're great either.
@@ -440,7 +440,7 @@ void SetupServerArgs(ArgsManager& argsman) | |||
" If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.", | |||
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); | |||
|
|||
argsman.AddArg("-addnode=<ip>", strprintf("Add a node to connect to and attempt to keep the connection open (see the addnode RPC help for more info). This option can be specified multiple times to add multiple nodes; connections are limited to %u at a time and are counted separately from the -maxconnections limit.", MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); | |||
argsman.AddArg("-addnode=<ip>[=manual-block-relay]", strprintf("Add a node to connect to and attempt to keep the connection open (see the addnode RPC help for more info). This option can be specified multiple times to add multiple nodes; connections are limited to %u at a time and are counted separately from the -maxconnections limit. Append =manual-block-relay to disable transaction and address relay.", MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); |
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.
Prefer =blocksonly
to be clearer and match the existing -blocksonly
option.
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.
-blocksonly
mode is different from block relay peers.
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.
How so? Maybe it needs better docs on the difference...
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.
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.
That doesn't contrast.
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.
Some differences:
- -blocksonly is a global startup option (applying to all connections) -block-relay-only is connection-specific
- A node in -blocksonly mode participates in address relay, we never send out addresses on -block-relay-only connections (and ignore incoming ones).
- A node in -blocksonly signals not to receive any transactions (
fRelay
) and will disconnect if that is breached, but it may in special cases send out transactions itself (if submitted via RPC). We'll never send a tx over a block-relay-only connection. - A node in -blocksonly mode still has two of their outbound connections as -block-relay-only (for which then the block-relay-only rules apply).
for (const std::string& it : m_added_nodes) { | ||
if (strNode == it) return false; | ||
for (const auto& it : m_added_nodes) { | ||
if (strNode == it.m_node_address) return false; |
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.
Supposed option modify case need not be handled?
If add node firstly with setting manual and then setting manual block relay.
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.
No, a modify option is not supported, especially since it is not possible to change the connection connection type of an existing connection. A user could instead use the 'remove' option and then re-add with another connection type (which would not change existing connections though).
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.
It might be an option to use std::any_of
here if using standard library algorithms is preferable, but personally I think it might end up a little less readable.
@@ -1258,6 +1260,7 @@ bool CConnman::AddConnection(const std::string& address, ConnectionType conn_typ | |||
switch (conn_type) { | |||
case ConnectionType::INBOUND: | |||
case ConnectionType::MANUAL: | |||
case ConnectionType::MANUAL_BLOCK_RELAY: |
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.
Addconnection rpc should not support manual block relay, so seems here need not check?
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.
It's true that it doesn't support this, just as it doesn't support INBOUND or MANUAL. But leaving it out her would result in a compiler warning for the switch, see comment below:
// no default case, so the compiler can warn about missing cases
if it helps, i found some related issues: (but i don’t know this either)
in the very unlikely scenario that everyone likes this connection and all bitcoin nodes on tor connect with clearnet only using this option - that wouldn't be good for the network right? (assuming an average tor user wouldn’t want their addresses/transactions relayed to their clearnet peers) |
We'd need enough inbound capacity on clearnet to accomodate the additional manual connections to it, and also some users relaying transactions on both networks, otherwise txns submitted through tor wouldn't make it into clearnet and vice versa. I think that there are many nodes with a lesser need for privacy (e.g. because they don't submit transactions regularly) that don't have the need for something like and would be fine having full connections to both networks. |
|
||
struct AddedNodeInfo { | ||
AddedNodeParams m_params; | ||
CService resolvedAddress; |
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.
What would be the difference between resolvedAddress
and m_node_address
?
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.
m_node_address
is a string, which is part of AddedNodeParams, which has just the unvalidated user input.
resolvedAddress
is a CService
built from m_node_address
, which is generated in GetAddedNodeInfo
and used in ThreadOpenConnection
/ rpc code.
src/rpc/net.cpp
Outdated
if (strCommand == "remove" && !request.params[2].isNull()) { | ||
throw JSONRPCError(RPC_INVALID_PARAMS, "Cannot specify a connection type with 'remove'"); | ||
} | ||
std::string connection_type_name{manual_name}; |
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.
perhaps there is a way to simplify it:
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 6155af323..b9e082a63 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -304,22 +304,19 @@ static RPCHelpMan addnode()
throw std::runtime_error(
self.ToString());
}
- const std::string manual_name{ConnectionTypeAsString(ConnectionType::MANUAL)};
- const std::string manual_block_relay_name{ConnectionTypeAsString(ConnectionType::MANUAL_BLOCK_RELAY)};
+
if (strCommand == "remove" && !request.params[2].isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMS, "Cannot specify a connection type with 'remove'");
}
- std::string connection_type_name{manual_name};
+
+ const std::string manual_block_relay_name{ConnectionTypeAsString(ConnectionType::MANUAL_BLOCK_RELAY)};
+ ConnectionType connection_type{ConnectionType::MANUAL};
if (!request.params[2].isNull()) {
- connection_type_name = request.params[2].get_str();
- }
- ConnectionType connection_type;
- if (connection_type_name == manual_block_relay_name) {
- connection_type = ConnectionType::MANUAL_BLOCK_RELAY;
- } else if (connection_type_name == manual_name) {
- connection_type = ConnectionType::MANUAL;
- } else {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unsupported connection type: " + connection_type_name);
+ const std::string connection_type_name{request.params[2].get_str()};
+ if (connection_type_name == manual_block_relay_name)
+ connection_type = ConnectionType::MANUAL_BLOCK_RELAY;
+ else
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unsupported connection type: " + connection_type_name);
}
NodeContext& node = EnsureAnyNodeContext(request.context);
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.
Yes, thanks, that's a bit simpler and should be equivalent, so I took your suggestion!
This is in preparation for the addition of manual block-relay-only connections. -BEGIN VERIFY SCRIPT- ren() { sed -i "s:\<$1\>:$2:g" $(git grep -l "\<$1\>" ./src ./test); } ren IsBlockOnlyConn IsAutomaticBlockRelayConn ren GetCurrentBlockRelayOnlyConns GetCurrentAutomaticBlockRelayConns -END VERIFY SCRIPT-
This commit implements the p2p behaviour, but the new connection type is not used yet.
b36db71
to
c13c986
Compare
This applies to both add mode and onetry mode Co-authored-by: dhruv <856960+dhruv@users.noreply.github.com> Co-authored-by: brunoerg <brunoely.gc@gmail.com>
This allows to specify manual-block-relay peers per command arg or bitcoin.conf
…ndOrBlockRelayConn to make it clearer that manual connections are not included -BEGIN VERIFY SCRIPT- ren() { sed -i "s:\<$1\>:$2:g" $(git grep -l "\<$1\>" ./src ./test); } ren IsOutboundOrBlockRelayConn IsAutomaticOutboundOrBlockRelayConn -END VERIFY SCRIPT-
to better distinguish from MANUAL_BLOCK_RELAY -BEGIN VERIFY SCRIPT- ren() { sed -i "s:\<$1\>:$2:g" $(git grep -l "\<$1\>" ./src ./test); } ren BLOCK_RELAY AUTOMATIC_BLOCK_RELAY -END VERIFY SCRIPT-
c13c986
to
41ce312
Compare
This implements the suggestion from #23763 to introduce an option to establish block-relay-connections manually with the
-addnode
RPC.Adding these can make sense for a node operator that wants to be connected to a anonymity networks like Tor or I2P, but also wants to have additional protection against eclipse attacks: Following the best chain can be more of an issue on anonymity networks because these are smaller and it can be easier to create a lot of sybil nodes there.
In that situation, manual block-relay-only connections to peers on clearnet networks can help us staying connected to the best chain, but in contrast to normal manual connections, transactions and addresses aren't transmitted on these links - in particular not our own address or transaction from our wallet. This increases privacy and will also make it harder to perform fingerprinting attacks (connecting our identities over different networks).
Manual Block-Relay connections:
-addnode
RPC, both with theadd
andonetry
command-addnode
bitcoind arg (or in bitcoin.conf) with<IP>=manual-block-relay