Пример #1
0
    def handle_agent_message(self, envelope: Envelope) -> None:
        """
        Dispatch the TACMessage to the right handler.

        If no handler is found for the provided type of TACMessage, return an "invalid TACMessage" error.
        If something bad happen, return a "generic" error.

        :param envelope: the envelope to handle
        :return: None
        """
        assert envelope.protocol_id == "tac"
        tac_msg = TACSerializer().decode(envelope.message)
        logger.debug(
            "[{}] on_message: origin={}".format(
                self.controller_agent.name, envelope.sender
            )
        )
        tac_msg_type = tac_msg.get("type")
        handle_tac_message = self.handlers.get(
            TACMessage.Type(tac_msg_type), None
        )  # type: Optional[TACMessageHandler]
        if handle_tac_message is None:
            logger.debug(
                "[{}]: Unknown message from {}".format(
                    self.controller_agent.name, envelope.sender
                )
            )
            tac_error = TACMessage(
                tac_type=TACMessage.Type.TAC_ERROR,
                error_code=TACMessage.ErrorCode.REQUEST_NOT_VALID.value,
            )
            tac_bytes = TACSerializer().encode(tac_error)
            self.controller_agent.mailbox.outbox.put_message(
                to=envelope.sender,
                sender=self.controller_agent.crypto.public_key,
                protocol_id=tac_error.protocol_id,
                message=tac_bytes,
            )
            return
        else:
            try:
                handle_tac_message(tac_msg, envelope.sender)
            except Exception as e:
                logger.debug(
                    "[{}]: Error caught: {}".format(self.controller_agent.name, str(e))
                )
                logger.exception(e)
                tac_error = TACMessage(
                    tac_type=TACMessage.Type.TAC_ERROR,
                    error_code=TACMessage.ErrorCode.GENERIC_ERROR.value,
                )
                tac_bytes = TACSerializer().encode(tac_error)
                self.controller_agent.mailbox.outbox.put_message(
                    to=envelope.sender,
                    sender=self.controller_agent.crypto.public_key,
                    protocol_id=tac_error.protocol_id,
                    message=tac_bytes,
                )
Пример #2
0
    def handle(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
        """
        if sender not in self.controller_agent.game_handler.registered_agents:
            logger.error("[{}]: Agent not registered: '{}'".format(
                self.controller_agent.name, sender))
            tac_msg = TACMessage(
                tac_type=TACMessage.Type.TAC_ERROR,
                error_code=TACMessage.ErrorCode.AGENT_NOT_REGISTERED)
            tac_bytes = TACSerializer().encode(tac_msg)
            self.controller_agent.mailbox.outbox.put_message(
                to=sender,
                sender=self.controller_agent.crypto.public_key,
                protocol_id=TACMessage.protocol_id,
                message=tac_bytes)
        else:
            logger.debug("[{}]: Agent unregistered: '{}'".format(
                self.controller_agent.name,
                self.controller_agent.game_handler.agent_pbk_to_name[sender]))
            self.controller_agent.game_handler.registered_agents.remove(sender)
            self.controller_agent.game_handler.agent_pbk_to_name.pop(sender)
Пример #3
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
        """
        logger.debug(
            "[{}]: Handling valid transaction: {}".format(
                self.controller_agent.name, message.get("transaction_id")
            )
        )

        # update the game state.
        self.controller_agent.game_handler.current_game.settle_transaction(transaction)

        # update the dashboard monitor
        self.controller_agent.game_handler.monitor.update()

        # send the transaction confirmation.
        tac_msg = TACMessage(
            tac_type=TACMessage.Type.TRANSACTION_CONFIRMATION,
            transaction_id=message.get("transaction_id"),
        )
        tac_bytes = TACSerializer().encode(tac_msg)
        self.controller_agent.outbox.put_message(
            to=sender,
            sender=self.controller_agent.crypto.public_key,
            protocol_id=TACMessage.protocol_id,
            message=tac_bytes,
        )
        self.controller_agent.outbox.put_message(
            to=message.get("counterparty"),
            sender=self.controller_agent.crypto.public_key,
            protocol_id=TACMessage.protocol_id,
            message=tac_bytes,
        )

        # log messages
        logger.debug(
            "[{}]: Transaction '{}' settled successfully.".format(
                self.controller_agent.name, message.get("transaction_id")
            )
        )
        holdings_summary = (
            self.controller_agent.game_handler.current_game.get_holdings_summary()
        )
        logger.debug(
            "[{}]: Current state:\n{}".format(
                self.controller_agent.name, holdings_summary
            )
        )
Пример #4
0
    def handle_controller_message(self, envelope: Envelope) -> None:
        """
        Handle messages from the controller.

        The controller does not expect a response for any of these messages.

        :param envelope: the controller message

        :return: None
        """
        assert envelope.protocol_id == "tac"
        tac_msg = TACSerializer().decode(envelope.message)
        tac_msg_type = TACMessage.Type(tac_msg.get("type"))
        logger.debug("[{}]: Handling controller response. type={}".format(
            self.agent_name, tac_msg_type))
        try:
            if envelope.sender != self.game_instance.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 self.game_instance.game_phase == GamePhase.PRE_GAME:
                raise ValueError(
                    "We do not expect a controller agent message in the pre game phase."
                )
            elif self.game_instance.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 self.game_instance.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 self.game_instance.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))
Пример #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)
        self.mailbox.outbox.put_message(to=self.game_instance.controller_pbk, sender=self.crypto.public_key,
                                        protocol_id=TACMessage.protocol_id, message=tac_bytes)
Пример #6
0
    def handle(self, message: TACMessage, sender: Address) -> None:
        """
        Handle a 'get agent state' TACMessage.

        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
        """
        logger.debug(
            "[{}]: Handling the 'get agent state' TACMessage: {}".format(
                self.controller_agent.name, message
            )
        )
        if not self.controller_agent.game_handler.is_game_running:
            logger.error(
                "[{}]: GetStateUpdate TACMessage is not valid while the competition is not running.".format(
                    self.controller_agent.name
                )
            )
            tac_msg = TACMessage(
                tac_type=TACMessage.Type.TAC_ERROR,
                error_code=TACMessage.ErrorCode.COMPETITION_NOT_RUNNING,
            )
        if sender not in self.controller_agent.game_handler.registered_agents:
            logger.error(
                "[{}]: Agent not registered: '{}'".format(
                    self.controller_agent.name, message.get("agent_name")
                )
            )
            tac_msg = TACMessage(
                tac_type=TACMessage.Type.TAC_ERROR,
                error_code=TACMessage.ErrorCode.AGENT_NOT_REGISTERED,
            )
        else:
            transactions = self.controller_agent.game_handler.confirmed_transaction_per_participant[
                sender
            ]  # type: List[Transaction]
            initial_game_data = self.controller_agent.game_handler.game_data_per_participant[
                sender
            ]  # type: GameData
            tac_msg = TACMessage(
                tac_type=TACMessage.Type.STATE_UPDATE,
                initial_state=initial_game_data,
                transactions=transactions,
            )
        tac_bytes = TACSerializer().encode(tac_msg)
        self.controller_agent.mailbox.outbox.put_message(
            to=sender,
            sender=self.controller_agent.crypto.public_key,
            protocol_id=TACMessage.protocol_id,
            message=tac_bytes,
        )
Пример #7
0
 def _handle_non_matching_transaction(self, message: TACMessage,
                                      sender: Address) -> None:
     """Handle non-matching transaction."""
     tac_msg = TACMessage(
         tac_type=TACMessage.Type.TAC_ERROR,
         error_code=TACMessage.ErrorCode.TRANSACTION_NOT_MATCHING)
     tac_bytes = TACSerializer().encode(tac_msg)
     self.controller_agent.mailbox.outbox.put_message(
         to=sender,
         sender=self.controller_agent.crypto.public_key,
         protocol_id=TACMessage.protocol_id,
         message=tac_bytes)
Пример #8
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")})
     tac_bytes = TACSerializer().encode(tac_msg)
     self.controller_agent.mailbox.outbox.put_message(
         to=sender,
         sender=self.controller_agent.crypto.public_key,
         protocol_id=TACMessage.protocol_id,
         message=tac_bytes)
Пример #9
0
 def notify_competition_cancelled(self):
     """Notify agents that the TAC is cancelled."""
     logger.debug("[{}]: Notifying agents that TAC is cancelled.".format(
         self.agent_name))
     for agent_pbk in self.registered_agents:
         tac_msg = TACMessage(tac_type=TACMessage.Type.CANCELLED)
         tac_bytes = TACSerializer().encode(tac_msg)
         self.mailbox.outbox.put_message(to=agent_pbk,
                                         sender=self.crypto.public_key,
                                         protocol_id=TACMessage.protocol_id,
                                         message=tac_bytes)
     # wait some time to make sure the connection delivers the messages
     time.sleep(2.0)
     self._game_phase = GamePhase.POST_GAME
Пример #10
0
    def _send_game_data_to_agents(self) -> None:
        """
        Send the data of every agent about the game (e.g. endowments, preferences, scores).

        Assuming that the agent labels are public keys of the OEF Agents.

        :return: None.
        """
        for public_key in self.current_game.configuration.agent_pbks:
            agent_state = self.current_game.get_agent_state_from_agent_pbk(public_key)
            game_data_response = GameData(
                public_key,
                agent_state.balance,
                agent_state.current_holdings,
                agent_state.utility_params,
                self.current_game.configuration.nb_agents,
                self.current_game.configuration.nb_goods,
                self.current_game.configuration.tx_fee,
                self.current_game.configuration.agent_pbk_to_name,
                self.current_game.configuration.good_pbk_to_name,
                self.current_game.configuration.version_id,
            )
            logger.debug(
                "[{}]: sending GameData to '{}': {}".format(
                    self.agent_name, public_key, str(game_data_response)
                )
            )
            self.game_data_per_participant[public_key] = game_data_response

            msg = TACMessage(
                tac_type=TACMessage.Type.GAME_DATA,
                money=agent_state.balance,
                endowment=agent_state.current_holdings,
                utility_params=agent_state.utility_params,
                nb_agents=self.current_game.configuration.nb_agents,
                nb_goods=self.current_game.configuration.nb_goods,
                tx_fee=self.current_game.configuration.tx_fee,
                agent_pbk_to_name=self.current_game.configuration.agent_pbk_to_name,
                good_pbk_to_name=self.current_game.configuration.good_pbk_to_name,
                version_id=self.current_game.configuration.version_id,
            )
            tac_bytes = TACSerializer().encode(msg)
            self.mailbox.outbox.put_message(
                to=public_key,
                sender=self.crypto.public_key,
                protocol_id=TACMessage.protocol_id,
                message=tac_bytes,
            )
Пример #11
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
        """
        self.game_instance.controller_pbk = controller_pbk
        self.game_instance._game_phase = GamePhase.GAME_SETUP
        tac_msg = TACMessage(tac_type=TACMessage.Type.GET_STATE_UPDATE)
        tac_bytes = TACSerializer().encode(tac_msg)
        self.mailbox.outbox.put_message(to=self.game_instance.controller_pbk,
                                        sender=self.crypto.public_key,
                                        protocol_id=TACMessage.protocol_id,
                                        message=tac_bytes)
Пример #12
0
    def on_match_accept(self, match_accept: Message,
                        dialogue: Dialogue) -> List[Envelope]:
        """
        Handle a matching Accept.

        :param match_accept: the envelope containing the MatchAccept
        :param dialogue: the dialogue
        :return: a Transaction
        """
        assert (match_accept.get("performative")
                == FIPAMessage.Performative.MATCH_ACCEPT
                and dialogue.dialogue_label in self.game_instance.
                transaction_manager.pending_initial_acceptances
                and match_accept.get("target")
                in self.game_instance.transaction_manager.
                pending_initial_acceptances[dialogue.dialogue_label])
        logger.debug(
            "[{}]: on_match_accept: msg_id={}, dialogue_id={}, origin={}, target={}"
            .format(
                self.agent_name,
                match_accept.get("id"),
                match_accept.get("dialogue_id"),
                dialogue.dialogue_label.dialogue_opponent_pbk,
                match_accept.get("target"),
            ))
        results = []
        transaction = self.game_instance.transaction_manager.pop_pending_initial_acceptance(
            dialogue.dialogue_label, match_accept.get("target"))
        tac_msg = TACMessage(
            tac_type=TACMessage.Type.TRANSACTION,
            transaction_id=transaction.transaction_id,
            is_sender_buyer=transaction.is_sender_buyer,
            counterparty=transaction.counterparty,
            amount=transaction.amount,
            quantities_by_good_pbk=transaction.quantities_by_good_pbk,
        )
        dialogue.outgoing_extend([tac_msg])
        tac_bytes = TACSerializer().encode(tac_msg)
        results.append(
            Envelope(
                to=self.game_instance.controller_pbk,
                sender=self.crypto.public_key,
                protocol_id=TACMessage.protocol_id,
                message=tac_bytes,
            ))
        return results
Пример #13
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
        """
        self.game_instance.controller_pbk = controller_pbk
        self.game_instance._game_phase = GamePhase.GAME_SETUP
        tac_msg = TACMessage(tac_type=TACMessage.Type.REGISTER,
                             agent_name=self.agent_name)
        tac_bytes = TACSerializer().encode(tac_msg)
        self.mailbox.outbox.put_message(to=self.game_instance.controller_pbk,
                                        sender=self.crypto.public_key,
                                        protocol_id=TACMessage.protocol_id,
                                        message=tac_bytes)
Пример #14
0
    def on_accept(self, accept: Message, dialogue: Dialogue) -> List[Envelope]:
        """
        Handle an Accept.

        :param accept: the message containing the Accept
        :param dialogue: the dialogue
        :return: a Decline, or an Accept and a Transaction, or a Transaction (in a Message object)
        """
        assert accept.get("performative") == FIPAMessage.Performative.ACCEPT \
            and dialogue.dialogue_label in self.game_instance.transaction_manager.pending_proposals \
            and accept.get("target") in self.game_instance.transaction_manager.pending_proposals[dialogue.dialogue_label]
        logger.debug(
            "[{}]: on_accept: msg_id={}, dialogue_id={}, origin={}, target={}".
            format(self.agent_name, accept.get("id"),
                   accept.get("dialogue_id"),
                   dialogue.dialogue_label.dialogue_opponent_pbk,
                   accept.get("target")))
        new_msg_id = accept.get("id") + 1
        results = []
        transaction = self.game_instance.transaction_manager.pop_pending_proposal(
            dialogue.dialogue_label, accept.get("target"))
        is_profitable_transaction, accept_log_msg = self.game_instance.is_profitable_transaction(
            transaction, dialogue)
        logger.debug(accept_log_msg)
        if is_profitable_transaction:
            if self.game_instance.strategy.is_world_modeling:
                self.game_instance.world_state.update_on_initial_accept(
                    transaction)
            logger.debug("[{}]: Locking the current state (as {}).".format(
                self.agent_name, dialogue.role))
            self.game_instance.transaction_manager.add_locked_tx(
                transaction, as_seller=dialogue.is_seller)

            tac_msg = TACMessage(
                tac_type=TACMessage.Type.TRANSACTION,
                transaction_id=transaction.transaction_id,
                is_sender_buyer=transaction.is_sender_buyer,
                counterparty=transaction.counterparty,
                amount=transaction.amount,
                quantities_by_good_pbk=transaction.quantities_by_good_pbk)
            dialogue.outgoing_extend([tac_msg])
            tac_bytes = TACSerializer().encode(tac_msg)
            results.append(
                Envelope(to=self.game_instance.controller_pbk,
                         sender=self.crypto.public_key,
                         protocol_id=TACMessage.protocol_id,
                         message=tac_bytes))

            msg = FIPAMessage(
                message_id=new_msg_id,
                dialogue_id=accept.get("dialogue_id"),
                target=accept.get("id"),
                performative=FIPAMessage.Performative.MATCH_ACCEPT)
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            results.append(
                Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk,
                         sender=self.crypto.public_key,
                         protocol_id=FIPAMessage.protocol_id,
                         message=msg_bytes))
        else:
            logger.debug("[{}]: Decline the accept (as {}).".format(
                self.agent_name, dialogue.role))

            msg = FIPAMessage(message_id=new_msg_id,
                              dialogue_id=accept.get("dialogue_id"),
                              target=accept.get("id"),
                              performative=FIPAMessage.Performative.DECLINE)
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            results.append(
                Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk,
                         sender=self.crypto.public_key,
                         protocol_id=FIPAMessage.protocol_id,
                         message=msg_bytes))
            self.game_instance.stats_manager.add_dialogue_endstate(
                EndState.DECLINED_ACCEPT, dialogue.is_self_initiated)
        return results
Пример #15
0
    def handle(self, message: TACMessage, sender: Address) -> None:
        """
        Handle a register message.

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

        :param message: the 'get agent state' TACMessage.
        :param sender: the public key of the sender
        :return: None
        """
        whitelist = self.controller_agent.game_handler.tac_parameters.whitelist
        agent_name = message.get("agent_name")
        if whitelist is not None and agent_name not in whitelist:
            logger.error("[{}]: Agent name not in whitelist: '{}'".format(
                self.controller_agent.name, agent_name))
            tac_msg = TACMessage(
                tac_type=TACMessage.Type.TAC_ERROR,
                error_code=TACMessage.ErrorCode.AGENT_NAME_NOT_IN_WHITELIST)
            tac_bytes = TACSerializer().encode(tac_msg)
            self.controller_agent.mailbox.outbox.put_message(
                to=sender,
                sender=self.controller_agent.crypto.public_key,
                protocol_id=TACMessage.protocol_id,
                message=tac_bytes)

        if sender in self.controller_agent.game_handler.registered_agents:
            logger.error("[{}]: Agent already registered: '{}'".format(
                self.controller_agent.name,
                self.controller_agent.game_handler.agent_pbk_to_name[sender]))
            tac_msg = TACMessage(
                tac_type=TACMessage.Type.TAC_ERROR,
                error_code=TACMessage.ErrorCode.AGENT_PBK_ALREADY_REGISTERED)
            tac_bytes = TACSerializer().encode(tac_msg)
            self.controller_agent.mailbox.outbox.put_message(
                to=sender,
                sender=self.controller_agent.crypto.public_key,
                protocol_id=TACMessage.protocol_id,
                message=tac_bytes)

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

        try:
            self.controller_agent.game_handler.monitor.dashboard.agent_pbk_to_name.update(
                {sender: agent_name})  # type: ignore
            self.controller_agent.game_handler.monitor.update()
        except Exception as e:
            logger.error(str(e))

        self.controller_agent.game_handler.agent_pbk_to_name[
            sender] = agent_name
        logger.debug("[{}]: Agent registered: '{}'".format(
            self.controller_agent.name,
            self.controller_agent.game_handler.agent_pbk_to_name[sender]))
        self.controller_agent.game_handler.registered_agents.add(sender)