def _handle_valid_transaction( self, tac_msg: TacMessage, tac_dialogue: TacDialogue, 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(transaction.id[-10:]) ) game.settle_transaction(transaction) # send the transaction confirmation. sender_tac_msg = TacMessage( performative=TacMessage.Performative.TRANSACTION_CONFIRMATION, dialogue_reference=tac_dialogue.dialogue_label.dialogue_reference, transaction_id=transaction.sender_hash, amount_by_currency_id=transaction.amount_by_currency_id, quantities_by_good_id=transaction.quantities_by_good_id, message_id=tac_msg.message_id + 1, target=tac_msg.message_id, ) sender_tac_msg.counterparty = transaction.sender_address assert tac_dialogue.update(sender_tac_msg) self.context.outbox.put_message(message=sender_tac_msg) tac_dialogues = cast(TacDialogues, self.context.tac_dialogues) recovered_tac_dialogue = tac_dialogues.dialogue_by_address.get( transaction.counterparty_address, None ) assert recovered_tac_dialogue is not None, "Error when retrieving dialogue." last_msg = recovered_tac_dialogue.last_message assert last_msg is not None, "Error when retrieving last message." counterparty_tac_msg = TacMessage( performative=TacMessage.Performative.TRANSACTION_CONFIRMATION, dialogue_reference=recovered_tac_dialogue.dialogue_label.dialogue_reference, transaction_id=transaction.counterparty_hash, amount_by_currency_id=transaction.amount_by_currency_id, quantities_by_good_id=transaction.quantities_by_good_id, message_id=last_msg.message_id + 1, target=last_msg.message_id, ) counterparty_tac_msg.counterparty = transaction.counterparty_address assert recovered_tac_dialogue.update(counterparty_tac_msg) self.context.outbox.put_message(message=counterparty_tac_msg) # log messages self.context.logger.info( "transaction '{}' settled successfully.".format(transaction.id[-10:]) ) self.context.logger.info("current state:\n{}".format(game.holdings_summary))
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.warning( "[{}]: 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, ) tac_msg.counterparty = message.counterparty self.context.outbox.put_message(message=tac_msg) return game = cast(Game, self.context.game) if message.counterparty in game.registration.agent_addr_to_name: self.context.logger.warning( "[{}]: 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, ) tac_msg.counterparty = message.counterparty self.context.outbox.put_message(message=tac_msg) if agent_name in game.registration.agent_addr_to_name.values(): self.context.logger.warning( "[{}]: 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, ) tac_msg.counterparty = message.counterparty self.context.outbox.put_message(message=tac_msg) game.registration.register_agent(message.counterparty, agent_name) self.context.logger.info("[{}]: Agent registered: '{}'".format( self.context.agent_name, agent_name))
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.Performative.CANCELLED) tac_msg.counterparty = agent_addr self.context.outbox.put_message(message=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 _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.warning( "agent not registered: '{}'".format(message.counterparty) ) tac_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, error_code=TacMessage.ErrorCode.AGENT_NOT_REGISTERED, ) tac_msg.counterparty = message.counterparty self.context.outbox.put_message(message=tac_msg) else: self.context.logger.debug( "agent unregistered: '{}'".format( game.conf.agent_addr_to_name[message.counterparty], ) ) game.registration.unregister_agent(message.counterparty)
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( game.holdings_summary)) self.context.logger.info( "computed theoretical equilibrium:\n{}".format( 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(agent_address)) self.context.logger.debug("game data={}".format(str(tac_msg))) tac_msg.counterparty = agent_address self.context.outbox.put_message(message=tac_msg)
def _cancel_tac(self, game: Game): """Notify agents that the TAC is cancelled.""" self.context.logger.info("notifying agents that TAC is cancelled.") tac_dialogues = cast(TacDialogues, self.context.tac_dialogues) for agent_address in game.registration.agent_addr_to_name.keys(): tac_dialogue = tac_dialogues.dialogue_by_address.get( agent_address, None) assert tac_dialogue is not None, "Error when retrieving dialogue." last_msg = tac_dialogue.last_message assert last_msg is not None, "Error when retrieving last message." tac_msg = TacMessage( performative=TacMessage.Performative.CANCELLED, dialogue_reference=tac_dialogue.dialogue_label. dialogue_reference, message_id=last_msg.message_id + 1, target=last_msg.message_id, ) tac_msg.counterparty = agent_address tac_dialogues.update(tac_msg) self.context.outbox.put_message(message=tac_msg) if game.phase == Phase.GAME: self.context.logger.info("finished competition:\n{}".format( game.holdings_summary)) self.context.logger.info("computed equilibrium:\n{}".format( game.equilibrium_summary)) self.context.is_active = False
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.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, currency_id_to_name=game.conf.currency_id_to_name, agent_addr_to_name=game.conf.agent_addr_to_name, good_id_to_name=game.conf.good_id_to_name, version_id=game.conf.version_id, ) self.context.logger.debug( "[{}]: sending game data to '{}': {}".format( self.context.agent_name, agent_address, str(tac_msg) ) ) tac_msg.counterparty = agent_address self.context.outbox.put_message(message=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(reason)) for agent_addr in game.registration.agent_addr_to_name.keys(): tac_msg = TacMessage( performative=TacMessage.Performative.CANCELLED) tac_msg.counterparty = agent_addr self.context.outbox.put_message(message=tac_msg)
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, ) sender_tac_msg.counterparty = transaction.sender_addr self.context.outbox.put_message(message=sender_tac_msg) counterparty_tac_msg.counterparty = transaction.counterparty_addr self.context.outbox.put_message(message=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 _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}, ) tac_msg.counterparty = message.counterparty self.context.outbox.put_message(message=tac_msg)
def _handle_invalid_transaction( self, tac_msg: TacMessage, tac_dialogue: TacDialogue ) -> None: """Handle an invalid transaction.""" self.context.logger.info( "handling invalid transaction: {}".format(tac_msg.transaction_id) ) error_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, dialogue_reference=tac_dialogue.dialogue_label.dialogue_reference, error_code=TacMessage.ErrorCode.TRANSACTION_NOT_VALID, info={"transaction_id": tac_msg.transaction_id}, message_id=tac_msg.message_id + 1, target=tac_msg.message_id, ) error_msg.counterparty = tac_msg.counterparty assert tac_dialogue.update(error_msg) self.context.outbox.put_message(message=error_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_msg.counterparty = controller_addr self.context.outbox.put_message(message=tac_msg) self.context.behaviours.tac.is_active = False
def _process_transactions(self) -> None: """ Process transactions. :return: None """ game = cast(Game, self.context.game) tac_dialogue = game.tac_dialogue transactions = cast(Dict[str, Dict[str, Any]], self.context.shared_state.get("transactions", {})) tx_ids = list(transactions.keys()) for tx_id in tx_ids: self.context.logger.info( "sending transaction {} to controller.".format(tx_id)) last_msg = tac_dialogue.last_message assert last_msg is not None, "No last message available." tx_content = transactions.pop(tx_id, None) assert tx_content is not None, "Tx for id={} not found.".format( tx_id) terms = tx_content["terms"] sender_signature = tx_content["sender_signature"] counterparty_signature = tx_content["counterparty_signature"] msg = TacMessage( performative=TacMessage.Performative.TRANSACTION, dialogue_reference=tac_dialogue.dialogue_label. dialogue_reference, message_id=last_msg.message_id + 1, target=last_msg.message_id, transaction_id=tx_id, ledger_id=terms.ledger_id, sender_address=terms.sender_address, counterparty_address=terms.counterparty_address, amount_by_currency_id=terms.amount_by_currency_id, fee_by_currency_id=terms.fee_by_currency_id, quantities_by_good_id=terms.quantities_by_good_id, sender_signature=sender_signature, counterparty_signature=counterparty_signature, nonce=terms.nonce, ) msg.counterparty = game.conf.controller_addr tac_dialogue.update(msg) self.context.outbox.put_message(message=msg)
def _handle_signed_transaction(self, signing_msg: SigningMessage, signing_dialogue: SigningDialogue) -> None: """ Handle an oef search message. :param signing_msg: the signing message :param signing_dialogue: the dialogue :return: None """ # 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( str, signing_msg.skill_callback_info.get("tx_counterparty_signature")) tx_counterparty_id = cast( str, signing_msg.skill_callback_info.get("tx_counterparty_id")) tx_id = cast(str, signing_msg.skill_callback_info.get("tx_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=signing_msg.terms.sender_address, tx_counterparty_addr=signing_msg.terms.counterparty_address, amount_by_currency_id=signing_msg.terms.amount_by_currency_id, is_sender_payable_tx_fee=signing_msg.terms. is_sender_payable_tx_fee, quantities_by_good_id=signing_msg.terms.quantities_by_good_id, tx_sender_signature=signing_msg.signed_transaction.body, tx_counterparty_signature=tx_counterparty_signature, tx_nonce=signing_msg.terms.nonce, ) msg.counterparty = game.conf.controller_addr self.context.outbox.put_message(message=msg) else: self.context.logger.warning( "[{}]: transaction has no counterparty id or signature!". format(self.context.agent_name))
def _on_unregister(self, tac_msg: TacMessage, tac_dialogue: TacDialogue) -> None: """ Handle a unregister message. If the address is not registered, answer with an error message. :param tac_msg: the tac message :param tac_dialogue: the tac dialogue :return: None """ game = cast(Game, self.context.game) if not game.phase == Phase.GAME_REGISTRATION: self.context.logger.warning( "received unregister outside of game registration phase: '{}'".format( tac_msg ) ) return if tac_msg.counterparty not in game.registration.agent_addr_to_name: self.context.logger.warning( "agent not registered: '{}'".format(tac_msg.counterparty) ) error_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, dialogue_reference=tac_dialogue.dialogue_label.dialogue_reference, error_code=TacMessage.ErrorCode.AGENT_NOT_REGISTERED, message_id=tac_msg.message_id + 1, target=tac_msg.message_id, ) error_msg.counterparty = tac_msg.counterparty assert tac_dialogue.update(error_msg) self.context.outbox.put_message(message=error_msg) else: self.context.logger.debug( "agent unregistered: '{}'".format( game.conf.agent_addr_to_name[tac_msg.counterparty], ) ) game.registration.unregister_agent(tac_msg.counterparty)
def _start_tac(self, game: Game): """Create a game and send the game configuration to every registered agent.""" game.create() self.context.logger.info("started competition:\n{}".format( game.holdings_summary)) self.context.logger.info("computed equilibrium:\n{}".format( game.equilibrium_summary)) tac_dialogues = cast(TacDialogues, self.context.tac_dialogues) for agent_address in game.conf.agent_addr_to_name.keys(): tac_dialogue = tac_dialogues.dialogue_by_address.get( agent_address, None) assert tac_dialogue is not None, "Error when retrieving dialogue." last_msg = tac_dialogue.last_message assert last_msg is not None, "Error when retrieving last message." agent_state = game.current_agent_states[agent_address] tac_msg = TacMessage( performative=TacMessage.Performative.GAME_DATA, dialogue_reference=tac_dialogue.dialogue_label. dialogue_reference, message_id=last_msg.message_id + 1, target=last_msg.message_id, 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, fee_by_currency_id=game.conf.fee_by_currency_id, currency_id_to_name=game.conf.currency_id_to_name, agent_addr_to_name=game.conf.agent_addr_to_name, good_id_to_name=game.conf.good_id_to_name, version_id=game.conf.version_id, ) tac_msg.counterparty = agent_address tac_dialogues.update(tac_msg) self.context.outbox.put_message(message=tac_msg) self.context.logger.debug("sending game data to '{}': {}".format( agent_address, str(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_dialogues = cast(TacDialogues, self.context.tac_dialogues) tac_msg = TacMessage( performative=TacMessage.Performative.REGISTER, dialogue_reference=tac_dialogues.new_self_initiated_dialogue_reference(), agent_name=self.context.agent_name, ) tac_msg.counterparty = controller_addr tac_dialogue = cast(Optional[TacDialogue], tac_dialogues.update(tac_msg)) assert tac_dialogue is not None, "TacDialogue not created." game.tac_dialogue = tac_dialogue self.context.outbox.put_message(message=tac_msg) self.context.behaviours.tac_search.is_active = False
def _on_register(self, tac_msg: TacMessage, tac_dialogue: TacDialogue) -> None: """ Handle a register message. If the address is not registered, answer with an error message. :param tac_msg: the tac message :param tac_dialogue: the tac dialogue :return: None """ game = cast(Game, self.context.game) if not game.phase == Phase.GAME_REGISTRATION: self.context.logger.warning( "received registration outside of game registration phase: '{}'".format( tac_msg ) ) return parameters = cast(Parameters, self.context.parameters) agent_name = tac_msg.agent_name if len(parameters.whitelist) != 0 and agent_name not in parameters.whitelist: self.context.logger.warning( "agent name not in whitelist: '{}'".format(agent_name) ) error_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, dialogue_reference=tac_dialogue.dialogue_label.dialogue_reference, error_code=TacMessage.ErrorCode.AGENT_NAME_NOT_IN_WHITELIST, message_id=tac_msg.message_id + 1, target=tac_msg.message_id, ) error_msg.counterparty = tac_msg.counterparty assert tac_dialogue.update(error_msg) self.context.outbox.put_message(message=error_msg) return game = cast(Game, self.context.game) if tac_msg.counterparty in game.registration.agent_addr_to_name: self.context.logger.warning( "agent already registered: '{}'".format( game.registration.agent_addr_to_name[tac_msg.counterparty], ) ) error_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, dialogue_reference=tac_dialogue.dialogue_label.dialogue_reference, error_code=TacMessage.ErrorCode.AGENT_ADDR_ALREADY_REGISTERED, message_id=tac_msg.message_id + 1, target=tac_msg.message_id, ) error_msg.counterparty = tac_msg.counterparty assert tac_dialogue.update(error_msg) self.context.outbox.put_message(message=error_msg) return if agent_name in game.registration.agent_addr_to_name.values(): self.context.logger.warning( "agent with this name already registered: '{}'".format(agent_name) ) error_msg = TacMessage( performative=TacMessage.Performative.TAC_ERROR, dialogue_reference=tac_dialogue.dialogue_label.dialogue_reference, error_code=TacMessage.ErrorCode.AGENT_NAME_ALREADY_REGISTERED, message_id=tac_msg.message_id + 1, target=tac_msg.message_id, ) error_msg.counterparty = tac_msg.counterparty assert tac_dialogue.update(error_msg) self.context.outbox.put_message(message=error_msg) return game.registration.register_agent(tac_msg.counterparty, agent_name) self.context.logger.info("agent registered: '{}'".format(agent_name))