def test_raw_message_encode_decode(): """Test encoding and decoding of raw_message.""" class RawMessageProtobufObject: raw_message_bytes = b"" ledger_id = "some_ledger" body = b"body" rm = RawMessage(ledger_id, body) RawMessage.encode(RawMessageProtobufObject, rm) recovered_rm = RawMessage.decode(RawMessageProtobufObject) assert rm == recovered_rm
def test_handle_message_signing_ethereum_deprecated(self): """Test message signing for ethereum deprecated.""" message = b"0x11f3f9487724404e3a1fb7252a3226" signing_dialogues = SigningDialogues( str(PublicId("author", "a_skill", "0.1.0"))) signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=signing_dialogues. new_self_initiated_dialogue_reference(), terms=Terms( ledger_id=ETHEREUM, sender_address="pk1", counterparty_address="pk2", amount_by_currency_id={"FET": -1}, is_sender_payable_tx_fee=True, quantities_by_good_id={"good_id": 10}, nonce="transaction nonce", ), raw_message=RawMessage(ETHEREUM, message, is_deprecated_mode=True), ) signing_dialogue = signing_dialogues.create_with_message( "decision_maker", signing_msg) assert signing_dialogue is not None self.decision_maker.message_in_queue.put_nowait(signing_msg) signing_msg_response = self.decision_maker.message_out_queue.get( timeout=2) recovered_dialogue = signing_dialogues.update(signing_msg_response) assert recovered_dialogue is not None and recovered_dialogue == signing_dialogue assert (signing_msg_response.performative == SigningMessage.Performative.SIGNED_MESSAGE) assert type(signing_msg_response.signed_message) == SignedMessage assert signing_msg_response.signed_message.is_deprecated_mode
def test_handle_message_signing_ethereum(self): """Test message signing for ethereum.""" message = b"0x11f3f9487724404e3a1fb7252a322656b90ba0455a2ca5fcdcbe6eeee5f8126d" signing_dialogues = SigningDialogues("agent4") signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(), skill_callback_ids=(str(PublicId("author", "a_skill", "0.1.0")),), skill_callback_info={}, terms=Terms( ledger_id=ETHEREUM, sender_address="pk1", counterparty_address="pk2", amount_by_currency_id={"FET": -1}, is_sender_payable_tx_fee=True, quantities_by_good_id={"good_id": 10}, nonce="transaction nonce", ), raw_message=RawMessage(ETHEREUM, message), ) signing_msg.counterparty = "decision_maker" self.decision_maker.message_in_queue.put_nowait(signing_msg) signing_msg_response = self.decision_maker.message_out_queue.get(timeout=2) assert ( signing_msg_response.performative == SigningMessage.Performative.SIGNED_MESSAGE ) assert signing_msg_response.skill_callback_ids == signing_msg.skill_callback_ids assert type(signing_msg_response.signed_message) == SignedMessage
def _handle_raw_message( self, contract_api_msg: ContractApiMessage, contract_api_dialogue: ContractApiDialogue, ) -> None: """ Handle a message of raw_message performative. :param contract_api_message: the ledger api message :param contract_api_dialogue: the ledger api dialogue """ self.context.logger.info( "received raw message={}".format(contract_api_msg)) signing_dialogues = cast(SigningDialogues, self.context.signing_dialogues) signing_msg, signing_dialogue = signing_dialogues.create( counterparty=self.context.decision_maker_address, performative=SigningMessage.Performative.SIGN_MESSAGE, raw_message=RawMessage( contract_api_msg.raw_message.ledger_id, contract_api_msg.raw_message.body, is_deprecated_mode=True, ), terms=contract_api_dialogue.associated_fipa_dialogue.terms, ) signing_dialogue = cast(SigningDialogue, signing_dialogue) signing_dialogue.associated_fipa_dialogue = ( contract_api_dialogue.associated_fipa_dialogue) self.context.decision_maker_message_queue.put_nowait(signing_msg) self.context.logger.info( "proposing the message to the decision maker. Waiting for confirmation ..." )
def get_raw_message( self, api: LedgerApi, message: ContractApiMessage, dialogue: ContractApiDialogue, ) -> ContractApiMessage: """ Send the request 'get_raw_message'. :param api: the API object. :param message: the Ledger API message :param dialogue: the contract API dialogue :return: None """ contract = self.contract_registry.make(message.contract_id) method_to_call = getattr(contract, message.callable) try: rm = method_to_call(api, message.contract_address, **message.kwargs.body) response = ContractApiMessage( performative=ContractApiMessage.Performative.RAW_MESSAGE, message_id=message.message_id + 1, target=message.message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, raw_message=RawMessage(message.ledger_id, rm), ) response.counterparty = message.counterparty dialogue.update(response) except Exception as e: # pylint: disable=broad-except # pragma: nocover response = self.get_error_message(e, api, message, dialogue) return response
def test_handle_message_signing_unknown_and_two_dialogues(self): """Test message signing for unknown.""" message = b"0x11f3f9487724404e3a1fb7252a322656b90ba0455a2ca5fcdcbe6eeee5f8126d" signing_dialogues = SigningDialogues("agent") signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(), skill_callback_ids=(str(PublicId("author", "a_skill", "0.1.0")),), skill_callback_info={}, terms=Terms( ledger_id="unknown", sender_address="pk1", counterparty_address="pk2", amount_by_currency_id={"FET": -1}, is_sender_payable_tx_fee=True, quantities_by_good_id={"good_id": 10}, nonce="transaction nonce", ), raw_message=RawMessage("unknown", message), ) signing_msg.counterparty = "decision_maker" signing_dialogue = signing_dialogues.update(signing_msg) assert signing_dialogue is not None self.decision_maker.message_in_queue.put_nowait(signing_msg) signing_msg_response = self.decision_maker.message_out_queue.get(timeout=2) signing_msg_response.counterparty = signing_msg.counterparty signing_msg_response.is_incoming = True recovered_dialogue = signing_dialogues.update(signing_msg_response) assert recovered_dialogue is not None and recovered_dialogue == signing_dialogue assert signing_msg_response.performative == SigningMessage.Performative.ERROR assert signing_msg_response.skill_callback_ids == signing_msg.skill_callback_ids assert ( signing_msg_response.error_code == SigningMessage.ErrorCode.UNSUCCESSFUL_MESSAGE_SIGNING )
def setup(self) -> None: """ Implement the setup. :return: None """ strategy = cast(Strategy, self.context.strategy) signing_dialogues = cast(SigningDialogues, self.context.signing_dialogues) msg, _ = signing_dialogues.create( counterparty=self.context.decision_maker_address, performative=SigningMessage.Performative.SIGN_MESSAGE, raw_message=RawMessage(strategy.ledger_id, strategy.ethereum_address.encode("utf-8")), terms=Terms( ledger_id=strategy.ledger_id, sender_address="", counterparty_address="", amount_by_currency_id={}, quantities_by_good_id={}, nonce="", ), ) self.context.logger.info("sending signing_msg to decision maker...") self.context.decision_maker_message_queue.put_nowait(msg)
def build_response(rm: bytes) -> ContractApiMessage: return ContractApiMessage( performative=ContractApiMessage.Performative.RAW_MESSAGE, message_id=message.message_id + 1, target=message.message_id, dialogue_reference=dialogue.dialogue_label.dialogue_reference, raw_message=RawMessage(message.ledger_id, rm), )
def build_response( rm: bytes, dialogue: ContractApiDialogue ) -> ContractApiMessage: return cast( ContractApiMessage, dialogue.reply( performative=ContractApiMessage.Performative.RAW_MESSAGE, raw_message=RawMessage(message.ledger_id, rm), ), )
def test_sign_message(self): """Test for an error for a sign transaction message.""" tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, terms=self.terms, raw_message=RawMessage(self.ledger_id, "message"), ) assert tx_msg._is_consistent() encoded_tx_msg = tx_msg.encode() decoded_tx_msg = tx_msg.serializer.decode(encoded_tx_msg) assert tx_msg == decoded_tx_msg
def test_init_raw_message(): """Test the raw_message object initialization.""" ledger_id = "some_ledger" body = b"body" rm = RawMessage(ledger_id, body) assert rm.ledger_id == ledger_id assert rm.body == body assert not rm.is_deprecated_mode assert ( str(rm) == f"RawMessage: ledger_id=some_ledger, body={body}, is_deprecated_mode=False" ) assert rm == rm
def build_response( rm: Union[bytes, JSONLike], dialogue: ContractApiDialogue) -> ContractApiMessage: if not isinstance(rm, bytes): raise ValueError( f"Invalid message type, got={type(rm)}, expected=bytes.") return cast( ContractApiMessage, dialogue.reply( performative=ContractApiMessage.Performative.RAW_MESSAGE, raw_message=RawMessage(message.ledger_id, rm), ), )
def test_message_consistency(self): """Test for an error in consistency of a message.""" tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_TRANSACTION, skill_callback_ids=self.skill_callback_ids, skill_callback_info=self.skill_callback_info, terms=self.terms, raw_transaction=RawTransaction(self.ledger_id, "transaction"), ) assert tx_msg._is_consistent() tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, skill_callback_ids=self.skill_callback_ids, skill_callback_info=self.skill_callback_info, terms=self.terms, raw_message=RawMessage(self.ledger_id, "message"), ) assert tx_msg._is_consistent() tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGNED_TRANSACTION, message_id=2, target=1, skill_callback_ids=self.skill_callback_ids, skill_callback_info=self.skill_callback_info, signed_transaction=SignedTransaction(self.ledger_id, "signature"), ) assert tx_msg._is_consistent() tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGNED_MESSAGE, message_id=2, target=1, skill_callback_ids=self.skill_callback_ids, skill_callback_info=self.skill_callback_info, signed_message=SignedMessage(self.ledger_id, "message"), ) assert tx_msg._is_consistent() tx_msg = SigningMessage( performative=SigningMessage.Performative.ERROR, message_id=2, target=1, skill_callback_ids=self.skill_callback_ids, skill_callback_info=self.skill_callback_info, error_code=SigningMessage.ErrorCode.UNSUCCESSFUL_MESSAGE_SIGNING, ) assert tx_msg._is_consistent() assert str(tx_msg.performative) == "error" assert len(tx_msg.valid_performatives) == 5
def _handle_raw_message( self, contract_api_msg: ContractApiMessage, contract_api_dialogue: ContractApiDialogue, ) -> None: """ Handle a message of raw_message performative. :param contract_api_message: the ledger api message :param contract_api_dialogue: the ledger api dialogue """ self.context.logger.info("[{}]: received raw message={}".format( self.context.agent_name, contract_api_msg)) signing_dialogues = cast(SigningDialogues, self.context.signing_dialogues) signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=signing_dialogues. new_self_initiated_dialogue_reference(), skill_callback_ids=(str(self.context.skill_id), ), raw_message=RawMessage( contract_api_msg.raw_message.ledger_id, contract_api_msg.raw_message.body, is_deprecated_mode=True, ), terms=contract_api_dialogue.terms, skill_callback_info={}, ) signing_msg.counterparty = "decision_maker" signing_dialogue = cast(Optional[SigningDialogue], signing_dialogues.update(signing_msg)) assert signing_dialogue is not None, "Error when creating signing dialogue." signing_dialogue.associated_contract_api_dialogue = contract_api_dialogue self.context.decision_maker_message_queue.put_nowait(signing_msg) self.context.logger.info( "[{}]: proposing the transaction to the decision maker. Waiting for confirmation ..." .format(self.context.agent_name))
def generate_signing_message( self, performative: SigningMessage.Performative, proposal_description: Description, dialogue_label: DialogueLabel, role: FipaDialogue.Role, agent_addr: Address, ) -> SigningMessage: """ Generate the transaction message from the description and the dialogue. :param proposal_description: the description of the proposal :param dialogue_label: the dialogue label :param role: the role of the agent (seller or buyer) :param agent_addr: the address of the agent :return: a transaction message """ is_seller = role == FipaDialogue.Role.SELLER goods_component = copy.copy(proposal_description.values) [ # pylint: disable=expression-not-assigned goods_component.pop(key) for key in ["fee", "price", "currency_id", "nonce", "ledger_id"] ] # switch signs based on whether seller or buyer role amount = (proposal_description.values["price"] if is_seller else -proposal_description.values["price"]) fee = proposal_description.values["fee"] if is_seller: for good_id in goods_component.keys(): goods_component[good_id] = goods_component[good_id] * (-1) amount_by_currency_id = { proposal_description.values["currency_id"]: amount } fee_by_currency_id = {proposal_description.values["currency_id"]: fee} nonce = proposal_description.values["nonce"] ledger_id = proposal_description.values["ledger_id"] terms = Terms( ledger_id=ledger_id, sender_address=agent_addr, counterparty_address=dialogue_label.dialogue_opponent_addr, amount_by_currency_id=amount_by_currency_id, is_sender_payable_tx_fee=not is_seller, quantities_by_good_id=goods_component, nonce=nonce, fee_by_currency_id=fee_by_currency_id, ) skill_callback_ids = (str(self.context.skill_id), ) signing_dialogues = cast(SigningDialogues, self.context.signing_dialogues) skill_callback_info = {"dialogue_label": str(dialogue_label)} raw_message = RawMessage(ledger_id=ledger_id, body=terms.sender_hash.encode("utf-8")) signing_msg = SigningMessage( performative=performative, dialogue_reference=signing_dialogues. new_self_initiated_dialogue_reference(), skill_callback_ids=skill_callback_ids, terms=terms, skill_callback_info=skill_callback_info, raw_message=raw_message, ) signing_msg.counterparty = "decision_maker" return signing_msg
def test_handle_messages_from_two_dialogues_same_agent(self): """Test message signing for unknown.""" message = b"0x11f3f9487724404e3a1fb7252a322656b90ba0455a2ca5fcdcbe6eeee5f8126d" signing_dialogues = SigningDialogues("agent") dialogue_reference = signing_dialogues.new_self_initiated_dialogue_reference() signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=dialogue_reference, skill_callback_ids=(str(PublicId("author", "a_skill", "0.1.0")),), skill_callback_info={}, terms=Terms( ledger_id="unknown", sender_address="pk1", counterparty_address="pk2", amount_by_currency_id={"FET": -1}, is_sender_payable_tx_fee=True, quantities_by_good_id={"good_id": 10}, nonce="transaction nonce", ), raw_message=RawMessage("unknown", message), ) signing_msg.counterparty = "decision_maker" signing_dialogue = signing_dialogues.update(signing_msg) assert signing_dialogue is not None self.decision_maker.message_in_queue.put_nowait(signing_msg) signing_msg_response = self.decision_maker.message_out_queue.get(timeout=2) assert signing_msg_response is not None signing_dialogues = SigningDialogues("agent") signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=dialogue_reference, skill_callback_ids=(str(PublicId("author", "a_skill", "0.1.0")),), skill_callback_info={}, terms=Terms( ledger_id="unknown", sender_address="pk1", counterparty_address="pk2", amount_by_currency_id={"FET": -1}, is_sender_payable_tx_fee=True, quantities_by_good_id={"good_id": 10}, nonce="transaction nonce", ), raw_message=RawMessage("unknown", message), ) signing_msg.counterparty = "decision_maker" signing_dialogue = signing_dialogues.update(signing_msg) assert signing_dialogue is not None with pytest.raises(Exception): # Exception occurs because the same counterparty sends two identical dialogue references self.decision_maker.message_out_queue.get(timeout=1) # test twice; should work again even from same agent signing_dialogues = SigningDialogues("agent") signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=signing_dialogues.new_self_initiated_dialogue_reference(), skill_callback_ids=(str(PublicId("author", "a_skill", "0.1.0")),), skill_callback_info={}, terms=Terms( ledger_id="unknown", sender_address="pk1", counterparty_address="pk2", amount_by_currency_id={"FET": -1}, is_sender_payable_tx_fee=True, quantities_by_good_id={"good_id": 10}, nonce="transaction nonce", ), raw_message=RawMessage("unknown", message), ) signing_msg.counterparty = "decision_maker" signing_dialogue = signing_dialogues.update(signing_msg) assert signing_dialogue is not None self.decision_maker.message_in_queue.put_nowait(signing_msg) signing_msg_response = self.decision_maker.message_out_queue.get(timeout=2) assert signing_msg_response is not None
def _on_accept(self, accept: FipaMessage, fipa_dialogue: FipaDialogue) -> None: """ Handle an Accept. :param accept: the Accept message :param fipa_dialogue: the fipa_dialogue :return: None """ transactions = cast(Transactions, self.context.transactions) terms = transactions.pop_pending_proposal(fipa_dialogue.dialogue_label, accept.target) strategy = cast(Strategy, self.context.strategy) if strategy.is_profitable_transaction(terms, role=cast( FipaDialogue.Role, fipa_dialogue.role)): transactions.add_locked_tx(terms, role=cast(FipaDialogue.Role, fipa_dialogue.role)) if strategy.is_contract_tx: contract_api_dialogues = cast( ContractApiDialogues, self.context.contract_api_dialogues) contract_api_msg, contract_api_dialogue = contract_api_dialogues.create( counterparty=LEDGER_API_ADDRESS, performative=ContractApiMessage.Performative. GET_RAW_MESSAGE, ledger_id=strategy.ledger_id, contract_id=strategy.contract_id, contract_address=strategy.contract_address, callable="get_hash_batch", kwargs=ContractApiMessage.Kwargs( strategy.kwargs_from_terms(fipa_dialogue.terms)), ) contract_api_dialogue = cast(ContractApiDialogue, contract_api_dialogue) contract_api_dialogue.associated_fipa_dialogue = fipa_dialogue self.context.logger.info( "requesting batch transaction hash, sending {} to {}, message={}" .format( contract_api_msg.performative, strategy.contract_id, contract_api_msg, )) self.context.outbox.put_message(message=contract_api_msg) else: signing_dialogues = cast(SigningDialogues, self.context.signing_dialogues) raw_message = RawMessage( ledger_id=terms.ledger_id, body=terms.sender_hash.encode("utf-8")) signing_msg, signing_dialogue = signing_dialogues.create( counterparty=self.context.decision_maker_address, performative=SigningMessage.Performative.SIGN_MESSAGE, terms=terms, raw_message=raw_message, ) signing_dialogue = cast(SigningDialogue, signing_dialogue) signing_dialogue.associated_fipa_dialogue = fipa_dialogue self.context.logger.info( "requesting signature, sending {} to decision_maker, message={}" .format( signing_msg.performative, signing_msg, )) self.context.decision_maker_message_queue.put(signing_msg) else: fipa_msg = fipa_dialogue.reply( performative=FipaMessage.Performative.DECLINE, target_message=accept, ) dialogues = cast(FipaDialogues, self.context.fipa_dialogues) dialogues.dialogue_stats.add_dialogue_endstate( FipaDialogue.EndState.DECLINED_ACCEPT, fipa_dialogue.is_self_initiated) self.context.logger.info( "sending {} to {} (as {}), message={}".format( fipa_msg.performative, fipa_msg.to[-5:], fipa_dialogue.role, fipa_msg, )) self.context.outbox.put_message(message=fipa_msg)
def _on_match_accept(self, match_accept: FipaMessage, fipa_dialogue: FipaDialogue) -> None: """ Handle a matching Accept. :param match_accept: the MatchAccept message :param fipa_dialogue: the fipa_dialogue :return: None """ counterparty_signature = match_accept.info.get("signature") if counterparty_signature is None: self.context.logger.info( f"{match_accept.performative} did not contain counterparty signature!" ) return fipa_dialogue.counterparty_signature = counterparty_signature strategy = cast(Strategy, self.context.strategy) if strategy.is_contract_tx: contract_api_dialogues = cast(ContractApiDialogues, self.context.contract_api_dialogues) contract_api_msg, contract_api_dialogue = contract_api_dialogues.create( counterparty=LEDGER_API_ADDRESS, performative=ContractApiMessage.Performative. GET_RAW_TRANSACTION, ledger_id=strategy.ledger_id, contract_id=strategy.contract_id, contract_address=strategy.contract_address, callable="get_atomic_swap_batch_transaction", kwargs=ContractApiMessage.Kwargs( strategy.kwargs_from_terms( fipa_dialogue.terms, signature=counterparty_signature, )), ) contract_api_dialogue = cast(ContractApiDialogue, contract_api_dialogue) contract_api_dialogue.associated_fipa_dialogue = fipa_dialogue self.context.logger.info( "requesting batch atomic swap transaction, sending {} to {}, message={}" .format( contract_api_msg.performative, strategy.contract_id, contract_api_msg, )) self.context.outbox.put_message(message=contract_api_msg) else: transactions = cast(Transactions, self.context.transactions) terms = transactions.pop_pending_initial_acceptance( fipa_dialogue.dialogue_label, match_accept.target) signing_dialogues = cast(SigningDialogues, self.context.signing_dialogues) raw_message = RawMessage(ledger_id=terms.ledger_id, body=terms.sender_hash.encode("utf-8")) signing_msg, signing_dialogue = signing_dialogues.create( counterparty=self.context.decision_maker_address, performative=SigningMessage.Performative.SIGN_MESSAGE, terms=terms, raw_message=raw_message, ) signing_dialogue = cast(SigningDialogue, signing_dialogue) signing_dialogue.associated_fipa_dialogue = fipa_dialogue self.context.logger.info( "requesting signature, sending {} to decision_maker, message={}" .format( signing_msg.performative, signing_msg, )) self.context.decision_maker_message_queue.put(signing_msg)