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))
def _on_start(self, tac_message: TACMessage, controller_pbk: Address) -> None: """ Handle the 'start' event emitted by the controller. :param tac_message: the game data :return: None """ logger.info( "[{}]: Received start event from the controller. Starting to compete..." .format(self.context.agent_name)) game = cast(Game, self.context.game) game.init(tac_message, controller_pbk) game.update_game_phase(Phase.GAME) state_update_msg = StateUpdateMessage( performative=StateUpdateMessage.Performative.INITIALIZE, amount_by_currency=cast(Dict[str, int], tac_message.get("amount_by_currency")), quantities_by_good_pbk=cast( Dict[str, int], tac_message.get("quantities_by_good_pbk")), exchange_params_by_currency=cast( Dict[str, float], tac_message.get("exchange_params_by_currency")), utility_params_by_good_pbk=cast( Dict[str, float], tac_message.get("utility_params_by_good_pbk")), tx_fee=cast(int, tac_message.get("tx_fee"))) self.context.decision_maker_message_queue.put_nowait(state_update_msg)
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))
def _on_tac_error(self, tac_message: TACMessage, controller_pbk: Address) -> None: """ Handle 'on tac error' event emitted by the controller. :param error: the error object :return: None """ error_code = TACMessage.ErrorCode(tac_message.get("error_code")) logger.error("[{}]: Received error from the controller. error_msg={}".format(self.context.agent_name, TACMessage._from_ec_to_msg.get(error_code))) if error_code == TACMessage.ErrorCode.TRANSACTION_NOT_VALID: start_idx_of_tx_id = len("Error in checking transaction: ") transaction_id = cast(str, tac_message.get("error_msg"))[start_idx_of_tx_id:] logger.warning("[{}]: Received error on transaction id: {}".format(self.context.agent_name, transaction_id))
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))
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))
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))
def _on_transaction_confirmed(self, message: TACMessage, controller_pbk: Address) -> None: """ Handle 'on transaction confirmed' event emitted by the controller. :param tx_confirmation: the transaction confirmation :return: None """ logger.info( "[{}]: Received transaction confirmation from the controller: transaction_id={}" .format(self.context.agent_name, message.get("transaction_id"))) state_update_msg = StateUpdateMessage( performative=StateUpdateMessage.Performative.APPLY, amount_by_currency=cast(Dict[str, int], message.get("amount_by_currency")), quantities_by_good_pbk=cast(Dict[str, int], message.get("quantities_by_good_pbk"))) self.context.decision_maker_message_queue.put_nowait(state_update_msg)
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)
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)
def _on_dialogue_error(self, tac_message: TACMessage, controller_pbk: Address) -> None: """ Handle dialogue error event emitted by the controller. :param message: the dialogue error message :param controller_pbk: the address of the controller :return: None """ logger.warning( "[{}]: Received Dialogue error from: details={}, sender={}".format( self.context.agent_name, tac_message.get("details"), controller_pbk))
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)
def from_message(cls, message: TACMessage, sender: Address) -> 'Transaction': """ Create a transaction from a proposal. :param message: the message :return: Transaction """ assert message.get('type') == TACMessage.Type.TRANSACTION return Transaction( cast(str, message.get("transaction_id")), sender, cast(str, message.get("counterparty")), cast(Dict[str, int], message.get("amount_by_currency")), cast(int, message.get("sender_tx_fee")), cast(int, message.get("counterparty_tx_fee")), cast(Dict[str, int], message.get("quantities_by_good_pbk")))
def handle(self, message: Message, sender: str) -> None: """ Implement the reaction to a message. :param message: the message :param sender: the sender :return: None """ tac_msg = cast(TACMessage, message) tac_msg_type = TACMessage.Type(tac_msg.get("type")) game = cast(Game, self.context.game) logger.debug("[{}]: Handling controller response. type={}".format( self.context.agent_name, tac_msg_type)) try: if 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, sender) elif game.phase.value == Phase.PRE_GAME.value: raise ValueError( "We do not expect a controller agent message in the pre game phase." ) elif game.phase.value == Phase.GAME_REGISTRATION.value: if tac_msg_type == TACMessage.Type.GAME_DATA: self._on_start(tac_msg, sender) elif tac_msg_type == TACMessage.Type.CANCELLED: self._on_cancelled() elif game.phase.value == Phase.GAME.value: if tac_msg_type == TACMessage.Type.TRANSACTION_CONFIRMATION: self._on_transaction_confirmed(tac_msg, 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, sender) elif game.phase.value == Phase.POST_GAME.value: raise ValueError( "We do not expect a controller agent message in the post game phase." ) except ValueError as e: logger.warning(str(e))
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)
def init(self, tac_message: TACMessage, controller_pbk: Address) -> None: """ Populate data structures with the game data. :param tac_message: the tac message with the game instance data :param controller_pbk: the public key of the controller :return: None """ assert tac_message.get( "type" ) == TACMessage.Type.GAME_DATA, "Wrong TACMessage for initialization of TAC game." assert controller_pbk == self.expected_controller_pbk, "TACMessage from unexpected controller." assert tac_message.get( "version_id" ) == self.expected_version_id, "TACMessage for unexpected game." self._game_configuration = GameConfiguration( cast(str, tac_message.get("version_id")), cast(int, tac_message.get("nb_agents")), cast(int, tac_message.get("nb_goods")), cast(float, tac_message.get("tx_fee")), cast(Dict[str, str], tac_message.get("agent_pbk_to_name")), cast(Dict[str, str], tac_message.get("good_pbk_to_name")), controller_pbk)
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))
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))
def _on_transaction_confirmed(self, message: TACMessage, controller_pbk: Address) -> None: """ Handle 'on transaction confirmed' event emitted by the controller. :param tx_confirmation: the transaction confirmation :return: None """ logger.debug("[{}]: Received transaction confirmation from the controller: transaction_id={}".format(self.context.agent_name, message.get("transaction_id")))
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
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
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.counterparty = msg.get("counterparty") tac_msg.amount_by_currency.extend( _from_dict_to_pairs(msg.get("amount_by_currency"))) tac_msg.sender_tx_fee = msg.get("sender_tx_fee") tac_msg.counterparty_tx_fee = msg.get("counterparty_tx_fee") tac_msg.quantities_by_good_pbk.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.amount_by_currency.extend( _from_dict_to_pairs(msg.get("amount_by_currency"))) tac_msg.exchange_params_by_currency.extend( _from_dict_to_pairs(msg.get("exchange_params_by_currency"))) tac_msg.quantities_by_good_pbk.extend( _from_dict_to_pairs(msg.get("quantities_by_good_pbk"))) tac_msg.utility_params_by_good_pbk.extend( _from_dict_to_pairs(msg.get("utility_params_by_good_pbk"))) 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_msg.version_id = msg.get("version_id") 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_msg.amount_by_currency.extend( _from_dict_to_pairs(msg.get("amount_by_currency"))) tac_msg.quantities_by_good_pbk.extend( _from_dict_to_pairs(msg.get("quantities_by_good_pbk"))) 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("game_data") # game_data = tac_pb2.TACController.GameData() # type: ignore # game_data.amount_by_currency.extend(_from_dict_to_pairs(cast(Dict[str, str], game_data_json["amount_by_currency"]))) # type: ignore # game_data.exchange_params_by_currency.extend(_from_dict_to_pairs(cast(Dict[str, str], game_data_json["exchange_params_by_currency"]))) # type: ignore # game_data.quantities_by_good_pbk.extend(_from_dict_to_pairs(cast(Dict[str, str], game_data_json["quantities_by_good_pbk"]))) # type: ignore # game_data.utility_params_by_good_pbk.extend(_from_dict_to_pairs(cast(Dict[str, str], game_data_json["utility_params_by_good_pbk"]))) # 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.counterparty = t.get("counterparty") # tx.amount_by_currency.extend(_from_dict_to_pairs(t.get("amount_by_currency"))) # tx.sender_tx_fee = t.get("sender_tx_fee") # tx.counterparty_tx_fee = t.get("counterparty_tx_fee") # tx.quantities_by_good_pbk.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
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"
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