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)
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)
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)
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()
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] )
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)
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)
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()
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
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()
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)
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)
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)
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)
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()
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
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, )
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, )