Exemple #1
0
    def is_permitted_for_new_dialogue(self, message: Message,
                                      known_pbks: List[str],
                                      sender: Address) -> bool:
        """
        Check whether an agent message is permitted for a new dialogue.

        That is, the message has to
        - be a CFP,
        - have the correct msg id and message target, and
        - be from a known public key.

        :param message: the agent message
        :param known_pbks: the list of known public keys

        :return: a boolean indicating whether the message is permitted for a new dialogue
        """
        msg_id = message.get("id")
        target = message.get("target")
        performative = message.get("performative")

        result = performative == FIPAMessage.Performative.CFP \
            and msg_id == STARTING_MESSAGE_ID\
            and target == STARTING_MESSAGE_TARGET \
            and (sender in known_pbks)
        return result
Exemple #2
0
    def get_dialogue(
        self, message: Message, sender: Address, agent_pbk: Address
    ) -> Dialogue:
        """
        Retrieve dialogue.

        :param message: the agent message
        :param agent_pbk: the public key of the agent

        :return: the dialogue
        """
        dialogue_id = message.get("dialogue_id")
        opponent = sender
        target = message.get("target")
        performative = message.get("performative")
        self_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent, agent_pbk)
        other_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent, opponent)
        if (
            performative == FIPAMessage.Performative.PROPOSE
            and target == PROPOSE_TARGET
            and self_initiated_dialogue_label in self.dialogues
        ):
            dialogue = self.dialogues[self_initiated_dialogue_label]
        elif performative == FIPAMessage.Performative.ACCEPT:
            if (
                target == ACCEPT_TARGET
                and other_initiated_dialogue_label in self.dialogues
            ):
                dialogue = self.dialogues[other_initiated_dialogue_label]
            else:
                raise ValueError("Should have found dialogue.")
        elif performative == FIPAMessage.Performative.MATCH_ACCEPT:
            if (
                target == MATCH_ACCEPT_TARGET
                and self_initiated_dialogue_label in self.dialogues
            ):
                dialogue = self.dialogues[self_initiated_dialogue_label]
            else:
                raise ValueError("Should have found dialogue.")
        elif performative == FIPAMessage.Performative.DECLINE:
            if (
                target == DECLINED_CFP_TARGET
                and self_initiated_dialogue_label in self.dialogues
            ):
                dialogue = self.dialogues[self_initiated_dialogue_label]
            elif (
                target == DECLINED_PROPOSE_TARGET
                and other_initiated_dialogue_label in self.dialogues
            ):
                dialogue = self.dialogues[other_initiated_dialogue_label]
            elif (
                target == DECLINED_ACCEPT_TARGET
                and self_initiated_dialogue_label in self.dialogues
            ):
                dialogue = self.dialogues[self_initiated_dialogue_label]
            else:
                raise ValueError("Should have found dialogue.")
        else:
            raise ValueError("Should have found dialogue.")
        return dialogue
Exemple #3
0
    def on_oef_error(self, oef_error: Message) -> None:
        """
        Handle an OEF error message.

        :param oef_error: the oef error

        :return: None
        """
        logger.error(
            "[{}]: Received OEF error: answer_id={}, operation={}".format(
                self.agent_name, oef_error.get("id"),
                oef_error.get("operation")))
Exemple #4
0
    def on_dialogue_error(self, dialogue_error: Message) -> None:
        """
        Handle a dialogue error message.

        :param dialogue_error: the dialogue error message

        :return: None
        """
        logger.error(
            "[{}]: Received Dialogue error: answer_id={}, dialogue_id={}, origin={}"
            .format(self.agent_name, dialogue_error.get("id"),
                    dialogue_error.get("dialogue_id"),
                    dialogue_error.get("origin")))
Exemple #5
0
    def is_belonging_to_registered_dialogue(self, message: Message,
                                            agent_pbk: Address,
                                            sender: Address) -> bool:
        """
        Check whether an agent message is part of a registered dialogue.

        :param message: the agent message
        :param agent_pbk: the public key of the agent

        :return: boolean indicating whether the message belongs to a registered dialogue
        """
        dialogue_id = message.get("dialogue_id")
        opponent = sender
        target = message.get("target")
        performative = message.get("performative")
        self_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent,
                                                      agent_pbk)
        other_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent,
                                                       opponent)
        result = False
        if performative == FIPAMessage.Performative.PROPOSE and target == PROPOSE_TARGET and self_initiated_dialogue_label in self.dialogues:
            self_initiated_dialogue = self.dialogues[
                self_initiated_dialogue_label]
            result = self_initiated_dialogue.is_expecting_propose()
        elif performative == FIPAMessage.Performative.ACCEPT:
            if target == ACCEPT_TARGET and other_initiated_dialogue_label in self.dialogues:
                other_initiated_dialogue = self.dialogues[
                    other_initiated_dialogue_label]
                result = other_initiated_dialogue.is_expecting_initial_accept()
        elif performative == FIPAMessage.Performative.MATCH_ACCEPT:
            if target == MATCH_ACCEPT_TARGET and self_initiated_dialogue_label in self.dialogues:
                self_initiated_dialogue = self.dialogues[
                    self_initiated_dialogue_label]
                result = self_initiated_dialogue.is_expecting_matching_accept()
        elif performative == FIPAMessage.Performative.DECLINE:
            if target == DECLINED_CFP_TARGET and self_initiated_dialogue_label in self.dialogues:
                self_initiated_dialogue = self.dialogues[
                    self_initiated_dialogue_label]
                result = self_initiated_dialogue.is_expecting_cfp_decline()
            elif target == DECLINED_PROPOSE_TARGET and other_initiated_dialogue_label in self.dialogues:
                other_initiated_dialogue = self.dialogues[
                    other_initiated_dialogue_label]
                result = other_initiated_dialogue.is_expecting_propose_decline(
                )
            elif target == DECLINED_ACCEPT_TARGET and self_initiated_dialogue_label in self.dialogues:
                self_initiated_dialogue = self.dialogues[
                    self_initiated_dialogue_label]
                result = self_initiated_dialogue.is_expecting_accept_decline()
        return result
Exemple #6
0
    def _handle(self, message: Message, dialogue: Dialogue) -> List[Envelope]:
        """
        Handle a message according to the defined behaviour.

        :param message: the agent message
        :param dialogue: the dialogue

        :return: a list of agent messages
        """
        dialogue.incoming_extend([message])
        results = []  # type: List[Envelope]
        performative = message.get("performative")
        if performative == FIPAMessage.Performative.CFP:
            result = self.negotiation_behaviour.on_cfp(message, dialogue)
            results = [result]
        elif performative == FIPAMessage.Performative.PROPOSE:
            result = self.negotiation_behaviour.on_propose(message, dialogue)
            results = [result]
        elif performative == FIPAMessage.Performative.ACCEPT:
            results = self.negotiation_behaviour.on_accept(message, dialogue)
        elif performative == FIPAMessage.Performative.MATCH_ACCEPT:
            results = self.negotiation_behaviour.on_match_accept(
                message, dialogue)
        elif performative == FIPAMessage.Performative.DECLINE:
            self.negotiation_behaviour.on_decline(message, dialogue)
            results = []
        else:
            raise ValueError("Performative not supported: {}".format(
                str(performative)))

        return results
Exemple #7
0
    def on_propose(self, propose: Message, dialogue: Dialogue) -> Envelope:
        """
        Handle a Propose.

        :param propose: the message containing the Propose
        :param dialogue: the dialogue

        :return: an Accept or a Decline
        """
        logger.debug("[{}]: on propose as {}.".format(self.agent_name,
                                                      dialogue.role))
        assert propose.get("performative") == FIPAMessage.Performative.PROPOSE
        proposal = propose.get("proposal")[0]
        transaction_id = generate_transaction_id(
            self.crypto.public_key,
            dialogue.dialogue_label.dialogue_opponent_pbk,
            dialogue.dialogue_label, dialogue.is_seller)
        transaction = Transaction.from_proposal(
            proposal=proposal,
            transaction_id=transaction_id,
            is_sender_buyer=not dialogue.is_seller,
            counterparty=dialogue.dialogue_label.dialogue_opponent_pbk,
            sender=self.crypto.public_key)
        new_msg_id = propose.get("id") + 1
        is_profitable_transaction, propose_log_msg = self.game_instance.is_profitable_transaction(
            transaction, dialogue)
        logger.debug(propose_log_msg)
        if is_profitable_transaction:
            logger.debug("[{}]: Accepting propose (as {}).".format(
                self.agent_name, dialogue.role))
            self.game_instance.transaction_manager.add_locked_tx(
                transaction, as_seller=dialogue.is_seller)
            self.game_instance.transaction_manager.add_pending_initial_acceptance(
                dialogue.dialogue_label, new_msg_id, transaction)
            msg = FIPAMessage(message_id=new_msg_id,
                              dialogue_id=propose.get("dialogue_id"),
                              target=propose.get("id"),
                              performative=FIPAMessage.Performative.ACCEPT)
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            result = Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk,
                              sender=self.crypto.public_key,
                              protocol_id=FIPAMessage.protocol_id,
                              message=msg_bytes)
        else:
            logger.debug("[{}]: Declining propose (as {})".format(
                self.agent_name, dialogue.role))
            msg = FIPAMessage(message_id=new_msg_id,
                              dialogue_id=propose.get("dialogue_id"),
                              target=propose.get("id"),
                              performative=FIPAMessage.Performative.DECLINE)
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            result = 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_PROPOSE, dialogue.is_self_initiated)
        return result
Exemple #8
0
    def on_new_dialogue(self, message: Message, sender: Address) -> None:
        """
        React to a new dialogue.

        :param message: the agent message

        :return: None
        """
        assert message.get("performative") == FIPAMessage.Performative.CFP
        services = json.loads(message.get("query").decode("utf-8"))
        is_seller = services["description"] == TAC_DEMAND_DATAMODEL_NAME
        dialogue = self.dialogues.create_opponent_initiated(
            sender, message.get("dialogue_id"), is_seller)
        logger.debug("[{}]: saving dialogue (as {}): dialogue_id={}".format(
            self.agent_name, dialogue.role,
            dialogue.dialogue_label.dialogue_id))
        envelopes = self._handle(message, dialogue)
        for envelope in envelopes:
            self.mailbox.outbox.put(envelope)
Exemple #9
0
    def on_decline(self, decline: Message, dialogue: Dialogue) -> None:
        """
        Handle a Decline.

        :param decline: the message  containing the Decline
        :param dialogue: the dialogue

        :return: None
        """
        assert decline.get("performative") == FIPAMessage.Performative.DECLINE
        logger.debug(
            "[{}]: on_decline: msg_id={}, dialogue_id={}, origin={}, target={}"
            .format(
                self.agent_name,
                decline.get("id"),
                decline.get("dialogue_id"),
                dialogue.dialogue_label.dialogue_opponent_pbk,
                decline.get("target"),
            ))
        target = decline.get("target")
        if target == 1:
            self.game_instance.stats_manager.add_dialogue_endstate(
                EndState.DECLINED_CFP, dialogue.is_self_initiated)
        elif target == 2:
            self.game_instance.stats_manager.add_dialogue_endstate(
                EndState.DECLINED_PROPOSE, dialogue.is_self_initiated)
            transaction = self.game_instance.transaction_manager.pop_pending_proposal(
                dialogue.dialogue_label, target)
            if self.game_instance.strategy.is_world_modeling:
                self.game_instance.world_state.update_on_declined_propose(
                    transaction)
        elif target == 3:
            self.game_instance.stats_manager.add_dialogue_endstate(
                EndState.DECLINED_ACCEPT, dialogue.is_self_initiated)
            transaction = self.game_instance.transaction_manager.pop_pending_initial_acceptance(
                dialogue.dialogue_label, target)
            self.game_instance.transaction_manager.pop_locked_tx(
                transaction.transaction_id)

        return None
Exemple #10
0
    def on_search_result(self, search_result: Message) -> None:
        """
        Split the search results from the OEF.

        :param search_result: the search result

        :return: None
        """
        search_id = search_result.get("id")
        agents = search_result.get("agents")
        logger.debug("[{}]: on search result: {} {}".format(
            self.agent_name, search_id, agents))
        if search_id in self.game_instance.search.ids_for_tac:
            self._on_controller_search_result(agents)
        elif search_id in self.game_instance.search.ids_for_sellers:
            self._on_services_search_result(agents,
                                            is_searching_for_sellers=True)
        elif search_id in self.game_instance.search.ids_for_buyers:
            self._on_services_search_result(agents,
                                            is_searching_for_sellers=False)
        else:
            logger.debug("[{}]: Unknown search id: search_id={}".format(
                self.agent_name, search_id))
Exemple #11
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
Exemple #12
0
    def on_cfp(self, cfp: Message, dialogue: Dialogue) -> Envelope:
        """
        Handle a CFP.

        :param cfp: the message containing the CFP
        :param dialogue: the dialogue

        :return: a Propose or a Decline
        """
        assert cfp.get("performative") == FIPAMessage.Performative.CFP
        goods_description = self.game_instance.get_service_description(
            is_supply=dialogue.is_seller)
        new_msg_id = cfp.get("id") + 1
        decline = False
        cfp_services = json.loads(cfp.get("query").decode('utf-8'))
        if not self.game_instance.is_matching(cfp_services, goods_description):
            decline = True
            logger.debug(
                "[{}]: Current holdings do not satisfy CFP query.".format(
                    self.agent_name))
        else:
            proposal = self.game_instance.generate_proposal(
                cfp_services, dialogue.is_seller)
            if proposal is None:
                decline = True
                logger.debug(
                    "[{}]: Current strategy does not generate proposal that satisfies CFP query."
                    .format(self.agent_name))

        if decline:
            logger.debug("[{}]: sending to {} a Decline{}".format(
                self.agent_name, dialogue.dialogue_label.dialogue_opponent_pbk,
                pprint.pformat({
                    "msg_id": new_msg_id,
                    "dialogue_id": cfp.get("dialogue_id"),
                    "origin": dialogue.dialogue_label.dialogue_opponent_pbk,
                    "target": cfp.get("target")
                })))
            msg = FIPAMessage(message_id=new_msg_id,
                              dialogue_id=cfp.get("dialogue_id"),
                              performative=FIPAMessage.Performative.DECLINE,
                              target=cfp.get("id"))
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            response = 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_CFP, dialogue.is_self_initiated)
        else:
            proposal = cast(Description, proposal)
            transaction_id = generate_transaction_id(
                self.crypto.public_key,
                dialogue.dialogue_label.dialogue_opponent_pbk,
                dialogue.dialogue_label, dialogue.is_seller)
            transaction = Transaction.from_proposal(
                proposal=proposal,
                transaction_id=transaction_id,
                is_sender_buyer=not dialogue.is_seller,
                counterparty=dialogue.dialogue_label.dialogue_opponent_pbk,
                sender=self.crypto.public_key)
            self.game_instance.transaction_manager.add_pending_proposal(
                dialogue.dialogue_label, new_msg_id, transaction)
            logger.debug("[{}]: sending to {} a Propose{}".format(
                self.agent_name, dialogue.dialogue_label.dialogue_opponent_pbk,
                pprint.pformat({
                    "msg_id": new_msg_id,
                    "dialogue_id": cfp.get("dialogue_id"),
                    "origin": dialogue.dialogue_label.dialogue_opponent_pbk,
                    "target": cfp.get("id"),
                    "propose": proposal.values
                })))
            msg = FIPAMessage(performative=FIPAMessage.Performative.PROPOSE,
                              message_id=new_msg_id,
                              dialogue_id=cfp.get("dialogue_id"),
                              target=cfp.get("id"),
                              proposal=[proposal])
            dialogue.outgoing_extend([msg])
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            response = Envelope(
                to=dialogue.dialogue_label.dialogue_opponent_pbk,
                sender=self.crypto.public_key,
                protocol_id=FIPAMessage.protocol_id,
                message=msg_bytes)
        return response
Exemple #13
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
Exemple #14
0
    def encode(self, msg: Message) -> bytes:
        """
        Decode the message.

        :param msg: the message object
        :return: the bytes
        """
        tac_type = TACMessage.Type(msg.get("type"))
        tac_container = tac_pb2.TACMessage()

        if tac_type == TACMessage.Type.REGISTER:
            agent_name = msg.get("agent_name")
            tac_msg = tac_pb2.TACAgent.Register()  # type: ignore
            tac_msg.agent_name = agent_name
            tac_container.register.CopyFrom(tac_msg)
        elif tac_type == TACMessage.Type.UNREGISTER:
            tac_msg = tac_pb2.TACAgent.Unregister()  # type: ignore
            tac_container.unregister.CopyFrom(tac_msg)
        elif tac_type == TACMessage.Type.TRANSACTION:
            tac_msg = tac_pb2.TACAgent.Transaction()  # type: ignore
            tac_msg.transaction_id = msg.get("transaction_id")
            tac_msg.is_sender_buyer = msg.get("is_sender_buyer")
            tac_msg.counterparty = msg.get("counterparty")
            tac_msg.amount = msg.get("amount")
            tac_msg.quantities.extend(
                _from_dict_to_pairs(msg.get("quantities_by_good_pbk")))
            tac_container.transaction.CopyFrom(tac_msg)
        elif tac_type == TACMessage.Type.GET_STATE_UPDATE:
            tac_msg = tac_pb2.TACAgent.GetStateUpdate()  # type: ignore
            tac_container.get_state_update.CopyFrom(tac_msg)
        elif tac_type == TACMessage.Type.CANCELLED:
            tac_msg = tac_pb2.TACController.Cancelled()  # type: ignore
            tac_container.cancelled.CopyFrom(tac_msg)
        elif tac_type == TACMessage.Type.GAME_DATA:
            tac_msg = tac_pb2.TACController.GameData()  # type: ignore
            tac_msg.money = msg.get("money")
            tac_msg.endowment.extend(msg.get("endowment"))
            tac_msg.utility_params.extend(msg.get("utility_params"))
            tac_msg.nb_agents = msg.get("nb_agents")
            tac_msg.nb_goods = msg.get("nb_goods")
            tac_msg.tx_fee = msg.get("tx_fee")
            tac_msg.agent_pbk_to_name.extend(
                _from_dict_to_pairs(msg.get("agent_pbk_to_name")))
            tac_msg.good_pbk_to_name.extend(
                _from_dict_to_pairs(msg.get("good_pbk_to_name")))
            tac_container.game_data.CopyFrom(tac_msg)
        elif tac_type == TACMessage.Type.TRANSACTION_CONFIRMATION:
            tac_msg = tac_pb2.TACController.TransactionConfirmation(
            )  # type: ignore
            tac_msg.transaction_id = msg.get("transaction_id")
            tac_container.transaction_confirmation.CopyFrom(tac_msg)
        elif tac_type == TACMessage.Type.STATE_UPDATE:
            tac_msg = tac_pb2.TACController.StateUpdate()  # type: ignore
            game_data_json = msg.get("initial_state")
            game_data = tac_pb2.TACController.GameData()  # type: ignore
            game_data.money = game_data_json["money"]  # type: ignore
            game_data.endowment.extend(
                game_data_json["endowment"])  # type: ignore
            game_data.utility_params.extend(
                game_data_json["utility_params"])  # type: ignore
            game_data.nb_agents = game_data_json["nb_agents"]  # type: ignore
            game_data.nb_goods = game_data_json["nb_goods"]  # type: ignore
            game_data.tx_fee = game_data_json["tx_fee"]  # type: ignore
            game_data.agent_pbk_to_name.extend(
                _from_dict_to_pairs(
                    cast(Dict[str, str],
                         game_data_json["agent_pbk_to_name"])))  # type: ignore
            game_data.good_pbk_to_name.extend(
                _from_dict_to_pairs(
                    cast(Dict[str, str],
                         game_data_json["good_pbk_to_name"])))  # type: ignore

            tac_msg.initial_state.CopyFrom(game_data)

            transactions = []
            msg_transactions = cast(List[Any], msg.get("transactions"))
            for t in msg_transactions:
                tx = tac_pb2.TACAgent.Transaction()  # type: ignore
                tx.transaction_id = t.get("transaction_id")
                tx.is_sender_buyer = t.get("is_sender_buyer")
                tx.counterparty = t.get("counterparty")
                tx.amount = t.get("amount")
                tx.quantities.extend(
                    _from_dict_to_pairs(t.get("quantities_by_good_pbk")))
                transactions.append(tx)
            tac_msg.txs.extend(transactions)
            tac_container.state_update.CopyFrom(tac_msg)
        elif tac_type == TACMessage.Type.TAC_ERROR:
            tac_msg = tac_pb2.TACController.Error()  # type: ignore
            tac_msg.error_code = TACMessage.ErrorCode(
                msg.get("error_code")).value
            if msg.is_set("error_msg"):
                tac_msg.error_msg = msg.get("error_msg")
            if msg.is_set("details"):
                tac_msg.details.update(msg.get("details"))

            tac_container.error.CopyFrom(tac_msg)
        else:
            raise ValueError("Type not recognized: {}.".format(tac_type))

        tac_message_bytes = tac_container.SerializeToString()
        return tac_message_bytes