-
Notifications
You must be signed in to change notification settings - Fork 130
[PAN-2614] Add simple PeerPermissions interface #1446
[PAN-2614] Add simple PeerPermissions interface #1446
Conversation
Rather than dropping peers from the peer table for any disconnect, drop peers from the table on permissions changes, when peers are requested to be disconnected, and when peers are disconnecting for failed permissions check.
@@ -108,7 +107,6 @@ public void startNode(final PantheonNode node) { | |||
.jsonRpcConfiguration(node.jsonRpcConfiguration()) | |||
.webSocketConfiguration(node.webSocketConfiguration()) | |||
.dataDir(node.homeDirectory()) | |||
.bannedNodeIds(Collections.emptySet()) |
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.
We no longer need to configure an empty set of banned nodes
*/ | ||
public long observePeerDroppedEvents(final Consumer<PeerDroppedEvent> observer) { | ||
checkNotNull(observer); | ||
return peerDroppedObservers.subscribe(observer); |
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 was dead code - listeners were never actually notified of dropped peers
@@ -80,7 +72,6 @@ | |||
/* The keypair used to sign messages. */ | |||
protected final SECP256K1.KeyPair keyPair; | |||
private final BytesValue id; | |||
private final PeerTable peerTable; |
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.
PeerTable
isn't used in PeerDiscoveryAgent
- moved it into the PeerDiscoveryController
where it is used.
@@ -367,27 +382,11 @@ private boolean addToPeerTable(final DiscoveryPeer peer) { | |||
return true; | |||
} | |||
|
|||
@VisibleForTesting | |||
boolean dropFromPeerTable(final DiscoveryPeer peer) { |
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.
Dead code
@@ -348,6 +349,7 @@ protected void initChannel(final SocketChannel ch) { | |||
|
|||
if (!isPeerAllowed(connection)) { | |||
connection.disconnect(DisconnectReason.UNKNOWN); | |||
peerDiscoveryAgent.dropPeer(connection.getPeer()); |
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.
Previously we were dropping all peers who disconnected. Now, we're explicitly dropping a subset of disconnected peers.
@@ -407,8 +411,9 @@ void attemptPeerConnections() { | |||
streamDiscoveredPeers() | |||
.filter(peer -> peer.getStatus() == PeerDiscoveryStatus.BONDED) | |||
.filter(peer -> !isConnected(peer) && !isConnecting(peer)) | |||
.sorted(Comparator.comparing(DiscoveryPeer::getLastAttemptedConnection)) |
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.
Try connecting to discovery peers who haven't recently been contacted.
|
||
doReturn(EvictResult.absent()).when(peerTableSpy).tryEvict(any()); | ||
|
||
controller = getControllerBuilder().peerDroppedObservers(peerDroppedSubscribers).build(); |
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 test hit the dead code that was removed
protected static final Logger LOG = LogManager.getLogger(); | ||
|
||
// The devp2p specification says only accept packets up to 1280, but some | ||
// clients ignore that, so we add in a little extra padding. | ||
private static final int MAX_PACKET_SIZE_BYTES = 1600; | ||
private static final long PEER_REFRESH_INTERVAL_MS = MILLISECONDS.convert(30, TimeUnit.MINUTES); |
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.
Moved this constant into PeerDiscoveryController
where it is used
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.
- Would some of the pre-review comments be better served as code comments?
- all "consider" comments are truly optional.
} | ||
|
||
// If we have an explicit list of peers, drop each peer from our discovery table | ||
affectedPeers.ifPresent(peers -> peers.forEach(this::dropPeer)); |
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.
If they are affected why are we summarily dropping them instead of re-evaluating? I'm confused.
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 current interface just has a blanket rule where peers are fully allowed or fully disallowed. So if the permissions were updated to add restrictions and a list of peers were provided, it means those peers are no longer allowed. The follow-up PR will make these checks more granular such that we'll re-evaluate at this point.
return; | ||
} | ||
|
||
// Load the peer from the table, or use the instance that comes in. | ||
final Optional<DiscoveryPeer> maybeKnownPeer = peerTable.get(sender); | ||
final DiscoveryPeer peer = maybeKnownPeer.orElse(sender); | ||
final boolean peerKnown = maybeKnownPeer.isPresent(); | ||
final boolean peerBlacklisted = peerBlacklist.contains(peer); |
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.
Without the blacklist peers that are "bad" will still hit our peer table. Is this acceptable per what we need to do? Will we advertise that we've seen "blackbeard" even if we refuse to talk to him? Should we be tracking peer-no-grata? Or are we checking permissions at every time we would pull said peer out of the peer table?
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 blacklist basically transformed into peerPermissions
which gets checked in the method isPeerPermittedToSendMessages
above on line 307. So we're still filtering out the bad peers here.
|
||
@FunctionalInterface | ||
public interface OutboundDiscoveryMessagingPermissions { | ||
boolean isPermitted(Peer remotePeer); |
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.
Maybe isRemotePermitted
? In case we add this interface to an uber-permissions object with inbound permissions implicit on the local peer, so the names won't collide?
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've got a follow-up PR that will rework these interfaces. Right now I'm moving towards an API like: isPermitted(localNode, remotePeer, action).
@@ -778,7 +761,6 @@ private void validate() { | |||
checkState( | |||
supportedCapabilities != null && supportedCapabilities.size() > 0, | |||
"Supported capabilities must be set and non-empty."); | |||
checkState(peerBlacklist != null, "PeerBlacklist must be set."); |
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.
do we handle null peerPermissions correctly?
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 think so - peerPermissions
gets initialized with a default value, and the setter does a null check so that it can't get overwritten with a null value.
} | ||
|
||
private static class CombinedPeerPermissions extends PeerPermissions { | ||
private final List<PeerPermissions> permissions; |
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.
Consider ImmutableList. Since we are allowing a list to shortcut other CombinedPeerPermissions by collapsing it into this list we should make it explicit that the object we are shortcutting won't mutate. There is an ImmutableList collector as part of Guava.
private PeerPermissionsBlacklist(final int initialCapacity, final OptionalInt maxSize) { | ||
if (maxSize.isPresent()) { | ||
blacklist = | ||
LimitedSet.create(initialCapacity, maxSize.getAsInt(), Mode.DROP_LEAST_RECENTLY_ACCESSED); |
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.
Consider Guava's Cache instead: https://github.com/google/guava/wiki/CachesExplained - it doesn't do Least Recently Accessed 100% but it does have better concurrency then being wrapped in a synchronized set.
Under the covers Java's sets are Maps with the key being the entry and I can't remember what the value is.
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 may follow-up on this suggestion in another PR. But skipping for now.
} | ||
} | ||
|
||
private static void validateNodeIdString(final String nodeId) { |
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.
could this be part of ENodeURL?
|
||
assertThat(commandOutput.toString()).isEmpty(); | ||
assertThat(commandErrorOutput.toString()).isEmpty(); | ||
} | ||
|
||
@Test | ||
public void callingWithBannedNodeidsOptionButNoValueMustDisplayErrorAndUsage() { |
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.
Can these be moved to 986? It makes the diff more comprehensible.
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 bannedNodeIds test was sandwiched in between some bootnode tests, so moved things around a bit for a more coherent test layout 🤷♀
PR description
This PR adds a simple
PeerPermissions
interface as a first step in trying to define a common interface for permission-related logic within the p2p package.Summary of changes:
PeerPermissions
interfacePeerBlacklist
into several smaller classes and put a subset of these classes behind the new interfacePeerPermissionsBlacklist
class that just holds a set of peers that are not allowed. The list can be optionally capped with a max number of tracked peers.LimitedSet
class to handle creating a capped setPeerReputationManager
classPeerReputationManager
--banned-node-ids
are valid node idsPeerDiscoveryController
Builder to make updates to the long list of constructor dependencies easier