def send_message_to_middleman_and_receive_response(
    message: AbstractFrame,
    config: configparser.ConfigParser,
    concent_private_key: bytes,
    concent_public_key: bytes,
) -> GolemMessageFrame:
    """ Sends message to MiddleMan using MiddleMan protocol and retrieves response. """
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

    try:
        client_socket.connect((
            config.get(Components.MIDDLEMAN.value, 'host'),
            int(config.get(Components.MIDDLEMAN.value, 'port')),
        ))
        send_over_stream(
            connection=client_socket,
            raw_message=message,
            private_key=concent_private_key,
        )
        receive_frame_generator = unescape_stream(connection=client_socket)
        raw_response = next(receive_frame_generator)
        return GolemMessageFrame.deserialize(
            raw_message=raw_response,
            public_key=concent_public_key,
        )
    finally:
        client_socket.close()
Ejemplo n.º 2
0
    def test_that_receiving_wrongly_encoded_message_should_return_none(
            self, unused_tcp_port):
        middleman_message = GolemMessageFrame(Ping(), self.request_id)
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY, )

        raw_message_encoded = escape_encode_raw_message(raw_message)
        raw_message_encoded = raw_message_encoded + ESCAPE_CHARACTER + b'\xff'
        raw_message_encoded = append_frame_separator(raw_message_encoded)

        with closing(socket.socket(socket.AF_INET,
                                   socket.SOCK_STREAM)) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     True)
            with closing(socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)) as client_socket:
                client_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, True)
                server_socket.bind(('127.0.0.1', unused_tcp_port))
                server_socket.listen(1)

                client_socket.connect(('127.0.0.1', unused_tcp_port))

                (connection, _address) = server_socket.accept()

                client_socket.send(raw_message_encoded)
                raw_message_received = next(
                    unescape_stream(connection=connection))

        assertpy.assert_that(raw_message_received).is_none()
Ejemplo n.º 3
0
def send_request_to_middleman(middleman_message: GolemMessageFrame) -> bytes:
    """
    Opens socket connection to Middleman, sends Frame to MiddleMan through MiddleMan Protocol and receive response.

    Returns raw Frame as bytes.
    """

    assert isinstance(middleman_message, GolemMessageFrame)

    with closing(socket.socket(socket.AF_INET,
                               socket.SOCK_STREAM)) as client_socket:
        client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        client_socket.connect(
            (settings.MIDDLEMAN_ADDRESS, settings.MIDDLEMAN_PORT))
        client_socket.settimeout(SCI_CALLBACK_MAXIMUM_TIMEOUT)

        # Send the TransactionSigningRequest.
        try:
            send_over_stream(
                connection=client_socket,
                raw_message=middleman_message,
                private_key=settings.CONCENT_PRIVATE_KEY,
            )

            # Read the response and close the connection.
            return next(unescape_stream(connection=client_socket))
        # Connection with Middleman times out before a complete response is received.
        except socket.timeout as exception:
            raise SCICallbackTimeoutError() from exception
    def _prepare_and_execute_handle_connection(
            self,
            raw_message,
            handle_connection_wrapper=None,
            expect_response_from_scoket=True):
        def mocked_generator():
            yield raw_message
            raise SigningServiceValidationError()

        with mock.patch('signing_service.signing_service.SigningService.run'):
            with closing(socket.socket(
                    socket.AF_INET,
                    socket.SOCK_STREAM)) as signing_service_socket:
                signing_service_socket.setsockopt(socket.SOL_SOCKET,
                                                  socket.SO_REUSEADDR, 1)
                with closing(socket.socket(
                        socket.AF_INET, socket.SOCK_STREAM)) as client_socket:
                    client_socket.setsockopt(socket.SOL_SOCKET,
                                             socket.SO_REUSEADDR, 1)
                    signing_service = SigningService(
                        self.host,
                        self.port,
                        self.initial_reconnect_delay,
                        CONCENT_PUBLIC_KEY,
                        SIGNING_SERVICE_PRIVATE_KEY,
                        TEST_ETHEREUM_PRIVATE_KEY,
                        SIGNING_SERVICE_DEFAULT_RECONNECT_ATTEMPTS,
                        ConsoleNotifier(),
                    )

                    # For test purposes we reverse roles, so signing service works as server.
                    signing_service_socket.bind(
                        ('127.0.0.1', self.signing_service_port))
                    signing_service_socket.listen(1)
                    client_socket.connect(
                        ('127.0.0.1', self.signing_service_port))
                    (connection, _address) = signing_service_socket.accept()
                    client_socket.setblocking(False)

                    with pytest.raises(SigningServiceValidationError):
                        if handle_connection_wrapper is not None:
                            handle_connection_wrapper(signing_service,
                                                      connection,
                                                      mocked_generator())
                        else:
                            signing_service._handle_connection(
                                mocked_generator(), connection)

                    if expect_response_from_scoket:
                        response = next(
                            unescape_stream(connection=client_socket))
                    else:
                        # We do not expect to get anything from the socket, dummy response is returned.
                        response = mock.sentinel.no_response

        return response
Ejemplo n.º 5
0
    def test_that_exceeding_maximum_frame_length_should_treat_exceeded_frame_as_invalid(
            self, unused_tcp_port):
        first_middleman_message = GolemMessageFrame(Ping(), self.request_id)
        first_raw_message = append_frame_separator(
            escape_encode_raw_message(
                first_middleman_message.serialize(
                    private_key=CONCENT_PRIVATE_KEY)))
        second_middleman_message = AuthenticationChallengeFrame(
            payload=b'',
            request_id=100,
        )
        second_raw_message = append_frame_separator(
            escape_encode_raw_message(
                second_middleman_message.serialize(
                    private_key=CONCENT_PRIVATE_KEY)))

        assert len(first_raw_message) > len(second_raw_message) + 10

        with closing(socket.socket(socket.AF_INET,
                                   socket.SOCK_STREAM)) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     True)
            with closing(socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)) as client_socket:
                client_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, True)
                server_socket.bind(('127.0.0.1', unused_tcp_port))
                server_socket.listen(1)

                client_socket.connect(('127.0.0.1', unused_tcp_port))

                (connection, _address) = server_socket.accept()

                client_socket.send(first_raw_message)
                client_socket.send(second_raw_message)

                with mock.patch(
                        'middleman_protocol.stream.MAXIMUM_FRAME_LENGTH',
                        len(first_raw_message) - 10):
                    raw_message_received = next(
                        unescape_stream(connection=connection))

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=CONCENT_PUBLIC_KEY,
        )

        assertpy.assert_that(deserialized_message.request_id).is_equal_to(100)
Ejemplo n.º 6
0
    def test_that_receiving_a_series_of_messages_should_be_handled_correctly(
            self, unused_tcp_port):
        payload = Ping()
        middleman_message = GolemMessageFrame(payload, self.request_id)

        with closing(socket.socket(socket.AF_INET,
                                   socket.SOCK_STREAM)) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     True)
            with closing(socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)) as client_socket:
                client_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, True)
                server_socket.bind(('127.0.0.1', unused_tcp_port))
                server_socket.listen(1)

                client_socket.connect(('127.0.0.1', unused_tcp_port))

                (connection, _address) = server_socket.accept()

                for _i in range(10):
                    send_over_stream(connection=client_socket,
                                     raw_message=middleman_message,
                                     private_key=CONCENT_PRIVATE_KEY)

                unescape_stream_generator = unescape_stream(
                    connection=connection)

                for _i in range(10):
                    raw_message_received = next(unescape_stream_generator)

                    deserialized_message = AbstractFrame.deserialize(
                        raw_message=raw_message_received,
                        public_key=CONCENT_PUBLIC_KEY,
                    )

                    assertpy.assert_that(deserialized_message).is_instance_of(
                        GolemMessageFrame)
                    assertpy.assert_that(
                        deserialized_message.payload).is_instance_of(Ping)
                    assertpy.assert_that(
                        deserialized_message.payload).is_equal_to(payload)
Ejemplo n.º 7
0
    def _connect(self, tcp_socket: socket.socket) -> None:
        """ Creates socket and connects to given HOST and PORT. """
        if self.current_reconnect_delay is not None:
            logger.info(f'Waiting {self.current_reconnect_delay} before connecting.')
            sleep(self.current_reconnect_delay)

        logger.info(f'Connecting to {self.host}:{self.port}.')
        tcp_socket.settimeout(CONNECTION_TIMEOUT)
        tcp_socket.connect((self.host, self.port))
        logger.info(f'Connection established.')
        # Frame generator must be created here and passed to _authenticate() and _handle_connection(), because upon its
        # destruction it closes the socket.
        receive_frame_generator = unescape_stream(connection=tcp_socket)

        self._authenticate(receive_frame_generator, tcp_socket)
        # Reset delay and reconnection counter on successful authentication and set flag that connection is established.
        self.current_reconnect_delay = None
        self.reconnection_counter = 0
        self.signing_service_daily_transaction_sum_so_far = self._get_signing_service_daily_transaction_sum_so_far()
        self._handle_connection(receive_frame_generator, tcp_socket)
Ejemplo n.º 8
0
    def _prepare_and_execute_handle_connection(self, raw_message):
        def mocked_generator():
            yield raw_message

        with mock.patch('signing_service.signing_service.SigningService.run'):
            with closing(socket.socket(
                    socket.AF_INET,
                    socket.SOCK_STREAM)) as signing_service_socket:
                signing_service_socket.setsockopt(socket.SOL_SOCKET,
                                                  socket.SO_REUSEADDR, 1)
                with closing(socket.socket(
                        socket.AF_INET, socket.SOCK_STREAM)) as client_socket:
                    client_socket.setsockopt(socket.SOL_SOCKET,
                                             socket.SO_REUSEADDR, 1)
                    signing_service = SigningService(
                        self.host,
                        self.port,
                        self.initial_reconnect_delay,
                        CONCENT_PUBLIC_KEY,
                        SIGNING_SERVICE_PRIVATE_KEY,
                        TEST_ETHEREUM_PRIVATE_KEY,
                        SIGNING_SERVICE_DEFAULT_RECONNECT_ATTEMPTS,
                        ConsoleNotifier(),
                    )

                    # For test purposes we reverse roles, so signing service works as server.
                    signing_service_socket.bind(
                        ('127.0.0.1', self.signing_service_port))
                    signing_service_socket.listen(1)
                    client_socket.connect(
                        ('127.0.0.1', self.signing_service_port))
                    (connection, _address) = signing_service_socket.accept()

                    signing_service._authenticate(mocked_generator(),
                                                  connection)
                    raw_message_received = next(
                        unescape_stream(connection=client_socket))

        return raw_message_received
Ejemplo n.º 9
0
    def test_that_sending_message_over_tcp_socket_should_preserve_original_data(
        self,
        middleman_message_type,
        payload,
        unused_tcp_port,
    ):
        middleman_message = middleman_message_type(payload, self.request_id)

        with closing(socket.socket(socket.AF_INET,
                                   socket.SOCK_STREAM)) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     True)
            with closing(socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)) as client_socket:
                client_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, True)
                server_socket.bind(('127.0.0.1', unused_tcp_port))
                server_socket.listen(1)

                client_socket.connect(('127.0.0.1', unused_tcp_port))

                (connection, _address) = server_socket.accept()

                send_over_stream(connection=client_socket,
                                 raw_message=middleman_message,
                                 private_key=CONCENT_PRIVATE_KEY)
                raw_message_received = next(
                    unescape_stream(connection=connection))

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=CONCENT_PUBLIC_KEY,
        )

        assertpy.assert_that(deserialized_message).is_instance_of(
            middleman_message_type)
        assertpy.assert_that(deserialized_message.payload).is_instance_of(
            type(payload))
        assertpy.assert_that(deserialized_message.payload).is_equal_to(payload)
Ejemplo n.º 10
0
    def test_that_receiving_encoded_message_should_decode_on_the_fly(
            self, unused_tcp_port):
        middleman_message = GolemMessageFrame(Ping(), self.request_id)
        raw_message = append_frame_separator(
            middleman_message.serialize(private_key=CONCENT_PRIVATE_KEY))

        raw_message = raw_message[:10] + ESCAPE_CHARACTER + raw_message[
            len(ESCAPE_CHARACTER) + 10:]
        raw_message_encoded = escape_encode_raw_message(raw_message)

        assert FRAME_SEPARATOR not in raw_message_encoded

        raw_message_encoded = append_frame_separator(raw_message_encoded)

        with closing(socket.socket(socket.AF_INET,
                                   socket.SOCK_STREAM)) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     True)
            with closing(socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)) as client_socket:
                client_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, True)
                server_socket.bind(('127.0.0.1', unused_tcp_port))
                server_socket.listen(1)

                client_socket.connect(('127.0.0.1', unused_tcp_port))

                (connection, _address) = server_socket.accept()

                client_socket.send(raw_message_encoded)
                raw_message_received = next(
                    unescape_stream(connection=connection))

        assertpy_bytes_starts_with(raw_message, raw_message_received)
        assertpy.assert_that(
            len(raw_message_received)).is_greater_than_or_equal_to(
                FRAME_PAYLOAD_STARTING_BYTE)
Ejemplo n.º 11
0
def test_case_1_middleman_recovery(config: ConfigParser,
                                   **kwargs: Any) -> None:
    """
    1. Spawn MiddleMan and SigningService processes.
    2. Client sends GolemMessageFrame with correct TransactionSigningRequest to MiddleMan.
    3. Middleman is restarted. The connection and latest message is lost.
    4. Client sends GolemMessageFrame with correct TransactionSigningRequest to MiddleMan.
    5. Client receives response for latest message.
    """

    try:
        middleman_process = run_middleman()
        signing_service_process = run_signing_service()

        # Waiting for MiddleMan and SigningService to start.
        sleep(SLEEP_TIME_AFTER_SPAWNING_PROCESS)

        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

        # Create GolemMessageFrame with correct TransactionSigningRequest.
        golem_message_frame = create_golem_message_frame_with_correct_transaction_signing_request(
            request_id=get_current_utc_timestamp(), )

        client_socket.connect((
            config.get(Components.MIDDLEMAN.value, 'host'),
            int(config.get(Components.MIDDLEMAN.value, 'port')),
        ))
        send_over_stream(
            connection=client_socket,
            raw_message=golem_message_frame,
            private_key=CONCENT_PRIVATE_KEY,
        )

        middleman_process.kill()

        # Waiting for MiddleMan to finish.
        sleep(SLEEP_TIME_AFTER_KILLING_PROCESS)

        middleman_process = run_middleman()

        # Waiting for MiddleMan to start.
        sleep(SLEEP_TIME_AFTER_SPAWNING_PROCESS)

        receive_frame_generator = unescape_stream(connection=client_socket)
        try:
            next(receive_frame_generator)
        except socket.error as exception:
            assert_condition(
                exception.args[0], socket.errno.ECONNRESET,
                f'Connection should be reset by peer.')  # type: ignore

        # Create GolemMessageFrame with correct TransactionSigningRequest.
        golem_message_frame = create_golem_message_frame_with_correct_transaction_signing_request(
            request_id=get_current_utc_timestamp(), )

        # Send message through wrapper and receive deserialized response.
        response = send_message_to_middleman_and_receive_response(
            message=golem_message_frame,
            config=config,
            concent_private_key=CONCENT_PRIVATE_KEY,
            concent_public_key=CONCENT_PUBLIC_KEY,
        )

        # Check response.
        assert_condition(
            type(response), GolemMessageFrame,
            f'Deserialized response type is {type(response)} instead of GolemMessageFrame.'
        )
        assert_condition(
            type(response.payload), SignedTransaction,
            f'Deserialized response payload type is {type(response.payload)} instead of SignedTransaction.'
        )
        assert_condition(
            response.request_id, golem_message_frame.request_id,
            f'Deserialized response request_id is {response.request_id} instead of {golem_message_frame.request_id}.'
        )

    finally:
        middleman_process.kill()
        signing_service_process.kill()