def get_dialogue(self, fipa_msg: Message, agent_addr: Address) -> Dialogue: """ Retrieve dialogue. :param fipa_msg: the fipa message :param agent_addr: the address of the agent :return: the dialogue """ result = None fipa_msg = cast(FIPAMessage, fipa_msg) dialogue_reference = fipa_msg.dialogue_reference self_initiated_dialogue_label = DialogueLabel( dialogue_reference, fipa_msg.counterparty, agent_addr ) other_initiated_dialogue_label = DialogueLabel( dialogue_reference, fipa_msg.counterparty, fipa_msg.counterparty ) if other_initiated_dialogue_label in self.dialogues: other_initiated_dialogue = cast( FIPADialogue, self.dialogues[other_initiated_dialogue_label] ) if other_initiated_dialogue.is_valid_next_message(fipa_msg): result = other_initiated_dialogue if self_initiated_dialogue_label in self.dialogues: self_initiated_dialogue = cast( FIPADialogue, self.dialogues[self_initiated_dialogue_label] ) if self_initiated_dialogue.is_valid_next_message(fipa_msg): result = self_initiated_dialogue if result is None: raise ValueError("Should have found dialogue.") return result
def get_dialogue( self, message: Message, sender: Address, agent_pbk: Address ) -> Dialogue: """ Retrieve dialogue. :param message: the agent message :param agent_pbk: the public key of the agent :return: the dialogue """ dialogue_id = message.get("dialogue_id") opponent = sender target = message.get("target") performative = message.get("performative") self_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent, agent_pbk) other_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent, opponent) if ( performative == FIPAMessage.Performative.PROPOSE and target == PROPOSE_TARGET and self_initiated_dialogue_label in self.dialogues ): dialogue = self.dialogues[self_initiated_dialogue_label] elif performative == FIPAMessage.Performative.ACCEPT: if ( target == ACCEPT_TARGET and other_initiated_dialogue_label in self.dialogues ): dialogue = self.dialogues[other_initiated_dialogue_label] else: raise ValueError("Should have found dialogue.") elif performative == FIPAMessage.Performative.MATCH_ACCEPT: if ( target == MATCH_ACCEPT_TARGET and self_initiated_dialogue_label in self.dialogues ): dialogue = self.dialogues[self_initiated_dialogue_label] else: raise ValueError("Should have found dialogue.") elif performative == FIPAMessage.Performative.DECLINE: if ( target == DECLINED_CFP_TARGET and self_initiated_dialogue_label in self.dialogues ): dialogue = self.dialogues[self_initiated_dialogue_label] elif ( target == DECLINED_PROPOSE_TARGET and other_initiated_dialogue_label in self.dialogues ): dialogue = self.dialogues[other_initiated_dialogue_label] elif ( target == DECLINED_ACCEPT_TARGET and self_initiated_dialogue_label in self.dialogues ): dialogue = self.dialogues[self_initiated_dialogue_label] else: raise ValueError("Should have found dialogue.") else: raise ValueError("Should have found dialogue.") return dialogue
def get_dialogue(self, fipa_msg: Message, sender: Address, agent_pbk: Address) -> Dialogue: """ Retrieve dialogue. :param fipa_msg: the fipa message :param sender_pbk: the sender public key :param agent_pbk: the public key of the agent :return: the dialogue """ fipa_msg = cast(FIPAMessage, fipa_msg) dialogue_reference = cast(Tuple[str, str], fipa_msg.get("dialogue_reference")) self_initiated_dialogue_label = DialogueLabel(dialogue_reference, sender, agent_pbk) other_initiated_dialogue_label = DialogueLabel(dialogue_reference, sender, sender) if other_initiated_dialogue_label in self.dialogues: other_initiated_dialogue = cast( FIPADialogue, self.dialogues[other_initiated_dialogue_label]) if other_initiated_dialogue.is_valid_next_message(fipa_msg): result = other_initiated_dialogue if self_initiated_dialogue_label in self.dialogues: self_initiated_dialogue = cast( FIPADialogue, self.dialogues[self_initiated_dialogue_label]) if self_initiated_dialogue.is_valid_next_message(fipa_msg): result = self_initiated_dialogue if result is None: raise ValueError('Should have found dialogue.') return result
def test_dialogue_label(self): """Test the dialogue_label.""" assert self.dialogue_label.dialogue_starter_reference == str(0) assert self.dialogue_label.dialogue_responder_reference == '' assert self.dialogue_label.dialogue_opponent_pbk == "opponent" assert self.dialogue_label.dialogue_starter_pbk == "starter" dialogue_label2 = DialogueLabel(dialogue_reference=(str(0), ''), dialogue_opponent_pbk="opponent", dialogue_starter_pbk="starter") assert dialogue_label2 == self.dialogue_label dialogue_label3 = "This is a test" assert not dialogue_label3 == self.dialogue_label assert hash(self.dialogue_label) == hash(self.dialogue.dialogue_label) assert self.dialogue_label.json == dict( dialogue_starter_reference=str(0), dialogue_responder_reference='', dialogue_opponent_pbk="opponent", dialogue_starter_pbk="starter") assert DialogueLabel.from_json( self.dialogue_label.json) == self.dialogue_label
def test_dialogue_label(self): """Test the dialogue_label.""" assert self.dialogue_label.dialogue_starter_reference == str(0) assert self.dialogue_label.dialogue_responder_reference == "" assert self.dialogue_label.dialogue_opponent_addr == "opponent" assert self.dialogue_label.dialogue_starter_addr == "starter" assert str(self.dialogue_label) == "{}_{}_{}_{}".format( self.dialogue_label.dialogue_starter_reference, self.dialogue_label.dialogue_responder_reference, self.dialogue_label.dialogue_opponent_addr, self.dialogue_label.dialogue_starter_addr, ) dialogue_label2 = DialogueLabel( dialogue_reference=(str(0), ""), dialogue_opponent_addr="opponent", dialogue_starter_addr="starter", ) assert dialogue_label2 == self.dialogue_label dialogue_label3 = "This is a test" assert not dialogue_label3 == self.dialogue_label assert hash(self.dialogue_label) == hash(self.dialogue.dialogue_label) assert self.dialogue_label.json == dict( dialogue_starter_reference=str(0), dialogue_responder_reference="", dialogue_opponent_addr="opponent", dialogue_starter_addr="starter", ) assert DialogueLabel.from_json( self.dialogue_label.json) == self.dialogue_label
def is_belonging_to_registered_dialogue( self, fipa_msg: Message, agent_addr: Address ) -> bool: """ Check whether an agent message is part of a registered dialogue. :param fipa_msg: the fipa message :param agent_addr: the address of the agent :return: boolean indicating whether the message belongs to a registered dialogue """ fipa_msg = cast(FIPAMessage, fipa_msg) dialogue_reference = fipa_msg.dialogue_reference alt_dialogue_reference = (dialogue_reference[0], "") self_initiated_dialogue_label = DialogueLabel( dialogue_reference, fipa_msg.counterparty, agent_addr ) alt_self_initiated_dialogue_label = DialogueLabel( alt_dialogue_reference, fipa_msg.counterparty, agent_addr ) other_initiated_dialogue_label = DialogueLabel( dialogue_reference, fipa_msg.counterparty, fipa_msg.counterparty ) result = False if other_initiated_dialogue_label in self.dialogues: other_initiated_dialogue = cast( FIPADialogue, self.dialogues[other_initiated_dialogue_label] ) result = other_initiated_dialogue.is_valid_next_message(fipa_msg) if self_initiated_dialogue_label in self.dialogues: self_initiated_dialogue = cast( FIPADialogue, self.dialogues[self_initiated_dialogue_label] ) result = self_initiated_dialogue.is_valid_next_message(fipa_msg) if alt_self_initiated_dialogue_label in self._initiated_dialogues: self_initiated_dialogue = cast( FIPADialogue, self._initiated_dialogues[alt_self_initiated_dialogue_label], ) result = self_initiated_dialogue.is_valid_next_message(fipa_msg) if result: self._initiated_dialogues.pop(alt_self_initiated_dialogue_label) final_dialogue_label = DialogueLabel( dialogue_reference, alt_self_initiated_dialogue_label.dialogue_opponent_addr, alt_self_initiated_dialogue_label.dialogue_starter_addr, ) self_initiated_dialogue.assign_final_dialogue_label( final_dialogue_label ) self._add(self_initiated_dialogue) return result
def is_belonging_to_registered_dialogue(self, message: Message, agent_pbk: Address, sender: Address) -> bool: """ Check whether an agent message is part of a registered dialogue. :param message: the agent message :param agent_pbk: the public key of the agent :return: boolean indicating whether the message belongs to a registered dialogue """ dialogue_id = message.get("dialogue_id") opponent = sender target = message.get("target") performative = message.get("performative") self_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent, agent_pbk) other_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent, opponent) result = False if performative == FIPAMessage.Performative.PROPOSE and target == PROPOSE_TARGET and self_initiated_dialogue_label in self.dialogues: self_initiated_dialogue = self.dialogues[ self_initiated_dialogue_label] result = self_initiated_dialogue.is_expecting_propose() elif performative == FIPAMessage.Performative.ACCEPT: if target == ACCEPT_TARGET and other_initiated_dialogue_label in self.dialogues: other_initiated_dialogue = self.dialogues[ other_initiated_dialogue_label] result = other_initiated_dialogue.is_expecting_initial_accept() elif performative == FIPAMessage.Performative.MATCH_ACCEPT: if target == MATCH_ACCEPT_TARGET and self_initiated_dialogue_label in self.dialogues: self_initiated_dialogue = self.dialogues[ self_initiated_dialogue_label] result = self_initiated_dialogue.is_expecting_matching_accept() elif performative == FIPAMessage.Performative.DECLINE: if target == DECLINED_CFP_TARGET and self_initiated_dialogue_label in self.dialogues: self_initiated_dialogue = self.dialogues[ self_initiated_dialogue_label] result = self_initiated_dialogue.is_expecting_cfp_decline() elif target == DECLINED_PROPOSE_TARGET and other_initiated_dialogue_label in self.dialogues: other_initiated_dialogue = self.dialogues[ other_initiated_dialogue_label] result = other_initiated_dialogue.is_expecting_propose_decline( ) elif target == DECLINED_ACCEPT_TARGET and self_initiated_dialogue_label in self.dialogues: self_initiated_dialogue = self.dialogues[ self_initiated_dialogue_label] result = self_initiated_dialogue.is_expecting_accept_decline() return result
def create_opponent_initiated( self, dialogue_opponent_addr: Address, dialogue_reference: Tuple[str, str], is_seller: bool, ) -> Dialogue: """ Save an opponent initiated dialogue. :param dialogue_opponent_addr: the address of the agent with which the dialogue is kept. :param dialogue_reference: the reference of the dialogue. :param is_seller: keeps track if the counterparty is a seller. :return: the created dialogue """ assert ( dialogue_reference[0] != "" and dialogue_reference[1] == "" ), "Cannot initiate dialogue with preassigned dialogue_responder_reference!" new_dialogue_reference = ( dialogue_reference[0], str(self._next_dialogue_nonce()), ) dialogue_label = DialogueLabel( new_dialogue_reference, dialogue_opponent_addr, dialogue_opponent_addr ) result = self._create(dialogue_label, is_seller) return result
def setup(cls): """Initialise the class.""" cls.dialogue_label = DialogueLabel(dialogue_reference=(str(0), ''), dialogue_opponent_pbk="opponent", dialogue_starter_pbk="starter") cls.dialogue = Dialogue(dialogue_label=cls.dialogue_label) cls.dialogues = Dialogues()
def handle(self, message: Message, sender: str) -> None: """ Implement the reaction to a message. :param message: the message :param sender: the sender :return: None """ tx_msg_response = cast(TransactionMessage, message) if tx_msg_response is not None and \ TransactionMessage.Performative(tx_msg_response.get("performative")) == TransactionMessage.Performative.ACCEPT: logger.info("[{}]: transaction was successful.".format(self.context.agent_name)) json_data = {'transaction_digest': tx_msg_response.get("transaction_digest")} dialogue_label = DialogueLabel.from_json(cast(Dict[str, str], tx_msg_response.get("dialogue_label"))) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] fipa_msg = cast(FIPAMessage, dialogue.last_incoming_message) new_message_id = cast(int, fipa_msg.get("message_id")) + 1 new_target_id = cast(int, fipa_msg.get("target")) + 1 counterparty_pbk = dialogue.dialogue_label.dialogue_opponent_pbk inform_msg = FIPAMessage(message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target_id, performative=FIPAMessage.Performative.INFORM, json_data=json_data) dialogue.outgoing_extend(inform_msg) self.context.outbox.put_message(to=counterparty_pbk, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(inform_msg)) logger.info("[{}]: informing counterparty={} of transaction digest.".format(self.context.agent_name, counterparty_pbk[-5:])) self._received_tx_message = True else: logger.info("[{}]: transaction was not successful.".format(self.context.agent_name))
def is_belonging_to_registered_dialogue(self, fipa_msg: Message, sender: Address, agent_pbk: Address) -> bool: """ Check whether an agent message is part of a registered dialogue. :param fipa_msg: the fipa message :param sender: the sender :param agent_pbk: the public key of the agent :return: boolean indicating whether the message belongs to a registered dialogue """ fipa_msg = cast(FIPAMessage, fipa_msg) dialogue_reference = cast(Tuple[str, str], fipa_msg.get("dialogue_reference")) alt_dialogue_reference = (dialogue_reference[0], '') self_initiated_dialogue_label = DialogueLabel(dialogue_reference, sender, agent_pbk) alt_self_initiated_dialogue_label = DialogueLabel( alt_dialogue_reference, sender, agent_pbk) other_initiated_dialogue_label = DialogueLabel(dialogue_reference, sender, sender) result = False if other_initiated_dialogue_label in self.dialogues: other_initiated_dialogue = cast( FIPADialogue, self.dialogues[other_initiated_dialogue_label]) result = other_initiated_dialogue.is_valid_next_message(fipa_msg) if self_initiated_dialogue_label in self.dialogues: self_initiated_dialogue = cast( FIPADialogue, self.dialogues[self_initiated_dialogue_label]) result = self_initiated_dialogue.is_valid_next_message(fipa_msg) if alt_self_initiated_dialogue_label in self._initiated_dialogues: self_initiated_dialogue = cast( FIPADialogue, self._initiated_dialogues[alt_self_initiated_dialogue_label]) result = self_initiated_dialogue.is_valid_next_message(fipa_msg) if result: self._initiated_dialogues.pop( alt_self_initiated_dialogue_label) final_dialogue_label = DialogueLabel( dialogue_reference, alt_self_initiated_dialogue_label.dialogue_opponent_pbk, alt_self_initiated_dialogue_label.dialogue_starter_pbk) self_initiated_dialogue.assign_final_dialogue_label( final_dialogue_label) self._add(self_initiated_dialogue) return result
def setup(cls): """Initialise the class.""" cls.dialogue_label = DialogueLabel( dialogue_reference=(str(0), ""), dialogue_opponent_addr="opponent", dialogue_starter_addr="starter", ) cls.dialogue = Dialogue(dialogue_label=cls.dialogue_label) cls.dialogues = Dialogues("address")
def test_default_dialogues(dialogues_classes): """Test default dialogues initialization.""" dialogue_class, dialogues_class = dialogues_classes dialogues = dialogues_class("agent_address") dialogue = dialogues.create_dialogue( DialogueLabel(("x", "y"), "opponent_addr", "starter_addr"), next(iter(dialogue_class.Role)), ) assert isinstance(dialogue, dialogue_class)
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): self.context.logger.info( "[{}]: transaction confirmed by decision maker".format( self.context.agent_name)) info = tx_message.info dialogue_label = DialogueLabel.from_json( cast(Dict[str, str], info.get("dialogue_label"))) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] fipa_message = cast(FipaMessage, dialogue.last_incoming_message) if (fipa_message is not None and fipa_message.performative == FipaMessage.Performative.ACCEPT): self.context.logger.info( "[{}]: sending match accept to {}.".format( self.context.agent_name, dialogue.dialogue_label.dialogue_opponent_addr[-5:], )) fipa_msg = FipaMessage( performative=FipaMessage.Performative. MATCH_ACCEPT_W_INFORM, message_id=fipa_message.message_id + 1, dialogue_reference=dialogue.dialogue_label. dialogue_reference, target=fipa_message.message_id, info={ "tx_signature": tx_message.signed_payload.get("tx_signature"), "tx_id": tx_message.tx_id, }, ) dialogue.outgoing_extend(fipa_msg) self.context.outbox.put_message( to=dialogue.dialogue_label.dialogue_opponent_addr, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(fipa_msg), ) else: self.context.logger.warning( "[{}]: last message should be of performative accept.". format(self.context.agent_name)) else: self.context.logger.info( "[{}]: transaction was not successful.".format( self.context.agent_name))
def get_dialogue(self, fipa_msg: Message, sender: Address, agent_pbk: Address) -> Dialogue: """ Retrieve dialogue. :param fipa_msg: the fipa message :param sender_pbk: the sender public key :param agent_pbk: the public key of the agent :return: the dialogue """ dialogue_id = cast(int, fipa_msg.get("dialogue_id")) opponent = sender target = cast(int, fipa_msg.get("target")) performative = fipa_msg.get("performative") self_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent, agent_pbk) other_initiated_dialogue_label = DialogueLabel(dialogue_id, opponent, opponent) if performative == FIPAMessage.Performative.PROPOSE and target == PROPOSE_TARGET and self_initiated_dialogue_label in self.dialogues: dialogue = self.dialogues[self_initiated_dialogue_label] elif performative == FIPAMessage.Performative.ACCEPT: if target == ACCEPT_TARGET and other_initiated_dialogue_label in self.dialogues: dialogue = self.dialogues[other_initiated_dialogue_label] else: raise ValueError('Should have found dialogue.') elif performative == FIPAMessage.Performative.MATCH_ACCEPT: if target == MATCH_ACCEPT_TARGET and self_initiated_dialogue_label in self.dialogues: dialogue = self.dialogues[self_initiated_dialogue_label] else: raise ValueError('Should have found dialogue.') elif performative == FIPAMessage.Performative.DECLINE: if target == DECLINED_CFP_TARGET and self_initiated_dialogue_label in self.dialogues: dialogue = self.dialogues[self_initiated_dialogue_label] elif target == DECLINED_PROPOSE_TARGET and other_initiated_dialogue_label in self.dialogues: dialogue = self.dialogues[other_initiated_dialogue_label] elif target == DECLINED_ACCEPT_TARGET and self_initiated_dialogue_label in self.dialogues: dialogue = self.dialogues[self_initiated_dialogue_label] else: raise ValueError('Should have found dialogue.') else: raise ValueError('Should have found dialogue.') dialogue = cast(Dialogue, dialogue) return dialogue
def create_self_initiated(self, dialogue_opponent_pbk: Address, dialogue_starter_pbk: Address, is_seller: bool) -> Dialogue: """ Create a self initiated dialogue. :param dialogue_opponent_pbk: the pbk of the agent with which the dialogue is kept. :param dialogue_starter_pbk: the pbk of the agent which started the dialogue :param is_seller: boolean indicating the agent role :return: the created dialogue. """ dialogue_label = DialogueLabel(self._next_dialogue_id(), dialogue_opponent_pbk, dialogue_starter_pbk) result = self._create(dialogue_label, is_seller) return result
def handle(self, message: Message) -> None: """ Implement the reaction to a message. :param message: the message :return: None """ tx_msg_response = cast(TransactionMessage, message) if ( tx_msg_response is not None and tx_msg_response.performative == TransactionMessage.Performative.SUCCESSFUL_SETTLEMENT ): self.context.logger.info( "[{}]: transaction was successful.".format(self.context.agent_name) ) json_data = {"transaction_digest": tx_msg_response.tx_digest} info = tx_msg_response.info dialogue_label = DialogueLabel.from_json( cast(Dict[str, str], info.get("dialogue_label")) ) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] fipa_msg = cast(FipaMessage, dialogue.last_incoming_message) new_message_id = fipa_msg.message_id + 1 new_target_id = fipa_msg.message_id counterparty_id = dialogue.dialogue_label.dialogue_opponent_addr inform_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target_id, performative=FipaMessage.Performative.INFORM, info=json_data, ) dialogue.outgoing_extend(inform_msg) self.context.outbox.put_message( to=counterparty_id, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(inform_msg), ) self.context.logger.info( "[{}]: informing counterparty={} of transaction digest.".format( self.context.agent_name, counterparty_id[-5:] ) ) self._received_tx_message = True else: self.context.logger.info( "[{}]: transaction was not successful.".format(self.context.agent_name) )
def handle(self, message: Message) -> None: """ Implement the reaction to a message. :param message: the message :return: None """ tx_msg_response = cast(TransactionMessage, message) if ( tx_msg_response.performative == TransactionMessage.Performative.SUCCESSFUL_SIGNING and ( tx_msg_response.tx_id == ERC1155Contract.Performative.CONTRACT_SIGN_HASH_SINGLE.value ) ): tx_signature = tx_msg_response.signed_payload.get("tx_signature") dialogue_label = DialogueLabel.from_json( cast(Dict[str, str], tx_msg_response.info.get("dialogue_label")) ) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] fipa_msg = cast(FipaMessage, dialogue.last_incoming_message) new_message_id = fipa_msg.message_id + 1 new_target = fipa_msg.message_id counterparty_addr = dialogue.dialogue_label.dialogue_opponent_addr inform_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FipaMessage.Performative.ACCEPT_W_INFORM, info={"tx_signature": tx_signature}, ) self.context.logger.info( "[{}]: sending ACCEPT_W_INFORM to agent={}: tx_signature={}".format( self.context.agent_name, counterparty_addr[-5:], tx_signature ) ) self.context.outbox.put_message( to=counterparty_addr, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(inform_msg), ) else: self.context.logger.info( "[{}]: signing failed: tx_msg_response={}".format( self.context.agent_name, tx_msg_response ) )
def dialogue_label_from_transaction_id(agent_pbk: Address, transaction_id: TransactionId) -> DialogueLabel: """ Recover dialogue label from transaction id. :param agent_pbk: the pbk of the agent. :param transaction_id: the transaction id :return: a dialogue label """ buyer_pbk, seller_pbk, dialogue_id, dialogue_starter_pbk = transaction_id.split('_') if agent_pbk == buyer_pbk: dialogue_opponent_pbk = seller_pbk else: dialogue_opponent_pbk = buyer_pbk dialogue_label = DialogueLabel(int(dialogue_id), dialogue_opponent_pbk, dialogue_starter_pbk) return dialogue_label
def create_opponent_initiated(self, dialogue_opponent_pbk: Address, dialogue_id: int, is_seller: bool) -> Dialogue: """ Save an opponent initiated dialogue. :param dialogue_opponent_pbk: the pbk of the agent with which the dialogue is kept. :param dialogue_id: the id of the dialogue :param is_seller: boolean indicating the agent role :return: the created dialogue """ dialogue_starter_pbk = dialogue_opponent_pbk dialogue_label = DialogueLabel(dialogue_id, dialogue_opponent_pbk, dialogue_starter_pbk) result = self._create(dialogue_label, is_seller) return result
def create_self_initiated(self, dialogue_opponent_pbk: Address, dialogue_starter_pbk: Address, is_seller: bool) -> Dialogue: """ Create a self initiated dialogue. :param dialogue_opponent_pbk: the pbk of the agent with which the dialogue is kept. :param dialogue_starter_pbk: the pbk of the agent which started the dialogue :param is_seller: boolean indicating the agent role :return: the created dialogue. """ dialogue_reference = (str(self._next_dialogue_nonce()), '') dialogue_label = DialogueLabel(dialogue_reference, dialogue_opponent_pbk, dialogue_starter_pbk) dialogue = FIPADialogue(dialogue_label, is_seller) self._initiated_dialogues.update({dialogue_label: dialogue}) return dialogue
def create_opponent_initiated(self, fipa_msg: FIPAMessage, sender: Address) -> Dialogue: """ Save an opponent initiated dialogue. :param fipa_msg: the fipa message :param sender: the pbk of the sender :return: the created dialogue """ dialogue_starter_pbk = sender dialogue_opponent_pbk = sender dialogue_id = fipa_msg.get("dialogue_id") dialogue_id = cast(int, dialogue_id) dialogue_label = DialogueLabel(dialogue_id, dialogue_opponent_pbk, dialogue_starter_pbk) query = cast(Query, fipa_msg.get("query")) assert query.model is not None, "Query has no data model." is_seller = query.model.name == DEMAND_DATAMODEL_NAME result = self._create(dialogue_label, is_seller) return result
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 match accept." .format(self.context.agent_name)) dialogue_label = DialogueLabel.from_json( cast(Dict[str, str], tx_message.get("dialogue_label"))) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] tac_message = dialogue.last_incoming_message if tac_message is not None and tac_message.get( "performative") == FIPAMessage.Performative.ACCEPT: fipa_msg = FIPAMessage( performative=FIPAMessage.Performative. MATCH_ACCEPT_W_ADDRESS, message_id=cast(int, tac_message.get("message_id")) + 1, dialogue_reference=dialogue.dialogue_label. dialogue_reference, target=tac_message.get("message_id"), address=tx_message.get("transaction_digest")) dialogue.outgoing_extend(fipa_msg) self.context.outbox.put_message( to=dialogue.dialogue_label.dialogue_opponent_pbk, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(fipa_msg)) else: logger.info( "[{}]: last message should be of performative accept.". format(self.context.agent_name)) else: logger.info("[{}]: transaction was not successful.".format( self.context.agent_name))
def create_opponent_initiated(self, dialogue_opponent_pbk: Address, dialogue_reference: Tuple[str, str], is_seller: bool) -> Dialogue: """ Save an opponent initiated dialogue. :param dialogue_opponent_pbk: the pbk of the agent with which the dialogue is kept. :param dialogue_id: the id of the dialogue :param sender: the pbk of the sender :return: the created dialogue """ assert dialogue_reference[0] != '' and dialogue_reference[ 1] == '', "Cannot initiate dialogue with preassigned dialogue_responder_reference!" new_dialogue_reference = (dialogue_reference[0], str(self._next_dialogue_nonce())) dialogue_label = DialogueLabel(new_dialogue_reference, dialogue_opponent_pbk, dialogue_opponent_pbk) result = self._create(dialogue_label, is_seller) return result
def test_dialogue(dialogue_classes): """Test dialogue initialization.""" dialogue_class, _ = dialogue_classes dialogue = dialogue_class( DialogueLabel(("x", "y"), "opponent_addr", "starer_addr")) assert dialogue.is_valid(MagicMock())
def test_generated_protocol_end_to_end(self): """Test that a generated protocol could be used in exchanging messages between two agents.""" agent_name_1 = "my_aea_1" agent_name_2 = "my_aea_2" builder_1 = AEABuilder() builder_1.set_name(agent_name_1) builder_1.add_private_key(DEFAULT_LEDGER, self.private_key_path_1) builder_1.set_default_ledger(DEFAULT_LEDGER) builder_1.set_default_connection(PublicId.from_str("fetchai/oef:0.7.0")) builder_1.add_protocol( Path(ROOT_DIR, "packages", "fetchai", "protocols", "fipa") ) builder_1.add_protocol( Path(ROOT_DIR, "packages", "fetchai", "protocols", "oef_search") ) builder_1.add_component( ComponentType.PROTOCOL, Path(PATH_TO_T_PROTOCOL), skip_consistency_check=True, ) builder_1.add_connection( Path(ROOT_DIR, "packages", "fetchai", "connections", "oef") ) builder_2 = AEABuilder() builder_2.set_name(agent_name_2) builder_2.add_private_key(DEFAULT_LEDGER, self.private_key_path_2) builder_2.set_default_ledger(DEFAULT_LEDGER) builder_2.add_protocol( Path(ROOT_DIR, "packages", "fetchai", "protocols", "fipa") ) builder_2.add_protocol( Path(ROOT_DIR, "packages", "fetchai", "protocols", "oef_search") ) builder_2.set_default_connection(PublicId.from_str("fetchai/oef:0.7.0")) builder_2.add_component( ComponentType.PROTOCOL, Path(PATH_TO_T_PROTOCOL), skip_consistency_check=True, ) builder_2.add_connection( Path(ROOT_DIR, "packages", "fetchai", "connections", "oef") ) # create AEAs aea_1 = builder_1.build(connection_ids=[PublicId.from_str("fetchai/oef:0.7.0")]) aea_2 = builder_2.build(connection_ids=[PublicId.from_str("fetchai/oef:0.7.0")]) # dialogues dialogue_label_1 = DialogueLabel( (str(1), ""), aea_2.identity.address, aea_1.identity.address ) aea_1_dialogue = TProtocolDialogue( dialogue_label_1, aea_1.identity.address, TProtocolDialogue.Role.ROLE_1 ) dialogue_label_2 = DialogueLabel( (str(1), str(1)), aea_1.identity.address, aea_1.identity.address ) aea_2_dialogue = TProtocolDialogue( dialogue_label_2, aea_2.identity.address, TProtocolDialogue.Role.ROLE_2 ) # message 1 message_1 = TProtocolMessage( message_id=1, dialogue_reference=(str(1), ""), target=0, performative=TProtocolMessage.Performative.PERFORMATIVE_PT, content_bytes=b"some bytes", content_int=42, content_float=42.7, content_bool=True, content_str="some string", ) message_1.counterparty = aea_2.identity.address message_1.is_incoming = False # message 2 message_2 = TProtocolMessage( message_id=2, dialogue_reference=(str(1), str(1)), target=1, performative=TProtocolMessage.Performative.PERFORMATIVE_PT, content_bytes=b"some other bytes", content_int=43, content_float=43.7, content_bool=False, content_str="some other string", ) message_2.counterparty = aea_1.identity.address message_2.is_incoming = False # add handlers to AEA resources skill_context_1 = SkillContext(aea_1.context) skill_1 = Skill(SkillConfig("fake_skill", "fetchai", "0.1.0"), skill_context_1) skill_context_1._skill = skill_1 agent_1_handler = Agent1Handler( skill_context=skill_context_1, name="fake_handler_1", dialogue=aea_1_dialogue, ) aea_1.resources._handler_registry.register( ( PublicId.from_str("fetchai/fake_skill:0.1.0"), TProtocolMessage.protocol_id, ), agent_1_handler, ) skill_context_2 = SkillContext(aea_2.context) skill_2 = Skill(SkillConfig("fake_skill", "fetchai", "0.1.0"), skill_context_2) skill_context_2._skill = skill_2 agent_2_handler = Agent2Handler( message=message_2, dialogue=aea_2_dialogue, skill_context=skill_context_2, name="fake_handler_2", ) aea_2.resources._handler_registry.register( ( PublicId.from_str("fetchai/fake_skill:0.1.0"), TProtocolMessage.protocol_id, ), agent_2_handler, ) # Start threads t_1 = Thread(target=aea_1.start) t_2 = Thread(target=aea_2.start) try: t_1.start() t_2.start() time.sleep(1.0) aea_1_dialogue.update(message_1) aea_1.outbox.put_message(message_1) time.sleep(5.0) assert ( agent_2_handler.handled_message.message_id == message_1.message_id ), "Message from Agent 1 to 2: message ids do not match" assert ( agent_2_handler.handled_message.dialogue_reference == message_1.dialogue_reference ), "Message from Agent 1 to 2: dialogue references do not match" assert ( agent_2_handler.handled_message.dialogue_reference[0] == message_1.dialogue_reference[0] ), "Message from Agent 1 to 2: dialogue reference[0]s do not match" assert ( agent_2_handler.handled_message.dialogue_reference[1] == message_1.dialogue_reference[1] ), "Message from Agent 1 to 2: dialogue reference[1]s do not match" assert ( agent_2_handler.handled_message.target == message_1.target ), "Message from Agent 1 to 2: targets do not match" assert ( agent_2_handler.handled_message.performative == message_1.performative ), "Message from Agent 1 to 2: performatives do not match" assert ( agent_2_handler.handled_message.content_bytes == message_1.content_bytes ), "Message from Agent 1 to 2: content_bytes do not match" assert ( agent_2_handler.handled_message.content_int == message_1.content_int ), "Message from Agent 1 to 2: content_int do not match" # assert ( # agent_2_handler.handled_message.content_float == message_1.content_float # ), "Message from Agent 1 to 2: content_float do not match" assert ( agent_2_handler.handled_message.content_bool == message_1.content_bool ), "Message from Agent 1 to 2: content_bool do not match" assert ( agent_2_handler.handled_message.content_str == message_1.content_str ), "Message from Agent 1 to 2: content_str do not match" assert ( agent_1_handler.handled_message.message_id == message_2.message_id ), "Message from Agent 1 to 2: dialogue references do not match" assert ( agent_1_handler.handled_message.dialogue_reference == message_2.dialogue_reference ), "Message from Agent 2 to 1: dialogue references do not match" assert ( agent_1_handler.handled_message.dialogue_reference[0] == message_2.dialogue_reference[0] ), "Message from Agent 2 to 1: dialogue reference[0]s do not match" assert ( agent_1_handler.handled_message.dialogue_reference[1] == message_2.dialogue_reference[1] ), "Message from Agent 2 to 1: dialogue reference[1]s do not match" assert ( agent_1_handler.handled_message.target == message_2.target ), "Message from Agent 2 to 1: targets do not match" assert ( agent_1_handler.handled_message.performative == message_2.performative ), "Message from Agent 2 to 1: performatives do not match" assert ( agent_1_handler.handled_message.content_bytes == message_2.content_bytes ), "Message from Agent 2 to 1: content_bytes do not match" assert ( agent_1_handler.handled_message.content_int == message_2.content_int ), "Message from Agent 2 to 1: content_int do not match" # assert ( # agent_1_handler.handled_message.content_float == message_2.content_float # ), "Message from Agent 2 to 1: content_float do not match" assert ( agent_1_handler.handled_message.content_bool == message_2.content_bool ), "Message from Agent 2 to 1: content_bool do not match" assert ( agent_1_handler.handled_message.content_str == message_2.content_str ), "Message from Agent 2 to 1: content_str do not match" time.sleep(2.0) finally: aea_1.stop() aea_2.stop() t_1.join() t_2.join()
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 """ strategy = cast(Strategy, self.context.strategy) if not strategy.is_contract_tx: self.context.logger.warning( "signed transaction handler only for contract case.") return None self.context.logger.info("transaction signed by decision maker.") dialogue_label = DialogueLabel.from_str( cast(str, signing_msg.skill_callback_info.get("dialogue_label"))) fipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues) fipa_dialogue = fipa_dialogues.dialogues[dialogue_label] last_fipa_message = cast(FipaMessage, fipa_dialogue.last_incoming_message) if (last_fipa_message is not None and last_fipa_message.performative == FipaMessage.Performative.ACCEPT): self.context.logger.info("sending match accept to {}.".format( fipa_dialogue.dialogue_label.dialogue_opponent_addr[-5:], )) fipa_msg = FipaMessage( performative=FipaMessage.Performative.MATCH_ACCEPT_W_INFORM, message_id=last_fipa_message.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label. dialogue_reference, target=last_fipa_message.message_id, info={ "tx_signature": signing_msg.signed_transaction, "tx_id": signing_msg.dialogue_reference[0], }, ) fipa_msg.counterparty = fipa_dialogue.dialogue_label.dialogue_opponent_addr fipa_dialogue.update(fipa_msg) self.context.outbox.put_message(message=fipa_msg) elif (last_fipa_message is not None and last_fipa_message.performative == FipaMessage.Performative.MATCH_ACCEPT_W_INFORM): self.context.logger.info("sending atomic swap tx to ledger.") tx_signed = signing_msg.signed_transaction tx_digest = self.context.ledger_apis.get_api( strategy.ledger_id).send_signed_transaction( tx_signed=tx_signed) # TODO; handle case when no tx_digest returned and remove loop assert tx_digest is not None, "Error when submitting tx." self.context.logger.info("tx_digest={}.".format(tx_digest)) count = 0 while (not self.context.ledger_apis.get_api( strategy.ledger_id).is_transaction_settled(tx_digest) and count < 20): self.context.logger.info( "waiting for tx to confirm. Sleeping for 3 seconds ...") time.sleep(3.0) count += 1 tx_receipt = self.context.ledger_apis.get_api( strategy.ledger_id).get_transaction_receipt( tx_digest=tx_digest) if tx_receipt is None: self.context.logger.info( "failed to get tx receipt for atomic swap.") elif tx_receipt.status != 1: self.context.logger.info("failed to conduct atomic swap.") else: self.context.logger.info( "successfully conducted atomic swap. Transaction digest: {}" .format(tx_digest)) # contract = cast(ERC1155Contract, self.context.contracts.erc1155) # result = contract.get_balances( # address=self.context.agent_address, # token_ids=[ # int(key) # for key in tx_message.terms.quantities_by_good_id.keys() # ] # + [ # int(key) # for key in tx_message.terms.amount_by_currency_id.keys() # ], # ) result = 0 self.context.logger.info("current balances: {}".format(result)) else: self.context.logger.warning( "last message should be of performative accept or match accept." )
def handle(self, message: Message) -> None: """ Dispatch message to relevant handler and respond. :param message: the message :return: None """ tx_message = cast(SigningMessage, message) if tx_message.performative == SigningMessage.Performative.SIGNED_MESSAGE: self.context.logger.info( "[{}]: transaction confirmed by decision maker".format( self.context.agent_name)) strategy = cast(Strategy, self.context.strategy) dialogue_label = DialogueLabel.from_json( cast(Dict[str, str], tx_message.skill_callback_info.get("dialogue_label"))) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] last_fipa_message = cast(FipaMessage, dialogue.last_incoming_message) if (last_fipa_message is not None and last_fipa_message.performative == FipaMessage.Performative.ACCEPT): self.context.logger.info( "[{}]: sending match accept to {}.".format( self.context.agent_name, dialogue.dialogue_label.dialogue_opponent_addr[-5:], )) fipa_msg = FipaMessage( performative=FipaMessage.Performative. MATCH_ACCEPT_W_INFORM, message_id=last_fipa_message.message_id + 1, dialogue_reference=dialogue.dialogue_label. dialogue_reference, target=last_fipa_message.message_id, info={ "tx_signature": tx_message.signed_transaction, "tx_id": tx_message.dialogue_reference[0], }, ) fipa_msg.counterparty = dialogue.dialogue_label.dialogue_opponent_addr dialogue.update(fipa_msg) self.context.outbox.put_message(message=fipa_msg) elif (last_fipa_message is not None and last_fipa_message.performative == FipaMessage.Performative.MATCH_ACCEPT_W_INFORM and strategy.is_contract_tx): self.context.logger.info( "[{}]: sending atomic swap tx to ledger.".format( self.context.agent_name)) tx_signed = tx_message.signed_transaction tx_digest = self.context.ledger_apis.get_api( strategy.ledger_id).send_signed_transaction( tx_signed=tx_signed) # TODO; handle case when no tx_digest returned and remove loop assert tx_digest is not None, "Error when submitting tx." self.context.logger.info("[{}]: tx_digest={}.".format( self.context.agent_name, tx_digest)) count = 0 while (not self.context.ledger_apis.get_api( strategy.ledger_id).is_transaction_settled(tx_digest) and count < 20): self.context.logger.info( "[{}]: waiting for tx to confirm. Sleeping for 3 seconds ..." .format(self.context.agent_name)) time.sleep(3.0) count += 1 tx_receipt = self.context.ledger_apis.get_api( strategy.ledger_id).get_transaction_receipt( tx_digest=tx_digest) if tx_receipt is None: self.context.logger.info( "[{}]: Failed to get tx receipt for atomic swap.". format(self.context.agent_name)) elif tx_receipt.status != 1: self.context.logger.info( "[{}]: Failed to conduct atomic swap.".format( self.context.agent_name)) else: self.context.logger.info( "[{}]: Successfully conducted atomic swap. Transaction digest: {}" .format(self.context.agent_name, tx_digest)) # contract = cast(ERC1155Contract, self.context.contracts.erc1155) # result = contract.get_balances( # address=self.context.agent_address, # token_ids=[ # int(key) # for key in tx_message.terms.quantities_by_good_id.keys() # ] # + [ # int(key) # for key in tx_message.terms.amount_by_currency_id.keys() # ], # ) result = 0 self.context.logger.info( "[{}]: Current balances: {}".format( self.context.agent_name, result)) else: self.context.logger.warning( "[{}]: last message should be of performative accept or match accept." .format(self.context.agent_name)) else: self.context.logger.info( "[{}]: transaction was not successful.".format( self.context.agent_name))
def _handle_signed_message(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 """ strategy = cast(Strategy, self.context.strategy) if strategy.is_contract_tx: self.context.logger.warning( "signed message handler only for non-contract case.") return None self.context.logger.info("message signed by decision maker.") dialogue_label = DialogueLabel.from_str( cast(str, signing_msg.skill_callback_info.get("dialogue_label"))) fipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues) fipa_dialogue = fipa_dialogues.dialogues[dialogue_label] last_fipa_message = cast(FipaMessage, fipa_dialogue.last_incoming_message) if (last_fipa_message is not None and last_fipa_message.performative == FipaMessage.Performative.ACCEPT): fipa_msg = FipaMessage( performative=FipaMessage.Performative.MATCH_ACCEPT_W_INFORM, message_id=last_fipa_message.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label. dialogue_reference, target=last_fipa_message.message_id, info={"signature": signing_msg.signed_message.body}, ) fipa_msg.counterparty = last_fipa_message.counterparty fipa_dialogue.update(fipa_msg) self.context.outbox.put_message(message=fipa_msg) self.context.logger.info("sending match accept to {}.".format( fipa_dialogue.dialogue_label.dialogue_opponent_addr[-5:], )) elif (last_fipa_message is not None and last_fipa_message.performative == FipaMessage.Performative.MATCH_ACCEPT_W_INFORM): counterparty_signature = cast( str, signing_msg.skill_callback_info.get("counterparty_signature")) if counterparty_signature is not None: last_signing_msg = cast(Optional[SigningMessage], signing_dialogue.last_outgoing_message) assert (last_signing_msg is not None), "Could not recover last signing message." tx_id = last_signing_msg.terms.sender_hash if "transactions" not in self.context.shared_state.keys(): self.context.shared_state["transactions"] = {} self.context.shared_state["transactions"][tx_id] = { "terms": last_signing_msg.terms, "sender_signature": signing_msg.signed_message.body, "counterparty_signature": counterparty_signature, } self.context.logger.info("sending transaction to controller.") else: self.context.logger.warning( "transaction has no counterparty signature!") else: self.context.logger.warning( "last message should be of performative accept or match accept." )