def test_performative_match_accept_with_inform(): """Test the serialization - deserialization of the match_accept_with_address performative.""" msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.MATCH_ACCEPT_W_INFORM, info={ "address": "dummy_address", "signature": "my_signature" }, ) 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")
def test_fipa_cfp_serialization(): """Test that the serialization for the 'fipa' protocol works.""" query = Query([Constraint("something", ConstraintType(">", 1))]) msg = FipaMessage( message_id=1, 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
def _handle_propose(self, msg: FipaMessage, dialogue: Dialogue) -> None: """ Handle the propose. :param msg: the message :param dialogue: the dialogue object :return: None """ new_message_id = msg.message_id + 1 new_target = msg.message_id proposal = msg.proposal self.context.logger.info( "[{}]: received proposal={} from sender={}".format( self.context.agent_name, proposal.values, msg.counterparty[-5:] ) ) strategy = cast(Strategy, self.context.strategy) acceptable = strategy.is_acceptable_proposal(proposal) affordable = strategy.is_affordable_proposal(proposal) if acceptable and affordable: self.context.logger.info( "[{}]: accepting the proposal from sender={}".format( self.context.agent_name, msg.counterparty[-5:] ) ) dialogue.proposal = proposal accept_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FipaMessage.Performative.ACCEPT, ) accept_msg.counterparty = msg.counterparty dialogue.update(accept_msg) self.context.outbox.put_message( to=msg.counterparty, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(accept_msg), ) else: self.context.logger.info( "[{}]: declining the proposal from sender={}".format( self.context.agent_name, msg.counterparty[-5:] ) ) decline_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FipaMessage.Performative.DECLINE, ) decline_msg.counterparty = msg.counterparty dialogue.update(decline_msg) self.context.outbox.put_message( to=msg.counterparty, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(decline_msg), )
def test_fipa_accept_serialization(): """Test that the serialization for the 'fipa' protocol works.""" msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.ACCEPT, ) msg.counterparty = "sender" 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) actual_msg.counterparty = "sender" expected_msg = msg assert expected_msg == actual_msg
def test_fipa_propose_serialization(): """Test that the serialization for the 'fipa' protocol works.""" proposal = Description({"foo1": 1, "bar1": 2}) msg = FipaMessage( message_id=1, 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.values == p2.values
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=1, dialogue_reference=(str(0), ""), target=0, query=Query([Constraint("something", ConstraintType(">", 1))]), ) with mock.patch( "packages.fetchai.protocols.fipa.message.FipaMessage.Performative" ) as mock_performative_enum: mock_performative_enum.CFP.value = "unknown" FipaSerializer().encode(msg), "Raises Value Error" with pytest.raises(EOFError): cfp_msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.CFP, query=Query([Constraint("something", ConstraintType(">", 1))]), ) cfp_msg.set("query", "hello") fipa_msg = fipa_pb2.FipaMessage() fipa_msg.message_id = cfp_msg.message_id dialogue_reference = cast(Dict[str, str], cfp_msg.dialogue_reference) fipa_msg.dialogue_starter_reference = dialogue_reference[0] fipa_msg.dialogue_responder_reference = dialogue_reference[1] fipa_msg.target = cfp_msg.target performative = fipa_pb2.FipaMessage.Cfp_Performative() 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=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.CFP, query=Query([Constraint("something", ConstraintType(">", 1))]), ) with mock.patch( "packages.fetchai.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.message_id dialogue_reference = cast(Dict[str, str], cfp_msg.dialogue_reference) fipa_msg.dialogue_starter_reference = dialogue_reference[0] fipa_msg.dialogue_responder_reference = dialogue_reference[1] fipa_msg.target = cfp_msg.target performative = fipa_pb2.FipaMessage.Cfp_Performative() fipa_msg.cfp.CopyFrom(performative) fipa_bytes = fipa_msg.SerializeToString() # The encoded message is not a FIPA message FipaSerializer().decode(fipa_bytes)
def _handle_cfp(self, msg: FipaMessage, 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 dialogue: the dialogue object :return: None """ new_message_id = msg.message_id + 1 new_target = msg.message_id self.context.logger.info("[{}]: received CFP from sender={}".format( self.context.agent_name, msg.counterparty[-5:])) query = cast(Query, msg.query) strategy = cast(Strategy, self.context.strategy) if strategy.is_matching_supply(query): proposal, temp_data = strategy.generate_proposal_and_data( query, msg.counterparty) dialogue.temp_data = temp_data dialogue.proposal = proposal self.context.logger.info( "[{}]: sending a PROPOSE with proposal={} to sender={}".format( self.context.agent_name, proposal.values, msg.counterparty[-5:])) proposal_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FipaMessage.Performative.PROPOSE, proposal=proposal, ) proposal_msg.counterparty = msg.counterparty dialogue.update(proposal_msg) self.context.outbox.put_message( to=msg.counterparty, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(proposal_msg), ) else: self.context.logger.info( "[{}]: declined the CFP from sender={}".format( self.context.agent_name, msg.counterparty[-5:])) decline_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FipaMessage.Performative.DECLINE, ) decline_msg.counterparty = msg.counterparty dialogue.update(decline_msg) self.context.outbox.put_message( to=msg.counterparty, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(decline_msg), )
def test_fipa_decoding_unknown_performative(): """Test that we raise an exception when the performative is unknown during decoding.""" msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.ACCEPT, ) encoded_msg = FipaSerializer().encode(msg) with pytest.raises(ValueError, match="Performative not valid:"): with mock.patch.object(FipaMessage.Performative, "__eq__", return_value=False): FipaSerializer().decode(encoded_msg)
def _handle_accept(self, msg: FipaMessage, dialogue: Dialogue) -> None: """ Handle the ACCEPT. Respond with a MATCH_ACCEPT_W_INFORM which contains the address to send the funds to. :param msg: the message :param dialogue: the dialogue object :return: None """ new_message_id = msg.message_id + 1 new_target = msg.message_id self.context.logger.info("[{}]: received ACCEPT from sender={}".format( self.context.agent_name, msg.counterparty[-5:])) self.context.logger.info( "[{}]: sending MATCH_ACCEPT_W_INFORM to sender={}".format( self.context.agent_name, msg.counterparty[-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_INFORM, info={"address": self.context.agent_addresses[identifier]}, ) dialogue.outgoing_extend(match_accept_msg) self.context.outbox.put_message( to=msg.counterparty, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(match_accept_msg), )
def _handle_match_accept(self, msg: FipaMessage, dialogue: Dialogue) -> None: """ Handle the match accept. :param msg: the message :param dialogue: the dialogue object :return: None """ strategy = cast(Strategy, self.context.strategy) if strategy.is_ledger_tx: self.context.logger.info( "[{}]: received MATCH_ACCEPT_W_INFORM from sender={}".format( self.context.agent_name, msg.counterparty[-5:])) info = msg.info address = cast(str, info.get("address")) proposal = cast(Description, dialogue.proposal) strategy = cast(Strategy, self.context.strategy) tx_msg = TransactionMessage( performative=TransactionMessage.Performative. PROPOSE_FOR_SETTLEMENT, skill_callback_ids=[ PublicId("fetchai", "carpark_client", "0.1.0") ], tx_id="transaction0", tx_sender_addr=self.context.agent_addresses["fetchai"], tx_counterparty_addr=address, tx_amount_by_currency_id={ proposal.values["currency_id"]: -proposal.values["price"] }, tx_sender_fee=strategy.max_buyer_tx_fee, tx_counterparty_fee=proposal.values["seller_tx_fee"], tx_quantities_by_good_id={}, ledger_id=proposal.values["ledger_id"], info={"dialogue_label": dialogue.dialogue_label.json}, ) self.context.decision_maker_message_queue.put_nowait(tx_msg) self.context.logger.info( "[{}]: proposing the transaction to the decision maker. Waiting for confirmation ..." .format(self.context.agent_name)) else: new_message_id = msg.message_id + 1 new_target = msg.message_id inform_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FipaMessage.Performative.INFORM, info={"Done": "Sending payment via bank transfer"}, ) dialogue.outgoing_extend(inform_msg) self.context.outbox.put_message( to=msg.counterparty, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(inform_msg), ) self.context.logger.info( "[{}]: informing counterparty={} of payment.".format( self.context.agent_name, msg.counterparty[-5:]))
def _handle_unidentified_dialogue(self, msg: FipaMessage) -> None: """ Handle an unidentified dialogue. Respond to the sender with a default message containing the appropriate error information. :param msg: the message :return: None """ self.context.logger.info("[{}]: unidentified dialogue.".format( self.context.agent_name)) default_msg = DefaultMessage( dialogue_reference=("", ""), message_id=1, target=0, performative=DefaultMessage.Performative.ERROR, error_code=DefaultMessage.ErrorCode.INVALID_DIALOGUE, error_msg="Invalid dialogue.", error_data={"fipa_message": FipaSerializer().encode(msg)}, ) self.context.outbox.put_message( to=msg.counterparty, sender=self.context.agent_address, protocol_id=DefaultMessage.protocol_id, message=DefaultSerializer().encode(default_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 = propose.message_id + 1 strategy = cast(Strategy, self.context.strategy) proposal_description = propose.proposal self.context.logger.debug("[{}]: on Propose as {}.".format( self.context.agent_name, dialogue.role)) transactions = cast(Transactions, self.context.transactions) transaction_msg = transactions.generate_transaction_message( TransactionMessage.Performative.PROPOSE_FOR_SIGNING, proposal_description, dialogue.dialogue_label, cast(Dialogue.AgentRole, dialogue.role), self.context.agent_address, ) if strategy.is_profitable_transaction(transaction_msg, role=cast( Dialogue.AgentRole, dialogue.role)): self.context.logger.info("[{}]: Accepting propose (as {}).".format( self.context.agent_name, dialogue.role)) transactions.add_locked_tx(transaction_msg, role=cast(Dialogue.AgentRole, dialogue.role)) 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.message_id, ) else: self.context.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.message_id, ) dialogues = cast(Dialogues, self.context.dialogues) dialogues.dialogue_stats.add_dialogue_endstate( Dialogue.EndState.DECLINED_PROPOSE, dialogue.is_self_initiated) fipa_msg.counterparty = propose.counterparty dialogue.update(fipa_msg) self.context.outbox.put_message( to=dialogue.dialogue_label.dialogue_opponent_addr, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(fipa_msg), )
async def test_messages(self): """Test that at the beginning, the search request returns an empty search result.""" msg = FipaMessage( performative=FipaMessage.Performative.CFP, dialogue_reference=(str(0), ""), message_id=1, target=0, query=Query([Constraint("something", ConstraintType(">", 1))]), ) msg_bytes = FipaSerializer().encode(msg) envelope = Envelope( to=DEFAULT_OEF, sender=self.address_1, protocol_id=FipaMessage.protocol_id, message=msg_bytes, ) with pytest.raises(AEAConnectionError): await _make_local_connection( self.address_1, self.node, ).send(envelope) self.multiplexer1.connect() msg = FipaMessage( performative=FipaMessage.Performative.CFP, dialogue_reference=(str(0), str(1)), message_id=1, target=0, query=Query([Constraint("something", ConstraintType(">", 1))]), ) msg_bytes = FipaSerializer().encode(msg) envelope = Envelope( to="this_address_does_not_exist", sender=self.address_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 == DefaultMessage.protocol_id assert response_envelope.sender == DEFAULT_OEF result = DefaultSerializer().decode(response_envelope.message) assert result.performative == DefaultMessage.Performative.ERROR
def _on_accept(self, accept: FipaMessage, dialogue: Dialogue) -> None: """ Handle an Accept. :param accept: the Accept message :param dialogue: the dialogue :return: None """ self.context.logger.debug( "[{}]: on_accept: msg_id={}, dialogue_reference={}, origin={}, target={}" .format( self.context.agent_name, accept.message_id, accept.dialogue_reference, dialogue.dialogue_label.dialogue_opponent_addr, accept.target, )) new_msg_id = accept.message_id + 1 transactions = cast(Transactions, self.context.transactions) transaction_msg = transactions.pop_pending_proposal( dialogue.dialogue_label, accept.target) strategy = cast(Strategy, self.context.strategy) if strategy.is_profitable_transaction(transaction_msg, role=cast( Dialogue.AgentRole, dialogue.role)): self.context.logger.info( "[{}]: locking the current state (as {}).".format( self.context.agent_name, dialogue.role)) transactions.add_locked_tx(transaction_msg, role=cast(Dialogue.AgentRole, dialogue.role)) self.context.decision_maker_message_queue.put(transaction_msg) else: self.context.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.message_id, ) fipa_msg.counterparty = accept.counterparty dialogue.update(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_addr, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(fipa_msg), )
def _handle_cfp(self, msg: FipaMessage, 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 dialogue: the dialogue object :return: None """ new_message_id = msg.message_id + 1 new_target = msg.message_id self.context.logger.info( "[{}]: received CFP from sender={}".format( self.context.agent_name, msg.counterparty[-5:] ) ) if self.context.behaviours.service_registration.is_items_minted: # simply send the same proposal, independent of the query strategy = cast(Strategy, self.context.strategy) contract = cast(ERC1155Contract, self.context.contracts.erc1155) trade_nonce = contract.generate_trade_nonce(self.context.agent_address) token_id = self.context.behaviours.service_registration.token_ids[0] proposal = Description( { "contract_address": contract.instance.address, "token_id": str(token_id), "trade_nonce": str(trade_nonce), "from_supply": str(strategy.from_supply), "to_supply": str(strategy.to_supply), "value": str(strategy.value), } ) dialogue.proposal = proposal 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.logger.info( "[{}]: Sending PROPOSE to agent={}: proposal={}".format( self.context.agent_name, msg.counterparty[-5:], proposal.values ) ) self.context.outbox.put_message( to=msg.counterparty, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(proposal_msg), ) else: self.context.logger.info("Contract items not minted yet. Try again later.")
def handle(self, message: Message) -> None: """ Dispatch message to relevant handler and respond. :param message: the message :return: None """ tx_message = cast(TransactionMessage, message) if (tx_message.performative == TransactionMessage.Performative.SUCCESSFUL_SIGNING): self.context.logger.info( "[{}]: transaction confirmed by decision maker".format( self.context.agent_name)) info = tx_message.info dialogue_label = DialogueLabel.from_json( cast(Dict[str, str], info.get("dialogue_label"))) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] fipa_message = cast(FipaMessage, dialogue.last_incoming_message) if (fipa_message is not None and fipa_message.performative == FipaMessage.Performative.ACCEPT): self.context.logger.info( "[{}]: sending match accept to {}.".format( self.context.agent_name, dialogue.dialogue_label.dialogue_opponent_addr[-5:], )) fipa_msg = FipaMessage( performative=FipaMessage.Performative. MATCH_ACCEPT_W_INFORM, message_id=fipa_message.message_id + 1, dialogue_reference=dialogue.dialogue_label. dialogue_reference, target=fipa_message.message_id, info={ "tx_signature": tx_message.signed_payload.get("tx_signature"), "tx_id": tx_message.tx_id, }, ) dialogue.outgoing_extend(fipa_msg) self.context.outbox.put_message( to=dialogue.dialogue_label.dialogue_opponent_addr, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(fipa_msg), ) else: self.context.logger.warning( "[{}]: last message should be of performative accept.". format(self.context.agent_name)) else: self.context.logger.info( "[{}]: transaction was not successful.".format( self.context.agent_name))
def test_accept(self): """Test that an Accept can be sent correctly.""" accept = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.ACCEPT, ) accept.counterparty = FETCHAI_ADDRESS_TWO self.multiplexer1.put( Envelope( to=FETCHAI_ADDRESS_TWO, sender=FETCHAI_ADDRESS_ONE, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(accept), )) envelope = self.multiplexer2.get(block=True, timeout=2.0) expected_accept = FipaSerializer().decode(envelope.message) expected_accept.counterparty = FETCHAI_ADDRESS_TWO assert expected_accept == accept
def test_decline(self): """Test that a Decline can be sent correctly.""" decline = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.DECLINE, ) decline.counterparty = FETCHAI_ADDRESS_TWO self.multiplexer1.put( Envelope( to=FETCHAI_ADDRESS_TWO, sender=FETCHAI_ADDRESS_ONE, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(decline), )) envelope = self.multiplexer2.get(block=True, timeout=2.0) expected_decline = FipaSerializer().decode(envelope.message) expected_decline.counterparty = FETCHAI_ADDRESS_TWO assert expected_decline == decline
def test_match_accept(self): """Test that a match accept can be sent correctly.""" # NOTE since the OEF SDK doesn't support the match accept, we have to use a fixed message id! match_accept = FipaMessage( message_id=4, dialogue_reference=(str(0), ""), target=3, performative=FipaMessage.Performative.MATCH_ACCEPT, ) match_accept.counterparty = FETCHAI_ADDRESS_TWO self.multiplexer1.put( Envelope( to=FETCHAI_ADDRESS_TWO, sender=FETCHAI_ADDRESS_ONE, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(match_accept), )) envelope = self.multiplexer2.get(block=True, timeout=2.0) expected_match_accept = FipaSerializer().decode(envelope.message) expected_match_accept.counterparty = FETCHAI_ADDRESS_TWO assert expected_match_accept == match_accept
def test_accept_w_inform(self): """Test that an accept with address can be sent correctly.""" accept_w_inform = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.ACCEPT_W_INFORM, info={"address": "my_address"}, ) accept_w_inform.counterparty = FETCHAI_ADDRESS_TWO self.multiplexer1.put( Envelope( to=FETCHAI_ADDRESS_TWO, sender=FETCHAI_ADDRESS_ONE, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(accept_w_inform), )) envelope = self.multiplexer2.get(block=True, timeout=2.0) returned_accept_w_inform = FipaSerializer().decode(envelope.message) returned_accept_w_inform.counterparty = FETCHAI_ADDRESS_TWO assert returned_accept_w_inform == accept_w_inform
def test_inform(self): """Test that an inform can be sent correctly.""" payload = {"foo": "bar"} inform = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.INFORM, info=payload, ) inform.counterparty = FETCHAI_ADDRESS_TWO self.multiplexer1.put( Envelope( to=FETCHAI_ADDRESS_TWO, sender=FETCHAI_ADDRESS_ONE, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(inform), )) envelope = self.multiplexer2.get(block=True, timeout=2.0) returned_inform = FipaSerializer().decode(envelope.message) returned_inform.counterparty = FETCHAI_ADDRESS_TWO assert returned_inform == inform
def test_match_accept_w_inform(self): """Test that a match accept with inform can be sent correctly.""" match_accept_w_inform = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.MATCH_ACCEPT_W_INFORM, info={"address": "my_address"}, ) match_accept_w_inform.counterparty = self.crypto2.address self.multiplexer1.put( Envelope( to=self.crypto2.address, sender=self.crypto1.address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(match_accept_w_inform), ) ) envelope = self.multiplexer2.get(block=True, timeout=2.0) returned_match_accept_w_inform = FipaSerializer().decode(envelope.message) returned_match_accept_w_inform.counterparty = self.crypto2.address assert returned_match_accept_w_inform == match_accept_w_inform
def handle(self, message: Message) -> None: """ Implement the reaction to a message. :param message: the message :return: None """ tx_msg_response = cast(TransactionMessage, message) if ( tx_msg_response is not None and tx_msg_response.performative == TransactionMessage.Performative.SUCCESSFUL_SETTLEMENT ): self.context.logger.info( "[{}]: transaction was successful.".format(self.context.agent_name) ) json_data = {"transaction_digest": tx_msg_response.tx_digest} info = tx_msg_response.info dialogue_label = DialogueLabel.from_json( cast(Dict[str, str], info.get("dialogue_label")) ) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] fipa_msg = cast(FipaMessage, dialogue.last_incoming_message) new_message_id = fipa_msg.message_id + 1 new_target_id = fipa_msg.message_id counterparty_id = dialogue.dialogue_label.dialogue_opponent_addr inform_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target_id, performative=FipaMessage.Performative.INFORM, info=json_data, ) dialogue.outgoing_extend(inform_msg) self.context.outbox.put_message( to=counterparty_id, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(inform_msg), ) self.context.logger.info( "[{}]: informing counterparty={} of transaction digest.".format( self.context.agent_name, counterparty_id[-5:] ) ) self._received_tx_message = True else: self.context.logger.info( "[{}]: transaction was not successful.".format(self.context.agent_name) )
def _handle_search(self, agents: Tuple[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: self.context.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_addr in agents: dialogue = dialogues.create_self_initiated( opponent_addr, self.context.agent_address, not is_searching_for_sellers, ) self.context.logger.info( "[{}]: sending CFP to agent={}".format( self.context.agent_name, opponent_addr[-5:])) fipa_msg = FipaMessage( message_id=Dialogue.STARTING_MESSAGE_ID, dialogue_reference=dialogue.dialogue_label. dialogue_reference, performative=FipaMessage.Performative.CFP, target=Dialogue.STARTING_TARGET, query=query, ) dialogue.outgoing_extend(fipa_msg) self.context.outbox.put_message( to=opponent_addr, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(fipa_msg), ) else: self.context.logger.info( "[{}]: found no {} agents on search_id={}, continue searching." .format(self.context.agent_name, searched_for, search_id))
def test_performative_match_accept(): """Test the serialization - deserialization of the match_accept performative.""" msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.MATCH_ACCEPT, ) msg_bytes = FipaSerializer().encode(msg) envelope = Envelope( to="receiver", sender="sender", protocol_id=FipaMessage.protocol_id, message=msg_bytes, ) msg.counterparty = "sender" 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")
def handle(self, message: Message) -> None: """ Implement the reaction to a message. :param message: the message :return: None """ tx_msg_response = cast(TransactionMessage, message) if ( tx_msg_response.performative == TransactionMessage.Performative.SUCCESSFUL_SIGNING and ( tx_msg_response.tx_id == ERC1155Contract.Performative.CONTRACT_SIGN_HASH_SINGLE.value ) ): tx_signature = tx_msg_response.signed_payload.get("tx_signature") dialogue_label = DialogueLabel.from_json( cast(Dict[str, str], tx_msg_response.info.get("dialogue_label")) ) dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.dialogues[dialogue_label] fipa_msg = cast(FipaMessage, dialogue.last_incoming_message) new_message_id = fipa_msg.message_id + 1 new_target = fipa_msg.message_id counterparty_addr = dialogue.dialogue_label.dialogue_opponent_addr inform_msg = FipaMessage( message_id=new_message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, target=new_target, performative=FipaMessage.Performative.ACCEPT_W_INFORM, info={"tx_signature": tx_signature}, ) self.context.logger.info( "[{}]: sending ACCEPT_W_INFORM to agent={}: tx_signature={}".format( self.context.agent_name, counterparty_addr[-5:], tx_signature ) ) self.context.outbox.put_message( to=counterparty_addr, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(inform_msg), ) else: self.context.logger.info( "[{}]: signing failed: tx_msg_response={}".format( self.context.agent_name, tx_msg_response ) )
def on_cfp( self, msg_id: int, dialogue_id: int, origin: Address, 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 address 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) # nosec except Exception as e: logger.debug( "When trying to unpickle the query the following exception occured: {}".format( e ) ) 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.address, sender=origin, protocol_id=FipaMessage.protocol_id, message=msg_bytes, ) asyncio.run_coroutine_threadsafe( self.in_queue.put(envelope), self.loop ).result()
def test_performative_inform(): """Test the serialization-deserialization of the inform performative.""" msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.INFORM, info={"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")
def test_propose(self): """Test that a Propose can be sent correctly.""" propose_empty = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.PROPOSE, proposal=Description({"foo": "bar"}), ) propose_empty.counterparty = self.crypto2.address self.multiplexer1.put( Envelope( to=self.crypto2.address, sender=self.crypto1.address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(propose_empty), ) ) envelope = self.multiplexer2.get(block=True, timeout=2.0) expected_propose_empty = FipaSerializer().decode(envelope.message) expected_propose_empty.counterparty = self.crypto2.address assert expected_propose_empty == propose_empty propose_descriptions = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.PROPOSE, proposal=Description( {"foo": "bar"}, DataModel("foobar", [Attribute("foo", str, True)]) ), ) propose_descriptions.counterparty = self.crypto2.address self.multiplexer1.put( Envelope( to=self.crypto2.address, sender=self.crypto1.address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(propose_descriptions), ) ) envelope = self.multiplexer2.get(block=True, timeout=2.0) expected_propose_descriptions = FipaSerializer().decode(envelope.message) expected_propose_descriptions.counterparty = self.crypto2.address assert expected_propose_descriptions == propose_descriptions
def _handle_search(self, agents: Tuple[str, ...]) -> None: """ Handle the search response. :param agents: the agents returned by the search :return: None """ if len(agents) > 0: self.context.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_addr = agents[0] dialogues = cast(Dialogues, self.context.dialogues) dialogue = dialogues.create_self_initiated( opponent_addr, self.context.agent_address, is_seller=False ) query = strategy.get_service_query() self.context.logger.info( "[{}]: sending CFP to agent={}".format( self.context.agent_name, opponent_addr[-5:] ) ) cfp_msg = FipaMessage( message_id=Dialogue.STARTING_MESSAGE_ID, dialogue_reference=dialogue.dialogue_label.dialogue_reference, performative=FipaMessage.Performative.CFP, target=Dialogue.STARTING_TARGET, query=query, ) dialogue.outgoing_extend(cfp_msg) self.context.outbox.put_message( to=opponent_addr, sender=self.context.agent_address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(cfp_msg), ) else: self.context.logger.info( "[{}]: found no agents, continue searching.".format( self.context.agent_name ) )