Example #1
0
    def _handle_valid_transaction(self, message: TACMessage, sender: Address, transaction: Transaction) -> None:
        """
        Handle a valid transaction.

        That is:
        - update the game state
        - send a transaction confirmation both to the buyer and the seller.

        :param tx: the transaction.
        :return: None
        """
        game = cast(Game, self.context.game)
        logger.debug("[{}]: Handling valid transaction: {}".format(self.context.agent_name, transaction.transaction_id))
        game.transactions.add_confirmed(transaction)
        game.settle_transaction(transaction)

        # send the transaction confirmation.
        sender_tac_msg = TACMessage(tac_type=TACMessage.Type.TRANSACTION_CONFIRMATION,
                                    transaction_id=transaction.transaction_id)
        counterparty_tac_msg = TACMessage(tac_type=TACMessage.Type.TRANSACTION_CONFIRMATION,
                                          transaction_id=transaction.transaction_id)
        self.context.outbox.put_message(to=sender,
                                        sender=self.context.public_key,
                                        protocol_id=TACMessage.protocol_id,
                                        message=TACSerializer().encode(sender_tac_msg))
        self.context.outbox.put_message(to=cast(str, message.get("counterparty")),
                                        sender=self.context.agent_public_key,
                                        protocol_id=TACMessage.protocol_id,
                                        message=TACSerializer().encode(counterparty_tac_msg))

        # log messages
        logger.debug("[{}]: Transaction '{}' settled successfully.".format(self.context.agent_name, transaction.transaction_id))
        logger.debug("[{}]: Current state:\n{}".format(self.context.agent_name, game.holdings_summary))
Example #2
0
    def handle(self, message: Message, sender: str) -> None:
        """
        Dispatch message to relevant handler and respond.

        :param message: the message
        :param sender: the sender
        :return: None
        """
        tx_message = cast(TransactionMessage, message)
        if TransactionMessage.Performative(tx_message.get(
                "performative")) == TransactionMessage.Performative.ACCEPT:
            logger.info(
                "[{}]: transaction confirmed by decision maker, sending to controller."
                .format(self.context.agent_name))
            game = cast(Game, self.context.game)
            msg = TACMessage(
                type=TACMessage.Type.TRANSACTION,
                transaction_id=tx_message.get("transaction_digest"),
                counterparty=tx_message.get("counterparty"),
                amount_by_currency={
                    tx_message.get("currency"): tx_message.get("amount")
                },
                sender_tx_fee=tx_message.get("sender_tx_fee"),
                counterparty_tx_fee=tx_message.get("counterparty_tx_fee"),
                quantities_by_good_pbk=tx_message.get(
                    "quantities_by_good_pbk"))
            self.context.outbox.put_message(
                to=game.configuration.controller_pbk,
                sender=self.context.agent_public_key,
                protocol_id=TACMessage.protocol_id,
                message=TACSerializer().encode(msg))
        else:
            logger.info("[{}]: transaction was not successful.".format(
                self.context.agent_name))
Example #3
0
 def _handle_invalid_transaction(self, message: TACMessage, sender: Address) -> None:
     """Handle an invalid transaction."""
     tac_msg = TACMessage(tac_type=TACMessage.Type.TAC_ERROR,
                          error_code=TACMessage.ErrorCode.TRANSACTION_NOT_VALID,
                          details={"transaction_id": message.get("transaction_id")})
     self.context.outbox.put_message(to=sender,
                                     sender=self.context.agent_public_key,
                                     protocol_id=TACMessage.protocol_id,
                                     message=TACSerializer().encode(tac_msg))
Example #4
0
    def _on_register(self, message: TACMessage, sender: Address) -> None:
        """
        Handle a register message.

        If the public key is not registered, answer with an error message.

        :param message: the 'get agent state' TACMessage.
        :param sender: the public key of the sender
        :return: None
        """
        parameters = cast(Parameters, self.context.parameters)
        agent_name = cast(str, message.get("agent_name"))
        if len(parameters.whitelist) != 0 and agent_name not in parameters.whitelist:
            logger.error("[{}]: Agent name not in whitelist: '{}'".format(self.context.agent_name, agent_name))
            tac_msg = TACMessage(tac_type=TACMessage.Type.TAC_ERROR,
                                 error_code=TACMessage.ErrorCode.AGENT_NAME_NOT_IN_WHITELIST)
            self.context.outbox.put_message(to=sender,
                                            sender=self.context.agent_public_key,
                                            protocol_id=TACMessage.protocol_id,
                                            message=TACSerializer().encode(tac_msg))
            return

        game = cast(Game, self.context.game)
        if sender in game.registration.agent_pbk_to_name:
            logger.error("[{}]: Agent already registered: '{}'".format(self.context.agent_name, game.registration.agent_pbk_to_name[sender]))
            tac_msg = TACMessage(tac_type=TACMessage.Type.TAC_ERROR,
                                 error_code=TACMessage.ErrorCode.AGENT_PBK_ALREADY_REGISTERED)
            self.context.outbox.put_message(to=sender,
                                            sender=self.context.agent_public_key,
                                            protocol_id=TACMessage.protocol_id,
                                            message=TACSerializer().encode(tac_msg))

        if agent_name in game.registration.agent_pbk_to_name.values():
            logger.error("[{}]: Agent with this name already registered: '{}'".format(self.context.agent_name, agent_name))
            tac_msg = TACMessage(tac_type=TACMessage.Type.TAC_ERROR,
                                 error_code=TACMessage.ErrorCode.AGENT_NAME_ALREADY_REGISTERED)
            self.context.outbox.put_message(to=sender,
                                            sender=self.context.agent_public_key,
                                            protocol_id=TACMessage.protocol_id,
                                            message=TACSerializer().encode(tac_msg))

        game.registration.register_agent(sender, agent_name)
        logger.info("[{}]: Agent registered: '{}'".format(self.context.agent_name, agent_name))
Example #5
0
    def _request_state_update(self) -> None:
        """
        Request current agent state from TAC Controller.

        :return: None
        """
        tac_msg = TACMessage(tac_type=TACMessage.Type.GET_STATE_UPDATE)
        tac_bytes = TACSerializer().encode(tac_msg)
        game = cast(Game, self.context.game)
        self.context.outbox.put_message(to=game.expected_controller_pbk, sender=self.context.agent_public_key, protocol_id=TACMessage.protocol_id, message=tac_bytes)
Example #6
0
 def _cancel_tac(self):
     """Notify agents that the TAC is cancelled."""
     game = cast(Game, self.context.game)
     logger.info("[{}]: Notifying agents that TAC is cancelled.".format(self.context.agent_name))
     for agent_pbk in game.registration.agent_pbk_to_name.keys():
         tac_msg = TACMessage(tac_type=TACMessage.Type.CANCELLED)
         self.context.outbox.put_message(to=agent_pbk,
                                         sender=self.context.agent_public_key,
                                         protocol_id=TACMessage.protocol_id,
                                         message=TACSerializer().encode(tac_msg))
Example #7
0
    def _rejoin_tac(self, controller_pbk: Address) -> None:
        """
        Rejoin the TAC run by a Controller.

        :param controller_pbk: the public key of the controller.

        :return: None
        """
        game = cast(Game, self.context.game)
        game.update_expected_controller_pbk(controller_pbk)
        game.update_game_phase(GamePhase.GAME_SETUP)
        tac_msg = TACMessage(tac_type=TACMessage.Type.GET_STATE_UPDATE)
        tac_bytes = TACSerializer().encode(tac_msg)
        self.context.outbox.put_message(to=controller_pbk, sender=self.context.agent_public_key, protocol_id=TACMessage.protocol_id, message=tac_bytes)
Example #8
0
    def _register_to_tac(self, controller_pbk: Address) -> None:
        """
        Register to active TAC Controller.

        :param controller_pbk: the public key of the controller.

        :return: None
        """
        game = cast(Game, self.context.game)
        game.update_expected_controller_pbk(controller_pbk)
        game.update_game_phase(GamePhase.GAME_SETUP)
        tac_msg = TACMessage(tac_type=TACMessage.Type.REGISTER, agent_name=self.context.agent_name)
        tac_bytes = TACSerializer().encode(tac_msg)
        self.context.outbox.put_message(to=controller_pbk, sender=self.context.agent_public_key, protocol_id=TACMessage.protocol_id, message=tac_bytes)
Example #9
0
    def handle_envelope(self, envelope: Envelope) -> None:
        """
        Implement the reaction to an envelope.

        :param envelope: the envelope
        :return: None
        """
        tac_msg = TACSerializer().decode(envelope.message)
        tac_msg_type = TACMessage.Type(tac_msg.get("type"))
        tac_msg = cast(TACMessage, tac_msg)
        game = cast(Game, self.context.game)
        logger.debug("[{}]: Handling controller response. type={}".format(self.context.agent_name, tac_msg_type))
        try:
            if envelope.sender != game.expected_controller_pbk:
                raise ValueError("The sender of the message is not the controller agent we registered with.")

            if tac_msg_type == TACMessage.Type.TAC_ERROR:
                self._on_tac_error(tac_msg, envelope.sender)
            elif game.game_phase == GamePhase.PRE_GAME:
                raise ValueError("We do not expect a controller agent message in the pre game phase.")
            elif game.game_phase == GamePhase.GAME_SETUP:
                if tac_msg_type == TACMessage.Type.GAME_DATA:
                    self._on_start(tac_msg, envelope.sender)
                elif tac_msg_type == TACMessage.Type.CANCELLED:
                    self._on_cancelled()
            elif game.game_phase == GamePhase.GAME:
                if tac_msg_type == TACMessage.Type.TRANSACTION_CONFIRMATION:
                    self._on_transaction_confirmed(tac_msg, envelope.sender)
                elif tac_msg_type == TACMessage.Type.CANCELLED:
                    self._on_cancelled()
                elif tac_msg_type == TACMessage.Type.STATE_UPDATE:
                    self._on_state_update(tac_msg, envelope.sender)
            elif game.game_phase == GamePhase.POST_GAME:
                raise ValueError("We do not expect a controller agent message in the post game phase.")
        except ValueError as e:
            logger.warning(str(e))
Example #10
0
    def _on_unregister(self, message: TACMessage, sender: Address) -> None:
        """
        Handle a unregister message.

        If the public key is not registered, answer with an error message.

        :param message: the 'get agent state' TACMessage.
        :param sender: the public key of the sender
        :return: None
        """
        game = cast(Game, self.context.game)
        if sender not in game.registration.agent_pbk_to_name:
            logger.error("[{}]: Agent not registered: '{}'".format(self.context.agent_name, sender))
            tac_msg = TACMessage(tac_type=TACMessage.Type.TAC_ERROR,
                                 error_code=TACMessage.ErrorCode.AGENT_NOT_REGISTERED)
            self.context.outbox.put_message(to=sender,
                                            sender=self.context.agent_public_key,
                                            protocol_id=TACMessage.protocol_id,
                                            message=TACSerializer().encode(tac_msg))
        else:
            logger.debug("[{}]: Agent unregistered: '{}'".format(self.context.agent_name, game.configuration.agent_pbk_to_name[sender]))
            game.registration.unregister_agent(sender)
Example #11
0
 def _start_tac(self):
     """Create a game and send the game configuration to every registered agent."""
     game = cast(Game, self.context.game)
     game.create()
     logger.info("[{}]: Started competition:\n{}".format(self.context.agent_name, game.holdings_summary))
     logger.info("[{}]: Computed equilibrium:\n{}".format(self.context.agent_name, game.equilibrium_summary))
     for agent_public_key in game.configuration.agent_pbks:
         agent_state = game.current_agent_states[agent_public_key]
         tac_msg = TACMessage(tac_type=TACMessage.Type.GAME_DATA,
                              amount_by_currency=agent_state.balance_by_currency,
                              exchange_params_by_currency=agent_state.exchange_params_by_currency,
                              quantities_by_good_pbk=agent_state.quantities_by_good_pbk,
                              utility_params_by_good_pbk=agent_state.utility_params_by_good_pbk,
                              tx_fee=game.configuration.tx_fee,
                              agent_pbk_to_name=game.configuration.agent_pbk_to_name,
                              good_pbk_to_name=game.configuration.good_pbk_to_name,
                              version_id=game.configuration.version_id)
         logger.debug("[{}]: sending game data to '{}': {}"
                      .format(self.context.agent_name, agent_public_key, str(tac_msg)))
         self.context.outbox.put_message(to=agent_public_key,
                                         sender=self.context.agent_public_key,
                                         protocol_id=TACMessage.protocol_id,
                                         message=TACSerializer().encode(tac_msg))
Example #12
0
def test_tac_serialization():
    """Test that the serialization for the tac message works."""
    msg = TACMessage(tac_type=TACMessage.Type.REGISTER, agent_name='some_name')
    msg_bytes = TACSerializer().encode(msg)
    actual_msg = TACSerializer().decode(msg_bytes)
    expected_msg = msg
    assert expected_msg == actual_msg

    msg = TACMessage(tac_type=TACMessage.Type.UNREGISTER)
    msg_bytes = TACSerializer().encode(msg)
    actual_msg = TACSerializer().decode(msg_bytes)
    expected_msg = msg
    assert expected_msg == actual_msg

    msg = TACMessage(tac_type=TACMessage.Type.TRANSACTION,
                     transaction_id='some_id',
                     counterparty='some_address',
                     amount_by_currency={'FET': 10},
                     sender_tx_fee=10,
                     counterparty_tx_fee=10,
                     quantities_by_good_pbk={
                         'good_1': 0,
                         'good_2': 10
                     })
    msg_bytes = TACSerializer().encode(msg)
    actual_msg = TACSerializer().decode(msg_bytes)
    expected_msg = msg
    assert expected_msg == actual_msg

    msg = TACMessage(tac_type=TACMessage.Type.GET_STATE_UPDATE)
    msg_bytes = TACSerializer().encode(msg)
    actual_msg = TACSerializer().decode(msg_bytes)
    expected_msg = msg
    assert expected_msg == actual_msg

    msg = TACMessage(tac_type=TACMessage.Type.CANCELLED)
    msg_bytes = TACSerializer().encode(msg)
    actual_msg = TACSerializer().decode(msg_bytes)
    expected_msg = msg
    assert expected_msg == actual_msg

    msg = TACMessage(tac_type=TACMessage.Type.GAME_DATA,
                     amount_by_currency={'FET': 10},
                     exchange_params_by_currency={'FET': 10.0},
                     quantities_by_good_pbk={
                         'good_1': 20,
                         'good_2': 15
                     },
                     utility_params_by_good_pbk={
                         'good_1': 30.0,
                         'good_2': 50.0
                     },
                     tx_fee=20,
                     agent_pbk_to_name={
                         'agent_1': 'Agent one',
                         'agent_2': 'Agent two'
                     },
                     good_pbk_to_name={
                         'good_1': 'First good',
                         'good_2': 'Second good'
                     },
                     version_id='game_version_1')
    msg_bytes = TACSerializer().encode(msg)
    actual_msg = TACSerializer().decode(msg_bytes)
    expected_msg = msg
    assert expected_msg == actual_msg

    msg = TACMessage(tac_type=TACMessage.Type.TRANSACTION_CONFIRMATION,
                     transaction_id='some_id',
                     amount_by_currency={'FET': 10},
                     quantities_by_good_pbk={
                         'good_1': 20,
                         'good_2': 15
                     })
    msg_bytes = TACSerializer().encode(msg)
    actual_msg = TACSerializer().decode(msg_bytes)
    expected_msg = msg
    assert expected_msg == actual_msg

    with pytest.raises(ValueError, match="Type not recognized."):
        with mock.patch('packages.protocols.tac.message.TACMessage.Type'
                        ) as mocked_type:
            mocked_type.TRANSACTION_CONFIRMATION.value = "unknown"
            TACSerializer().encode(msg)

    msg = TACMessage(tac_type=TACMessage.Type.TAC_ERROR,
                     error_code=TACMessage.ErrorCode.GENERIC_ERROR)
    msg_bytes = TACSerializer().encode(msg)
    actual_msg = TACSerializer().decode(msg_bytes)
    expected_msg = msg
    assert expected_msg == actual_msg