async def connect(self, remote: Node) -> BasePeer: """ Connect to the given remote and return a Peer instance when successful. Returns None if the remote is unreachable, times out or is useless. """ if remote.pubkey == self.privkey.public_key: Logger.warning_every_n( "Skipping {} that has the same public key as local node, quite possible we are trying to connect to ourselves" .format(remote), 100, ) return None if remote in self.connected_nodes: self.logger.debug("Skipping %s; already connected to it", remote) return None expected_exceptions = ( HandshakeFailure, PeerConnectionLost, TimeoutError, UnreachablePeer, ) try: self.logger.debug("Connecting to %s...", remote) # We use self.wait() as well as passing our CancelToken to handshake() as a workaround # for https://github.com/ethereum/py-evm/issues/670. peer = await self.wait(handshake(remote, self.get_peer_factory())) return peer except OperationCancelled: # Pass it on to instruct our main loop to stop. raise except BadAckMessage: # This is kept separate from the `expected_exceptions` to be sure that we aren't # silencing an error in our authentication code. self.logger.error("Got bad auth ack from %r", remote) # dump the full stacktrace in the debug logs self.logger.debug("Got bad auth ack from %r", remote, exc_info=True) except MalformedMessage: # This is kept separate from the `expected_exceptions` to be sure that we aren't # silencing an error in how we decode messages during handshake. self.logger.error( "Got malformed response from %r during handshake", remote) # dump the full stacktrace in the debug logs self.logger.debug("Got malformed response from %r", remote, exc_info=True) except expected_exceptions as e: self.logger.debug("Could not complete handshake with %r: %s", remote, repr(e)) except Exception: self.logger.exception( "Unexpected error during auth/p2p handshake with %r", remote) return None
def get_random_nodes(self, count: int) -> Iterator[Node]: if count > len(self): if time.monotonic() - self._initialized_at > 30: Logger.warning_every_n( "Cannot get {} nodes as RoutingTable contains only {} nodes" .format(count, len(self)), 100, ) count = len(self) seen = [] # This is a rather inneficient way of randomizing nodes from all buckets, but even if we # iterate over all nodes in the routing table, the time it takes would still be # insignificant compared to the time it takes for the network roundtrips when connecting # to nodes. while len(seen) < count: bucket = random.choice(self.buckets) if not bucket.nodes: continue node = random.choice(bucket.nodes) if node not in seen: yield node seen.append(node)
async def connect(self, remote: Node) -> BasePeer: """ Connect to the given remote and return a Peer instance when successful. Returns None if the remote is unreachable, times out or is useless. """ if remote.pubkey == self.privkey.public_key: Logger.warning_every_n( "Skipping {} that has the same public key as local node, quite possible we are trying to connect to ourselves" .format(remote), 100, ) return None if remote in self.connected_nodes: self.logger.debug("Skipping %s; already connected to it", remote) return None if self.chk_dialout_blacklist(remote.address): Logger.warning_every_n( "failed to connect {} at least once, will not connect again; discovery should have removed it" .format(remote.address), 100) return None blacklistworthy_exceptions = ( HandshakeFailure, # after secure handshake handshake, when negotiating p2p command, eg. parsing hello failed; no matching p2p capabilities PeerConnectionLost, # conn lost while reading TimeoutError, # eg. read timeout (raised by CancelToken) UnreachablePeer, # ConnectionRefusedError, OSError ) expected_exceptions = ( HandshakeDisconnectedFailure, # during secure handshake, disconnected before getting ack; or got Disconnect cmd for some known reason ) try: self.logger.debug("Connecting to %s...", remote) # We use self.wait() as well as passing our CancelToken to handshake() as a workaround # for https://github.com/ethereum/py-evm/issues/670. peer = await self.wait(handshake(remote, self.get_peer_factory())) return peer except OperationCancelled: # Pass it on to instruct our main loop to stop. raise except BadAckMessage: # This is kept separate from the `expected_exceptions` to be sure that we aren't # silencing an error in our authentication code. Logger.error_every_n("Got bad auth ack from {}".format(remote), 100) # dump the full stacktrace in the debug logs self.logger.debug("Got bad auth ack from %r", remote, exc_info=True) self.dialout_blacklist(remote.address) except MalformedMessage: # This is kept separate from the `expected_exceptions` to be sure that we aren't # silencing an error in how we decode messages during handshake. Logger.error_every_n( "Got malformed response from {} during handshake".format( remote), 100) # dump the full stacktrace in the debug logs self.logger.debug("Got malformed response from %r", remote, exc_info=True) self.dialout_blacklist(remote.address) except blacklistworthy_exceptions as e: self.logger.debug("Could not complete handshake with %r: %s", remote, repr(e)) Logger.error_every_n( "Could not complete handshake with {}: {}".format( repr(remote), repr(e)), 100) self.dialout_blacklist(remote.address) except expected_exceptions as e: self.logger.debug("Disconnected during handshake %r: %s", remote, repr(e)) Logger.error_every_n( "Disconnected during handshake {}: {}".format( repr(remote), repr(e)), 100) except Exception: self.logger.exception( "Unexpected error during auth/p2p handshake with %r", remote) self.dialout_blacklist(remote.address) if remote.__repr__() in auth.opened_connections: reader, writer = auth.opened_connections[remote.__repr__()] reader.feed_eof() writer.close() Logger.error_every_n( "Closing connection to {}".format(remote.__repr__()), 100) del auth.opened_connections[remote.__repr__()] return None