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.counterparty = "receiver" envelope = Envelope( to="receiver", sender="sender", protocol_id=FipaMessage.protocol_id, message=msg, ) msg.counterparty = "receiver" envelope_bytes = envelope.encode() actual_envelope = Envelope.decode(envelope_bytes) expected_envelope = envelope assert expected_envelope.to == actual_envelope.to assert expected_envelope.sender == actual_envelope.sender assert expected_envelope.protocol_id == actual_envelope.protocol_id assert expected_envelope.message != actual_envelope.message actual_msg = FipaMessage.serializer.decode(actual_envelope.message) actual_msg.counterparty = actual_envelope.to 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 _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 _handle_propose( self, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue ) -> None: """ Handle the propose. :param fipa_msg: the message :param fipa_dialogue: the dialogue object :return: None """ self.context.logger.info( "received proposal={} from sender={}".format( fipa_msg.proposal.values, fipa_msg.counterparty[-5:], ) ) strategy = cast(GenericStrategy, self.context.strategy) acceptable = strategy.is_acceptable_proposal(fipa_msg.proposal) affordable = strategy.is_affordable_proposal(fipa_msg.proposal) if acceptable and affordable: self.context.logger.info( "accepting the proposal from sender={}".format( fipa_msg.counterparty[-5:] ) ) terms = strategy.terms_from_proposal( fipa_msg.proposal, fipa_msg.counterparty ) fipa_dialogue.terms = terms accept_msg = FipaMessage( message_id=fipa_msg.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label.dialogue_reference, target=fipa_msg.message_id, performative=FipaMessage.Performative.ACCEPT, ) accept_msg.counterparty = fipa_msg.counterparty fipa_dialogue.update(accept_msg) self.context.outbox.put_message(message=accept_msg) else: self.context.logger.info( "declining the proposal from sender={}".format( fipa_msg.counterparty[-5:] ) ) decline_msg = FipaMessage( message_id=fipa_msg.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label.dialogue_reference, target=fipa_msg.message_id, performative=FipaMessage.Performative.DECLINE, ) decline_msg.counterparty = fipa_msg.counterparty fipa_dialogue.update(decline_msg) self.context.outbox.put_message(message=decline_msg)
def _handle_cfp(self, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue) -> None: """ Handle the CFP. If the CFP matches the supplied services then send a PROPOSE, otherwise send a DECLINE. :param fipa_msg: the message :param fipa_dialogue: the dialogue object :return: None """ self.context.logger.info("[{}]: received CFP from sender={}".format( self.context.agent_name, fipa_msg.counterparty[-5:])) strategy = cast(GenericStrategy, self.context.strategy) if strategy.is_matching_supply(fipa_msg.query): proposal, terms, data_for_sale = strategy.generate_proposal_terms_and_data( fipa_msg.query, fipa_msg.counterparty) fipa_dialogue.data_for_sale = data_for_sale fipa_dialogue.terms = terms self.context.logger.info( "[{}]: sending a PROPOSE with proposal={} to sender={}".format( self.context.agent_name, proposal.values, fipa_msg.counterparty[-5:])) proposal_msg = FipaMessage( performative=FipaMessage.Performative.PROPOSE, message_id=fipa_msg.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label. dialogue_reference, target=fipa_msg.message_id, proposal=proposal, ) proposal_msg.counterparty = fipa_msg.counterparty fipa_dialogue.update(proposal_msg) self.context.outbox.put_message(message=proposal_msg) else: self.context.logger.info( "[{}]: declined the CFP from sender={}".format( self.context.agent_name, fipa_msg.counterparty[-5:])) decline_msg = FipaMessage( message_id=fipa_msg.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label. dialogue_reference, target=fipa_msg.message_id, performative=FipaMessage.Performative.DECLINE, ) decline_msg.counterparty = fipa_msg.counterparty fipa_dialogue.update(decline_msg) self.context.outbox.put_message(message=decline_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) tx_msg = TransactionMessage( performative=TransactionMessage.Performative. PROPOSE_FOR_SETTLEMENT, skill_callback_ids=[self.context.skill_id], tx_id="transaction0", tx_sender_addr=self.context.agent_addresses[ proposal.values["ledger_id"]], 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}, tx_nonce=proposal.values["tx_nonce"], ) 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"}, ) inform_msg.counterparty = msg.counterparty dialogue.update(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_transaction_digest( self, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue) -> None: """ Handle a message of transaction_digest performative. :param ledger_api_message: the ledger api message :param ledger_api_dialogue: the ledger api dialogue """ fipa_dialogue = ledger_api_dialogue.associated_fipa_dialogue self.context.logger.info( "transaction was successfully submitted. Transaction digest={}". format(ledger_api_msg.transaction_digest)) fipa_msg = cast(Optional[FipaMessage], fipa_dialogue.last_incoming_message) assert fipa_msg is not None, "Could not retrieve fipa message" inform_msg = FipaMessage( performative=FipaMessage.Performative.INFORM, message_id=fipa_msg.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label.dialogue_reference, target=fipa_msg.message_id, info={ "transaction_digest": ledger_api_msg.transaction_digest.body }, ) inform_msg.counterparty = fipa_dialogue.dialogue_label.dialogue_opponent_addr fipa_dialogue.update(inform_msg) self.context.outbox.put_message(message=inform_msg) self.context.logger.info( "informing counterparty={} of transaction digest.".format( fipa_dialogue.dialogue_label.dialogue_opponent_addr[-5:], ))
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]}, ) match_accept_msg.counterparty = msg.counterparty dialogue.update(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 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.counterparty = "receiver" envelope = Envelope( to="receiver", sender="sender", protocol_id=FipaMessage.protocol_id, message=msg, ) envelope_bytes = envelope.encode() actual_envelope = Envelope.decode(envelope_bytes) expected_envelope = envelope assert expected_envelope.to == actual_envelope.to assert expected_envelope.sender == actual_envelope.sender assert expected_envelope.protocol_id == actual_envelope.protocol_id assert expected_envelope.message != actual_envelope.message actual_msg = FipaMessage.serializer.decode(actual_envelope.message) actual_msg.counterparty = actual_envelope.to expected_msg = msg assert expected_msg == actual_msg
def _handle_search(self, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue) -> None: """ Handle the search response. :param agents: the agents returned by the search :return: None """ if len(oef_search_msg.agents) == 0: self.context.logger.info( "[{}]: found no agents, continue searching.".format( self.context.agent_name)) return self.context.logger.info( "[{}]: found agents={}, stopping search.".format( self.context.agent_name, list(map(lambda x: x[-5:], oef_search_msg.agents)), )) strategy = cast(Strategy, self.context.strategy) strategy.is_searching = False query = strategy.get_service_query() fipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues) for opponent_address in oef_search_msg.agents: cfp_msg = FipaMessage( dialogue_reference=fipa_dialogues. new_self_initiated_dialogue_reference(), performative=FipaMessage.Performative.CFP, query=query, ) cfp_msg.counterparty = opponent_address fipa_dialogues.update(cfp_msg) self.context.logger.info("[{}]: sending CFP to agent={}".format( self.context.agent_name, opponent_address[-5:])) self.context.outbox.put_message(message=cfp_msg)
def _handle_signed_message(self, signing_msg: SigningMessage, signing_dialogue: SigningDialogue) -> None: """ Handle a signed message. :param signing_msg: the signing message :param signing_dialogue: the dialogue :return: None """ fipa_dialogue = (signing_dialogue.associated_contract_api_dialogue. associated_fipa_dialogue) last_fipa_msg = fipa_dialogue.last_incoming_message assert last_fipa_msg is not None, "Could not retrieve last fipa message." inform_msg = FipaMessage( message_id=last_fipa_msg.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label.dialogue_reference, target=last_fipa_msg.message_id, performative=FipaMessage.Performative.ACCEPT_W_INFORM, info={"tx_signature": signing_msg.signed_message.body}, ) inform_msg.counterparty = last_fipa_msg.counterparty self.context.logger.info( "[{}]: sending ACCEPT_W_INFORM to agent={}: tx_signature={}". format( self.context.agent_name, last_fipa_msg.counterparty[-5:], signing_msg.signed_message, )) self.context.outbox.put_message(message=inform_msg)
def _handle_accept(self, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue) -> None: """ Handle the ACCEPT. Respond with a MATCH_ACCEPT_W_INFORM which contains the address to send the funds to. :param fipa_msg: the message :param fipa_dialogue: the dialogue object :return: None """ self.context.logger.info("[{}]: received ACCEPT from sender={}".format( self.context.agent_name, fipa_msg.counterparty[-5:])) match_accept_msg = FipaMessage( performative=FipaMessage.Performative.MATCH_ACCEPT_W_INFORM, message_id=fipa_msg.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label.dialogue_reference, target=fipa_msg.message_id, info={"address": fipa_dialogue.terms.sender_address}, ) self.context.logger.info( "[{}]: sending MATCH_ACCEPT_W_INFORM to sender={} with info={}". format( self.context.agent_name, fipa_msg.counterparty[-5:], match_accept_msg.info, )) match_accept_msg.counterparty = fipa_msg.counterparty fipa_dialogue.update(match_accept_msg) self.context.outbox.put_message(message=match_accept_msg)
def _handle_search(self, oef_search_msg: OefSearchMessage, oef_search_dialogue: OefSearchDialogue) -> None: """ Handle the search response. :param agents: the agents returned by the search :return: None """ if len(oef_search_msg.agents) == 0: self.context.logger.info("found no agents, continue searching.") return self.context.logger.info("found agents={}, stopping search.".format( list(map(lambda x: x[-5:], oef_search_msg.agents)), )) strategy = cast(GenericStrategy, self.context.strategy) strategy.is_searching = False # stopping search query = strategy.get_service_query() fipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues) for idx, counterparty in enumerate(oef_search_msg.agents): if idx >= strategy.max_negotiations: continue cfp_msg = FipaMessage( performative=FipaMessage.Performative.CFP, dialogue_reference=fipa_dialogues. new_self_initiated_dialogue_reference(), query=query, ) cfp_msg.counterparty = counterparty fipa_dialogues.update(cfp_msg) self.context.outbox.put_message(message=cfp_msg) self.context.logger.info("sending CFP to agent={}".format( counterparty[-5:]))
def test_performative_accept_with_inform(): """Test the serialization - deserialization of the accept_with_address performative.""" msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.ACCEPT_W_INFORM, info={"address": "dummy_address"}, ) msg.counterparty = "receiver" envelope = Envelope( to="receiver", sender="sender", protocol_id=FipaMessage.protocol_id, message=msg, ) envelope_bytes = envelope.encode() actual_envelope = Envelope.decode(envelope_bytes) expected_envelope = envelope assert expected_envelope.to == actual_envelope.to assert expected_envelope.sender == actual_envelope.sender assert expected_envelope.protocol_id == actual_envelope.protocol_id assert expected_envelope.message != actual_envelope.message actual_msg = FipaMessage.serializer.decode(actual_envelope.message) actual_msg.counterparty = actual_envelope.to expected_msg = msg assert expected_msg == actual_msg
def test_error_decoding_error(self): """Test the decoding error.""" self.my_aea._inbox._history = [] msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.ACCEPT, ) msg.counterparty = self.address envelope = Envelope( to=self.address, sender=self.address, protocol_id=DefaultMessage.protocol_id, message=msg, ) self.my_error_handler.send_decoding_error(envelope) wait_for_condition(lambda: len(self.my_aea._inbox._history) >= 1, timeout=5) envelope = self.my_aea._inbox._history[-1] msg = envelope.message assert msg.performative == DefaultMessage.Performative.ERROR assert msg.error_code == DefaultMessage.ErrorCode.DECODING_ERROR
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_error_unsupported_skill(self): """Test the unsupported skill.""" msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.ACCEPT, ) msg.counterparty = self.address msg.sender = self.address envelope = Envelope( to=msg.counterparty, sender=msg.sender, protocol_id=msg.protocol_id, message=msg, ) self.my_error_handler.send_unsupported_skill(envelope=envelope) wait_for_condition(lambda: len(self.my_aea._inbox._history) >= 1, timeout=5) envelope = self.my_aea._inbox._history[-1] msg = envelope.message assert msg.performative == DefaultMessage.Performative.ERROR assert msg.error_code == DefaultMessage.ErrorCode.UNSUPPORTED_SKILL
def test_fipa_cfp_serialization_bytes(): """Test that the serialization - deserialization 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.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 deserialised_msg = FipaSerializer().decode(envelope.message) deserialised_msg.counterparty = "sender" assert msg.get("performative") == deserialised_msg.get("performative")
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), )
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 _on_propose(self, propose: FipaMessage, fipa_dialogue: FipaDialogue) -> None: """ Handle a Propose. :param propose: the message containing the Propose :param fipa_dialogue: the fipa_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( fipa_dialogue.role)) transactions = cast(Transactions, self.context.transactions) signing_msg = transactions.generate_signing_message( SigningMessage.Performative.SIGN_MESSAGE, proposal_description, fipa_dialogue.dialogue_label, cast(FipaDialogue.Role, fipa_dialogue.role), self.context.agent_address, ) if strategy.is_profitable_transaction(signing_msg, role=cast( FipaDialogue.Role, fipa_dialogue.role)): self.context.logger.info("accepting propose (as {}).".format( fipa_dialogue.role)) transactions.add_locked_tx(signing_msg, role=cast(FipaDialogue.Role, fipa_dialogue.role)) transactions.add_pending_initial_acceptance( fipa_dialogue.dialogue_label, new_msg_id, signing_msg) fipa_msg = FipaMessage( performative=FipaMessage.Performative.ACCEPT, message_id=new_msg_id, dialogue_reference=fipa_dialogue.dialogue_label. dialogue_reference, target=propose.message_id, ) else: self.context.logger.info("declining propose (as {})".format( fipa_dialogue.role)) fipa_msg = FipaMessage( performative=FipaMessage.Performative.DECLINE, message_id=new_msg_id, dialogue_reference=fipa_dialogue.dialogue_label. dialogue_reference, target=propose.message_id, ) fipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues) fipa_dialogues.dialogue_stats.add_dialogue_endstate( FipaDialogue.EndState.DECLINED_PROPOSE, fipa_dialogue.is_self_initiated) fipa_msg.counterparty = propose.counterparty fipa_dialogue.update(fipa_msg) self.context.outbox.put_message(message=fipa_msg)
def _handle_match_accept( self, fipa_msg: FipaMessage, fipa_dialogue: FipaDialogue ) -> None: """ Handle the match accept. :param fipa_msg: the message :param fipa_dialogue: the dialogue object :return: None """ self.context.logger.info( "received MATCH_ACCEPT_W_INFORM from sender={} with info={}".format( fipa_msg.counterparty[-5:], fipa_msg.info ) ) strategy = cast(GenericStrategy, self.context.strategy) if strategy.is_ledger_tx: transfer_address = fipa_msg.info.get("address", None) if transfer_address is not None and isinstance(transfer_address, str): fipa_dialogue.terms.counterparty_address = transfer_address ledger_api_dialogues = cast( LedgerApiDialogues, self.context.ledger_api_dialogues ) ledger_api_msg = LedgerApiMessage( performative=LedgerApiMessage.Performative.GET_RAW_TRANSACTION, dialogue_reference=ledger_api_dialogues.new_self_initiated_dialogue_reference(), terms=fipa_dialogue.terms, ) ledger_api_msg.counterparty = LEDGER_API_ADDRESS ledger_api_dialogue = cast( Optional[LedgerApiDialogue], ledger_api_dialogues.update(ledger_api_msg) ) assert ( ledger_api_dialogue is not None ), "Error when creating ledger api dialogue." ledger_api_dialogue.associated_fipa_dialogue = fipa_dialogue fipa_dialogue.associated_ledger_api_dialogue = ledger_api_dialogue self.context.outbox.put_message(message=ledger_api_msg) self.context.logger.info( "requesting transfer transaction from ledger api..." ) else: inform_msg = FipaMessage( message_id=fipa_msg.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label.dialogue_reference, target=fipa_msg.message_id, performative=FipaMessage.Performative.INFORM, info={"Done": "Sending payment via bank transfer"}, ) inform_msg.counterparty = fipa_msg.counterparty fipa_dialogue.update(inform_msg) self.context.outbox.put_message(message=inform_msg) self.context.logger.info( "informing counterparty={} of payment.".format( fipa_msg.counterparty[-5:] ) )
def test_cfp(self): """Test that a CFP can be sent correctly.""" cfp_message = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.CFP, query=Query([Constraint("something", ConstraintType(">", 1))]), ) cfp_message.counterparty = self.crypto2.address self.multiplexer1.put( Envelope( to=self.crypto2.address, sender=self.crypto1.address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(cfp_message), ) ) envelope = self.multiplexer2.get(block=True, timeout=5.0) expected_cfp_message = FipaSerializer().decode(envelope.message) expected_cfp_message.counterparty = self.crypto2.address assert expected_cfp_message == cfp_message cfp_none = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.CFP, query=Query([Constraint("something", ConstraintType(">", 1))]), ) cfp_none.counterparty = self.crypto2.address self.multiplexer1.put( Envelope( to=self.crypto2.address, sender=self.crypto1.address, protocol_id=FipaMessage.protocol_id, message=FipaSerializer().encode(cfp_none), ) ) envelope = self.multiplexer2.get(block=True, timeout=5.0) expected_cfp_none = FipaSerializer().decode(envelope.message) expected_cfp_none.counterparty = self.crypto2.address assert expected_cfp_none == cfp_none
def test_error_handler_handle(self): """Test the handle function.""" msg = FipaMessage( message_id=1, dialogue_reference=(str(0), ""), target=0, performative=FipaMessage.Performative.ACCEPT, ) msg.counterparty = "a_counterparty" self.my_error_handler.handle(message=msg)
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), )
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=self.dialogues.new_self_initiated_dialogue_reference(), query=Query([Constraint("something", ConstraintType(">", 1))]), ) msg.counterparty = str(OEFLocalConnection.connection_id) sending_dialogue = cast(Optional[FipaDialogue], self.dialogues.update(msg)) assert sending_dialogue is not None envelope = Envelope( to=msg.counterparty, sender=msg.sender, protocol_id=msg.protocol_id, message=msg, ) 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=self.dialogues.new_self_initiated_dialogue_reference(), query=Query([Constraint("something", ConstraintType(">", 1))]), ) msg.counterparty = "this_address_does_not_exist" sending_dialogue = cast(Optional[FipaDialogue], self.dialogues.update(msg)) assert sending_dialogue is not None envelope = Envelope( to=msg.counterparty, sender=msg.sender, protocol_id=msg.protocol_id, message=msg, ) 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 == str(OEFLocalConnection.connection_id) result = response_envelope.message assert result.performative == DefaultMessage.Performative.ERROR
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, ) proposal_msg.counterparty = msg.counterparty dialogue.update(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_transaction_receipt( self, ledger_api_msg: LedgerApiMessage, ledger_api_dialogue: LedgerApiDialogue) -> None: """ Handle a message of balance performative. :param ledger_api_message: the ledger api message :param ledger_api_dialogue: the ledger api dialogue """ fipa_dialogue = ledger_api_dialogue.associated_fipa_dialogue is_settled = LedgerApis.is_transaction_settled( fipa_dialogue.terms.ledger_id, ledger_api_msg.transaction_receipt.receipt) is_valid = LedgerApis.is_transaction_valid( fipa_dialogue.terms.ledger_id, ledger_api_msg.transaction_receipt.transaction, fipa_dialogue.terms.sender_address, fipa_dialogue.terms.counterparty_address, fipa_dialogue.terms.nonce, fipa_dialogue.terms.counterparty_payable_amount, ) if is_settled and is_valid: last_message = cast(Optional[FipaMessage], fipa_dialogue.last_incoming_message) assert last_message is not None, "Cannot retrieve last fipa message." inform_msg = FipaMessage( message_id=last_message.message_id + 1, dialogue_reference=fipa_dialogue.dialogue_label. dialogue_reference, target=last_message.message_id, performative=FipaMessage.Performative.INFORM, info=fipa_dialogue.data_for_sale, ) inform_msg.counterparty = last_message.counterparty fipa_dialogue.update(inform_msg) self.context.outbox.put_message(message=inform_msg) fipa_dialogues = cast(FipaDialogues, self.context.fipa_dialogues) fipa_dialogues.dialogue_stats.add_dialogue_endstate( FipaDialogue.EndState.SUCCESSFUL, fipa_dialogue.is_self_initiated) self.context.logger.info( "[{}]: transaction confirmed, sending data={} to buyer={}.". format( self.context.agent_name, fipa_dialogue.data_for_sale, last_message.counterparty[-5:], )) else: self.context.logger.info( "[{}]: transaction_receipt={} not settled or not valid, aborting" .format(self.context.agent_name, ledger_api_msg.transaction_receipt))
def _handle_search(self, agents: Tuple[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() self.context.logger.info( "[{}]: found agents={}, stopping search.".format( self.context.agent_name, list(map(lambda x: x[-5:], agents)) ) ) # pick first agent found opponent_addr = agents[0] dialogues = cast(Dialogues, self.context.dialogues) 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=dialogues.new_self_initiated_dialogue_reference(), performative=FipaMessage.Performative.CFP, target=Dialogue.STARTING_TARGET, query=query, ) cfp_msg.counterparty = opponent_addr dialogues.update(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 ) ) strategy.on_search_failed()
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, is_search_query=False) for opponent_addr in agents: 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=dialogues. new_self_initiated_dialogue_reference(), performative=FipaMessage.Performative.CFP, target=Dialogue.STARTING_TARGET, query=query, ) fipa_msg.counterparty = opponent_addr dialogues.update(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))