def _handle_valid_transaction(self, message: TacMessage, 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 transaction: the transaction. :return: None """ game = cast(Game, self.context.game) self.context.logger.info("[{}]: Handling valid transaction: {}".format( self.context.agent_name, transaction.id[-10:])) game.settle_transaction(transaction) tx_sender_id, tx_counterparty_id = transaction.id.split("_") # send the transaction confirmation. sender_tac_msg = TacMessage( performative=TacMessage.Performative.TRANSACTION_CONFIRMATION, tx_id=tx_sender_id, amount_by_currency_id=transaction.amount_by_currency_id, quantities_by_good_id=transaction.quantities_by_good_id, ) counterparty_tac_msg = TacMessage( performative=TacMessage.Performative.TRANSACTION_CONFIRMATION, tx_id=tx_counterparty_id, amount_by_currency_id=transaction.amount_by_currency_id, quantities_by_good_id=transaction.quantities_by_good_id, ) self.context.outbox.put_message( to=transaction.sender_addr, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(sender_tac_msg), ) self.context.outbox.put_message( to=transaction.counterparty_addr, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(counterparty_tac_msg), ) # log messages self.context.logger.info( "[{}]: Transaction '{}' settled successfully.".format( self.context.agent_name, transaction.id[-10:])) self.context.logger.info("[{}]: Current state:\n{}".format( self.context.agent_name, game.holdings_summary))
def _on_unregister(self, message: TacMessage) -> None: """ Handle a unregister message. If the address is not registered, answer with an error message. :param message: the 'get agent state' TacMessage. :return: None """ game = cast(Game, self.context.game) if message.counterparty not in game.registration.agent_addr_to_name: self.context.logger.error( "[{}]: Agent not registered: '{}'".format( self.context.agent_name, message.counterparty)) tac_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, error_code=TacMessage.ErrorCode.AGENT_NOT_REGISTERED, ) self.context.outbox.put_message( to=message.counterparty, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), ) else: self.context.logger.debug("[{}]: Agent unregistered: '{}'".format( self.context.agent_name, game.configuration.agent_addr_to_name[message.counterparty], )) game.registration.unregister_agent(message.counterparty)
def _cancel_tac(self): """Notify agents that the TAC is cancelled.""" game = cast(Game, self.context.game) self.context.logger.info( "[{}]: Notifying agents that TAC is cancelled.".format( self.context.agent_name ) ) for agent_addr in game.registration.agent_addr_to_name.keys(): tac_msg = TacMessage(performative=TacMessage.Type.CANCELLED) self.context.outbox.put_message( to=agent_addr, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), ) if game.phase == Phase.GAME: self.context.logger.info( "[{}]: Finished competition:\n{}".format( self.context.agent_name, game.holdings_summary ) ) self.context.logger.info( "[{}]: Computed equilibrium:\n{}".format( self.context.agent_name, game.equilibrium_summary ) ) self.context.is_active = False
def handle(self, message: Message) -> None: """ Dispatch message to relevant handler and respond. :param message: the message :return: None """ tx_message = cast(TransactionMessage, message) if ( tx_message.performative == TransactionMessage.Performative.SUCCESSFUL_SIGNING ): # TODO: // Need to modify here and add the contract option in case we are using one. self.context.logger.info( "[{}]: transaction confirmed by decision maker, sending to controller.".format( self.context.agent_name ) ) game = cast(Game, self.context.game) tx_counterparty_signature = cast( bytes, tx_message.info.get("tx_counterparty_signature") ) tx_counterparty_id = cast(str, tx_message.info.get("tx_counterparty_id")) if (tx_counterparty_signature is not None) and ( tx_counterparty_id is not None ): tx_id = tx_message.tx_id + "_" + tx_counterparty_id msg = TacMessage( performative=TacMessage.Performative.TRANSACTION, tx_id=tx_id, tx_sender_addr=tx_message.tx_sender_addr, tx_counterparty_addr=tx_message.tx_counterparty_addr, amount_by_currency_id=tx_message.tx_amount_by_currency_id, tx_sender_fee=tx_message.tx_sender_fee, tx_counterparty_fee=tx_message.tx_counterparty_fee, quantities_by_good_id=tx_message.tx_quantities_by_good_id, tx_sender_signature=tx_message.signed_payload.get("tx_signature"), tx_counterparty_signature=tx_message.info.get( "tx_counterparty_signature" ), tx_nonce=tx_message.info.get("tx_nonce"), ) self.context.outbox.put_message( to=game.configuration.controller_addr, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(msg), ) else: self.context.logger.warning( "[{}]: transaction has no counterparty id or signature!".format( self.context.agent_name ) ) else: self.context.logger.info( "[{}]: transaction was not successful.".format(self.context.agent_name) )
def _start_tac(self): """Create a game and send the game configuration to every registered agent.""" game = cast(Game, self.context.game) # game.create() self.context.logger.info("[{}]: Started competition:\n{}".format( self.context.agent_name, game.holdings_summary)) self.context.logger.info("[{}]: Computed equilibrium:\n{}".format( self.context.agent_name, game.equilibrium_summary)) for agent_address in game.configuration.agent_addr_to_name.keys(): agent_state = game.current_agent_states[agent_address] tac_msg = TacMessage( type=TacMessage.Performative.GAME_DATA, amount_by_currency_id=agent_state.amount_by_currency_id, exchange_params_by_currency_id=agent_state. exchange_params_by_currency_id, quantities_by_good_id=agent_state.quantities_by_good_id, utility_params_by_good_id=agent_state. utility_params_by_good_id, tx_fee=game.configuration.tx_fee, agent_addr_to_name=game.configuration.agent_addr_to_name, good_id_to_name=game.configuration.good_id_to_name, version_id=game.configuration.version_id, contract_address=self.context.contracts.erc1155.instance. address, ) self.context.logger.debug( "[{}]: sending game data to '{}': {}".format( self.context.agent_name, agent_address, str(tac_msg))) self.context.outbox.put_message( to=agent_address, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), )
def _end_tac(self, game: Game, reason: str) -> None: """Notify agents that the TAC is cancelled.""" self.context.logger.info( "[{}]: Notifying agents that TAC is {}.".format( self.context.agent_name, reason ) ) for agent_addr in game.registration.agent_addr_to_name.keys(): tac_msg = TacMessage(performative=TacMessage.Performative.CANCELLED) self.context.outbox.put_message( to=agent_addr, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), )
def _handle_invalid_transaction(self, message: TacMessage) -> None: """Handle an invalid transaction.""" tx_id = message.tx_id[-10:] self.context.logger.info( "[{}]: Handling invalid transaction: {}".format( self.context.agent_name, tx_id)) tac_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, error_code=TacMessage.ErrorCode.TRANSACTION_NOT_VALID, info={"transaction_id": message.tx_id}, ) self.context.outbox.put_message( to=message.counterparty, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), )
def _start_tac(self, game: Game) -> None: """Create a game and send the game configuration to every registered agent.""" self.context.logger.info( "[{}]: Starting competition with configuration:\n{}".format( self.context.agent_name, game.holdings_summary ) ) self.context.logger.info( "[{}]: Computed theoretical equilibrium:\n{}".format( self.context.agent_name, game.equilibrium_summary ) ) for agent_address in game.conf.agent_addr_to_name.keys(): agent_state = game.current_agent_states[agent_address] tac_msg = TacMessage( performative=TacMessage.Performative.GAME_DATA, amount_by_currency_id=agent_state.amount_by_currency_id, exchange_params_by_currency_id=agent_state.exchange_params_by_currency_id, quantities_by_good_id=agent_state.quantities_by_good_id, utility_params_by_good_id=agent_state.utility_params_by_good_id, tx_fee=game.conf.tx_fee, agent_addr_to_name=game.conf.agent_addr_to_name, good_id_to_name=game.conf.good_id_to_name, currency_id_to_name=game.conf.currency_id_to_name, version_id=game.conf.version_id, info={"contract_address": game.conf.contract_address}, ) self.context.logger.debug( "[{}]: sending game data to '{}'.".format( self.context.agent_name, agent_address ) ) self.context.logger.debug( "[{}]: game data={}".format(self.context.agent_name, str(tac_msg)) ) self.context.outbox.put_message( to=agent_address, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), )
def _register_to_tac(self, controller_addr: Address) -> None: """ Register to active TAC Controller. :param controller_addr: the address of the controller. :return: None """ game = cast(Game, self.context.game) game.update_expected_controller_addr(controller_addr) game.update_game_phase(Phase.GAME_REGISTRATION) tac_msg = TacMessage( performative=TacMessage.Performative.REGISTER, agent_name=self.context.agent_name, ) tac_bytes = TacSerializer().encode(tac_msg) self.context.outbox.put_message( to=controller_addr, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=tac_bytes, )
def _on_register(self, message: TacMessage) -> None: """ Handle a register message. If the address is not registered, answer with an error message. :param message: the 'get agent state' TacMessage. :return: None """ parameters = cast(Parameters, self.context.parameters) agent_name = message.agent_name if len(parameters.whitelist ) != 0 and agent_name not in parameters.whitelist: self.context.logger.error( "[{}]: Agent name not in whitelist: '{}'".format( self.context.agent_name, agent_name)) tac_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, error_code=TacMessage.ErrorCode.AGENT_NAME_NOT_IN_WHITELIST, ) self.context.outbox.put_message( to=message.counterparty, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), ) return game = cast(Game, self.context.game) if message.counterparty in game.registration.agent_addr_to_name: self.context.logger.error( "[{}]: Agent already registered: '{}'".format( self.context.agent_name, game.registration.agent_addr_to_name[message.counterparty], )) tac_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, error_code=TacMessage.ErrorCode.AGENT_ADDR_ALREADY_REGISTERED, ) self.context.outbox.put_message( to=message.counterparty, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), ) if agent_name in game.registration.agent_addr_to_name.values(): self.context.logger.error( "[{}]: Agent with this name already registered: '{}'".format( self.context.agent_name, agent_name)) tac_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, error_code=TacMessage.ErrorCode.AGENT_NAME_ALREADY_REGISTERED, ) self.context.outbox.put_message( to=message.counterparty, sender=self.context.agent_address, protocol_id=TacMessage.protocol_id, message=TacSerializer().encode(tac_msg), ) self.context.shared_state["agents_participants_counter"] += 1 game.registration.register_agent(message.counterparty, agent_name) self.context.logger.info("[{}]: Agent registered: '{}'".format( self.context.agent_name, agent_name))
def test_tac_serialization(): """Test that the serialization for the tac message works.""" msg = TacMessage( performative=TacMessage.Performative.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(performative=TacMessage.Performative.UNREGISTER) msg_bytes = TacSerializer().encode(msg) actual_msg = TacSerializer().decode(msg_bytes) expected_msg = msg assert expected_msg == actual_msg msg = TacMessage( performative=TacMessage.Performative.TRANSACTION, tx_id="some_id", tx_sender_addr="some_address", tx_counterparty_addr="some_other_address", amount_by_currency_id={"FET": -10}, tx_sender_fee=10, tx_counterparty_fee=10, quantities_by_good_id={"123": 0, "1234": 10}, tx_nonce=1, tx_sender_signature=b"some_signature", tx_counterparty_signature=b"some_other_signature", ) msg_bytes = TacSerializer().encode(msg) actual_msg = TacSerializer().decode(msg_bytes) expected_msg = msg assert expected_msg == actual_msg msg = TacMessage(performative=TacMessage.Performative.GET_STATE_UPDATE) msg_bytes = TacSerializer().encode(msg) actual_msg = TacSerializer().decode(msg_bytes) expected_msg = msg assert expected_msg == actual_msg msg = TacMessage(performative=TacMessage.Performative.CANCELLED) msg_bytes = TacSerializer().encode(msg) actual_msg = TacSerializer().decode(msg_bytes) expected_msg = msg assert expected_msg == actual_msg msg = TacMessage( performative=TacMessage.Performative.GAME_DATA, amount_by_currency_id={"FET": 10}, exchange_params_by_currency_id={"FET": 10.0}, quantities_by_good_id={"123": 20, "1234": 15}, utility_params_by_good_id={"123": 30.0, "1234": 50.0}, tx_fee=20, agent_addr_to_name={"agent_1": "Agent one", "agent_2": "Agent two"}, currency_id_to_name={"FET": "currency_name"}, good_id_to_name={"123": "First good", "1234": "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( performative=TacMessage.Performative.TRANSACTION_CONFIRMATION, tx_id="some_id", amount_by_currency_id={"FET": 10}, quantities_by_good_id={"123": 20, "1234": 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="Performative not valid: transaction_confirmation" ): with mock.patch( "packages.fetchai.protocols.tac.message.TacMessage.Performative" ) as mocked_type: mocked_type.TRANSACTION_CONFIRMATION.value = "unknown" TacSerializer().encode(msg) msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, error_code=TacMessage.ErrorCode.GENERIC_ERROR, info={"msg": "This is info msg."}, ) msg_bytes = TacSerializer().encode(msg) actual_msg = TacSerializer().decode(msg_bytes) expected_msg = msg assert expected_msg == actual_msg