def test_serialisation_fipa(self): """Tests a Value Error flag for wrong CFP query.""" with pytest.raises(ValueError): msg = FIPAMessage(performative=FIPAMessage.Performative.CFP, message_id=0, dialogue_reference=(str(0), ''), target=1, query=None) with mock.patch( "aea.protocols.fipa.message.FIPAMessage.Performative" ) as mock_performative_enum: mock_performative_enum.CFP.value = "unknown" FIPASerializer().encode(msg), "Raises Value Error" with pytest.raises(ValueError): msg.set("query", "Hello") # query type is not supported FIPASerializer().encode(msg) with pytest.raises(ValueError): cfp_msg = FIPAMessage(message_id=0, dialogue_reference=(str(0), ''), target=0, performative=FIPAMessage.Performative.CFP, query=b"hello") cfp_msg.set("query", "hello") fipa_msg = fipa_pb2.FIPAMessage() fipa_msg.message_id = cfp_msg.get("message_id") dialogue_reference = cast(Dict[str, str], cfp_msg.get("dialogue_reference")) fipa_msg.dialogue_starter_reference = dialogue_reference[0] fipa_msg.dialogue_responder_reference = dialogue_reference[1] fipa_msg.target = cfp_msg.get("target") performative = fipa_pb2.FIPAMessage.CFP() fipa_msg.cfp.CopyFrom(performative) fipa_bytes = fipa_msg.SerializeToString() # The encoded message is not a valid FIPA message. FIPASerializer().decode(fipa_bytes) with pytest.raises(ValueError): cfp_msg = FIPAMessage(message_id=0, dialogue_reference=(str(0), ''), target=0, performative=FIPAMessage.Performative.CFP, query=b"hello") with mock.patch( "aea.protocols.fipa.message.FIPAMessage.Performative" ) as mock_performative_enum: mock_performative_enum.CFP.value = "unknown" fipa_msg = fipa_pb2.FIPAMessage() fipa_msg.message_id = cfp_msg.get("message_id") dialogue_reference = cast(Dict[str, str], cfp_msg.get("dialogue_reference")) fipa_msg.dialogue_starter_reference = dialogue_reference[0] fipa_msg.dialogue_responder_reference = dialogue_reference[1] fipa_msg.target = cfp_msg.get("target") performative = fipa_pb2.FIPAMessage.CFP() fipa_msg.cfp.CopyFrom(performative) fipa_bytes = fipa_msg.SerializeToString() # The encoded message is not a FIPA message FIPASerializer().decode(fipa_bytes)
def test_propose(self): """Test that a Propose can be sent correctly.""" propose_empty = FIPAMessage( message_id=0, dialogue_id=0, target=0, performative=FIPAMessage.Performative.PROPOSE, proposal=[]) self.mailbox1.outbox.put_message( to=self.crypto2.public_key, sender=self.crypto1.public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(propose_empty)) envelope = self.mailbox2.inbox.get(block=True, timeout=2.0) expected_propose_empty = FIPASerializer().decode(envelope.message) assert expected_propose_empty == propose_empty propose_descriptions = FIPAMessage( message_id=0, dialogue_id=0, target=0, performative=FIPAMessage.Performative.PROPOSE, proposal=[ Description({"foo": "bar"}, DataModel("foobar", [Attribute("foo", str, True)])) ]) self.mailbox1.outbox.put_message( to=self.crypto2.public_key, sender=self.crypto1.public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(propose_descriptions)) envelope = self.mailbox2.inbox.get(block=True, timeout=2.0) expected_propose_descriptions = FIPASerializer().decode( envelope.message) assert expected_propose_descriptions == propose_descriptions
def on_propose(self, propose: Message, dialogue: Dialogue) -> Envelope: """ Handle a Propose. :param propose: the message containing the Propose :param dialogue: the dialogue :return: an Accept or a Decline """ logger.debug("[{}]: on propose as {}.".format(self.agent_name, dialogue.role)) assert propose.get("performative") == FIPAMessage.Performative.PROPOSE proposal = propose.get("proposal")[0] 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) new_msg_id = propose.get("id") + 1 is_profitable_transaction, propose_log_msg = self.game_instance.is_profitable_transaction( transaction, dialogue) logger.debug(propose_log_msg) if is_profitable_transaction: logger.debug("[{}]: Accepting propose (as {}).".format( self.agent_name, dialogue.role)) self.game_instance.transaction_manager.add_locked_tx( transaction, as_seller=dialogue.is_seller) self.game_instance.transaction_manager.add_pending_initial_acceptance( dialogue.dialogue_label, new_msg_id, transaction) msg = FIPAMessage(message_id=new_msg_id, dialogue_id=propose.get("dialogue_id"), target=propose.get("id"), performative=FIPAMessage.Performative.ACCEPT) dialogue.outgoing_extend([msg]) msg_bytes = FIPASerializer().encode(msg) result = Envelope(to=dialogue.dialogue_label.dialogue_opponent_pbk, sender=self.crypto.public_key, protocol_id=FIPAMessage.protocol_id, message=msg_bytes) else: logger.debug("[{}]: Declining propose (as {})".format( self.agent_name, dialogue.role)) msg = FIPAMessage(message_id=new_msg_id, dialogue_id=propose.get("dialogue_id"), target=propose.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.crypto.public_key, protocol_id=FIPAMessage.protocol_id, message=msg_bytes) self.game_instance.stats_manager.add_dialogue_endstate( EndState.DECLINED_PROPOSE, dialogue.is_self_initiated) return result
def test_cfp(self): """Test that a CFP can be sent correctly.""" cfp_bytes = FIPAMessage( message_id=0, dialogue_id=0, target=0, performative=FIPAMessage.Performative.CFP, query=Query([Constraint('something', ConstraintType('>', 1))])) self.mailbox1.outbox.put_message( to=self.crypto2.public_key, sender=self.crypto1.public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(cfp_bytes)) envelope = self.mailbox2.inbox.get(block=True, timeout=5.0) expected_cfp_bytes = FIPASerializer().decode(envelope.message) assert expected_cfp_bytes == cfp_bytes cfp_none = FIPAMessage(message_id=0, dialogue_id=0, target=0, performative=FIPAMessage.Performative.CFP, query=None) self.mailbox1.outbox.put_message( to=self.crypto2.public_key, sender=self.crypto1.public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(cfp_none)) envelope = self.mailbox2.inbox.get(block=True, timeout=5.0) expected_cfp_none = FIPASerializer().decode(envelope.message) assert expected_cfp_none == cfp_none
def _handle_propose(self, msg: FIPAMessage, sender: str, message_id: int, dialogue: Dialogue) -> None: """ Handle the propose. :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_id = message_id proposals = cast(List[Description], msg.get("proposal")) if proposals is not []: # only take the first proposal proposal = proposals[0] logger.info("[{}]: received proposal={} from sender={}".format( self.context.agent_name, proposal.values, sender[-5:])) strategy = cast(Strategy, self.context.strategy) acceptable = strategy.is_acceptable_proposal(proposal) affordable = self.context.ledger_apis.token_balance( 'fetchai', cast(str, self.context.agent_addresses.get('fetchai'))) >= cast( int, proposal.values.get('price')) if acceptable and affordable: logger.info( "[{}]: accepting the proposal from sender={}".format( self.context.agent_name, sender[-5:])) dialogue.proposal = proposal accept_msg = FIPAMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label. dialogue_reference, target=new_target_id, performative=FIPAMessage.Performative.ACCEPT) dialogue.outgoing_extend(accept_msg) self.context.outbox.put_message( to=sender, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(accept_msg)) else: logger.info( "[{}]: declining the proposal from sender={}".format( self.context.agent_name, sender[-5:])) decline_msg = FIPAMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label. dialogue_reference, target=new_target_id, performative=FIPAMessage.Performative.DECLINE) dialogue.outgoing_extend(decline_msg) self.context.outbox.put_message( to=sender, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(decline_msg))
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))
def _handle_cfp(self, msg: FIPAMessage, sender: str, message_id: int, dialogue: Dialogue) -> None: """ Handle the CFP. If the CFP matches the supplied services then send a PROPOSE, otherwise send a DECLINE. :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 CFP from sender={}".format( self.context.agent_name, sender[-5:])) query = cast(Query, msg.get("query")) strategy = cast(Strategy, self.context.strategy) if strategy.is_matching_supply(query): proposal, weather_data = strategy.generate_proposal_and_data(query) dialogue.weather_data = weather_data dialogue.proposal = proposal logger.info( "[{}]: sending sender={} a PROPOSE with proposal={}".format( self.context.agent_name, sender[-5:], proposal.values)) proposal_msg = FIPAMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FIPAMessage.Performative.PROPOSE, proposal=[proposal]) dialogue.outgoing_extend(proposal_msg) self.context.outbox.put_message( to=sender, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(proposal_msg)) else: logger.info("[{}]: declined the CFP from sender={}".format( self.context.agent_name, sender[-5:])) decline_msg = FIPAMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FIPAMessage.Performative.DECLINE) dialogue.outgoing_extend(decline_msg) self.context.outbox.put_message( to=sender, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(decline_msg))
def handle_cfp(self, msg: FIPAMessage, sender: str, message_id: int, dialogue_id: int) -> None: """ Handle the CFP calls. :param msg: the message :param sender: the sender :param message_id: the message id :param dialogue_id: the dialogue id :return: None """ new_message_id = message_id + 1 new_target = message_id fetched_data = self.db.get_data_for_specific_dates(DATE_ONE, DATE_TWO) if len(fetched_data) >= 1: self.fetched_data = fetched_data total_price = self.fet_price * len(fetched_data) proposal = [ Description({ "Rows": len(fetched_data), "Price": total_price }) ] logger.info( "[{}]: sending sender={} a proposal at price={}".format( self.context.agent_name, sender, total_price)) proposal_msg = FIPAMessage( message_id=new_message_id, dialogue_id=dialogue_id, target=new_target, performative=FIPAMessage.Performative.PROPOSE, proposal=proposal) self.context.outbox.put_message( to=sender, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(proposal_msg)) else: logger.info("[{}]: declined the CFP from sender={}".format( self.context.agent_name, sender)) decline_msg = FIPAMessage( message_id=new_message_id, dialogue_id=dialogue_id, target=new_target, performative=FIPAMessage.Performative.DECLINE) self.context.outbox.put_message( to=sender, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(decline_msg))
def test_serialisation_fipa(self): """Tests a Value Error flag for wrong CFP query.""" with pytest.raises(ValueError): msg = FIPAMessage(performative=FIPAMessage.Performative.CFP, message_id=0, dialogue_id=0, destination="publicKey", target=1) with mock.patch("aea.protocols.fipa.message.FIPAMessage.Performative")\ as mock_performative_enum: mock_performative_enum.CFP.value = "unknown" assert FIPASerializer().encode(msg), "Raises Value Error" with pytest.raises(ValueError): msg.set("query", "Hello") assert FIPASerializer().encode(msg), "Query type is Supported!" with pytest.raises(ValueError): cfp_msg = FIPAMessage(message_id=0, dialogue_id=0, target=0, performative=FIPAMessage.Performative.CFP, query=b"hello") cfp_msg.set("query", "hello") fipa_msg = fipa_pb2.FIPAMessage() fipa_msg.message_id = cfp_msg.get("id") fipa_msg.dialogue_id = cfp_msg.get("dialogue_id") fipa_msg.target = cfp_msg.get("target") performative = fipa_pb2.FIPAMessage.CFP() fipa_msg.cfp.CopyFrom(performative) fipa_bytes = fipa_msg.SerializeToString() assert FIPASerializer().decode(fipa_bytes),\ "The encoded message is a valid FIPA message." with pytest.raises(ValueError): cfp_msg = FIPAMessage(message_id=0, dialogue_id=0, target=0, performative=FIPAMessage.Performative.CFP, query=b"hello") with mock.patch("aea.protocols.fipa.message.FIPAMessage.Performative")\ as mock_performative_enum: mock_performative_enum.CFP.value = "unknown" fipa_msg = fipa_pb2.FIPAMessage() fipa_msg.message_id = cfp_msg.get("id") fipa_msg.dialogue_id = cfp_msg.get("dialogue_id") fipa_msg.target = cfp_msg.get("target") performative = fipa_pb2.FIPAMessage.CFP() fipa_msg.cfp.CopyFrom(performative) fipa_bytes = fipa_msg.SerializeToString() assert FIPASerializer().decode(fipa_bytes),\ "The encoded message is a FIPA message"
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 """ assert self.in_queue is not None assert self.loop is not None logger.warning( 'Accepting on_cfp from deprecated API: msg_id={}, dialogue_id={}, origin={}, target={}. Continuing dialogue via envelopes!' .format(msg_id, dialogue_id, origin, target)) try: query = pickle.loads(query) except Exception: pass msg = FIPAMessage(message_id=msg_id, dialogue_reference=(str(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) asyncio.run_coroutine_threadsafe(self.in_queue.put(envelope), self.loop).result()
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 _handle_search(self, agents: List[str]) -> None: """ Handle the search response. :param agents: the agents returned by the search :return: None """ if len(agents) > 0: logger.info("[{}]: found agents={}, stopping search.".format(self.context.agent_name, list(map(lambda x: x[-5:], agents)))) strategy = cast(Strategy, self.context.strategy) # stopping search strategy.is_searching = False # 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=FIPAMessage.STARTING_MESSAGE_ID, dialogue_reference=dialogue.dialogue_label.dialogue_reference, performative=FIPAMessage.Performative.CFP, target=FIPAMessage.STARTING_TARGET, 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))
def _handle_accept(self, msg: FIPAMessage, sender: str, message_id: int, dialogue: Dialogue) -> None: """ Handle the ACCEPT. Respond with a MATCH_ACCEPT_W_ADDRESS which contains the address to send the funds to. :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 ACCEPT from sender={}".format( self.context.agent_name, sender[-5:])) logger.info("[{}]: sending MATCH_ACCEPT_W_ADDRESS to sender={}".format( self.context.agent_name, sender[-5:])) match_accept_msg = FIPAMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FIPAMessage.Performative.MATCH_ACCEPT_W_ADDRESS, address=self.context.agent_addresses['fetchai']) dialogue.outgoing_extend(match_accept_msg) self.context.outbox.put_message( to=sender, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(match_accept_msg)) strategy = cast(Strategy, self.context.strategy) strategy.db.set_dialogue_status(str(dialogue.dialogue_label), sender[-5:], "received_accept", "send_match_accept")
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_reference=(str(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 msg.set("query", "not_supported_query") with pytest.raises(ValueError, match="Query type not supported:"): FIPASerializer().encode(msg)
def on_propose(self, msg_id: int, dialogue_id: int, origin: str, target: int, b_proposals: PROPOSE_TYPES) -> None: """ On propose 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 b_proposals: the proposals. :return: None """ if type(b_proposals) == bytes: proposals = pickle.loads(b_proposals) # type: List[Description] else: raise ValueError("No support for non-bytes proposals.") msg = FIPAMessage(message_id=msg_id, dialogue_id=dialogue_id, target=target, performative=FIPAMessage.Performative.PROPOSE, proposal=proposals) 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)
def _handle_accept(self, msg: FIPAMessage, sender: str, message_id: int, dialogue: Dialogue) -> None: """ Handle the ACCEPT. Respond with a MATCH_ACCEPT_W_ADDRESS which contains the address to send the funds to. :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 ACCEPT from sender={}".format( self.context.agent_name, sender[-5:])) logger.info("[{}]: sending MATCH_ACCEPT_W_ADDRESS to sender={}".format( self.context.agent_name, sender[-5:])) # proposal = cast(Description, dialogue.proposal) # identifier = cast(str, proposal.values.get("ledger_id")) match_accept_msg = FIPAMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FIPAMessage.Performative.MATCH_ACCEPT_W_ADDRESS, address="no_address") dialogue.outgoing_extend(match_accept_msg) self.context.outbox.put_message( to=sender, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(match_accept_msg))
def test_fipa_cfp_serialization_bytes(): """Test that the serialization - deserialization for the 'fipa' protocol works.""" query = b'Hello' msg = FIPAMessage(message_id=0, dialogue_reference=(str(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 deserialised_msg = FIPASerializer().decode(envelope.message) assert msg.get("performative") == deserialised_msg.get("performative")
def handle_envelope(self, envelope: Envelope) -> None: """ Implement the reaction to an envelope. :param envelope: the envelope :return: None """ msg = OEFSerializer().decode(envelope.message) msg_type = OEFMessage.Type(msg.get("type")) if msg_type is OEFMessage.Type.SEARCH_RESULT: agents = cast(List[str], msg.get("agents")) logger.info("[{}]: found agents={}".format(self.context.agent_name, agents)) for agent in agents: msg = FIPAMessage(message_id=STARTING_MESSAGE_ID, dialogue_id=self.dialogue_id, performative=FIPAMessage.Performative.CFP, target=STARTING_TARGET_ID, query=None) self.dialogue_id += 1 self.context.outbox.put_message( to=agent, sender=self.context.agent_public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(msg))
def test_fipa_propose_serialization(): """Test that the serialization for the 'fipa' protocol works.""" proposal = [ Description({"foo1": 1, "bar1": 2}), Description({"foo2": 1, "bar2": 2}), ] msg = FIPAMessage(message_id=0, dialogue_reference=(str(0), ''), target=0, performative=FIPAMessage.Performative.PROPOSE, proposal=proposal) 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 p1 = actual_msg.get("proposal") p2 = expected_msg.get("proposal") assert p1[0].values == p2[0].values assert p1[1].values == p2[1].values
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 """ try: query = pickle.loads(query) except Exception: pass 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)
def _handle_match_accept(self, msg: FIPAMessage, sender: str, message_id: int, dialogue: Dialogue) -> None: """ Handle the match accept. :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_id = message_id 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={"Done": "Sending payment via bank transfer"}) 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 payment.".format( self.context.agent_name, counterparty_pbk[-5:])) self._received_tx_message = True
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 """ logger.debug("[{}]: on propose as {}.".format(self.context.agent_name, dialogue.role)) proposals = cast(List[Description], propose.get("proposal")) for num, proposal_description in enumerate(proposals): if num > 0: continue # TODO: allow for dialogue branching with multiple proposals 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']) new_msg_id = cast(int, propose.get("id")) + 1 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) if strategy.is_profitable_transaction(self.context.preferences, ownership_state_after_locks, transaction_msg): logger.debug("[{}]: Accepting propose (as {}).".format(self.context.agent_name, dialogue.role)) transactions.add_locked_tx(transaction_msg, as_seller=dialogue.is_seller) transactions.add_pending_initial_acceptance(dialogue.dialogue_label, new_msg_id, transaction_msg) msg = FIPAMessage(message_id=new_msg_id, dialogue_id=propose.get("dialogue_id"), target=propose.get("id"), performative=FIPAMessage.Performative.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("[{}]: Declining propose (as {})".format(self.context.agent_name, dialogue.role)) msg = FIPAMessage(message_id=new_msg_id, dialogue_id=propose.get("dialogue_id"), target=propose.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_PROPOSE, dialogue.is_self_initiated) self.context.outbox.put(result)
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)
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, )
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
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)
def test_performative_not_recognized(): """Tests an unknown Performative.""" msg = FIPAMessage( performative=FIPAMessage.Performative.ACCEPT, message_id=0, dialogue_reference=(str(0), ''), target=1) with mock.patch("aea.protocols.fipa.message.FIPAMessage.Performative")\ as mock_performative_enum: mock_performative_enum.ACCEPT.value = "unknown" assert not msg.check_consistency(),\ "We expect that the check_consistency will return False"
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()
def test_decline(self): """Test that a Decline can be sent correctly.""" decline = FIPAMessage(message_id=0, dialogue_id=0, target=0, performative=FIPAMessage.Performative.DECLINE) self.mailbox1.outbox.put_message( to=self.crypto2.public_key, sender=self.crypto1.public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(decline)) envelope = self.mailbox2.inbox.get(block=True, timeout=2.0) expected_decline = FIPASerializer().decode(envelope.message) assert expected_decline == decline
def test_accept(self): """Test that an Accept can be sent correctly.""" accept = FIPAMessage(message_id=0, dialogue_id=0, target=0, performative=FIPAMessage.Performative.ACCEPT) self.mailbox1.outbox.put_message( to=self.crypto2.public_key, sender=self.crypto1.public_key, protocol_id=FIPAMessage.protocol_id, message=FIPASerializer().encode(accept)) envelope = self.mailbox2.inbox.get(block=True, timeout=2.0) expected_accept = FIPASerializer().decode(envelope.message) assert expected_accept == accept