Пример #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))
Пример #2
0
def test_tac_message_instantiation():
    """Test instantiation of the tac message."""
    assert TACMessage(tac_type=TACMessage.Type.REGISTER,
                      agent_name='some_name')
    assert TACMessage(tac_type=TACMessage.Type.UNREGISTER)
    assert 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
                      })
    assert TACMessage(tac_type=TACMessage.Type.GET_STATE_UPDATE)
    assert TACMessage(tac_type=TACMessage.Type.CANCELLED)
    assert 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')
    assert 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
                      })
    assert TACMessage(tac_type=TACMessage.Type.TAC_ERROR,
                      error_code=TACMessage.ErrorCode.GENERIC_ERROR)
    assert str(TACMessage.Type.REGISTER) == 'register'

    msg = TACMessage(tac_type=TACMessage.Type.REGISTER, agent_name='some_name')
    with mock.patch(
            'packages.protocols.tac.message.TACMessage.Type') as mocked_type:
        mocked_type.REGISTER.value = "unknown"
        assert not msg.check_consistency(), \
            "Expect the consistency to return False"
Пример #3
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))
Пример #4
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))
Пример #5
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))
Пример #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))
Пример #7
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)
Пример #8
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)
Пример #9
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)
Пример #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)
Пример #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))
Пример #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
Пример #13
0
    def decode(self, obj: bytes) -> Message:
        """
        Decode the message.

        :param obj: the bytes object
        :return: the message
        """
        tac_container = tac_pb2.TACMessage()
        tac_container.ParseFromString(obj)

        new_body = {}  # type: Dict[str, Any]
        tac_type = tac_container.WhichOneof("content")

        if tac_type == "register":
            new_body["type"] = TACMessage.Type.REGISTER
            new_body["agent_name"] = tac_container.register.agent_name
        elif tac_type == "unregister":
            new_body["type"] = TACMessage.Type.UNREGISTER
        elif tac_type == "transaction":
            new_body["type"] = TACMessage.Type.TRANSACTION
            new_body[
                "transaction_id"] = tac_container.transaction.transaction_id
            new_body["counterparty"] = tac_container.transaction.counterparty
            new_body["amount_by_currency"] = _from_pairs_to_dict(
                tac_container.transaction.amount_by_currency)
            new_body["sender_tx_fee"] = tac_container.transaction.sender_tx_fee
            new_body[
                "counterparty_tx_fee"] = tac_container.transaction.counterparty_tx_fee
            new_body["quantities_by_good_pbk"] = _from_pairs_to_dict(
                tac_container.transaction.quantities_by_good_pbk)
        elif tac_type == "get_state_update":
            new_body["type"] = TACMessage.Type.GET_STATE_UPDATE
        elif tac_type == "cancelled":
            new_body["type"] = TACMessage.Type.CANCELLED
        elif tac_type == "game_data":
            new_body["type"] = TACMessage.Type.GAME_DATA
            new_body["amount_by_currency"] = _from_pairs_to_dict(
                tac_container.game_data.amount_by_currency)
            new_body["exchange_params_by_currency"] = _from_pairs_to_dict(
                tac_container.game_data.exchange_params_by_currency)
            new_body["quantities_by_good_pbk"] = _from_pairs_to_dict(
                tac_container.game_data.quantities_by_good_pbk)
            new_body["utility_params_by_good_pbk"] = _from_pairs_to_dict(
                tac_container.game_data.utility_params_by_good_pbk)
            new_body["tx_fee"] = tac_container.game_data.tx_fee
            new_body["agent_pbk_to_name"] = _from_pairs_to_dict(
                tac_container.game_data.agent_pbk_to_name)
            new_body["good_pbk_to_name"] = _from_pairs_to_dict(
                tac_container.game_data.good_pbk_to_name)
            new_body["version_id"] = tac_container.game_data.version_id
        elif tac_type == "transaction_confirmation":
            new_body["type"] = TACMessage.Type.TRANSACTION_CONFIRMATION
            new_body[
                "transaction_id"] = tac_container.transaction_confirmation.transaction_id
            new_body["amount_by_currency"] = _from_pairs_to_dict(
                tac_container.transaction_confirmation.amount_by_currency)
            new_body["quantities_by_good_pbk"] = _from_pairs_to_dict(
                tac_container.transaction_confirmation.quantities_by_good_pbk)
        # elif tac_type == "state_update":
        #     new_body["type"] = TACMessage.Type.STATE_UPDATE
        #     game_data = dict(
        #         amount_by_currency=_from_pairs_to_dict(tac_container.state_update.game_data.amount_by_currency),
        #         exchange_params_by_currency=_from_pairs_to_dict(tac_container.state_update.game_data.exchange_params_by_currency),
        #         quantities_by_good_pbk=_from_pairs_to_dict(tac_container.state_update.game_data.quantities_by_good_pbk),
        #         utility_params_by_good_pbk=_from_pairs_to_dict(tac_container.state_update.game_data.utility_params_by_good_pbk),
        #         tx_fee=tac_container.state_update.game_data.tx_fee,
        #         agent_pbk_to_name=_from_pairs_to_dict(tac_container.state_update.game_data.agent_pbk_to_name),
        #         good_pbk_to_name=_from_pairs_to_dict(tac_container.state_update.game_data.good_pbk_to_name),
        #         version_id=tac_container.state_update.game_data.version_id
        #     )
        #     new_body["game_data"] = game_data
        #     transactions = []
        #     for transaction in tac_container.state_update.transactions:
        #         tx_json = dict(
        #             transaction_id=transaction.transaction_id,
        #             counterparty=transaction.counterparty,
        #             amount_by_currency=_from_pairs_to_dict(transaction.amount_by_currency),
        #             sender_tx_fee=transaction.sender_tx_fee,
        #             counterparty_tx_fee=transaction.counterparty_tx_fee,
        #             quantities_by_good_pbk=_from_pairs_to_dict(transaction.quantities_by_good_pbk),
        #         )
        #         transactions.append(tx_json)
        #     new_body["transactions"] = transactions
        elif tac_type == "error":
            new_body["type"] = TACMessage.Type.TAC_ERROR
            new_body["error_code"] = TACMessage.ErrorCode(
                tac_container.error.error_code)
            if tac_container.error.error_msg:
                new_body["error_msg"] = tac_container.error.error_msg
            if tac_container.error.details:
                new_body["details"] = dict(tac_container.error.details)
        else:
            raise ValueError("Type not recognized.")

        tac_type = TACMessage.Type(new_body["type"])
        new_body["type"] = tac_type
        tac_message = TACMessage(tac_type=tac_type, body=new_body)
        return tac_message
Пример #14
0
    def decode(self, obj: bytes) -> Message:
        """
        Decode the message.

        :param obj: the bytes object
        :return: the message
        """
        tac_container = tac_pb2.TACMessage()
        tac_container.ParseFromString(obj)

        new_body = {}  # type: Dict[str, Any]
        tac_type = tac_container.WhichOneof("content")

        if tac_type == "register":
            new_body["type"] = TACMessage.Type.REGISTER
            new_body["agent_name"] = tac_container.register.agent_name
        elif tac_type == "unregister":
            new_body["type"] = TACMessage.Type.UNREGISTER
        elif tac_type == "transaction":
            new_body["type"] = TACMessage.Type.TRANSACTION
            new_body[
                "transaction_id"] = tac_container.transaction.transaction_id
            new_body[
                "is_sender_buyer"] = tac_container.transaction.is_sender_buyer
            new_body["counterparty"] = tac_container.transaction.counterparty
            new_body["amount"] = tac_container.transaction.amount
            new_body["quantities_by_good_pbk"] = _from_pairs_to_dict(
                tac_container.transaction.quantities)
        elif tac_type == "get_state_update":
            new_body["type"] = TACMessage.Type.GET_STATE_UPDATE
        elif tac_type == "cancelled":
            new_body["type"] = TACMessage.Type.CANCELLED
        elif tac_type == "game_data":
            new_body["type"] = TACMessage.Type.GAME_DATA
            new_body["money"] = tac_container.game_data.money
            new_body["endowment"] = list(tac_container.game_data.endowment)
            new_body["utility_params"] = list(
                tac_container.game_data.utility_params)
            new_body["nb_agents"] = tac_container.game_data.nb_agents
            new_body["nb_goods"] = tac_container.game_data.nb_goods
            new_body["tx_fee"] = tac_container.game_data.tx_fee
            new_body["agent_pbk_to_name"] = _from_pairs_to_dict(
                tac_container.game_data.agent_pbk_to_name)
            new_body["good_pbk_to_name"] = _from_pairs_to_dict(
                tac_container.game_data.good_pbk_to_name)
        elif tac_type == "transaction_confirmation":
            new_body["type"] = TACMessage.Type.TRANSACTION_CONFIRMATION
            new_body[
                "transaction_id"] = tac_container.transaction_confirmation.transaction_id
        elif tac_type == "state_update":
            new_body["type"] = TACMessage.Type.STATE_UPDATE
            game_data = dict(
                money=tac_container.state_update.initial_state.money,
                endowment=tac_container.state_update.initial_state.endowment,
                utility_params=tac_container.state_update.initial_state.
                utility_params,
                nb_agents=tac_container.state_update.initial_state.nb_agents,
                nb_goods=tac_container.state_update.initial_state.nb_goods,
                tx_fee=tac_container.state_update.initial_state.tx_fee,
                agent_pbk_to_name=_from_pairs_to_dict(
                    tac_container.state_update.initial_state.agent_pbk_to_name
                ),
                good_pbk_to_name=_from_pairs_to_dict(
                    tac_container.state_update.initial_state.good_pbk_to_name),
            )
            new_body["initial_state"] = game_data
            transactions = []
            for t in tac_container.state_update.txs:
                tx_json = dict(
                    transaction_id=t.transaction_id,
                    is_sender_buyer=t.is_sender_buyer,
                    counterparty=t.counterparty,
                    amount=t.amount,
                    quantities_by_good_pbk=_from_pairs_to_dict(t.quantities),
                )
                transactions.append(tx_json)
            new_body["transactions"] = transactions
        elif tac_type == "error":
            new_body["type"] = TACMessage.Type.TAC_ERROR
            new_body["error_code"] = TACMessage.ErrorCode(
                tac_container.error.error_code)
            if tac_container.error.error_msg:
                new_body["error_msg"] = tac_container.error.error_msg
            if tac_container.error.details:
                new_body["details"] = dict(tac_container.error.details)
        else:
            raise ValueError("Type not recognized.")

        tac_type = TACMessage.Type(new_body["type"])
        new_body["type"] = tac_type
        tac_message = TACMessage(tac_type=tac_type, body=new_body)
        return tac_message