diff --git a/libp2p/exceptions.py b/libp2p/exceptions.py index bdecfad0..5b781145 100644 --- a/libp2p/exceptions.py +++ b/libp2p/exceptions.py @@ -8,3 +8,9 @@ class ValidationError(BaseLibp2pError): class ParseError(BaseLibp2pError): pass + + +class MultiError(BaseLibp2pError): + """Raised with multiple exceptions.""" + + # todo: find some way for this to fancy-print all encapsulated errors diff --git a/libp2p/network/swarm.py b/libp2p/network/swarm.py index 0c405106..5099a5d2 100644 --- a/libp2p/network/swarm.py +++ b/libp2p/network/swarm.py @@ -19,6 +19,7 @@ from libp2p.transport.transport_interface import ITransport from libp2p.transport.upgrader import TransportUpgrader from libp2p.typing import StreamHandlerFn +from ..exceptions import MultiError from .connection.raw_connection import RawConnection from .connection.swarm_connection import SwarmConn from .exceptions import SwarmException @@ -90,14 +91,49 @@ class Swarm(INetwork): except PeerStoreError: raise SwarmException(f"No known addresses to peer {peer_id}") - if not addrs: + if len(addrs) == 0: raise SwarmException(f"No known addresses to peer {peer_id}") - multiaddr = addrs[0] + exceptions: List[SwarmException] = [] + + # Try all known addresses + for multiaddr in addrs: + try: + return await self.dial_addr(multiaddr, peer_id) + except SwarmException as e: + exceptions.append(e) + logger.debug( + "encountered swarm exception when trying to connect to %s, " + "trying next address...", + multiaddr, + exc_info=e, + ) + + # Tried all addresses, raising exception. + if len(exceptions) > 0: + raise SwarmException( + "unable to connect to %s, all addresses failed to dial (with exceptions)", + peer_id, + ) from MultiError(exceptions) + else: + raise SwarmException( + "unable to connect to %s, all addresses failed to dial", peer_id + ) + + async def dial_addr(self, addr: Multiaddr, peer_id: ID) -> INetConn: + """ + dial_addr try to create a connection to peer_id with addr. + + :param addr: the address we want to connect with + :param peer_id: the peer we want to connect to + :raises SwarmException: raised when an error occurs + :return: muxed connection + """ + # Dial peer (connection to peer does not yet exist) # Transport dials peer (gets back a raw conn) try: - raw_conn = await self.transport.dial(multiaddr) + raw_conn = await self.transport.dial(addr) except OpenConnectionError as error: logger.debug("fail to dial peer %s over base transport", peer_id) raise SwarmException( @@ -137,7 +173,7 @@ class Swarm(INetwork): async def new_stream(self, peer_id: ID) -> INetStream: """ :param peer_id: peer_id of destination - :param protocol_id: protocol id + :param protocol_id: protocol id fixme: protocol_id not in parameters :raises SwarmException: raised when an error occurs :return: net stream instance """