Exemplo n.º 1
0
    async def ensure_same_side_on_dao_fork(
            self, vm_configuration: Tuple[Tuple[int, Type[BaseVM]],
                                          ...]) -> None:
        for start_block, vm_class in vm_configuration:
            if not issubclass(vm_class, HomesteadVM):
                continue
            elif not vm_class.support_dao_fork:
                break
            elif start_block > vm_class.dao_fork_block_number:
                # VM comes after the fork, so stop checking
                break

            fork_block = vm_class.dao_fork_block_number
            try:
                header, parent = await self.wait(
                    self._get_headers_at_chain_split(fork_block),
                    timeout=CHAIN_SPLIT_CHECK_TIMEOUT)
            except TimeoutError:
                # Logging as INFO because if we start seeing this too often we might need to
                # increase the timeout used here.
                self.logger.info(
                    "Timed out waiting for DAO fork header from %s", self)
                raise
            except ValidationError as e:
                raise HandshakeFailure(
                    "Peer failed DAO fork check retrieval: {}".format(e))

            try:
                vm_class.validate_header(header, parent, check_seal=True)
            except ValidationError as e:
                raise HandshakeFailure(
                    "Peer failed DAO fork check validation: {}".format(e))
Exemplo n.º 2
0
async def _handshake(initiator: 'HandshakeInitiator', reader: asyncio.StreamReader,
                     writer: asyncio.StreamWriter,
                     ) -> Tuple[bytes, bytes, sha3.keccak_256, sha3.keccak_256]:
    """See the handshake() function above.

    This code was factored out into this helper so that we can create Peers with directly
    connected readers/writers for our tests.
    """
    initiator_nonce = keccak(os.urandom(HASH_LEN))
    auth_msg = initiator.create_auth_message(initiator_nonce)
    auth_init = initiator.encrypt_auth_message(auth_msg)

    if writer.transport.is_closing():
        raise HandshakeFailure("Error during handshake with {initiator.remote!r}. Writer closed.")

    writer.write(auth_init)

    auth_ack = await asyncio.wait_for(reader.read(ENCRYPTED_AUTH_ACK_LEN), timeout=REPLY_TIMEOUT)

    if reader.at_eof():
        # This is what happens when Parity nodes have blacklisted us
        # (https://github.com/ethereum/py-evm/issues/901).
        raise HandshakeFailure(f"{initiator.remote!r} disconnected before sending auth ack")

    ephemeral_pubkey, responder_nonce = initiator.decode_auth_ack_message(auth_ack)
    aes_secret, mac_secret, egress_mac, ingress_mac = initiator.derive_secrets(
        initiator_nonce,
        responder_nonce,
        ephemeral_pubkey,
        auth_init,
        auth_ack
    )

    return aes_secret, mac_secret, egress_mac, ingress_mac
Exemplo n.º 3
0
    async def process_p2p_handshake(self, cmd: protocol.Command,
                                    msg: protocol.PayloadType) -> None:
        msg = cast(Dict[str, Any], msg)
        if not isinstance(cmd, Hello):
            await self.disconnect(DisconnectReason.bad_protocol)
            raise HandshakeFailure(
                f"Expected a Hello msg, got {cmd}, disconnecting")

        # Check whether to support Snappy Compression or not
        # based on other peer's p2p protocol version
        snappy_support = msg['version'] >= SNAPPY_PROTOCOL_VERSION

        if snappy_support:
            # Now update the base protocol to support snappy compression
            # This is needed so that Trinity is compatible with parity since
            # parity sends Ping immediately after Handshake
            self.base_protocol = P2PProtocol(self,
                                             snappy_support=snappy_support)

        remote_capabilities = msg['capabilities']
        try:
            self.sub_proto = self.select_sub_protocol(remote_capabilities,
                                                      snappy_support)
        except NoMatchingPeerCapabilities:
            await self.disconnect(DisconnectReason.useless_peer)
            raise HandshakeFailure(
                f"No matching capabilities between us ({self.capabilities}) and {self.remote} "
                f"({remote_capabilities}), disconnecting")

        self.logger.debug(
            "Finished P2P handshake with %s, using sub-protocol %s",
            self.remote, self.sub_proto)
Exemplo n.º 4
0
    async def do_p2p_handshake(self) -> None:
        """Perform the handshake for the P2P base protocol.

        Raises HandshakeFailure if the handshake is not successful.
        """
        self.base_protocol.send_handshake()

        try:
            cmd, msg = await self.read_msg()
        except rlp.DecodingError:
            raise HandshakeFailure("Got invalid rlp data during handshake")
        except MalformedMessage as e:
            raise HandshakeFailure(
                "Got malformed message during handshake") from e

        if isinstance(cmd, Disconnect):
            msg = cast(Dict[str, Any], msg)
            # Peers sometimes send a disconnect msg before they send the initial P2P handshake.
            if msg['reason'] == DisconnectReason.too_many_peers.value:
                raise TooManyPeersFailure(
                    f'{self} disconnected from us before handshake')
            raise HandshakeFailure(
                f"{self} disconnected before completing sub-proto handshake: {msg['reason_name']}"
            )
        await self.process_p2p_handshake(cmd, msg)
Exemplo n.º 5
0
    async def process_sub_proto_handshake(self, cmd: Command,
                                          msg: _DecodedMsgType) -> None:
        if not isinstance(cmd, Status):
            await self.disconnect(DisconnectReason.subprotocol_error)
            raise HandshakeFailure(
                f"Expected a BCC Status msg, got {cmd}, disconnecting")

        msg = cast(StatusMessage, msg)
        if msg['network_id'] != self.network_id:
            await self.disconnect(DisconnectReason.useless_peer)
            raise HandshakeFailure(
                f"{self} network ({msg['network_id']}) does not match ours "
                f"({self.network_id}), disconnecting")
        # TODO: pass accurate `block_class: Type[BaseBeaconBlock]` under per BeaconStateMachine fork
        genesis_block = await self.chain_db.coro_get_canonical_block_by_slot(
            self.context.genesis_slot,
            BeaconBlock,
        )
        # TODO change message descriptor to 'genesis_root', accounting for the spec
        if msg['genesis_hash'] != genesis_block.signing_root:
            await self.disconnect(DisconnectReason.useless_peer)
            raise HandshakeFailure(
                f"{self} genesis ({encode_hex(msg['genesis_hash'])}) does not "
                f"match ours ({encode_hex(genesis_block.signing_root)}), disconnecting"
            )

        self.head_slot = msg['head_slot']
Exemplo n.º 6
0
    async def do_handshake(self, multiplexer: MultiplexerAPI,
                           protocol: ETHProtocolV63) -> ETHV63HandshakeReceipt:
        """
        Perform the handshake for the sub-protocol agreed with the remote peer.

        Raise HandshakeFailure if the handshake is not successful.
        """

        protocol.send(StatusV63(self.handshake_params))

        async for cmd in multiplexer.stream_protocol_messages(protocol):
            if not isinstance(cmd, StatusV63):
                raise HandshakeFailure(
                    f"Expected a ETH Status msg, got {cmd}, disconnecting")

            receipt = ETHV63HandshakeReceipt(protocol, cmd.payload)

            validate_base_receipt(multiplexer.remote, receipt,
                                  self.handshake_params)

            break
        else:
            raise HandshakeFailure(
                "Message stream exited before finishing handshake")

        return receipt
Exemplo n.º 7
0
 async def process_sub_proto_handshake(
         self, cmd: Command, msg: _DecodedMsgType) -> None:
     if not isinstance(cmd, (Status, StatusV2)):
         await self.disconnect(DisconnectReason.subprotocol_error)
         raise HandshakeFailure(f"Expected a LES Status msg, got {cmd}, disconnecting")
     msg = cast(Dict[str, Any], msg)
     if msg['networkId'] != self.network_id:
         await self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(
             f"{self} network ({msg['networkId']}) does not match ours "
             f"({self.network_id}), disconnecting"
         )
     genesis = await self.genesis
     if msg['genesisHash'] != genesis.hash:
         await self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(
             f"{self} genesis ({encode_hex(msg['genesisHash'])}) does not "
             f"match ours ({genesis.hex_hash}), disconnecting"
         )
     # Eventually we might want to keep connections to peers where we are the only side serving
     # data, but right now both our chain syncer and the Peer.boot() method expect the remote
     # to reply to header requests, so if they don't we simply disconnect here.
     if 'serveHeaders' not in msg:
         await self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(f"{self} doesn't serve headers, disconnecting")
     self.head_td = msg['headTd']
     self.head_hash = msg['headHash']
     self.head_number = msg['headNum']
Exemplo n.º 8
0
    async def process_p2p_handshake(self, cmd: Command,
                                    msg: PayloadType) -> None:
        msg = cast(Dict[str, Any], msg)
        if not isinstance(cmd, Hello):
            await self.disconnect(DisconnectReason.bad_protocol)
            raise HandshakeFailure(
                f"Expected a Hello msg, got {cmd}, disconnecting")

        # limit number of chars to be displayed, and try to keep printable ones only
        # MAGIC 256: arbitrary, "should be enough for everybody"
        original_version = msg['client_version_string']
        client_version_string = original_version[:256] + (
            '...' if original_version[256:] else '')
        if client_version_string.isprintable():
            self.client_version_string = client_version_string.strip()
        else:
            self.client_version_string = repr(client_version_string)

        # Check whether to support Snappy Compression or not
        # based on other peer's p2p protocol version
        snappy_support = msg['version'] >= SNAPPY_PROTOCOL_VERSION

        if snappy_support:
            # Now update the base protocol to support snappy compression
            # This is needed so that Trinity is compatible with parity since
            # parity sends Ping immediately after Handshake
            self.base_protocol = P2PProtocol(
                self.transport,
                snappy_support=snappy_support,
                capabilities=self.capabilities,
                listen_port=self.listen_port,
            )

        remote_capabilities = msg['capabilities']
        matched_proto_classes = match_protocols_with_capabilities(
            self.supported_sub_protocols,
            remote_capabilities,
        )
        if len(matched_proto_classes) == 1:
            self.sub_proto = matched_proto_classes[0](
                self.transport,
                self.base_protocol.cmd_length,
                snappy_support,
            )
        elif len(matched_proto_classes) > 1:
            raise NotImplementedError(
                f"Peer {self.remote} connection matched on multiple protocols "
                f"{matched_proto_classes}.  Support for multiple protocols is not "
                f"yet supported")
        else:
            await self.disconnect(DisconnectReason.useless_peer)
            raise HandshakeFailure(
                f"No matching capabilities between us ({self.capabilities}) and {self.remote} "
                f"({remote_capabilities}), disconnecting")

        self.logger.debug(
            "Finished P2P handshake with %s, using sub-protocol %s",
            self.remote, self.sub_proto)
Exemplo n.º 9
0
async def _do_p2p_handshake(transport: TransportAPI,
                            capabilities: Capabilities,
                            p2p_handshake_params: DevP2PHandshakeParams,
                            base_protocol: BaseP2PProtocol,
                            token: CancelToken) -> Tuple[DevP2PReceipt, BaseP2PProtocol]:
    client_version_string, listen_port, p2p_version = p2p_handshake_params
    base_protocol.send(Hello(HelloPayload(
        client_version_string=client_version_string,
        capabilities=capabilities,
        listen_port=listen_port,
        version=p2p_version,
        remote_public_key=transport.public_key.to_bytes(),
    )))

    # The base `p2p` protocol handshake directly streams the messages as it has
    # strict requirements about receiving the `Hello` message first.
    async for _, cmd in stream_transport_messages(transport, base_protocol, token=token):
        if not isinstance(cmd, Hello):
            raise HandshakeFailure(
                f"First message across the DevP2P connection must be a Hello "
                f"msg, got {cmd}, disconnecting"
            )

        protocol: BaseP2PProtocol

        if base_protocol.version >= DEVP2P_V5:
            # Check whether to support Snappy Compression or not
            # based on other peer's p2p protocol version
            snappy_support = cmd.payload.version >= DEVP2P_V5

            if snappy_support:
                # Now update the base protocol to support snappy compression
                # This is needed so that Trinity is compatible with parity since
                # parity sends Ping immediately after handshake
                protocol = P2PProtocolV5(
                    transport,
                    command_id_offset=0,
                    snappy_support=True,
                )
            else:
                protocol = base_protocol
        else:
            protocol = base_protocol

        devp2p_receipt = DevP2PReceipt(
            protocol=protocol,
            version=cmd.payload.version,
            client_version_string=cmd.payload.client_version_string,
            capabilities=cmd.payload.capabilities,
            remote_public_key=cmd.payload.remote_public_key,
            listen_port=cmd.payload.listen_port,
        )
        break
    else:
        raise HandshakeFailure("DevP2P message stream exited before finishing handshake")

    return devp2p_receipt, protocol
Exemplo n.º 10
0
 def decrypt_and_validate_message(self,
                                  auth_header_packet: AuthHeaderPacket,
                                  decryption_key: AES128Key) -> BaseMessage:
     try:
         return auth_header_packet.decrypt_message(decryption_key)
     except DecryptionError as error:
         raise HandshakeFailure(
             "Failed to decrypt message in AuthHeader packet with newly established session keys"
         ) from error
     except ValidationError as error:
         raise HandshakeFailure("Received invalid message") from error
Exemplo n.º 11
0
 def process_p2p_handshake(self, cmd: protocol.Command, msg: protocol._DecodedMsgType) -> None:
     msg = cast(Dict[str, Any], msg)
     if not isinstance(cmd, Hello):
         self.disconnect(DisconnectReason.other)
         raise HandshakeFailure("Expected a Hello msg, got {}, disconnecting".format(cmd))
     remote_capabilities = msg['capabilities']
     self.sub_proto = self.select_sub_protocol(remote_capabilities)
     if self.sub_proto is None:
         self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(
             "No matching capabilities between us ({}) and {} ({}), disconnecting".format(
                 self.capabilities, self.remote, remote_capabilities))
     self.logger.debug(
         "Finished P2P handshake with %s, using sub-protocol %s",
         self.remote, self.sub_proto)
Exemplo n.º 12
0
def test_fails_when_closed():
    peer_info = MemoryPeerInfo()
    peer_info.close()

    node = random_node()
    with pytest.raises(persistence.ClosedException):
        peer_info.record_failure(node, HandshakeFailure())
Exemplo n.º 13
0
    async def do_sub_proto_handshake(self) -> None:
        """Perform the handshake for the sub-protocol agreed with the remote peer.

        Raises HandshakeFailure if the handshake is not successful.
        """
        await self.send_sub_proto_handshake()
        cmd, msg = await self.read_msg()
        if isinstance(cmd, Ping):
            # Parity sends a Ping before the sub-proto handshake, so respond to that and read the
            # next one, which hopefully will be the actual handshake.
            self.base_protocol.send_pong()
            cmd, msg = await self.read_msg()
        if isinstance(cmd, Disconnect):
            msg = cast(Dict[str, Any], msg)
            try:
                reason = DisconnectReason(msg['reason'])
            except TypeError:
                self.logger.warning('Unrecognized disconnect reason: %s',
                                    msg['reason'])
            else:
                self.disconnect_reason = reason
                # Peers sometimes send a disconnect msg before they send the sub-proto handshake.
                if reason is DisconnectReason.too_many_peers:
                    raise TooManyPeersFailure(
                        f'{self} disconnected from us before handshake')
            raise HandshakeFailure(
                f"{self} disconnected before completing sub-proto handshake: {msg['reason_name']}"
            )
        await self.process_sub_proto_handshake(cmd, msg)
        self.logger.debug("Finished %s handshake with %s", self.sub_proto,
                          self.remote)
Exemplo n.º 14
0
    async def handle_outgoing_message_pre_handshake(self,
                                                    outgoing_message: OutgoingMessage,
                                                    ) -> None:
        if not self.is_pre_handshake:
            raise ValueError("Can only handle message pre handshake")

        try:
            local_enr = await self.enr_db.get(self.local_node_id)
        except KeyError:
            raise ValueError(
                f"Unable to find local ENR in DB by node id {encode_hex(self.local_node_id)}"
            )
        try:
            remote_enr = await self.enr_db.get(self.remote_node_id)
        except KeyError:
            self.logger.warning(
                "Unable to initiate handshake with %s as their ENR is not present in the DB",
                encode_hex(self.remote_node_id),
            )
            raise HandshakeFailure()

        self.logger.info("Initiating handshake to send %s", outgoing_message)
        self.start_handshake_as_initiator(
            local_enr=local_enr,
            remote_enr=remote_enr,
            message=outgoing_message.message,
        )
        self.logger.debug("Sending initiating packet")
        await self.send_first_handshake_packet(outgoing_message.receiver_endpoint)
Exemplo n.º 15
0
    async def process_sub_proto_handshake(self, cmd: Command,
                                          msg: _DecodedMsgType) -> None:
        if not isinstance(cmd, Status):
            await self.disconnect(DisconnectReason.subprotocol_error)
            raise HandshakeFailure(
                f"Expected a ETH Status msg, got {cmd}, disconnecting")

        msg = cast(Dict[str, Any], msg)

        self.head_td = msg['td']
        self.head_hash = msg['best_hash']
        self.network_id = msg['network_id']
        self.genesis_hash = msg['genesis_hash']

        if msg['network_id'] != self.local_network_id:
            await self.disconnect(DisconnectReason.useless_peer)
            raise WrongNetworkFailure(
                f"{self} network ({msg['network_id']}) does not match ours "
                f"({self.local_network_id}), disconnecting")

        local_genesis_hash = await self._get_local_genesis_hash()
        if msg['genesis_hash'] != local_genesis_hash:
            await self.disconnect(DisconnectReason.useless_peer)
            raise WrongGenesisFailure(
                f"{self} genesis ({encode_hex(msg['genesis_hash'])}) does not "
                f"match ours ({local_genesis_hash}), disconnecting")
Exemplo n.º 16
0
    async def do_handshake(
            self, multiplexer: MultiplexerAPI,
            protocol: Union[LESProtocolV1,
                            LESProtocolV2]) -> LESHandshakeReceipt:
        """Perform the handshake for the sub-protocol agreed with the remote peer.

        Raises HandshakeFailure if the handshake is not successful.
        """

        protocol.send(protocol.status_command_type(self.handshake_params))

        async for cmd in multiplexer.stream_protocol_messages(protocol):
            if not isinstance(cmd, (StatusV1, StatusV2)):
                raise HandshakeFailure(
                    f"Expected a LES Status msg, got {cmd}, disconnecting")

            if cmd.payload.network_id != self.handshake_params.network_id:
                raise WrongNetworkFailure(
                    f"{multiplexer.remote} network "
                    f"({cmd.payload.network_id}) does not match ours "
                    f"({self.handshake_params.network_id}), disconnecting")

            if cmd.payload.genesis_hash != self.handshake_params.genesis_hash:
                raise WrongGenesisFailure(
                    f"{multiplexer.remote} genesis "
                    f"({encode_hex(cmd.payload.genesis_hash)}) does "
                    f"not match ours "
                    f"({encode_hex(self.handshake_params.genesis_hash)}), "
                    f"disconnecting")

            # Eventually we might want to keep connections to peers where we
            # are the only side serving data, but right now both our chain
            # syncer and the Peer.boot() method expect the remote to reply to
            # header requests, so if they don't we simply disconnect here.
            if cmd.payload.serve_headers is False:
                raise HandshakeFailure(
                    f"{multiplexer.remote} doesn't serve headers, disconnecting"
                )

            receipt = LESHandshakeReceipt(protocol, cmd.payload)
            break
        else:
            raise HandshakeFailure(
                "Message stream exited before finishing handshake")

        return receipt
Exemplo n.º 17
0
    async def do_p2p_handshake(self):
        """Perform the handshake for the P2P base protocol.

        Raises HandshakeFailure if the handshake is not successful.
        """
        self.base_protocol.send_handshake()

        try:
            cmd, msg = await self.read_msg()
        except rlp.DecodingError:
            raise HandshakeFailure("Got invalid rlp data during handshake")

        if isinstance(cmd, Disconnect):
            # Peers sometimes send a disconnect msg before they send the initial P2P handshake.
            raise HandshakeFailure("{} disconnected before completing handshake: {}".format(
                self, msg['reason_name']))
        self.process_p2p_handshake(cmd, msg)
Exemplo n.º 18
0
 async def do_handshake(self, peer: BasePeer) -> None:
     try:
         await peer.do_p2p_handshake(),
     except MalformedMessage as e:
         raise HandshakeFailure() from e
     await peer.do_sub_proto_handshake()
     await peer.ensure_same_side_on_dao_fork(self.chain.vm_configuration)
     self._start_peer(peer)
Exemplo n.º 19
0
    async def do_handshake(self,
                           multiplexer: MultiplexerAPI,
                           protocol: ProtocolAPI) -> ETHHandshakeReceipt:
        """Perform the handshake for the sub-protocol agreed with the remote peer.

        Raises HandshakeFailure if the handshake is not successful.
        """
        protocol = cast(ETHProtocol, protocol)
        protocol.send_handshake(self.handshake_params)

        async for cmd, msg in multiplexer.stream_protocol_messages(protocol):
            if not isinstance(cmd, Status):
                raise HandshakeFailure(f"Expected a ETH Status msg, got {cmd}, disconnecting")

            msg = cast(Dict[str, Any], msg)

            remote_params = ETHHandshakeParams(
                version=msg['protocol_version'],
                network_id=msg['network_id'],
                total_difficulty=msg['td'],
                head_hash=msg['best_hash'],
                genesis_hash=msg['genesis_hash'],
            )
            receipt = ETHHandshakeReceipt(protocol, remote_params)

            if receipt.handshake_params.network_id != self.handshake_params.network_id:
                raise WrongNetworkFailure(
                    f"{multiplexer.remote} network "
                    f"({receipt.handshake_params.network_id}) does not match ours "
                    f"({self.handshake_params.network_id}), disconnecting"
                )

            if receipt.handshake_params.genesis_hash != self.handshake_params.genesis_hash:
                raise WrongGenesisFailure(
                    f"{multiplexer.remote} genesis "
                    f"({encode_hex(receipt.handshake_params.genesis_hash)}) does "
                    f"not match ours ({encode_hex(self.handshake_params.genesis_hash)}), "
                    f"disconnecting"
                )

            break
        else:
            raise HandshakeFailure("Message stream exited before finishing handshake")

        return receipt
Exemplo n.º 20
0
 async def check_handshake_timeout(self, handshake_successful_event: trio.Event) -> None:
     try:
         with trio.fail_after(HANDSHAKE_TIMEOUT):
             # Only the timeout for successful handshakes has to be checked as a failure during
             # handshake will make the service as a whole fail.
             await handshake_successful_event.wait()
     except trio.TooSlowError as too_slow_error:
         self.logger.warning("Handshake with %s has timed out", encode_hex(self.remote_node_id))
         raise HandshakeFailure("Handshake has timed out") from too_slow_error
Exemplo n.º 21
0
 async def process_p2p_handshake(
         self, cmd: protocol.Command, msg: protocol.PayloadType) -> None:
     msg = cast(Dict[str, Any], msg)
     if not isinstance(cmd, Hello):
         await self.disconnect(DisconnectReason.bad_protocol)
         raise HandshakeFailure(f"Expected a Hello msg, got {cmd}, disconnecting")
     remote_capabilities = msg['capabilities']
     try:
         self.sub_proto = self.select_sub_protocol(remote_capabilities)
     except NoMatchingPeerCapabilities:
         await self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(
             f"No matching capabilities between us ({self.capabilities}) and {self.remote} "
             f"({remote_capabilities}), disconnecting"
         )
     self.logger.debug(
         "Finished P2P handshake with %s, using sub-protocol %s",
         self.remote, self.sub_proto)
Exemplo n.º 22
0
 def process_sub_proto_handshake(self, cmd: protocol.Command,
                                 msg: protocol._DecodedMsgType) -> None:
     if not isinstance(cmd, eth.Status):
         self.disconnect(DisconnectReason.other)
         raise HandshakeFailure(
             "Expected a ETH Status msg, got {}, disconnecting".format(cmd))
     msg = cast(Dict[str, Any], msg)
     if msg['network_id'] != self.network_id:
         self.disconnect(DisconnectReason.other)
         raise HandshakeFailure(
             "{} network ({}) does not match ours ({}), disconnecting".
             format(self, msg['network_id'], self.network_id))
     if msg['genesis_hash'] != self.genesis.hash:
         self.disconnect(DisconnectReason.other)
         raise HandshakeFailure(
             "{} genesis ({}) does not match ours ({}), disconnecting".
             format(self, encode_hex(msg['genesis_hash']),
                    self.genesis.hex_hash))
async def test_records_failures():
    connection_tracker = MemoryConnectionTracker()

    node = NodeFactory()
    assert await connection_tracker.should_connect_to(node) is True

    connection_tracker.record_failure(node, HandshakeFailure())

    assert await connection_tracker.should_connect_to(node) is False
    assert connection_tracker._record_exists(node.uri())
Exemplo n.º 24
0
async def process_v4_p2p_handshake(self: BasePeer, cmd: protocol.Command,
                                   msg: protocol.Payload) -> None:
    """
    This function is the replacement to the existing process_p2p_handshake
    function.
    This is used to simulate the v4 P2PProtocol node.
    The only change that has been made is to remove the snappy support irrespective
    of whether the other client supports it or not.
    """
    msg = cast(Dict[str, Any], msg)
    if not isinstance(cmd, Hello):
        await self.disconnect(DisconnectReason.bad_protocol)
        raise HandshakeFailure(
            f"Expected a Hello msg, got {cmd}, disconnecting")

    # As far as a v4 P2PProtocol client is concerned,
    # it never support snappy compression
    snappy_support = False

    remote_capabilities = msg['capabilities']
    matched_proto_classes = match_protocols_with_capabilities(
        self.supported_sub_protocols,
        remote_capabilities,
    )
    if len(matched_proto_classes) == 1:
        self.sub_proto = matched_proto_classes[0](
            self.transport,
            self.base_protocol.cmd_length,
            snappy_support,
        )
    elif len(matched_proto_classes) > 1:
        raise NotImplementedError(
            f"Peer {self.remote} connection matched on multiple protocols "
            f"{matched_proto_classes}.  Support for multiple protocols is not "
            f"yet supported")
    else:
        await self.disconnect(DisconnectReason.useless_peer)
        raise HandshakeFailure(
            f"No matching capabilities between us ({self.capabilities}) and {self.remote} "
            f"({remote_capabilities}), disconnecting")

    self.logger.debug("Finished P2P handshake with %s, using sub-protocol %s",
                      self.remote, self.sub_proto)
Exemplo n.º 25
0
    async def process_p2p_handshake(self, cmd: protocol.Command,
                                    msg: protocol.PayloadType) -> None:
        msg = cast(Dict[str, Any], msg)
        if not isinstance(cmd, Hello):
            await self.disconnect(DisconnectReason.bad_protocol)
            raise HandshakeFailure(
                f"Expected a Hello msg, got {cmd}, disconnecting")

        # limit number of chars to be displayed, and try to keep printable ones only
        # MAGIC 256: arbitrary, "should be enough for everybody"
        original_version = msg['client_version_string']
        client_version_string = original_version[:256] + (
            '...' if original_version[256:] else '')
        if client_version_string.isprintable():
            self.client_version_string = client_version_string.strip()
        else:
            self.client_version_string = repr(client_version_string)

        # Check whether to support Snappy Compression or not
        # based on other peer's p2p protocol version
        snappy_support = msg['version'] >= SNAPPY_PROTOCOL_VERSION

        if snappy_support:
            # Now update the base protocol to support snappy compression
            # This is needed so that Trinity is compatible with parity since
            # parity sends Ping immediately after Handshake
            self.base_protocol = P2PProtocol(self,
                                             snappy_support=snappy_support)

        remote_capabilities = msg['capabilities']
        try:
            self.sub_proto = self.select_sub_protocol(remote_capabilities,
                                                      snappy_support)
        except NoMatchingPeerCapabilities:
            await self.disconnect(DisconnectReason.useless_peer)
            raise HandshakeFailure(
                f"No matching capabilities between us ({self.capabilities}) and {self.remote} "
                f"({remote_capabilities}), disconnecting")

        self.logger.debug(
            "Finished P2P handshake with %s, using sub-protocol %s",
            self.remote, self.sub_proto)
Exemplo n.º 26
0
Arquivo: peer.py Projeto: wimel/py-evm
 async def process_sub_proto_handshake(
         self, cmd: Command, msg: _DecodedMsgType) -> None:
     if not isinstance(cmd, Status):
         await self.disconnect(DisconnectReason.subprotocol_error)
         raise HandshakeFailure(
             "Expected a ETH Status msg, got {}, disconnecting".format(cmd))
     msg = cast(Dict[str, Any], msg)
     if msg['network_id'] != self.network_id:
         await self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(
             "{} network ({}) does not match ours ({}), disconnecting".format(
                 self, msg['network_id'], self.network_id))
     genesis = await self.genesis
     if msg['genesis_hash'] != genesis.hash:
         await self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(
             "{} genesis ({}) does not match ours ({}), disconnecting".format(
                 self, encode_hex(msg['genesis_hash']), genesis.hex_hash))
     self.head_td = msg['td']
     self.head_hash = msg['best_hash']
Exemplo n.º 27
0
 async def process_sub_proto_handshake(
         self, cmd: protocol.Command, msg: protocol._DecodedMsgType) -> None:
     if not isinstance(cmd, (les.Status, les.StatusV2)):
         self.disconnect(DisconnectReason.subprotocol_error)
         raise HandshakeFailure(
             "Expected a LES Status msg, got {}, disconnecting".format(cmd))
     msg = cast(Dict[str, Any], msg)
     if msg['networkId'] != self.network_id:
         self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(
             "{} network ({}) does not match ours ({}), disconnecting".format(
                 self, msg['networkId'], self.network_id))
     genesis = await self.genesis
     if msg['genesisHash'] != genesis.hash:
         self.disconnect(DisconnectReason.useless_peer)
         raise HandshakeFailure(
             "{} genesis ({}) does not match ours ({}), disconnecting".format(
                 self, encode_hex(msg['genesisHash']), genesis.hex_hash))
     # TODO: Disconnect if the remote doesn't serve headers.
     self.head_info = cmd.as_head_info(msg)
Exemplo n.º 28
0
def test_timeout_for_failure():
    get_timeout = persistence.timeout_for_failure

    assert get_timeout(WrongGenesisFailure()) == 60 * 60 * 24
    assert get_timeout(HandshakeFailure()) == 10

    class UnknownException(Exception):
        pass

    with pytest.raises(Exception, match="Unknown failure type"):
        assert get_timeout(UnknownException()) is None
Exemplo n.º 29
0
    async def do_handshake(self, multiplexer: MultiplexerAPI,
                           protocol: BaseETHProtocol) -> ETHHandshakeReceipt:
        """
        Perform the handshake for the sub-protocol agreed with the remote peer.

        Raise HandshakeFailure if the handshake is not successful.
        """

        self.logger.debug("Performing %s handshake with %s", protocol,
                          multiplexer.remote)
        protocol.send(Status(self.handshake_params))

        async for cmd in multiplexer.stream_protocol_messages(protocol):
            if not isinstance(cmd, Status):
                raise HandshakeFailure(
                    f"Expected a ETH Status msg, got {cmd}, disconnecting")

            receipt = ETHHandshakeReceipt(protocol, cmd.payload)

            validate_base_receipt(multiplexer.remote, receipt,
                                  self.handshake_params)

            try:
                validate_forkid(
                    receipt.fork_id,
                    self.handshake_params.genesis_hash,
                    self.head_number,
                    self.fork_blocks,
                )
            except BaseForkIDValidationError as exc:
                raise WrongForkIDFailure(
                    f"{multiplexer.remote} forkid "
                    f"({receipt.handshake_params.fork_id}) is incompatible to ours ({exc})"
                    f"({self.handshake_params.fork_id}), disconnecting")

            break
        else:
            raise HandshakeFailure(
                "Message stream exited before finishing handshake")

        return receipt
async def test_records_failures():
    connection_tracker = MemoryConnectionTracker()

    node = NodeFactory()
    blacklisted_ids = await connection_tracker.get_blacklisted()
    assert node.id not in blacklisted_ids

    connection_tracker.record_failure(node, HandshakeFailure())

    blacklisted_ids = await connection_tracker.get_blacklisted()
    assert node.id in blacklisted_ids
    assert connection_tracker._record_exists(node.id)