def decode(obj: bytes) -> Message: """ Decode bytes into a 'Signing' message. :param obj: the bytes object. :return: the 'Signing' message. """ message_pb = ProtobufMessage() signing_pb = signing_pb2.SigningMessage() message_pb.ParseFromString(obj) message_id = message_pb.dialogue_message.message_id dialogue_reference = ( message_pb.dialogue_message.dialogue_starter_reference, message_pb.dialogue_message.dialogue_responder_reference, ) target = message_pb.dialogue_message.target signing_pb.ParseFromString(message_pb.dialogue_message.content) performative = signing_pb.WhichOneof("performative") performative_id = SigningMessage.Performative(str(performative)) performative_content = dict() # type: Dict[str, Any] if performative_id == SigningMessage.Performative.SIGN_TRANSACTION: pb2_terms = signing_pb.sign_transaction.terms terms = Terms.decode(pb2_terms) performative_content["terms"] = terms pb2_raw_transaction = signing_pb.sign_transaction.raw_transaction raw_transaction = RawTransaction.decode(pb2_raw_transaction) performative_content["raw_transaction"] = raw_transaction elif performative_id == SigningMessage.Performative.SIGN_MESSAGE: pb2_terms = signing_pb.sign_message.terms terms = Terms.decode(pb2_terms) performative_content["terms"] = terms pb2_raw_message = signing_pb.sign_message.raw_message raw_message = RawMessage.decode(pb2_raw_message) performative_content["raw_message"] = raw_message elif performative_id == SigningMessage.Performative.SIGNED_TRANSACTION: pb2_signed_transaction = signing_pb.signed_transaction.signed_transaction signed_transaction = SignedTransaction.decode( pb2_signed_transaction) performative_content["signed_transaction"] = signed_transaction elif performative_id == SigningMessage.Performative.SIGNED_MESSAGE: pb2_signed_message = signing_pb.signed_message.signed_message signed_message = SignedMessage.decode(pb2_signed_message) performative_content["signed_message"] = signed_message elif performative_id == SigningMessage.Performative.ERROR: pb2_error_code = signing_pb.error.error_code error_code = ErrorCode.decode(pb2_error_code) performative_content["error_code"] = error_code else: raise ValueError( "Performative not valid: {}.".format(performative_id)) return SigningMessage(message_id=message_id, dialogue_reference=dialogue_reference, target=target, performative=performative, **performative_content)
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_sign_transaction(self): """Test for an error for a sign transaction message.""" tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_TRANSACTION, terms=self.terms, raw_transaction=RawTransaction(self.ledger_id, "transaction"), ) 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_signed_message(self): """Test for an error for a signed message.""" tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGNED_MESSAGE, message_id=2, target=1, signed_message=SignedMessage(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_signed_transaction(self): """Test for an error for a signed transaction.""" tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGNED_TRANSACTION, message_id=2, target=1, signed_transaction=SignedTransaction(self.ledger_id, "signature"), ) 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_error_message(self): """Test for an error for an error message.""" tx_msg = SigningMessage( performative=SigningMessage.Performative.ERROR, message_id=2, target=1, error_code=SigningMessage.ErrorCode.UNSUCCESSFUL_MESSAGE_SIGNING, ) 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 assert str(tx_msg.performative) == "error" assert len(tx_msg.valid_performatives) == 5
def test_handle_internal_messages(self): """Test that the internal messages are handled.""" t = SigningMessage( performative=SigningMessage.Performative.SIGNED_TRANSACTION, signed_transaction=SignedTransaction("ledger_id", "tx"), ) t.to = str(PublicId("dummy_author", "dummy", "0.1.0")) t.sender = "decision_maker" self.aea._filter.handle_internal_message(t) internal_handlers_list = self.aea.resources.get_handlers(t.protocol_id) assert len(internal_handlers_list) == 1 internal_handler = internal_handlers_list[0] assert len(internal_handler.handled_internal_messages) == 1 self.aea.teardown()
def test_handle_tx_signing_unknown(self): """Test tx signing for unknown.""" tx = {} signing_dialogues = SigningDialogues( str(PublicId("author", "a_skill", "0.1.0"))) signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_TRANSACTION, dialogue_reference=signing_dialogues. new_self_initiated_dialogue_reference(), 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_transaction=RawTransaction("unknown", tx), ) 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.ERROR assert (signing_msg_response.error_code == SigningMessage.ErrorCode.UNSUCCESSFUL_TRANSACTION_SIGNING)
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 setup(cls): """Setup the test class.""" super().setup() cls.signing_handler = cast(SigningHandler, cls._skill.skill_context.handlers.signing) cls.ledger_api_dialogues = cast( LedgerApiDialogues, cls._skill.skill_context.ledger_api_dialogues) cls.signing_dialogues = cast( SigningDialogues, cls._skill.skill_context.signing_dialogues) cls.logger = cls.signing_handler.context.logger cls.terms = Terms( "some_ledger_id", cls._skill.skill_context.agent_address, "counterprty", {"currency_id": 50}, {"good_id": -10}, "some_nonce", ) cls.list_of_signing_messages = (DialogueMessage( SigningMessage.Performative.SIGN_TRANSACTION, { "terms": cls.terms, "raw_transaction": SigningMessage.RawTransaction("some_ledger_id", {"some_key": "some_value"}), }, ), )
def test_serialization_negative(): """Test serialization when performative is not recognized.""" tx_msg = SigningMessage( performative=SigningMessage.Performative.ERROR, message_id=2, target=1, error_code=SigningMessage.ErrorCode.UNSUCCESSFUL_MESSAGE_SIGNING, ) with patch.object(SigningMessage.Performative, "__eq__", return_value=False): with pytest.raises( ValueError, match=f"Performative not valid: {tx_msg.performative}"): tx_msg.serializer.encode(tx_msg) encoded_tx_bytes = tx_msg.serializer.encode(tx_msg) with patch.object(SigningMessage.Performative, "__eq__", return_value=False): with pytest.raises( ValueError, match=f"Performative not valid: {tx_msg.performative}"): tx_msg.serializer.decode(encoded_tx_bytes)
def test_handle_message_signing_unknown_and_two_dialogues(self): """Test message signing for unknown.""" message = b"0x11f3f9487724404e3a1fb7252a322656b90ba0455a2ca5fcdcbe6eeee5f8126d" 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="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_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.ERROR assert (signing_msg_response.error_code == SigningMessage.ErrorCode.UNSUCCESSFUL_MESSAGE_SIGNING)
def test_handle_tx_signing_ethereum(self): """Test tx signing for ethereum.""" tx = {"gasPrice": 30, "nonce": 1, "gas": 20000} signing_dialogues = SigningDialogues( str(PublicId("author", "a_skill", "0.1.0"))) signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_TRANSACTION, 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_transaction=RawTransaction(ETHEREUM, tx), ) 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_TRANSACTION) assert (type(signing_msg_response.signed_transaction.body) == eth_account.datastructures.SignedTransaction)
def test_handle_signed_transaction(self, ): """Test the _handle_signed_transaction method of the signing handler.""" # setup signing_counterparty = self.skill.skill_context.decision_maker_address signing_dialogue = cast( SigningDialogue, self.prepare_skill_dialogue( dialogues=self.signing_dialogues, messages=self.list_of_signing_messages[:1], counterparty=signing_counterparty, ), ) incoming_message = cast( SigningMessage, self.build_incoming_message_for_skill_dialogue( dialogue=signing_dialogue, performative=SigningMessage.Performative.SIGNED_TRANSACTION, signed_transaction=SigningMessage.SignedTransaction( "some_ledger_id", {"some_key": "some_value"}), ), ) # operation with patch.object(self.logger, "log") as mock_logger: self.signing_handler.handle(incoming_message) # after mock_logger.assert_any_call(logging.INFO, "transaction signing was successful.") self.assert_quantity_in_outbox(1) message = self.get_message_from_outbox() has_attributes, error_str = self.message_has_attributes( actual_message=message, message_type=LedgerApiMessage, performative=LedgerApiMessage.Performative.SEND_SIGNED_TRANSACTION, to=LEDGER_API_ADDRESS, sender=self.skill.skill_context.agent_address, signed_transaction=incoming_message.signed_transaction, ) assert has_attributes, error_str assert (cast( LedgerApiDialogue, self.ledger_api_dialogues.get_dialogue( message)).associated_signing_dialogue == signing_dialogue) mock_logger.assert_any_call(logging.INFO, "sending transaction to ledger.")
def test_handle_tx_signing_fetchai(self): """Test tx signing for fetchai.""" fetchai_api = FetchAIApi(**FETCHAI_TESTNET_CONFIG) account = FetchAICrypto() fc2 = FetchAICrypto() amount = 10000 transfer_transaction = fetchai_api.get_transfer_transaction( sender_address=account.address, destination_address=fc2.address, amount=amount, tx_fee=1000, tx_nonce="something", ) signing_dialogues = SigningDialogues( str(PublicId("author", "a_skill", "0.1.0"))) signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_TRANSACTION, dialogue_reference=signing_dialogues. new_self_initiated_dialogue_reference(), terms=Terms( ledger_id=FETCHAI, 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_transaction=RawTransaction(FETCHAI, transfer_transaction), ) 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_TRANSACTION) assert type(signing_msg_response.signed_transaction.body) == dict
def test_handle_invalid(self): """Test the _handle_invalid method of the signing handler.""" # setup invalid_performative = SigningMessage.Performative.SIGN_TRANSACTION incoming_message = self.build_incoming_message( message_type=SigningMessage, dialogue_reference=("1", ""), performative=invalid_performative, terms=self.terms, raw_transaction=SigningMessage.RawTransaction( "some_ledger_id", {"some_key": "some_value"}), to=str(self.skill.skill_context.skill_id), ) # operation with patch.object(self.logger, "log") as mock_logger: self.signing_handler.handle(incoming_message) # after mock_logger.assert_any_call( logging.WARNING, f"cannot handle signing message of performative={invalid_performative} in dialogue={self.signing_dialogues.get_dialogue(incoming_message)}.", )
def test_handle_messages_from_two_dialogues_same_agent(self): """Test message signing for unknown.""" message = b"0x11f3f9487724404e3a1fb7252a322656b90ba0455a2ca5fcdcbe6eeee5f8126d" signing_dialogues = SigningDialogues( str(PublicId("author", "a_skill", "0.1.0"))) dialogue_reference = signing_dialogues.new_self_initiated_dialogue_reference( ) signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=dialogue_reference, 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_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) assert signing_msg_response is not None signing_dialogues = SigningDialogues( str(PublicId("author", "a_skill", "0.1.0"))) signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_MESSAGE, dialogue_reference=dialogue_reference, 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_dialogue = signing_dialogues.create_with_message( "decision_maker", 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( 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="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_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) assert signing_msg_response is not None
def test_consistency_check_negative(): """Test the consistency check, negative case.""" tx_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_TRANSACTION, ) assert not tx_msg._is_consistent()
def setup(cls): """Setup the test class.""" super().setup() cls.ledger_api_handler = cast( LedgerApiHandler, cls._skill.skill_context.handlers.ledger_api) cls.ledger_api_dialogues = cast( LedgerApiDialogues, cls._skill.skill_context.ledger_api_dialogues) cls.signing_dialogues = cast( SigningDialogues, cls._skill.skill_context.signing_dialogues) cls.contract_api_dialogues = cast( ContractApiDialogues, cls._skill.skill_context.contract_api_dialogues) cls.parameters = cast(Parameters, cls._skill.skill_context.parameters) cls.game = cast(Game, cls._skill.skill_context.game) cls.logger = cls.ledger_api_handler.context.logger cls.ledger_id = "some_ledger_id" cls.contract_id = "some_contract_id" cls.callable = "some_callable" cls.kwargs = Kwargs({"some_key": "some_value"}) cls.body = {"some_key": "some_value"} cls.body_str = "some_body" cls.contract_address = "some_contract_address" cls.raw_transaction = RawTransaction(cls.ledger_id, cls.body) cls.signed_transaction = SignedTransaction(cls.ledger_id, cls.body) cls.transaction_digest = TransactionDigest(cls.ledger_id, cls.body_str) cls.receipt = {"contractAddress": cls.contract_address} cls.transaction_receipt = TransactionReceipt( cls.ledger_id, cls.receipt, {"transaction_key": "transaction_value"}) cls.terms = Terms( cls.ledger_id, cls._skill.skill_context.agent_address, "counterprty", {"currency_id": 50}, {"good_id": -10}, "some_nonce", ) cls.list_of_signing_messages = (DialogueMessage( SigningMessage.Performative.SIGN_TRANSACTION, { "terms": cls.terms, "raw_transaction": SigningMessage.RawTransaction(cls.ledger_id, cls.body), }, ), ) cls.list_of_contract_api_messages = (DialogueMessage( ContractApiMessage.Performative.GET_DEPLOY_TRANSACTION, { "ledger_id": cls.ledger_id, "contract_id": cls.contract_id, "callable": cls.callable, "kwargs": cls.kwargs, }, ), ) cls.list_of_ledger_api_messages = ( DialogueMessage(LedgerApiMessage.Performative.GET_RAW_TRANSACTION, {"terms": cls.terms}), DialogueMessage( LedgerApiMessage.Performative.RAW_TRANSACTION, {"raw_transaction": cls.raw_transaction}, ), DialogueMessage( LedgerApiMessage.Performative.SEND_SIGNED_TRANSACTION, {"signed_transaction": cls.signed_transaction}, ), DialogueMessage( LedgerApiMessage.Performative.TRANSACTION_DIGEST, {"transaction_digest": cls.transaction_digest}, ), DialogueMessage( LedgerApiMessage.Performative.GET_TRANSACTION_RECEIPT, {"transaction_digest": cls.transaction_digest}, ), )
def run(): """Run demo.""" # Create a private key create_private_key(FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_1) # Instantiate the builder and build the AEA # By default, the default protocol, error skill and stub connection are added builder = AEABuilder() builder.set_name("my_aea") builder.add_private_key(FetchAICrypto.identifier, FETCHAI_PRIVATE_KEY_FILE_1) # Create our AEA my_aea = builder.build() # add a simple skill with handler skill_context = SkillContext(my_aea.context) skill_config = SkillConfig(name="simple_skill", author="fetchai", version="0.1.0") signing_handler = SigningHandler(skill_context=skill_context, name="signing_handler") signing_dialogues_model = SigningDialogues( skill_context=skill_context, name="signing_dialogues", self_address=str(skill_config.public_id), ) simple_skill = Skill( skill_config, skill_context, handlers={signing_handler.name: signing_handler}, models={signing_dialogues_model.name: signing_dialogues_model}, ) my_aea.resources.add_skill(simple_skill) # create a second identity create_private_key(FetchAICrypto.identifier, private_key_file=FETCHAI_PRIVATE_KEY_FILE_2) counterparty_wallet = Wallet( {FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE_2}) counterparty_identity = Identity( name="counterparty_aea", addresses=counterparty_wallet.addresses, default_address_key=FetchAICrypto.identifier, ) # create signing message for decision maker to sign terms = Terms( ledger_id=FetchAICrypto.identifier, sender_address=my_aea.identity.address, counterparty_address=counterparty_identity.address, amount_by_currency_id={"FET": -1}, quantities_by_good_id={"some_service": 1}, nonce="some_nonce", fee_by_currency_id={"FET": 0}, ) signing_dialogues = cast(SigningDialogues, skill_context.signing_dialogues) stub_transaction = LedgerApis.get_transfer_transaction( terms.ledger_id, terms.sender_address, terms.counterparty_address, terms.sender_payable_amount, terms.sender_fee, terms.nonce, ) signing_msg = SigningMessage( performative=SigningMessage.Performative.SIGN_TRANSACTION, dialogue_reference=signing_dialogues. new_self_initiated_dialogue_reference(), raw_transaction=RawTransaction(FetchAICrypto.identifier, stub_transaction), terms=terms, ) signing_dialogue = cast( Optional[SigningDialogue], signing_dialogues.create_with_message("decision_maker", signing_msg), ) assert signing_dialogue is not None my_aea.context.decision_maker_message_queue.put_nowait(signing_msg) # Set the AEA running in a different thread try: logger.info("STARTING AEA NOW!") t = Thread(target=my_aea.start) t.start() # Let it run long enough to interact with the decision maker time.sleep(1) finally: # Shut down the AEA logger.info("STOPPING AEA NOW!") my_aea.stop() t.join()