예제 #1
0
def test_fipa_encoding_unknown_performative():
    """Test that we raise an exception when the performative is unknown during encoding."""
    msg = FIPAMessage(message_id=0,
                      dialogue_reference=(str(0), ''),
                      target=1,
                      performative=FIPAMessage.Performative.ACCEPT)

    with pytest.raises(ValueError, match="Performative not valid:"):
        with mock.patch.object(FIPAMessage.Performative, "__eq__", return_value=False):
            FIPASerializer().encode(msg)
예제 #2
0
def test_performative_match_accept():
    """Test the serialization - deserialization of the match_accept performative."""
    msg = FIPAMessage(message_id=0,
                      dialogue_reference=(str(0), ''),
                      target=1,
                      performative=FIPAMessage.Performative.MATCH_ACCEPT)

    msg_bytes = FIPASerializer().encode(msg)
    envelope = Envelope(to="receiver",
                        sender="sender",
                        protocol_id=FIPAMessage.protocol_id,
                        message=msg_bytes)
    envelope_bytes = envelope.encode()

    actual_envelope = Envelope.decode(envelope_bytes)
    expected_envelope = envelope
    assert expected_envelope == actual_envelope
    deserialised_msg = FIPASerializer().decode(envelope.message)
    assert msg.get("performative") == deserialised_msg.get("performative")
예제 #3
0
    def _on_propose(self, propose: FIPAMessage, dialogue: Dialogue) -> None:
        """
        Handle a Propose.

        :param propose: the message containing the Propose
        :param dialogue: the dialogue
        :return: None
        """
        new_msg_id = cast(int, propose.get("message_id")) + 1
        strategy = cast(Strategy, self.context.strategy)
        proposals = cast(List[Description], propose.get("proposal"))
        logger.debug("[{}]: on Propose as {}.".format(self.context.agent_name,
                                                      dialogue.role))

        for num, proposal_description in enumerate(proposals):
            if num > 0:
                continue  # TODO: allow for dialogue branching with multiple proposals
            transaction_msg = generate_transaction_message(
                proposal_description, dialogue.dialogue_label,
                dialogue.is_seller, self.context.agent_public_key)

            if strategy.is_profitable_transaction(
                    transaction_msg, is_seller=dialogue.is_seller):
                logger.info("[{}]: Accepting propose (as {}).".format(
                    self.context.agent_name, dialogue.role))
                transactions = cast(Transactions, self.context.transactions)
                transactions.add_locked_tx(transaction_msg,
                                           as_seller=dialogue.is_seller)
                transactions.add_pending_initial_acceptance(
                    dialogue.dialogue_label, new_msg_id, transaction_msg)
                fipa_msg = FIPAMessage(
                    performative=FIPAMessage.Performative.ACCEPT,
                    message_id=new_msg_id,
                    dialogue_reference=dialogue.dialogue_label.
                    dialogue_reference,
                    target=propose.get("message_id"))
            else:
                logger.info("[{}]: Declining propose (as {})".format(
                    self.context.agent_name, dialogue.role))
                fipa_msg = FIPAMessage(
                    performative=FIPAMessage.Performative.DECLINE,
                    message_id=new_msg_id,
                    dialogue_reference=dialogue.dialogue_label.
                    dialogue_reference,
                    target=propose.get("message_id"))
                dialogues = cast(Dialogues, self.context.dialogues)
                dialogues.dialogue_stats.add_dialogue_endstate(
                    Dialogue.EndState.DECLINED_PROPOSE,
                    dialogue.is_self_initiated)
            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))
예제 #4
0
    def _on_services_search_result(self, agent_pbks: List[str],
                                   is_searching_for_sellers: bool) -> None:
        """
        Process the search result for services.

        :param agent_pbks: the agent public keys matching the search query
        :param is_searching_for_sellers: whether it is searching for sellers or not

        :return: None
        """
        agent_pbks_set = set(agent_pbks)
        if self.crypto.public_key in agent_pbks_set:
            agent_pbks_set.remove(self.crypto.public_key)
        agent_pbks = list(agent_pbks_set)
        searched_for = "sellers" if is_searching_for_sellers else "buyers"
        logger.debug("[{}]: Found potential {}: {}".format(
            self.agent_name, searched_for, agent_pbks))

        services = self.game_instance.build_services_dict(
            is_supply=not is_searching_for_sellers)
        if services is None:
            response = "demanding" if is_searching_for_sellers else "supplying"
            logger.debug("[{}]: No longer {} any goods...".format(
                self.agent_name, response))
            return
        for agent_pbk in agent_pbks:
            dialogue = self.game_instance.dialogues.create_self_initiated(
                agent_pbk, self.crypto.public_key,
                not is_searching_for_sellers)
            cfp = FIPAMessage(
                message_id=STARTING_MESSAGE_ID,
                dialogue_id=dialogue.dialogue_label.dialogue_id,
                target=STARTING_MESSAGE_TARGET,
                performative=FIPAMessage.Performative.CFP,
                query=json.dumps(services).encode("utf-8"),
            )
            dialogue.outgoing_extend([cfp])
            cfp_bytes = FIPASerializer().encode(cfp)
            logger.debug(
                "[{}]: send_cfp_as_{}: msg_id={}, dialogue_id={}, destination={}, target={}, services={}"
                .format(
                    self.agent_name,
                    dialogue.role,
                    cfp.get("id"),
                    cfp.get("dialogue_id"),
                    agent_pbk,
                    cfp.get("target"),
                    services,
                ))
            self.mailbox.outbox.put_message(
                to=agent_pbk,
                sender=self.crypto.public_key,
                protocol_id=FIPAMessage.protocol_id,
                message=cfp_bytes,
            )
예제 #5
0
def test_performative_inform():
    """Test the serialization-deserialization of the inform performative."""
    msg = FIPAMessage(message_id=0,
                      dialogue_reference=(str(0), ''),
                      target=1,
                      performative=FIPAMessage.Performative.INFORM,
                      json_data={"foo": "bar"})

    msg_bytes = FIPASerializer().encode(msg)
    envelope = Envelope(to="receiver",
                        sender="sender",
                        protocol_id=FIPAMessage.protocol_id,
                        message=msg_bytes)
    envelope_bytes = envelope.encode()

    actual_envelope = Envelope.decode(envelope_bytes)
    expected_envelope = envelope
    assert expected_envelope == actual_envelope
    deserialised_msg = FIPASerializer().decode(envelope.message)
    assert msg.get("performative") == deserialised_msg.get("performative")
예제 #6
0
def test_fipa_accept_serialization():
    """Test that the serialization for the 'fipa' protocol works."""
    msg = FIPAMessage(message_id=0,
                      dialogue_reference=(str(0), ''),
                      target=0,
                      performative=FIPAMessage.Performative.ACCEPT)
    msg_bytes = FIPASerializer().encode(msg)
    envelope = Envelope(to="receiver",
                        sender="sender",
                        protocol_id=FIPAMessage.protocol_id,
                        message=msg_bytes)
    envelope_bytes = envelope.encode()

    actual_envelope = Envelope.decode(envelope_bytes)
    expected_envelope = envelope
    assert expected_envelope == actual_envelope

    actual_msg = FIPASerializer().decode(actual_envelope.message)
    expected_msg = msg
    assert expected_msg == actual_msg
예제 #7
0
 def test_error_handler_handle(self):
     """Test the handle function."""
     msg = FIPAMessage(message_id=0,
                       dialogue_reference=(str(0), ''),
                       target=0,
                       performative=FIPAMessage.Performative.ACCEPT)
     msg_bytes = FIPASerializer().encode(msg)
     envelope = Envelope(to=self.public_key,
                         sender=self.public_key,
                         protocol_id=FIPAMessage.protocol_id,
                         message=msg_bytes)
     self.my_error_handler.handle(message=msg, sender=envelope.sender)
예제 #8
0
    async def test_messages(self):
        """Test that at the beginning, the search request returns an empty search result."""
        msg = FIPAMessage((str(0), ''), 0, 0, FIPAMessage.Performative.CFP, query=None)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to=DEFAULT_OEF, sender=self.public_key_1, protocol_id=FIPAMessage.protocol_id, message=msg_bytes)
        with pytest.raises(AEAConnectionError):
            await OEFLocalConnection(self.public_key_1, self.node).send(envelope)

        self.multiplexer1.connect()
        msg = FIPAMessage((str(0), str(1)), 0, 0, FIPAMessage.Performative.CFP, query=None)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to="this_public_key_does_not_exist",
                            sender=self.public_key_1, protocol_id=FIPAMessage.protocol_id, message=msg_bytes)
        self.multiplexer1.put(envelope)

        # check the result
        response_envelope = self.multiplexer1.get(block=True, timeout=5.0)
        assert response_envelope.protocol_id == OEFMessage.protocol_id
        assert response_envelope.sender == DEFAULT_OEF
        result = OEFSerializer().decode(response_envelope.message)
        assert result.get("type") == OEFMessage.Type.DIALOGUE_ERROR
예제 #9
0
    def handle_envelope(self, envelope: Envelope) -> None:
        """
        Implement the reaction to an envelope.

        :param envelope: the envelope
        :return: None
        """
        msg = FIPASerializer().decode(envelope.message)
        msg_performative = FIPAMessage.Performative(msg.get('performative'))
        proposals = cast(List[Description], msg.get("proposal"))
        message_id = cast(int, msg.get("id"))
        dialogue_id = cast(int, msg.get("dialogue_id"))
        if msg_performative == FIPAMessage.Performative.PROPOSE:
            if proposals is not []:
                for item in proposals:
                    logger.info(
                        "[{}]: received proposal={} in dialogue={}".format(
                            self.context.agent_name, item.values, dialogue_id))
                    if "Price" in item.values.keys():
                        if item.values["Price"] < self.max_price:
                            self.handle_accept(envelope.sender, message_id,
                                               dialogue_id)
                        else:
                            self.handle_decline(envelope.sender, message_id,
                                                dialogue_id)
예제 #10
0
    def handle_dialogue_message(self, envelope: Envelope) -> None:
        """
        Handle messages from the other agents.

        The agents expect a response.

        :param envelope: the envelope.

        :return: None
        """
        message = FIPASerializer().decode(envelope.message)  # type: Message
        logger.debug("Handling Dialogue message. type={}".format(
            type(message.get("performative"))))
        if self.dialogues.is_belonging_to_registered_dialogue(
                message, self.crypto.public_key, envelope.sender):
            self.on_existing_dialogue(message, envelope.sender)
        elif self.dialogues.is_permitted_for_new_dialogue(
                message, self.game_instance.game_configuration.agent_pbks,
                envelope.sender):
            self.on_new_dialogue(message, envelope.sender)
        else:
            self.on_unidentified_dialogue(message, envelope.sender)
예제 #11
0
def test_fipa_cfp_serialization():
    """Test that the serialization for the 'fipa' protocol works."""
    query = Query(Constraint('something', ConstraintType('>', 1)))
    msg = FIPAMessage(message_id=0,
                      dialogue_id=0,
                      target=0,
                      performative=FIPAMessage.Performative.CFP,
                      query=query)
    msg_bytes = FIPASerializer().encode(msg)
    envelope = Envelope(to="receiver",
                        sender="sender",
                        protocol_id=FIPAMessage.protocol_id,
                        message=msg_bytes)
    envelope_bytes = envelope.encode()

    actual_envelope = Envelope.decode(envelope_bytes)
    expected_envelope = envelope
    assert expected_envelope == actual_envelope

    actual_msg = FIPASerializer().decode(actual_envelope.message)
    expected_msg = msg
    assert expected_msg == actual_msg
예제 #12
0
def test_handle():
    """Tests handle method of an agent."""
    node = LocalNode()
    agent_name = "MyAgent"
    private_key_pem_path = os.path.join(CUR_PATH, "data", "priv.pem")
    crypto = Crypto(private_key_pem_path=private_key_pem_path)
    public_key = crypto.public_key
    mailbox = MailBox(OEFLocalConnection(public_key, node))

    msg = DefaultMessage(type=DefaultMessage.Type.BYTES, content=b"hello")
    message_bytes = DefaultSerializer().encode(msg)

    envelope = Envelope(to="Agent1",
                        sender=public_key,
                        protocol_id="unknown_protocol",
                        message=message_bytes)

    agent = AEA(agent_name,
                mailbox,
                private_key_pem_path=private_key_pem_path,
                directory=str(Path(CUR_PATH, "data", "dummy_aea")))
    t = Thread(target=agent.start)
    try:
        t.start()
        agent.mailbox.inbox._queue.put(envelope)
        env = agent.mailbox.outbox._queue.get(block=True, timeout=5.0)
        assert env.protocol_id == "default", \
            "The envelope is not the expected protocol (Unsupported protocol)"

        #   DECODING ERROR
        msg = "hello".encode("utf-8")
        envelope = Envelope(to=public_key,
                            sender=public_key,
                            protocol_id='default',
                            message=msg)
        agent.mailbox.inbox._queue.put(envelope)
        #   UNSUPPORTED SKILL
        msg = FIPASerializer().encode(
            FIPAMessage(performative=FIPAMessage.Performative.ACCEPT,
                        message_id=0,
                        dialogue_id=0,
                        destination=public_key,
                        target=1))
        envelope = Envelope(to=public_key,
                            sender=public_key,
                            protocol_id="fipa",
                            message=msg)
        agent.mailbox.inbox._queue.put(envelope)
    finally:
        agent.stop()
        t.join()
예제 #13
0
    def handle_envelope(self, envelope: Envelope) -> None:
        """
        Dispatch envelope to relevant handler and respond.

        :param envelope: the envelope
        :return: None
        """
        fipa_msg = FIPASerializer().decode(envelope.message)
        fipa_msg = cast(FIPAMessage, fipa_msg)
        fipa_msg_performative = fipa_msg.get("performative")  # FIPAMessage.Performative(fipa_msg.get("performative"))

        logger.debug("[{}]: Identifying dialogue of FIPAMessage={}".format(self.context.agent_name, fipa_msg))
        dialogues = cast(Dialogues, self.context.dialogues)
        if dialogues.is_belonging_to_registered_dialogue(fipa_msg, envelope.sender, self.context.agent_public_key):
            dialogue = dialogues.get_dialogue(fipa_msg, envelope.sender, self.context.agent_public_key)
            dialogue.incoming_extend(fipa_msg)
        elif dialogues.is_permitted_for_new_dialogue(fipa_msg, envelope.sender):
            dialogue = dialogues.create_opponent_initiated(fipa_msg, envelope.sender)
            dialogue.incoming_extend(fipa_msg)
        else:
            logger.debug("[{}]: Unidentified dialogue.".format(self.context.agent_name))
            default_msg = DefaultMessage(type=DefaultMessage.Type.BYTES, content=b'This message belongs to an unidentified dialogue.')
            msg_bytes = DefaultSerializer().encode(default_msg)
            self.context.outbox.put_message(to=envelope.sender, sender=self.context.agent_public_key, protocol_id=DefaultMessage.protocol_id, message=msg_bytes)
            return

        logger.debug("[{}]: Handling FIPAMessage of performative={}".format(self.context.agent_name, fipa_msg_performative))
        fipa_msg = cast(FIPAMessage, fipa_msg)
        if fipa_msg_performative == FIPAMessage.Performative.CFP:
            self._on_cfp(fipa_msg, dialogue)
        elif fipa_msg_performative == FIPAMessage.Performative.PROPOSE:
            self._on_propose(fipa_msg, dialogue)
        elif fipa_msg_performative == FIPAMessage.Performative.DECLINE:
            self._on_decline(fipa_msg, dialogue)
        elif fipa_msg_performative == FIPAMessage.Performative.ACCEPT:
            self._on_accept(fipa_msg, dialogue)
        elif fipa_msg_performative == FIPAMessage.Performative.MATCH_ACCEPT:
            self._on_match_accept(fipa_msg, dialogue)
예제 #14
0
    def _on_accept(self, accept: FIPAMessage, dialogue: Dialogue) -> None:
        """
        Handle an Accept.

        :param accept: the Accept message
        :param dialogue: the dialogue
        :return: None
        """
        transactions = cast(Transactions, self.context.transactions)
        assert dialogue.dialogue_label in transactions.pending_proposals \
            and accept.get("target") in transactions.pending_proposals[dialogue.dialogue_label]
        logger.debug("[{}]: on_accept: msg_id={}, dialogue_id={}, origin={}, target={}"
                     .format(self.context.agent_name, accept.get("id"), accept.get("dialogue_id"), dialogue.dialogue_label.dialogue_opponent_pbk, accept.get("target")))
        new_msg_id = cast(int, accept.get("id")) + 1
        transaction_msg = transactions.pop_pending_proposal(dialogue.dialogue_label, cast(int, accept.get("target")))
        strategy = cast(Strategy, self.context.strategy)
        ownership_state_after_locks = transactions.ownership_state_after_locks(self.context.ownership_state, is_seller=dialogue.is_seller)
        if strategy.is_profitable_transaction(self.context.preferences, ownership_state_after_locks, transaction_msg):
            if strategy.is_world_modeling:
                pass  # TODO
                # strategy.world_state.update_on_initial_accept(transaction_msg)
            logger.debug("[{}]: Locking the current state (as {}).".format(self.context.agent_name, dialogue.role))
            transactions.add_locked_tx(transaction_msg, as_seller=dialogue.is_seller)
            self.context.decision_maker_message_queue.put(transaction_msg)
            msg = FIPAMessage(message_id=new_msg_id, dialogue_id=accept.get("dialogue_id"), target=accept.get("id"), performative=FIPAMessage.Performative.MATCH_ACCEPT)
            dialogue.outgoing_extend(msg)
            msg_bytes = FIPASerializer().encode(msg)
            result = Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=msg_bytes)
        else:
            logger.debug("[{}]: Decline the accept (as {}).".format(self.context.agent_name, dialogue.role))
            msg = FIPAMessage(message_id=new_msg_id, dialogue_id=accept.get("dialogue_id"), target=accept.get("id"), performative=FIPAMessage.Performative.DECLINE)
            dialogue.outgoing_extend(msg)
            msg_bytes = FIPASerializer().encode(msg)
            result = Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=msg_bytes)
            dialogues = cast(Dialogues, self.context.dialogues)
            dialogues.dialogue_stats.add_dialogue_endstate(Dialogue.EndState.DECLINED_ACCEPT, dialogue.is_self_initiated)
        self.context.outbox.put(result)
예제 #15
0
    def _on_accept(self, accept: FIPAMessage, dialogue: Dialogue) -> None:
        """
        Handle an Accept.

        :param accept: the Accept message
        :param dialogue: the dialogue
        :return: None
        """
        logger.debug(
            "[{}]: on_accept: msg_id={}, dialogue_id={}, origin={}, target={}".
            format(self.context.agent_name, accept.get("message_id"),
                   accept.get("dialogue_id"),
                   dialogue.dialogue_label.dialogue_opponent_pbk,
                   accept.get("target")))
        new_msg_id = cast(int, accept.get("message_id")) + 1
        transactions = cast(Transactions, self.context.transactions)
        transaction_msg = transactions.pop_pending_proposal(
            dialogue.dialogue_label, cast(int, accept.get("target")))
        strategy = cast(Strategy, self.context.strategy)

        if strategy.is_profitable_transaction(transaction_msg,
                                              is_seller=dialogue.is_seller):
            logger.info("[{}]: locking the current state (as {}).".format(
                self.context.agent_name, dialogue.role))
            transactions.add_locked_tx(transaction_msg,
                                       as_seller=dialogue.is_seller)
            self.context.decision_maker_message_queue.put(transaction_msg)
        else:
            logger.debug("[{}]: decline the Accept (as {}).".format(
                self.context.agent_name, dialogue.role))
            fipa_msg = FIPAMessage(
                performative=FIPAMessage.Performative.DECLINE,
                message_id=new_msg_id,
                dialogue_reference=dialogue.dialogue_label.dialogue_reference,
                target=accept.get("message_id"),
            )
            dialogue.outgoing_extend(fipa_msg)
            dialogues = cast(Dialogues, self.context.dialogues)
            dialogues.dialogue_stats.add_dialogue_endstate(
                Dialogue.EndState.DECLINED_ACCEPT, dialogue.is_self_initiated)
            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))
예제 #16
0
    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))
예제 #17
0
    def test_error_invalid_message(self):
        """Test the invalid message."""
        msg = FIPAMessage(message_id=0,
                          dialogue_reference=(str(0), ''),
                          target=0,
                          performative=FIPAMessage.Performative.ACCEPT)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to=self.public_key,
                            sender=self.public_key,
                            protocol_id=OEFMessage.protocol_id,
                            message=msg_bytes)

        self.my_error_handler.send_invalid_message(envelope)

        envelope = self.my_aea.inbox.get(block=True, timeout=1.0)
        msg = DefaultSerializer().decode(envelope.message)
        assert msg.get("type") == DefaultMessage.Type.ERROR
        assert msg.get(
            "error_code") == DefaultMessage.ErrorCode.INVALID_MESSAGE.value
예제 #18
0
    def test_error_skill_unsupported_protocol(self):
        """Test the unsupported error message."""
        msg = FIPAMessage(message_id=0,
                          dialogue_reference=(str(0), ''),
                          target=0,
                          performative=FIPAMessage.Performative.ACCEPT)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to=self.public_key,
                            sender=self.public_key,
                            protocol_id=FIPAMessage.protocol_id,
                            message=msg_bytes)

        self.my_error_handler.send_unsupported_protocol(envelope)

        envelope = self.my_aea.inbox.get(block=True, timeout=1.0)
        msg = DefaultSerializer().decode(envelope.message)
        assert msg.get("type") == DefaultMessage.Type.ERROR
        assert msg.get("error_code"
                       ) == DefaultMessage.ErrorCode.UNSUPPORTED_PROTOCOL.value
예제 #19
0
    def handle(self, message: Message, sender: str) -> None:
        """
        Dispatch message to relevant handler and respond.

        :param message: the message
        :param sender: the sender
        :return: None
        """
        tx_message = cast(TransactionMessage, message)
        if TransactionMessage.Performative(tx_message.get(
                "performative")) == TransactionMessage.Performative.ACCEPT:
            logger.info(
                "[{}]: transaction confirmed by decision maker, sending 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))
예제 #20
0
    def _handle_search(self, agents: List[str], search_id: int,
                       is_searching_for_sellers: bool) -> None:
        """
        Handle the search response.

        :param agents: the agents returned by the search
        :param is_searching_for_sellers: whether the agent is searching for sellers
        :return: None
        """
        searched_for = 'sellers' if is_searching_for_sellers else 'buyers'
        if len(agents) > 0:
            logger.info(
                "[{}]: found potential {} agents={} on search_id={}.".format(
                    self.context.agent_name, searched_for,
                    list(map(lambda x: x[-5:], agents)), search_id))
            strategy = cast(Strategy, self.context.strategy)
            dialogues = cast(Dialogues, self.context.dialogues)
            query = strategy.get_own_services_query(is_searching_for_sellers)

            for opponent_pbk in agents:
                dialogue = dialogues.create_self_initiated(
                    opponent_pbk, self.context.agent_public_key,
                    not is_searching_for_sellers)
                logger.info("[{}]: sending CFP to agent={}".format(
                    self.context.agent_name, opponent_pbk[-5:]))
                fipa_msg = FIPAMessage(
                    message_id=FIPAMessage.STARTING_MESSAGE_ID,
                    dialogue_reference=dialogue.dialogue_label.
                    dialogue_reference,
                    performative=FIPAMessage.Performative.CFP,
                    target=FIPAMessage.STARTING_TARGET,
                    query=query)
                dialogue.outgoing_extend(fipa_msg)
                self.context.outbox.put_message(
                    to=opponent_pbk,
                    sender=self.context.agent_public_key,
                    protocol_id=FIPAMessage.protocol_id,
                    message=FIPASerializer().encode(fipa_msg))
        else:
            logger.info(
                "[{}]: found no {} agents on search_id={}, continue searching."
                .format(self.context.agent_name, searched_for, search_id))
예제 #21
0
    def handle_accept(self, sender: str, message_id: int, dialogue_id: int):
        """
        Handle sending accept message.

        :param sender: the sender of the message
        :param message_id: the message id
        :param dialogue_id: the dialogue id
        """
        new_message_id = message_id + 1
        new_target_id = message_id
        logger.info("[{}]: accepting the proposal from sender={}".format(
            self.context.agent_name, sender))
        msg = FIPAMessage(message_id=new_message_id,
                          dialogue_id=dialogue_id,
                          target=new_target_id,
                          performative=FIPAMessage.Performative.ACCEPT)
        self.context.outbox.put_message(to=sender,
                                        sender=self.context.agent_public_key,
                                        protocol_id=FIPAMessage.protocol_id,
                                        message=FIPASerializer().encode(msg))
예제 #22
0
    def on_decline(self, msg_id: int, dialogue_id: int, origin: str,
                   target: int) -> None:
        """
        On decline event handler.

        :param msg_id: the message id.
        :param dialogue_id: the dialogue id.
        :param origin: the public key of the sender.
        :param target: the message target.
        :return: None
        """
        msg = FIPAMessage(message_id=msg_id,
                          dialogue_id=dialogue_id,
                          target=target,
                          performative=FIPAMessage.Performative.DECLINE)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to=self.public_key,
                            sender=origin,
                            protocol_id=FIPAMessage.protocol_id,
                            message=msg_bytes)
        self.in_queue.put(envelope)
예제 #23
0
    def on_accept(self, msg_id: int, dialogue_id: int, origin: str,
                  target: int) -> None:
        """
        On accept event handler.

        :param msg_id: the message id.
        :param dialogue_id: the dialogue id.
        :param origin: the public key of the sender.
        :param target: the message target.
        :return: None
        """
        performative = FIPAMessage.Performative.MATCH_ACCEPT if msg_id == 4 and target == 3 else FIPAMessage.Performative.ACCEPT
        msg = FIPAMessage(message_id=msg_id,
                          dialogue_id=dialogue_id,
                          target=target,
                          performative=performative)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to=self.public_key,
                            sender=origin,
                            protocol_id=FIPAMessage.protocol_id,
                            message=msg_bytes)
        self.in_queue.put(envelope)
예제 #24
0
    def _handle_search(self, agents: List[str]) -> None:
        """
        Handle the search response.

        :param agents: the agents returned by the search
        :return: None
        """
        strategy = cast(Strategy, self.context.strategy)
        if len(agents) > 0:
            strategy.on_search_success()

            logger.info("[{}]: found agents={}, stopping search.".format(
                self.context.agent_name, list(map(lambda x: x[-5:], agents))))

            # pick first agent found
            opponent_pbk = agents[0]
            dialogues = cast(Dialogues, self.context.dialogues)
            dialogue = dialogues.create_self_initiated(
                opponent_pbk, self.context.agent_public_key, is_seller=False)
            query = strategy.get_service_query()
            logger.info("[{}]: sending CFP to agent={}".format(
                self.context.agent_name, opponent_pbk[-5:]))
            cfp_msg = FIPAMessage(
                message_id=STARTING_MESSAGE_ID,
                dialogue_reference=dialogue.dialogue_label.dialogue_reference,
                performative=FIPAMessage.Performative.CFP,
                target=STARTING_TARGET_ID,
                query=query)
            dialogue.outgoing_extend(cfp_msg)
            self.context.outbox.put_message(
                to=opponent_pbk,
                sender=self.context.agent_public_key,
                protocol_id=FIPAMessage.protocol_id,
                message=FIPASerializer().encode(cfp_msg))
        else:
            logger.info("[{}]: found no agents, continue searching.".format(
                self.context.agent_name))
            strategy.on_search_failed()
예제 #25
0
    def _handle_inform(self, msg: FIPAMessage, sender: str, message_id: int,
                       dialogue: Dialogue) -> None:
        """
        Handle the INFORM.

        If the INFORM message contains the transaction_digest then verify that it is settled, otherwise do nothing.
        If the transaction is settled send the weather data, otherwise do nothing.

        :param msg: the message
        :param sender: the sender
        :param message_id: the message id
        :param dialogue: the dialogue object
        :return: None
        """
        new_message_id = message_id + 1
        new_target = message_id
        logger.info("[{}]: received INFORM from sender={}".format(
            self.context.agent_name, sender[-5:]))

        json_data = cast(dict, msg.get("json_data"))
        if "Done" in json_data:
            inform_msg = FIPAMessage(
                message_id=new_message_id,
                dialogue_reference=dialogue.dialogue_label.dialogue_reference,
                target=new_target,
                performative=FIPAMessage.Performative.INFORM,
                json_data=dialogue.weather_data)
            dialogue.outgoing_extend(inform_msg)
            # import pdb; pdb.set_trace()
            self.context.outbox.put_message(
                to=sender,
                sender=self.context.agent_public_key,
                protocol_id=FIPAMessage.protocol_id,
                message=FIPASerializer().encode(inform_msg))
            # dialogues = cast(Dialogues, self.context.dialogues)
            # dialogues.dialogue_stats.add_dialogue_endstate(Dialogue.EndState.SUCCESSFUL)
        else:
            logger.warning("I didn't receive the transaction digest!")
예제 #26
0
    def on_cfp(self, msg_id: int, dialogue_id: int, origin: str, target: int,
               query: CFP_TYPES) -> None:
        """
        On cfp event handler.

        :param msg_id: the message id.
        :param dialogue_id: the dialogue id.
        :param origin: the public key of the sender.
        :param target: the message target.
        :param query: the query.
        :return: None
        """
        msg = FIPAMessage(message_id=msg_id,
                          dialogue_id=dialogue_id,
                          target=target,
                          performative=FIPAMessage.Performative.CFP,
                          query=query if query != b"" else None)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to=self.public_key,
                            sender=origin,
                            protocol_id=FIPAMessage.protocol_id,
                            message=msg_bytes)
        self.in_queue.put(envelope)
예제 #27
0
def test_communication():
    """Test that two multiplexer can communicate through the node."""
    with LocalNode() as node:

        multiplexer1 = Multiplexer([OEFLocalConnection("multiplexer1", node)])
        multiplexer2 = Multiplexer([OEFLocalConnection("multiplexer2", node)])

        multiplexer1.connect()
        multiplexer2.connect()

        msg = DefaultMessage(type=DefaultMessage.Type.BYTES, content=b"hello")
        msg_bytes = DefaultSerializer().encode(msg)
        envelope = Envelope(to="multiplexer2",
                            sender="multiplexer1",
                            protocol_id=DefaultMessage.protocol_id,
                            message=msg_bytes)
        multiplexer1.put(envelope)

        msg = FIPAMessage((str(0), ''),
                          0,
                          0,
                          FIPAMessage.Performative.CFP,
                          query=None)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to="multiplexer2",
                            sender="multiplexer1",
                            protocol_id=FIPAMessage.protocol_id,
                            message=msg_bytes)
        multiplexer1.put(envelope)

        msg = FIPAMessage((str(0), str(1)),
                          0,
                          0,
                          FIPAMessage.Performative.PROPOSE,
                          proposal=[])
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to="multiplexer2",
                            sender="multiplexer1",
                            protocol_id=FIPAMessage.protocol_id,
                            message=msg_bytes)
        multiplexer1.put(envelope)

        msg = FIPAMessage((str(0), str(1)), 0, 0,
                          FIPAMessage.Performative.ACCEPT)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to="multiplexer2",
                            sender="multiplexer1",
                            protocol_id=FIPAMessage.protocol_id,
                            message=msg_bytes)
        multiplexer1.put(envelope)

        msg = FIPAMessage((str(0), str(1)), 0, 0,
                          FIPAMessage.Performative.DECLINE)
        msg_bytes = FIPASerializer().encode(msg)
        envelope = Envelope(to="multiplexer2",
                            sender="multiplexer1",
                            protocol_id=FIPAMessage.protocol_id,
                            message=msg_bytes)
        multiplexer1.put(envelope)

        envelope = multiplexer2.get(block=True, timeout=1.0)
        msg = DefaultSerializer().decode(envelope.message)
        assert envelope.protocol_id == "default"
        assert msg.get("content") == b"hello"
        envelope = multiplexer2.get(block=True, timeout=1.0)
        msg = FIPASerializer().decode(envelope.message)
        assert envelope.protocol_id == "fipa"
        assert msg.get("performative") == FIPAMessage.Performative.CFP
        envelope = multiplexer2.get(block=True, timeout=1.0)
        msg = FIPASerializer().decode(envelope.message)
        assert envelope.protocol_id == "fipa"
        assert msg.get("performative") == FIPAMessage.Performative.PROPOSE
        envelope = multiplexer2.get(block=True, timeout=1.0)
        msg = FIPASerializer().decode(envelope.message)
        assert envelope.protocol_id == "fipa"
        assert msg.get("performative") == FIPAMessage.Performative.ACCEPT
        envelope = multiplexer2.get(block=True, timeout=1.0)
        msg = FIPASerializer().decode(envelope.message)
        assert envelope.protocol_id == "fipa"
        assert msg.get("performative") == FIPAMessage.Performative.DECLINE
        multiplexer1.disconnect()
        multiplexer2.disconnect()
예제 #28
0
    def on_cfp(self, cfp: Message, dialogue: Dialogue) -> Envelope:
        """
        Handle a CFP.

        :param cfp: the message containing the CFP
        :param dialogue: the dialogue

        :return: a Propose or a Decline
        """
        assert cfp.get("performative") == FIPAMessage.Performative.CFP
        goods_description = self.game_instance.get_service_description(
            is_supply=dialogue.is_seller)
        new_msg_id = cfp.get("id") + 1
        decline = False
        cfp_services = json.loads(cfp.get("query").decode('utf-8'))
        if not self.game_instance.is_matching(cfp_services, goods_description):
            decline = True
            logger.debug(
                "[{}]: Current holdings do not satisfy CFP query.".format(
                    self.agent_name))
        else:
            proposal = self.game_instance.generate_proposal(
                cfp_services, dialogue.is_seller)
            if proposal is None:
                decline = True
                logger.debug(
                    "[{}]: Current strategy does not generate proposal that satisfies CFP query."
                    .format(self.agent_name))

        if decline:
            logger.debug("[{}]: sending to {} a Decline{}".format(
                self.agent_name, dialogue.dialogue_label.dialogue_opponent_pbk,
                pprint.pformat({
                    "msg_id": new_msg_id,
                    "dialogue_id": cfp.get("dialogue_id"),
                    "origin": dialogue.dialogue_label.dialogue_opponent_pbk,
                    "target": cfp.get("target")
                })))
            msg = FIPAMessage(message_id=new_msg_id,
                              dialogue_id=cfp.get("dialogue_id"),
                              performative=FIPAMessage.Performative.DECLINE,
                              target=cfp.get("id"))
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            response = Envelope(
                to=dialogue.dialogue_label.dialogue_opponent_pbk,
                sender=self.crypto.public_key,
                protocol_id=FIPAMessage.protocol_id,
                message=msg_bytes)
            self.game_instance.stats_manager.add_dialogue_endstate(
                EndState.DECLINED_CFP, dialogue.is_self_initiated)
        else:
            proposal = cast(Description, proposal)
            transaction_id = generate_transaction_id(
                self.crypto.public_key,
                dialogue.dialogue_label.dialogue_opponent_pbk,
                dialogue.dialogue_label, dialogue.is_seller)
            transaction = Transaction.from_proposal(
                proposal=proposal,
                transaction_id=transaction_id,
                is_sender_buyer=not dialogue.is_seller,
                counterparty=dialogue.dialogue_label.dialogue_opponent_pbk,
                sender=self.crypto.public_key)
            self.game_instance.transaction_manager.add_pending_proposal(
                dialogue.dialogue_label, new_msg_id, transaction)
            logger.debug("[{}]: sending to {} a Propose{}".format(
                self.agent_name, dialogue.dialogue_label.dialogue_opponent_pbk,
                pprint.pformat({
                    "msg_id": new_msg_id,
                    "dialogue_id": cfp.get("dialogue_id"),
                    "origin": dialogue.dialogue_label.dialogue_opponent_pbk,
                    "target": cfp.get("id"),
                    "propose": proposal.values
                })))
            msg = FIPAMessage(performative=FIPAMessage.Performative.PROPOSE,
                              message_id=new_msg_id,
                              dialogue_id=cfp.get("dialogue_id"),
                              target=cfp.get("id"),
                              proposal=[proposal])
            dialogue.outgoing_extend([msg])
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            response = Envelope(
                to=dialogue.dialogue_label.dialogue_opponent_pbk,
                sender=self.crypto.public_key,
                protocol_id=FIPAMessage.protocol_id,
                message=msg_bytes)
        return response
예제 #29
0
    def on_accept(self, accept: Message, dialogue: Dialogue) -> List[Envelope]:
        """
        Handle an Accept.

        :param accept: the message containing the Accept
        :param dialogue: the dialogue
        :return: a Decline, or an Accept and a Transaction, or a Transaction (in a Message object)
        """
        assert accept.get("performative") == FIPAMessage.Performative.ACCEPT \
            and dialogue.dialogue_label in self.game_instance.transaction_manager.pending_proposals \
            and accept.get("target") in self.game_instance.transaction_manager.pending_proposals[dialogue.dialogue_label]
        logger.debug(
            "[{}]: on_accept: msg_id={}, dialogue_id={}, origin={}, target={}".
            format(self.agent_name, accept.get("id"),
                   accept.get("dialogue_id"),
                   dialogue.dialogue_label.dialogue_opponent_pbk,
                   accept.get("target")))
        new_msg_id = accept.get("id") + 1
        results = []
        transaction = self.game_instance.transaction_manager.pop_pending_proposal(
            dialogue.dialogue_label, accept.get("target"))
        is_profitable_transaction, accept_log_msg = self.game_instance.is_profitable_transaction(
            transaction, dialogue)
        logger.debug(accept_log_msg)
        if is_profitable_transaction:
            if self.game_instance.strategy.is_world_modeling:
                self.game_instance.world_state.update_on_initial_accept(
                    transaction)
            logger.debug("[{}]: Locking the current state (as {}).".format(
                self.agent_name, dialogue.role))
            self.game_instance.transaction_manager.add_locked_tx(
                transaction, as_seller=dialogue.is_seller)

            tac_msg = TACMessage(
                tac_type=TACMessage.Type.TRANSACTION,
                transaction_id=transaction.transaction_id,
                is_sender_buyer=transaction.is_sender_buyer,
                counterparty=transaction.counterparty,
                amount=transaction.amount,
                quantities_by_good_pbk=transaction.quantities_by_good_pbk)
            dialogue.outgoing_extend([tac_msg])
            tac_bytes = TACSerializer().encode(tac_msg)
            results.append(
                Envelope(to=self.game_instance.controller_pbk,
                         sender=self.crypto.public_key,
                         protocol_id=TACMessage.protocol_id,
                         message=tac_bytes))

            msg = FIPAMessage(
                message_id=new_msg_id,
                dialogue_id=accept.get("dialogue_id"),
                target=accept.get("id"),
                performative=FIPAMessage.Performative.MATCH_ACCEPT)
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            results.append(
                Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk,
                         sender=self.crypto.public_key,
                         protocol_id=FIPAMessage.protocol_id,
                         message=msg_bytes))
        else:
            logger.debug("[{}]: Decline the accept (as {}).".format(
                self.agent_name, dialogue.role))

            msg = FIPAMessage(message_id=new_msg_id,
                              dialogue_id=accept.get("dialogue_id"),
                              target=accept.get("id"),
                              performative=FIPAMessage.Performative.DECLINE)
            dialogue.outgoing_extend([msg])
            msg_bytes = FIPASerializer().encode(msg)
            results.append(
                Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk,
                         sender=self.crypto.public_key,
                         protocol_id=FIPAMessage.protocol_id,
                         message=msg_bytes))
            self.game_instance.stats_manager.add_dialogue_endstate(
                EndState.DECLINED_ACCEPT, dialogue.is_self_initiated)
        return results
예제 #30
0
    def _on_cfp(self, cfp: FIPAMessage, dialogue: Dialogue) -> None:
        """
        Handle a CFP.

        :param cfp: the fipa message containing the CFP
        :param dialogue: the dialogue

        :return: None
        """
        strategy = cast(Strategy, self.context.strategy)
        transactions = cast(Transactions, self.context.transactions)
        ownership_state_after_locks = transactions.ownership_state_after_locks(self.context.ownership_state, is_seller=dialogue.is_seller)
        own_service_description = strategy.get_own_service_description(ownership_state_after_locks, is_supply=dialogue.is_seller)
        new_msg_id = cast(int, cfp.get("id")) + 1
        cfp_query = cfp.get("query")
        cfp_query = cast(Query, cfp_query)
        decline = False
        if not cfp_query.check(own_service_description):
            decline = True
            logger.debug("[{}]: Current holdings do not satisfy CFP query.".format(self.context.agent_name))
        else:
            proposal_description = strategy.get_proposal_for_query(cfp_query, self.context.preferences, ownership_state_after_locks, is_seller=dialogue.is_seller, tx_fee=1.0)
            if proposal_description is None:
                decline = True
                logger.debug("[{}]: Current strategy does not generate proposal that satisfies CFP query.".format(self.context.agent_name))

        if decline:
            logger.debug("[{}]: sending to {} a Decline{}".format(self.context.agent_name, dialogue.dialogue_label.dialogue_opponent_pbk,
                                                                  pprint.pformat({
                                                                      "msg_id": new_msg_id,
                                                                      "dialogue_id": cfp.get("dialogue_id"),
                                                                      "origin": dialogue.dialogue_label.dialogue_opponent_pbk,
                                                                      "target": cfp.get("target")
                                                                  })))
            msg = FIPAMessage(message_id=new_msg_id, dialogue_id=cfp.get("dialogue_id"), performative=FIPAMessage.Performative.DECLINE, target=cfp.get("id"))
            dialogue.outgoing_extend(msg)
            msg_bytes = FIPASerializer().encode(msg)
            result = Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=msg_bytes)
            dialogues = cast(Dialogues, self.context.dialogues)
            dialogues.dialogue_stats.add_dialogue_endstate(Dialogue.EndState.DECLINED_CFP, dialogue.is_self_initiated)
        else:
            assert proposal_description is not None
            transaction_id = generate_transaction_id(self.context.agent_public_key, dialogue.dialogue_label.dialogue_opponent_pbk, dialogue.dialogue_label, dialogue.is_seller)
            transaction_msg = TransactionMessage(transaction_id=transaction_id,
                                                 sender=self.context.agent_public_key,
                                                 counterparty=dialogue.dialogue_label.dialogue_opponent_pbk,
                                                 currency='FET',
                                                 amount=proposal_description.values['amount'],
                                                 is_sender_buyer=not dialogue.is_seller,
                                                 sender_tx_fee=1,
                                                 counterparty_tx_fee=1,
                                                 quantities_by_good_pbk=proposal_description.values['description'])
            transactions = cast(Transactions, self.context.transactions)
            transactions.add_pending_proposal(dialogue.dialogue_label, new_msg_id, transaction_msg)
            logger.debug("[{}]: sending to {} a Propose{}".format(self.context.agent_name, dialogue.dialogue_label.dialogue_opponent_pbk,
                                                                  pprint.pformat({
                                                                      "msg_id": new_msg_id,
                                                                      "dialogue_id": cfp.get("dialogue_id"),
                                                                      "origin": dialogue.dialogue_label.dialogue_opponent_pbk,
                                                                      "target": cfp.get("id"),
                                                                      "propose": proposal_description.values
                                                                  })))
            msg = FIPAMessage(performative=FIPAMessage.Performative.PROPOSE, message_id=new_msg_id, dialogue_id=cfp.get("dialogue_id"), target=cfp.get("id"), proposal=[proposal_description])
            dialogue.outgoing_extend(msg)
            msg_bytes = FIPASerializer().encode(msg)
            result = Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=msg_bytes)
        self.context.outbox.put(result)