Example #1
0
    def verify_owners(self,
                      provider_public_key=None,
                      requestor_public_key=None,
                      concent_public_key=None):
        """
        verifies both that the whole message chain is consistent with respect
        to the expected message ownership and that the roles in the message
        chain match the expected provider/requestor keys

        if provided, `provider_public_key` / `requestor_public_key` will be
        verified against the roles extracted from the included message chain.

        :param provider_public_key:
        :param requestor_public_key:
        :param concent_public_key: must be provided if any of the child
                                   messages is expected to be signed
                                   by the Concent
        :return:
        """
        def assert_role(role, expected, actual):
            if expected != actual:
                raise exceptions.OwnershipMismatch(
                    "%s: Task %s mismatch - expected: %s, actual: %s" %
                    (self.__class__.__name__, role, expected, actual))

        if provider_public_key:
            assert_role('provider', provider_public_key,
                        decode_hex(self.task_to_compute.provider_public_key))

        if requestor_public_key:
            assert_role('requestor', requestor_public_key,
                        decode_hex(self.task_to_compute.requestor_public_key))

        self.validate_ownership_chain(concent_public_key=concent_public_key)
        return True
Example #2
0
    def validate_ownership(self, concent_public_key=None):
        """
        validates that the message is signed by one of the expected parties

        requires `concent_public_key` if the Concent is one of the possible
        owners
        """
        owner_map = {
            TaskMessage.OWNER_CHOICES.provider:
            decode_hex(self.task_to_compute.provider_public_key),
            TaskMessage.OWNER_CHOICES.requestor:
            decode_hex(self.task_to_compute.requestor_public_key),
            TaskMessage.OWNER_CHOICES.concent:
            concent_public_key,
        }

        for owner in self.EXPECTED_OWNERS:
            try:
                if self.verify_signature(public_key=owner_map.get(owner)):
                    return True
            except exceptions.InvalidSignature:
                pass

        exc = exceptions.InvalidSignature(
            '%s is not signed by the %s' %
            (self.__class__.__name__, ' or '.join([
                '%s: %s' % (o.value, owner_map.get(o))
                for o in self.EXPECTED_OWNERS
            ])))
        exc.message = self
        raise exc
Example #3
0
def hex_to_bytes_convert(client_public_key: str):
    if not isinstance(client_public_key, str):
        raise Http400("Client public key must be string",
                      error_code=ErrorCode.MESSAGE_VALUE_NOT_STRING)
    if not len(client_public_key) == GOLEM_PUBLIC_KEY_HEX_LENGTH:
        raise Http400("Client public key must be length of 128 characters",
                      error_code=ErrorCode.MESSAGE_VALUE_WRONG_LENGTH)
    key_bytes = decode_hex(client_public_key)
    assert len(key_bytes) == GOLEM_PUBLIC_KEY_LENGTH
    return key_bytes
Example #4
0
def ethereum_public_key_to_address(ethereum_public_key: str) -> str:
    return to_checksum_address(
        sha3(decode_hex(ethereum_public_key))[12:].hex())
Example #5
0
def generate_ethereum_address_from_ethereum_public_key_bytes(
        ethereum_public_key: str) -> bytes:
    assert isinstance(ethereum_public_key, str)
    assert len(ethereum_public_key) == ETHEREUM_PUBLIC_KEY_LENGTH

    return sha3(decode_hex(ethereum_public_key))[12:]
    def test_sum_of_payments_when_lists_of_transactions_from_payment_api_are_empty(
            self):
        """
       Expected message exchange:
        Provider  -> Concent:    ForcePayment
        Concent   -> Provider:   ForcePaymentCommitted
        Concent   -> Requestor:  ForcePaymentCommitted
        """
        task_to_compute = self._get_deserialized_task_to_compute(
            timestamp="2018-02-05 10:00:00",
            deadline="2018-02-05 10:00:10",
            subtask_id=self._get_uuid('1'),
            price=20000,
        )

        subtask_results_accepted_list = [
            self._get_deserialized_subtask_results_accepted(
                timestamp="2018-02-05 10:00:15",
                payment_ts="2018-02-05 12:00:00",
                report_computed_task=self.
                _get_deserialized_report_computed_task(
                    timestamp="2018-02-05 10:00:05",
                    task_to_compute=task_to_compute)),
            self._get_deserialized_subtask_results_accepted(
                timestamp="2018-02-05 9:00:15",
                payment_ts="2018-02-05 11:00:00",
                report_computed_task=self.
                _get_deserialized_report_computed_task(
                    timestamp="2018-02-05 10:00:05",
                    task_to_compute=self._get_deserialized_task_to_compute(
                        timestamp="2018-02-05 9:00:00",
                        deadline="2018-02-05 9:00:10",
                        subtask_id=self._get_uuid('2'),
                        price=5000,
                    )))
        ]
        serialized_force_payment = self._get_serialized_force_payment(
            timestamp="2018-02-05 12:00:20",
            subtask_results_accepted_list=subtask_results_accepted_list)

        with freeze_time("2018-02-05 12:00:20"):
            with mock.patch(
                    'core.message_handlers.bankster.settle_overdue_acceptances',
                    side_effect=self.settle_overdue_acceptances_mock
            ) as settle_overdue_acceptances:
                response_1 = self.send_request(
                    url='core:send',
                    data=serialized_force_payment,
                )

        settle_overdue_acceptances.assert_called_with(
            requestor_ethereum_address=task_to_compute.
            requestor_ethereum_address,
            provider_ethereum_address=task_to_compute.
            provider_ethereum_address,
            acceptances=subtask_results_accepted_list,
            requestor_public_key=hex_to_bytes_convert(
                task_to_compute.requestor_public_key),
        )

        self._test_response(
            response_1,
            status=200,
            key=self.PROVIDER_PRIVATE_KEY,
            message_type=message.concents.ForcePaymentCommitted,
            fields={
                'recipient_type':
                message.concents.ForcePaymentCommitted.Actor.Provider,
                'timestamp':
                parse_iso_date_to_timestamp("2018-02-05 12:00:20"),
                'amount_pending': self.amount_pending,
                'amount_paid': self.amount_paid,
            })
        self._assert_stored_message_counter_not_increased()

        with freeze_time("2018-02-05 12:00:21"):
            response_2 = self.send_request(
                url='core:receive',
                data=self._create_requestor_auth_message(),
            )
        self._test_response(
            response_2,
            status=200,
            key=self.REQUESTOR_PRIVATE_KEY,
            message_type=message.concents.ForcePaymentCommitted,
            fields={
                'recipient_type':
                message.concents.ForcePaymentCommitted.Actor.Requestor,
                'timestamp':
                parse_iso_date_to_timestamp("2018-02-05 12:00:21"),
                'amount_pending':
                self.amount_pending,
                'amount_paid':
                self.amount_paid,
                'task_owner_key':
                decode_hex(task_to_compute.requestor_ethereum_public_key),
            })
        self._assert_stored_message_counter_not_increased()
    def test_provider_send_correct_force_payment_concent_should_accept(self):
        """
        Expected message exchange:
        Provider  -> Concent:    ForcePayment
        Concent   -> Provider:   ForcePaymentCommitted
        Concent   -> Requestor:  ForcePaymentCommitted
        """
        task_to_compute = self._get_deserialized_task_to_compute(
            timestamp="2018-02-05 10:00:00",
            deadline="2018-02-05 10:00:10",
            subtask_id=self._get_uuid('1'),
            price=15000,
        )

        subtask_results_accepted_list = [
            self._get_deserialized_subtask_results_accepted(
                timestamp="2018-02-05 10:00:15",
                payment_ts="2018-02-05 11:55:00",
                report_computed_task=self.
                _get_deserialized_report_computed_task(
                    timestamp="2018-02-05 10:00:05",
                    task_to_compute=task_to_compute,
                )),
            self._get_deserialized_subtask_results_accepted(
                timestamp="2018-02-05 9:00:15",
                payment_ts="2018-02-05 11:55:00",
                report_computed_task=self.
                _get_deserialized_report_computed_task(
                    timestamp="2018-02-05 9:00:05",
                    task_to_compute=self._get_deserialized_task_to_compute(
                        timestamp="2018-02-05 9:00:00",
                        deadline="2018-02-05 9:00:10",
                        subtask_id=self._get_uuid('2'),
                        price=7000,
                    )))
        ]

        serialized_force_payment = self._get_serialized_force_payment(
            timestamp="2018-02-05 12:00:20",
            subtask_results_accepted_list=subtask_results_accepted_list)

        with freeze_time("2018-02-05 12:00:20"):
            with mock.patch(
                    'core.message_handlers.bankster.settle_overdue_acceptances',
                    side_effect=self.settle_overdue_acceptances_mock
            ) as settle_overdue_acceptances:
                response_1 = self.send_request(
                    url='core:send',
                    data=serialized_force_payment,
                )

        settle_overdue_acceptances.assert_called_with(
            requestor_ethereum_address=task_to_compute.
            requestor_ethereum_address,
            provider_ethereum_address=task_to_compute.
            provider_ethereum_address,
            acceptances=subtask_results_accepted_list,
            requestor_public_key=hex_to_bytes_convert(
                task_to_compute.requestor_public_key),
        )

        self._test_response(
            response_1,
            status=200,
            key=self.PROVIDER_PRIVATE_KEY,
            message_type=message.concents.ForcePaymentCommitted,
            fields={
                'recipient_type':
                message.concents.ForcePaymentCommitted.Actor.Provider,
                'timestamp':
                parse_iso_date_to_timestamp("2018-02-05 12:00:20"),
                'amount_pending': self.amount_pending,
                'amount_paid': self.amount_paid,
            })
        self._assert_stored_message_counter_not_increased()

        last_pending_message = PendingResponse.objects.filter(
            delivered=False).order_by('created_at').last()
        self.assertEqual(
            last_pending_message.response_type,
            PendingResponse.ResponseType.ForcePaymentCommitted.name)  # pylint: disable=no-member
        self.assertEqual(last_pending_message.client.public_key_bytes,
                         self.REQUESTOR_PUBLIC_KEY)

        with freeze_time("2018-02-05 12:00:21"):
            response_2 = self.send_request(
                url='core:receive',
                data=self._create_requestor_auth_message(),
            )
        self._test_response(
            response_2,
            status=200,
            key=self.REQUESTOR_PRIVATE_KEY,
            message_type=message.concents.ForcePaymentCommitted,
            fields={
                'recipient_type':
                message.concents.ForcePaymentCommitted.Actor.Requestor,
                'timestamp':
                parse_iso_date_to_timestamp("2018-02-05 12:00:21"),
                'amount_pending':
                self.amount_pending,
                'amount_paid':
                self.amount_paid,
                'task_owner_key':
                decode_hex(task_to_compute.requestor_ethereum_public_key),
            })
        self._assert_stored_message_counter_not_increased()
        last_pending_message = PendingResponse.objects.filter(
            delivered=False).last()
        self.assertIsNone(last_pending_message)
    def test_sum_of_payments_when_lists_of_transactions_from_payment_api_are_empty(
            self):
        """
       Expected message exchange:
        Provider  -> Concent:    ForcePayment
        Concent   -> Provider:   ForcePaymentCommitted
        Concent   -> Requestor:  ForcePaymentCommitted
        """
        task_to_compute = self._get_deserialized_task_to_compute(
            timestamp="2018-02-05 10:00:00",
            deadline="2018-02-05 10:00:10",
            subtask_id='2',
            price=20000,
        )

        subtask_results_accepted_list = [
            self._get_deserialized_subtask_results_accepted(
                timestamp="2018-02-05 10:00:15",
                payment_ts="2018-02-05 12:00:00",
                task_to_compute=task_to_compute),
            self._get_deserialized_subtask_results_accepted(
                timestamp="2018-02-05 9:00:15",
                payment_ts="2018-02-05 11:00:00",
                task_to_compute=self._get_deserialized_task_to_compute(
                    timestamp="2018-02-05 9:00:00",
                    deadline="2018-02-05 9:00:10",
                    subtask_id='3',
                    price=5000,
                ))
        ]
        serialized_force_payment = self._get_serialized_force_payment(
            timestamp="2018-02-05 12:00:20",
            subtask_results_accepted_list=subtask_results_accepted_list)

        with freeze_time("2018-02-05 12:00:20"):
            with mock.patch(
                'core.message_handlers.payments_service.get_list_of_payments',
                side_effect=self._get_empty_list_of_transactions
            ) as get_list_of_payments_mock_function,\
                mock.patch(
                'core.message_handlers.payments_service.make_force_payment_to_provider',
                side_effect=self._make_force_payment_to_provider
            ) as make_force_payment_to_provider_mock_function:
                response_1 = self.client.post(
                    reverse('core:send'),
                    data=serialized_force_payment,
                    content_type='application/octet-stream',
                )

        make_force_payment_to_provider_mock_function.assert_called_with(
            requestor_eth_address=task_to_compute.requestor_ethereum_address,
            provider_eth_address=task_to_compute.provider_ethereum_address,
            value=25000,
            payment_ts=parse_iso_date_to_timestamp("2018-02-05 12:00:20"),
        )

        get_list_of_payments_mock_function.assert_called_with(
            requestor_eth_address=task_to_compute.requestor_ethereum_address,
            provider_eth_address=task_to_compute.provider_ethereum_address,
            payment_ts=parse_iso_date_to_timestamp("2018-02-05 11:00:10"),
            current_time=parse_iso_date_to_timestamp("2018-02-05 12:00:20"),
            transaction_type=TransactionType.FORCE,
        )

        self._test_response(
            response_1,
            status=200,
            key=self.PROVIDER_PRIVATE_KEY,
            message_type=message.concents.ForcePaymentCommitted,
            fields={
                'recipient_type':
                message.concents.ForcePaymentCommitted.Actor.Provider,
                'timestamp':
                parse_iso_date_to_timestamp("2018-02-05 12:00:20"),
                'amount_pending': 25000,
                'amount_paid': 0,
            })
        self._assert_stored_message_counter_not_increased()

        with freeze_time("2018-02-05 12:00:21"):
            response_2 = self.client.post(
                reverse('core:receive_out_of_band'),
                data=self._create_requestor_auth_message(),
                content_type='application/octet-stream',
            )
        self._test_response(
            response_2,
            status=200,
            key=self.REQUESTOR_PRIVATE_KEY,
            message_type=message.concents.ForcePaymentCommitted,
            fields={
                'recipient_type':
                message.concents.ForcePaymentCommitted.Actor.Requestor,
                'timestamp':
                parse_iso_date_to_timestamp("2018-02-05 12:00:21"),
                'amount_pending':
                25000,
                'amount_paid':
                0,
                'task_owner_key':
                decode_hex(task_to_compute.requestor_ethereum_public_key),
            })
        self._assert_stored_message_counter_not_increased()
    def test_provider_send_correct_force_payment_concent_should_accept(self):
        """
        Expected message exchange:
        Provider  -> Concent:    ForcePayment
        Concent   -> Provider:   ForcePaymentCommitted
        Concent   -> Requestor:  ForcePaymentCommitted
        """
        task_to_compute = self._get_deserialized_task_to_compute(
            timestamp="2018-02-05 10:00:00",
            deadline="2018-02-05 10:00:10",
            subtask_id='2',
            price=15000,
        )

        subtask_results_accepted_list = [
            self._get_deserialized_subtask_results_accepted(
                timestamp="2018-02-05 10:00:15",
                payment_ts="2018-02-05 11:55:00",
                task_to_compute=task_to_compute),
            self._get_deserialized_subtask_results_accepted(
                timestamp="2018-02-05 9:00:15",
                payment_ts="2018-02-05 11:55:00",
                task_to_compute=self._get_deserialized_task_to_compute(
                    timestamp="2018-02-05 9:00:00",
                    deadline="2018-02-05 9:00:10",
                    subtask_id='3',
                    price=7000,
                ))
        ]

        serialized_force_payment = self._get_serialized_force_payment(
            timestamp="2018-02-05 12:00:20",
            subtask_results_accepted_list=subtask_results_accepted_list)

        with freeze_time("2018-02-05 12:00:20"):
            fake_responses = [
                self._get_list_of_batch_transactions(),
                self._get_list_of_force_transactions()
            ]
            with mock.patch(
                'core.message_handlers.payments_service.make_force_payment_to_provider',
                side_effect=self._make_force_payment_to_provider
            ) as make_force_payment_to_provider_mock_function,\
                mock.patch(
                'core.message_handlers.payments_service.get_list_of_payments',
                side_effect=fake_responses
            ) as get_list_of_payments_mock_function:
                response_1 = self.client.post(
                    reverse('core:send'),
                    data=serialized_force_payment,
                    content_type='application/octet-stream',
                )

        make_force_payment_to_provider_mock_function.assert_called_with(
            requestor_eth_address=task_to_compute.requestor_ethereum_address,
            provider_eth_address=task_to_compute.provider_ethereum_address,
            value=9000,
            payment_ts=parse_iso_date_to_timestamp("2018-02-05 12:00:20"),
        )

        get_list_of_payments_mock_function.assert_called_with(
            requestor_eth_address=task_to_compute.requestor_ethereum_address,
            provider_eth_address=task_to_compute.provider_ethereum_address,
            payment_ts=parse_iso_date_to_timestamp("2018-02-05 11:55:10"),
            current_time=parse_iso_date_to_timestamp("2018-02-05 12:00:20"),
            transaction_type=TransactionType.FORCE,
        )

        # Sum of prices from force and batch lists of transactions which have been paid
        amount_paid = 10000 + 3000
        # Sum of price in all TaskToCompute messages minus amount_paid
        amount_pending = 15000 + 7000 - amount_paid

        self._test_response(
            response_1,
            status=200,
            key=self.PROVIDER_PRIVATE_KEY,
            message_type=message.concents.ForcePaymentCommitted,
            fields={
                'recipient_type':
                message.concents.ForcePaymentCommitted.Actor.Provider,
                'timestamp':
                parse_iso_date_to_timestamp("2018-02-05 12:00:20"),
                'amount_pending': amount_pending,
                'amount_paid': amount_paid,
            })
        self._assert_stored_message_counter_not_increased()

        last_pending_message = PendingResponse.objects.filter(
            delivered=False).order_by('created_at').last()
        self.assertEqual(
            last_pending_message.response_type,
            PendingResponse.ResponseType.ForcePaymentCommitted.name)  # pylint: disable=no-member
        self.assertEqual(last_pending_message.client.public_key_bytes,
                         self.REQUESTOR_PUBLIC_KEY)

        with freeze_time("2018-02-05 12:00:21"):
            response_2 = self.client.post(
                reverse('core:receive_out_of_band'),
                data=self._create_requestor_auth_message(),
                content_type='application/octet-stream',
            )
        self._test_response(
            response_2,
            status=200,
            key=self.REQUESTOR_PRIVATE_KEY,
            message_type=message.concents.ForcePaymentCommitted,
            fields={
                'recipient_type':
                message.concents.ForcePaymentCommitted.Actor.Requestor,
                'timestamp':
                parse_iso_date_to_timestamp("2018-02-05 12:00:21"),
                'amount_pending':
                amount_pending,
                'amount_paid':
                amount_paid,
                'task_owner_key':
                decode_hex(task_to_compute.requestor_ethereum_public_key),
            })
        self._assert_stored_message_counter_not_increased()
        last_pending_message = PendingResponse.objects.filter(
            delivered=False).last()
        self.assertIsNone(last_pending_message)