Exemplo n.º 1
0
    def test_that_serializing_and_deserializing_message_with_wrong_payload_type_should_raise_exception(
            self):
        middleman_message = GolemMessageFrame(Ping(), self.request_id)
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        payload_type_position = FRAME_SIGNATURE_BYTES_LENGTH + FRAME_REQUEST_ID_BYTES_LENGTH
        invalid_payload_type = 100

        # Sanity check for payload type in this case to be between expected bytes
        assert raw_message[payload_type_position:payload_type_position +
                           1] == b'\x00'
        assert invalid_payload_type not in PAYLOAD_TYPE_TO_MIDDLEMAN_MESSAGE_CLASS

        # Replace bytes with payload length
        raw_message = (
            raw_message[:payload_type_position] +
            bytes(bytearray([invalid_payload_type])) +
            raw_message[payload_type_position + FRAME_PAYLOAD_TYPE_LENGTH:])

        # Replace message signature
        new_signature = ecdsa_sign(CONCENT_PRIVATE_KEY,
                                   raw_message[FRAME_SIGNATURE_BYTES_LENGTH:])
        raw_message_with_new_signature = new_signature + raw_message[
            FRAME_SIGNATURE_BYTES_LENGTH:]

        with pytest.raises(PayloadTypeInvalidMiddlemanProtocolError):
            AbstractFrame.deserialize(raw_message_with_new_signature,
                                      CONCENT_PUBLIC_KEY)
    def test_that__handle_connection_should_send_error_frame_if_payload_golem_message_type_cannot_be_deserialized(
            self):
        # Prepare frame payload which is Golem message that cannot be deserialized.
        middleman_message = GolemMessageFrame(
            payload=self._get_deserialized_transaction_signing_request(
                nonce=
                'not_int_nonce_which_will_fail_on_deserialization_causing_message_error'
            ),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        raw_message_received = self._prepare_and_execute_handle_connection(
            raw_message)

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

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.InvalidPayload)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
Exemplo n.º 3
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)
        raw_message = escape_encode_raw_message(
            middleman_message.serialize(private_key=CONCENT_PRIVATE_KEY))
        raw_message_with_separator = append_frame_separator(raw_message)

        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):
                    client_socket.send(raw_message_with_separator)

                split_stream_generator = split_stream(connection=connection)

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

                    assertpy.assert_that(raw_message).is_equal_to(
                        raw_message_received)
Exemplo n.º 4
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()
    def test_that__handle_connection_should_send_error_frame_if_payload_golem_message_type_is_unexpected(
            self):
        # Prepare frame payload which is Golem message other than TransactionSigningRequest.
        middleman_message = GolemMessageFrame(
            payload=Ping(),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        raw_message_received = self._prepare_and_execute_handle_connection(
            raw_message)

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

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.UnexpectedMessage)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
    def test_that__handle_connection_should_send_golem_message_signed_transaction_if_warning_daily_threshold_exceeded(
            self):
        middleman_message = GolemMessageFrame(
            payload=self._get_deserialized_transaction_signing_request(),
            request_id=99,
        )
        middleman_message.payload.value = WARNING_DAILY_THRESHOLD + 1
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        def handle_connection_wrapper(signing_service, connection,
                                      receive_frame_generator):
            with mock.patch(
                    'signing_service.signing_service.SigningService._get_signed_transaction',
                    return_value=self._get_deserialized_signed_transaction(),
            ):
                with mock.patch(
                        'signing_service.signing_service.SigningService._add_payload_value_to_daily_transactions_sum'
                ):
                    signing_service._handle_connection(receive_frame_generator,
                                                       connection)

        raw_message_received = self._prepare_and_execute_handle_connection(
            raw_message,
            handle_connection_wrapper,
        )

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

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(SignedTransaction)
    def test_that__handle_connection_should_send_error_frame_if_frame_signature_is_wrong(
            self):
        # Prepare message with wrong signature.
        middleman_message = GolemMessageFrame(
            payload=self._get_deserialized_transaction_signing_request(),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)
        first_byte = 2 if raw_message[0] == 0 else raw_message[0]
        malformed_raw_message = bytes(bytearray([first_byte - 1
                                                 ])) + raw_message[1:]

        raw_message_received = self._prepare_and_execute_handle_connection(
            malformed_raw_message)

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

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.InvalidFrameSignature)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
Exemplo n.º 8
0
def test_that_sent_data_is_escaped_and_contains_frame_separator(event_loop):
    frame = GolemMessageFrame(Ping(), 777)
    expected_data = append_frame_separator(
        escape_encode_raw_message(frame.serialize(CONCENT_PRIVATE_KEY)))
    mocked_writer = prepare_mocked_writer()

    task = _run_test_in_event_loop(event_loop, send_over_stream_async, frame,
                                   mocked_writer, CONCENT_PRIVATE_KEY)

    assert_that(task.done()).is_true()
    mocked_writer.write.assert_called_once_with(expected_data)
    mocked_writer.drain.mock.assert_called_once_with()
Exemplo n.º 9
0
    def test_that_serializing_different_golem_message_middleman_messages_should_keep_part_of_header_the_same(
            self):
        message_1 = GolemMessageFrame(
            ServiceRefused(reason=ServiceRefused.REASON.InvalidRequest),
            self.request_id).serialize(private_key=CONCENT_PRIVATE_KEY)
        message_2 = GolemMessageFrame(
            Ping(), self.request_id).serialize(private_key=CONCENT_PRIVATE_KEY)

        assertpy.assert_that(
            message_1[FRAME_SIGNATURE_BYTES_LENGTH:FRAME_PAYLOAD_STARTING_BYTE]
        ).is_equal_to(
            message_2[FRAME_SIGNATURE_BYTES_LENGTH:FRAME_PAYLOAD_STARTING_BYTE]
        )
Exemplo n.º 10
0
def test_that_when_frame_with_escaped_sequence_and_separator_is_received_unescaped_frame_is_returned(
        event_loop):
    golem_message_frame = GolemMessageFrame(Ping(), 777)
    data_to_send = escape_encode_raw_message(
        golem_message_frame.serialize(CONCENT_PRIVATE_KEY)
    ) + ESCAPE_SEQUENCES[ESCAPE_CHARACTER] + FRAME_SEPARATOR
    mocked_reader = prepare_mocked_reader(data_to_send)

    task = _run_test_in_event_loop(event_loop, handle_frame_receive_async,
                                   mocked_reader, CONCENT_PUBLIC_KEY)

    assert_that(task.done()).is_true()
    mocked_reader.readuntil.mock.assert_called_once_with(FRAME_SEPARATOR)
    assert_that(task.result()).is_equal_to(golem_message_frame)
Exemplo n.º 11
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)
Exemplo n.º 12
0
    def test_that_raising_error_in_generator_should_call_close_on_socket(
            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()

                send_over_stream(connection=client_socket,
                                 raw_message=middleman_message,
                                 private_key=CONCENT_PRIVATE_KEY)

                split_stream_generator = split_stream(connection=connection)

                with mock.patch('middleman_protocol.stream.socket.socket.recv',
                                side_effect=Exception()):
                    with mock.patch(
                            'middleman_protocol.stream.socket.socket.close'
                    ) as mock_socket_close:
                        with pytest.raises(Exception):
                            next(split_stream_generator)

                mock_socket_close.assert_called_once()
Exemplo n.º 13
0
def sci_callback(transaction: Transaction) -> Transaction:
    """
    This is callback that submits the transaction to the Signing Service
    and does not rely on having access to the private key.

    Returns signed Ethereum Transaction.
    """
    assert isinstance(transaction, Transaction)

    # Create a TransactionSigningRequest.
    transaction_signing_request = create_transaction_signing_request(transaction)

    # Generate request ID.
    request_id = RequestIDGenerator.generate_request_id()

    # Create a MiddleMan Protocol Golem Message Frame.
    middleman_message = GolemMessageFrame(
        payload=transaction_signing_request,
        request_id=request_id,
    )

    # Send Frame to MiddleMan through MiddleMan Protocol and receive response.
    raw_response = send_request_to_middleman(middleman_message)

    # Deserialize received Frame and its payload and handle related errors.
    signed_transaction = deserialize_response_and_handle_errors(raw_response, request_id)

    assert isinstance(signed_transaction, SignedTransaction)

    # Verify received SignedTransaction signature and handle related errors.
    verify_data_and_signature(signed_transaction, transaction)

    # If the response is a valid SignedTransaction, copy received signature into Transaction.
    copy_transaction_signature(signed_transaction, transaction)
    return transaction
Exemplo n.º 14
0
    async def test_that_when_connection_exists_item_from_the_queue_is_sent_via_writer(self, event_loop):
        with patch("middleman.asynchronous_operations.logger") as mocked_logger:
            with override_settings(
                CONCENT_PRIVATE_KEY=CONCENT_PRIVATE_KEY,
                CONCENT_PUBLIC_KEY=CONCENT_PUBLIC_KEY,
            ):
                expected_data = append_frame_separator(
                    escape_encode_raw_message(
                        GolemMessageFrame(self.golem_message, self.signing_service_request_id).serialize(CONCENT_PRIVATE_KEY)
                    )
                )

                await self.queue.put(self.request_queue_item)
                consumer_task = event_loop.create_task(
                    request_consumer(
                        self.queue,
                        self.queue_pool,
                        self.message_tracker,
                        self.mocked_writer
                    )
                )
                await self.queue.join()
                consumer_task.cancel()

                assert_that(self.message_tracker[self.signing_service_request_id]).is_equal_to(
                    MessageTrackerItem(
                        self.request_id,
                        self.connection_id,
                        self.golem_message,
                        FROZEN_TIMESTAMP
                    )
                )
                mocked_logger.debug.assert_not_called()
                self.mocked_writer.write.assert_called_once_with(expected_data)
                self.mocked_writer.drain.mock.assert_called_once_with()
Exemplo n.º 15
0
    async def test_that_received_item_received_via_response_queue_is_sent_to_concent(self, event_loop):
        with override_settings(
            CONCENT_PRIVATE_KEY=CONCENT_PRIVATE_KEY,
            CONCENT_PUBLIC_KEY=CONCENT_PUBLIC_KEY,
            SIGNING_SERVICE_PUBLIC_KEY=SIGNING_SERVICE_PUBLIC_KEY
        ):
            connection_id = 11
            concent_request_id = 77
            response_queue = Queue(loop=event_loop)
            golem_message = Ping()
            response_queue_item = ResponseQueueItem(golem_message, concent_request_id, FROZEN_TIMESTAMP)
            expected_data = append_frame_separator(
                escape_encode_raw_message(
                    GolemMessageFrame(golem_message, concent_request_id).serialize(settings.CONCENT_PRIVATE_KEY)
                )
            )
            mocked_writer = prepare_mocked_writer()

            await response_queue.put(response_queue_item)
            consumer_task = event_loop.create_task(
                response_consumer(
                    response_queue,
                    mocked_writer,
                    connection_id
                )
            )
            await response_queue.join()
            consumer_task.cancel()

            mocked_writer.write.assert_called_once_with(expected_data)
            mocked_writer.drain.mock.assert_called_once_with()
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()
 def test_that_sci_callback_should_raise_exception_when_response_is_not_signed_transaction_golem_message(self):
     with mock.patch('core.payments.sci_callback.socket.socket.connect'):
         with mock.patch('core.payments.sci_callback.send_over_stream'):
             with mock.patch('core.payments.sci_callback.unescape_stream', side_effect=self.frame_iterator):
                 with mock.patch(
                     'middleman_protocol.message.AbstractFrame.deserialize',
                     return_value=GolemMessageFrame(payload=Ping(), request_id=self.request_id),
                 ):
                     with self.assertRaises(SCICallbackPayloadError):
                         sci_callback(self.transaction)
Exemplo n.º 18
0
    def test_that__handle_connection_should_send_error_frame_if_payload_type_is_invalid(
            self):
        # Prepare frame with malformed payload_type.
        middleman_message = GolemMessageFrame(
            payload=self._get_deserialized_transaction_signing_request(),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        payload_type_position = FRAME_SIGNATURE_BYTES_LENGTH + FRAME_REQUEST_ID_BYTES_LENGTH
        invalid_payload_type = 100

        # Replace bytes with payload length.
        malformed_raw_message = (
            raw_message[:payload_type_position] +
            bytes(bytearray([invalid_payload_type])) +
            raw_message[payload_type_position + FRAME_PAYLOAD_TYPE_LENGTH:])

        # Replace message signature
        new_signature = ecdsa_sign(
            CONCENT_PRIVATE_KEY,
            malformed_raw_message[FRAME_SIGNATURE_BYTES_LENGTH:])
        malformed_raw_message_with_new_signature = new_signature + malformed_raw_message[
            FRAME_SIGNATURE_BYTES_LENGTH:]

        raw_message_received = self._prepare_and_execute_handle_connection(
            malformed_raw_message_with_new_signature)

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

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.InvalidFrame)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
    def setUp(self):
        super().setUp()

        self.v = 28
        self.r = 43021479287739768723523510158222935518435169120980980279247970098168969365906
        self.s = 27167722793113753385347871828548141783025585937756999455305671053201800240244

        self.request_id = RequestIDGenerator.generate_request_id() + 1
        self.transaction = self._create_unsigned_transaction()
        self.signed_transaction_golem_message = self._create_signed_transaction()
        self.signed_transaction_golem_message.sign_message(SIGNING_SERVICE_PRIVATE_KEY)
        self.frame = GolemMessageFrame(
            payload=self.signed_transaction_golem_message,
            request_id=self.request_id,
        ).serialize(private_key=CONCENT_PRIVATE_KEY)

        def iterator(connection):  # pylint: disable=unused-argument
            yield self.frame

        self.frame_iterator = iterator
        self.middleman_message = GolemMessageFrame(payload=Ping(), request_id=self.request_id)
    def test_that_sci_callback_should_raise_exception_when_golem_message_is_not_signed_by_signing_service(self):
        wrong_signed_golem_message = self._create_signed_transaction()
        wrong_signed_golem_message.sign_message(DIFFERENT_CONCENT_PRIVATE_KEY)

        with mock.patch('core.payments.sci_callback.socket.socket.connect'):
            with mock.patch('core.payments.sci_callback.send_over_stream'):
                with mock.patch('core.payments.sci_callback.unescape_stream', side_effect=self.frame_iterator):
                    with mock.patch(
                        'middleman_protocol.message.AbstractFrame.deserialize',
                        return_value=GolemMessageFrame(payload=wrong_signed_golem_message, request_id=self.request_id),
                    ):
                        with self.assertRaises(SCICallbackPayloadSignatureError):
                            sci_callback(self.transaction)
    def test_that_sci_callback_should_raise_exception_when_response_signature_is_not_correct(self):
        wrong_signed_transaction = self._create_signed_transaction()
        wrong_signed_transaction.v = self.v - 10
        wrong_signed_transaction.sign_message(SIGNING_SERVICE_PRIVATE_KEY)

        with mock.patch('core.payments.sci_callback.socket.socket.connect'):
            with mock.patch('core.payments.sci_callback.send_over_stream'):
                with mock.patch('core.payments.sci_callback.unescape_stream', side_effect=self.frame_iterator):
                    with mock.patch(
                        'middleman_protocol.message.AbstractFrame.deserialize',
                        return_value=GolemMessageFrame(payload=wrong_signed_transaction, request_id=self.request_id),
                    ):
                        with self.assertRaises(SCICallbackTransactionSignatureError):
                            sci_callback(self.transaction)
Exemplo n.º 22
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)
Exemplo n.º 23
0
    def test_that__handle_connection_should_send_error_frame_if_payload_is_invalid(
            self):
        # Prepare frame payload which is not Golem message.
        middleman_message = GolemMessageFrame(
            payload=Ping(),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)
        malformed_raw_message = (
            raw_message[:FRAME_PAYLOAD_STARTING_BYTE] +
            AbstractFrame.get_frame_format(
            ).signed_part_of_the_frame.payload.build(b'\x00' * 100))

        # Replace message signature
        new_signature = ecdsa_sign(
            CONCENT_PRIVATE_KEY,
            malformed_raw_message[FRAME_SIGNATURE_BYTES_LENGTH:])
        malformed_raw_message_with_new_signature = new_signature + malformed_raw_message[
            FRAME_SIGNATURE_BYTES_LENGTH:]

        raw_message_received = self._prepare_and_execute_handle_connection(
            malformed_raw_message_with_new_signature)

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

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.InvalidPayload)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
Exemplo n.º 24
0
 def setUp(self, unused_tcp_port_factory, event_loop):
     golem_message_frame = GolemMessageFrame(
         Ping(), 777).serialize(CONCENT_PRIVATE_KEY)
     self.patcher = mock.patch("middleman.middleman_server.crash_logger")
     self.crash_logger_mock = self.patcher.start()
     self.internal_port, self.external_port = unused_tcp_port_factory(
     ), unused_tcp_port_factory()
     self.data_to_send = append_frame_separator(
         escape_encode_raw_message(golem_message_frame))
     self.timeout = 0.2
     self.short_delay = 0.1
     self.middleman = MiddleMan(internal_port=self.internal_port,
                                external_port=self.external_port,
                                loop=event_loop)
     yield self.internal_port, self.external_port
     self.patcher.stop()
Exemplo n.º 25
0
def deserialize_response_and_handle_errors(
        raw_response: bytes, request_id: int) -> SignedTransaction:
    """
    Deserialize received Frame and its payload and handle related errors.

    Returns SignedTransaction message.
    """

    assert isinstance(raw_response, bytes)
    assert isinstance(request_id, int)

    try:
        deserialized_message = GolemMessageFrame.deserialize(
            raw_message=raw_response,
            public_key=settings.CONCENT_PUBLIC_KEY,
        )
    # Received data frame is invalid or the signature does not match its content.
    except MiddlemanProtocolError as exception:
        raise SCICallbackFrameError() from exception
    # If the received Golem message is malformed.
    except MessageError as exception:
        raise SCICallbackPayloadError() from exception

    if deserialized_message.request_id != request_id:
        raise SCICallbackRequestIdError(
            'MiddleMan response request_id does not match requested.')

    if isinstance(deserialized_message, ErrorFrame):
        raise SCICallbackPayloadError('Received frame is ErrorFrame.')

    if isinstance(deserialized_message.payload, TransactionRejected):
        raise SCICallbackPayloadError(
            'Received frame is contains Golem message TransactionRejected.')

    if not isinstance(deserialized_message.payload, SignedTransaction):
        raise SCICallbackPayloadError(
            'Received frame payload is not Golem message SignedTransaction instance.'
        )

    # If the received Golem message is not signed by the Signing Service.
    try:
        deserialized_message.payload.verify_signature(
            settings.SIGNING_SERVICE_PUBLIC_KEY)
    except InvalidSignature as exception:
        raise SCICallbackPayloadSignatureError() from exception

    return deserialized_message.payload
Exemplo n.º 26
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)
def create_golem_message_frame(payload: Message,
                               request_id: int) -> GolemMessageFrame:
    return GolemMessageFrame(
        payload=payload,
        request_id=request_id,
    )
Exemplo n.º 28
0
    def _handle_connection(
        self,
        receive_frame_generator: Iterator[Optional[bytes]],
        tcp_socket: socket.socket
    ) -> None:
        """ Inner loop that handles data exchange over socket. """
        # Set socket back blocking mode.
        tcp_socket.setblocking(True)
        for raw_message_received in receive_frame_generator:
            try:
                middleman_message = AbstractFrame.deserialize(
                    raw_message_received,
                    public_key=self.concent_public_key,
                )
                # Heartbeat is received: connection is still active and Signing Service doesn't have to respond.
                if middleman_message.payload_type == PayloadType.HEARTBEAT:
                    continue

                if (
                    not middleman_message.payload_type == PayloadType.GOLEM_MESSAGE or
                    not isinstance(middleman_message.payload, TransactionSigningRequest)
                ):
                    raise SigningServiceUnexpectedMessageError

            # Is the frame correct according to the protocol? If not, error code is InvalidFrame.
            except (
                FrameInvalidMiddlemanProtocolError,
                PayloadTypeInvalidMiddlemanProtocolError,
                RequestIdInvalidTypeMiddlemanProtocolError,
            ) as exception:
                middleman_message_response = self._prepare_error_response(ErrorCode.InvalidFrame, exception)
            # Is frame signature correct? If not, error code is InvalidFrameSignature.
            except SignatureInvalidMiddlemanProtocolError as exception:
                middleman_message_response = self._prepare_error_response(ErrorCode.InvalidFrameSignature, exception)
            # Is the content of the message valid? Do types match the schema and all values are within allowed ranges?
            # If not, error code is InvalidPayload.
            # Can the payload be decoded as a Golem message? If not, error code is InvalidPayload.
            # Is payload message signature correct? If not, error code is InvalidPayload.
            except (MessageError, PayloadInvalidMiddlemanProtocolError) as exception:
                middleman_message_response = self._prepare_error_response(ErrorCode.InvalidPayload, exception)
            # Is frame type GOLEM_MESSAGE? If not, error code is UnexpectedMessage.
            # Is Golem message type TransactionSigningRequest? If not, error code is UnexpectedMessage.
            except SigningServiceUnexpectedMessageError as exception:
                middleman_message_response = self._prepare_error_response(ErrorCode.UnexpectedMessage, exception)
            # If received frame is correct, validate transaction.
            else:
                self._update_daily_transactions_limit_file_name()
                golem_message_response = self._get_signed_transaction(middleman_message.payload)
                if isinstance(golem_message_response, SignedTransaction):
                    transaction_sum_combined = self.signing_service_daily_transaction_sum_so_far + middleman_message.payload.value
                    if transaction_sum_combined > MAXIMUM_DAILY_THRESHOLD:
                        logger.warning(
                            f'Signing Service is unable to transact more then {MAXIMUM_DAILY_THRESHOLD} GNTB today.'
                            f'Transaction from {middleman_message.payload.from_address} rejected.'
                        )
                        self.notifier.send(
                            f'Signing Service is unable to transact more then {MAXIMUM_DAILY_THRESHOLD} GNTB today.'
                        )
                        golem_message_response = TransactionRejected(
                            reason=TransactionRejected.REASON.DailyLimitExceeded,
                            nonce=middleman_message.payload.nonce,
                        )
                    elif transaction_sum_combined > WARNING_DAILY_THRESHOLD:
                        logger.warning(f'Signing Service has signed transactions worth {transaction_sum_combined} GNTB today.')
                        self.notifier.send(
                            f'Signing Service has signed transactions worth {transaction_sum_combined} GNTB today.'
                        )
                        self._add_payload_value_to_daily_transactions_sum(transaction_sum_combined)
                    else:
                        self._add_payload_value_to_daily_transactions_sum(transaction_sum_combined)
                golem_message_response.sign_message(private_key=self.signing_service_private_key)
                middleman_message_response = GolemMessageFrame(
                    payload=golem_message_response,
                    request_id=middleman_message.request_id,
                )

            logger.info(
                f'Sending Middleman protocol message with request_id: {middleman_message_response.request_id}.'
            )
            send_over_stream(
                connection=tcp_socket,
                raw_message=middleman_message_response,
                private_key=self.signing_service_private_key,
            )