diff --git a/docs/OPTIONS.rst b/docs/OPTIONS.rst index 02ec1bfb6..794707f5a 100644 --- a/docs/OPTIONS.rst +++ b/docs/OPTIONS.rst @@ -48,6 +48,12 @@ Use the following command to see all options: load_device Load custom configuration to the device. nem_get_address Get NEM address for specified path. nem_sign_tx Sign (and optionally broadcast) NEM... + ontology_get_address Get Ontology address for specified path. + ontology_get_public_key Get Ontology public key for specified path. + ontology_sign_transfer Sign Ontology Transfer... + ontology_sign_withdraw Sign Ontology Withdraw Ong... + ontology_sign_register Sign Ontology ONT ID Registration... + ontology_sign_add_attr Sign Ontology ONT ID Attributes adding... ping Send ping message. recovery_device Start safe recovery workflow. reset_device Perform device setup and generate new seed. diff --git a/trezorctl b/trezorctl index 271d0badf..5954f1a6b 100755 --- a/trezorctl +++ b/trezorctl @@ -44,6 +44,7 @@ from trezorlib import ( messages as proto, misc, nem, + ontology, protobuf, ripple, stellar, @@ -1386,6 +1387,209 @@ def ripple_sign_tx(connect, address, file): click.echo(binascii.hexlify(result.serialized_tx)) +# +# Ontology functions +# + + +@cli.command(help="Get Ontology address for specified path.") +@click.option( + "-n", "--address", required=True, help="BIP-32 path, e.g. m/44'/888'/0'/0/0" +) +@click.option("-d", "--show-display", is_flag=True) +@click.pass_obj +def ontology_get_address(connect, address, show_display): + client = connect() + address_n = tools.parse_path(address) + return ontology.get_address(client, address_n, show_display) + + +@cli.command(help="Get Ontology public key for specified path.") +@click.option( + "-n", "--address", required=True, help="BIP-32 path, e.g. m/44'/888'/0'/0/0" +) +@click.option("-d", "--show-display", is_flag=True) +@click.pass_obj +def ontology_get_public_key(connect, address, show_display): + client = connect() + address_n = tools.parse_path(address) + result = ontology.get_public_key(client, address_n, show_display) + + return binascii.hexlify(result.public_key).decode() + + +@cli.command(help="Sign Ontology transfer.") +@click.option( + "-n", + "--address", + required=True, + help="BIP-32 path to signing key, e.g. m/44'/888'/0'/0/0", +) +@click.option( + "-tx", + "--transaction", + type=click.File("r"), + default="-", + help="Transaction in JSON format", +) +@click.option( + "-tr", + "--transfer", + type=click.File("r"), + default="-", + help="Transfer in JSON format", +) +@click.pass_obj +def ontology_sign_transfer(connect, address, transaction_f, transfer_f): + client = connect() + address_n = tools.parse_path(address) + transaction = protobuf.dict_to_proto( + proto.OntologyTransaction, json.load(transaction_f) + ) + transfer = protobuf.dict_to_proto(proto.OntologyTransfer, json.load(transfer_f)) + + result = ontology.sign_transfer(client, address_n, transaction, transfer) + + output = { + "payload": binascii.hexlify(result.payload).decode(), + "signature": binascii.hexlify(result.signature).decode(), + } + + return output + + +@cli.command(help="Sign Ontology withdraw Ong.") +@click.option( + "-n", + "--address", + required=True, + help="BIP-32 path to signing key, e.g. m/44'/888'/0'/0/0", +) +@click.option( + "-tx", + "--transaction", + type=click.File("r"), + default="-", + help="Transaction in JSON format", +) +@click.option( + "-wi", + "--withdraw_ong", + type=click.File("r"), + default="-", + help="Withdrawal in JSON format", +) +@click.pass_obj +def ontology_sign_withdraw_ong(connect, address, transaction_f, withdraw_ong_f): + client = connect() + address_n = tools.parse_path(address) + transaction = protobuf.dict_to_proto( + proto.OntologyTransaction, json.load(transaction_f) + ) + withdraw_ong = protobuf.dict_to_proto( + proto.OntologyWithdrawOng, json.load(withdraw_ong_f) + ) + + result = ontology.sign_withdrawal(client, address_n, transaction, withdraw_ong) + + output = { + "payload": binascii.hexlify(result.payload).decode(), + "signature": binascii.hexlify(result.signature).decode(), + } + + return output + + +@cli.command(help="Sign Ontology ONT ID Registration.") +@click.option( + "-n", + "--address", + required=True, + help="BIP-32 path to signing key, e.g. m/44'/888'/0'/0/0", +) +@click.option( + "-tx", + "--transaction", + type=click.File("r"), + default="-", + help="Transaction in JSON format", +) +@click.option( + "-re", + "--register", + type=click.File("r"), + default="-", + help="Register in JSON format", +) +@click.argument("transaction") +@click.argument("ont_id_register") +@click.pass_obj +def ontology_sign_ont_id_register(connect, address, transaction_f, ont_id_register_f): + client = connect() + address_n = tools.parse_path(address) + transaction = protobuf.dict_to_proto( + proto.OntologyTransaction, json.load(transaction_f) + ) + ont_id_register = protobuf.dict_to_proto( + proto.OntologyOntIdRegister, json.load(ont_id_register_f) + ) + + result = ontology.sign_register(client, address_n, transaction, ont_id_register) + + output = { + "payload": binascii.hexlify(result.payload).decode(), + "signature": binascii.hexlify(result.signature).decode(), + } + + return output + + +@cli.command(help="Sign Ontology ONT ID Attributes adding.") +@click.option( + "-n", + "--address", + required=True, + help="BIP-32 path to signing key, e.g. m/44'/888'/0'/0/0", +) +@click.option( + "-tx", + "--transaction", + type=click.File("r"), + default="-", + help="Transaction in JSON format", +) +@click.option( + "-aa", + "--add_attr", + type=click.File("r"), + default="-", + help="Add attributes in JSON format", +) +@click.pass_obj +def ontology_sign_ont_id_add_attributes( + connect, address, transaction_f, ont_id_add_attributes_f +): + client = connect() + address_n = tools.parse_path(address) + transaction = protobuf.dict_to_proto( + proto.OntologyTransaction, json.load(transaction_f) + ) + ont_id_add_attributes = protobuf.dict_to_proto( + proto.OntologyOntIdAddAttributes, json.load(ont_id_add_attributes_f) + ) + + result = ontology.sign_add_attr( + client, address_n, transaction, ont_id_add_attributes + ) + + output = { + "payload": binascii.hexlify(result.payload).decode(), + "signature": binascii.hexlify(result.signature).decode(), + } + + return output + + # # Main # diff --git a/trezorlib/ontology.py b/trezorlib/ontology.py new file mode 100644 index 000000000..cd7f12056 --- /dev/null +++ b/trezorlib/ontology.py @@ -0,0 +1,71 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + + +from . import messages +from .tools import expect + +# +# Ontology functions +# + + +@expect(messages.OntologyAddress, field="address") +def get_address(client, address_n, show_display=False): + return client.call( + messages.OntologyGetAddress(address_n=address_n, show_display=show_display) + ) + + +@expect(messages.OntologyPublicKey) +def get_public_key(client, address_n, show_display=False): + return client.call( + messages.OntologyGetPublicKey(address_n=address_n, show_display=show_display) + ) + + +@expect(messages.OntologySignedTransfer) +def sign_transfer(client, address_n, t, tr): + return client.call( + messages.OntologySignTransfer(address_n=address_n, transaction=t, transfer=tr) + ) + + +@expect(messages.OntologySignedWithdrawOng) +def sign_withdrawal(client, address_n, t, w): + return client.call( + messages.OntologySignWithdrawOng( + address_n=address_n, transaction=t, withdraw_ong=w + ) + ) + + +@expect(messages.OntologySignedOntIdRegister) +def sign_register(client, address_n, t, r): + return client.call( + messages.OntologySignOntIdRegister( + address_n=address_n, transaction=t, ont_id_register=r + ) + ) + + +@expect(messages.OntologySignedOntIdAddAttributes) +def sign_add_attr(client, address_n, t, a): + return client.call( + messages.OntologySignOntIdAddAttributes( + address_n=address_n, transaction=t, ont_id_add_attributes=a + ) + ) diff --git a/trezorlib/tests/device_tests/test_msg_ontology_getaddress.py b/trezorlib/tests/device_tests/test_msg_ontology_getaddress.py new file mode 100644 index 000000000..9e1cc1be5 --- /dev/null +++ b/trezorlib/tests/device_tests/test_msg_ontology_getaddress.py @@ -0,0 +1,43 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import pytest + +from trezorlib import ontology +from trezorlib.tools import parse_path + +from .common import TrezorTest + + +@pytest.mark.xfail +@pytest.mark.ontology +@pytest.mark.skip_t1 +class TestMsgOntologyGetaddress(TrezorTest): + def test_ontology_get_ont_address(self): + self.setup_mnemonic_nopin_nopassphrase() + + assert ( + ontology.get_address(self.client, parse_path("m/44'/1024'/0'/0/0")) + == "ANzeepWmi9hoLBA3UiwVhUm7Eku196VUHk" + ) + + def test_ontology_get_neo_address(self): + self.setup_mnemonic_nopin_nopassphrase() + + assert ( + ontology.get_address(self.client, parse_path("m/44'/888'/0'/0/0")) + == "AZEMburLePcdfqBFnVfdbsXKiBSnmtgFZr" + ) diff --git a/trezorlib/tests/device_tests/test_msg_ontology_sign_ont_id_add_attributes.py b/trezorlib/tests/device_tests/test_msg_ontology_sign_ont_id_add_attributes.py new file mode 100644 index 000000000..24922c0c7 --- /dev/null +++ b/trezorlib/tests/device_tests/test_msg_ontology_sign_ont_id_add_attributes.py @@ -0,0 +1,92 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import time +from binascii import unhexlify + +import pytest + +from trezorlib import messages +from trezorlib.tools import parse_path + +from .common import TrezorTest + + +@pytest.mark.xfail +@pytest.mark.ontology +@pytest.mark.skip_t1 +class TestMsgOntologySignOntIdAddAttributes(TrezorTest): + def test_ontology_sign_ont_id_add_attributes(self): + self.setup_mnemonic_nopin_nopassphrase() + + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7f7f1ceb, + type=0xd1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + ont_id_add_attributes = messages.OntologyOntIdAddAttributes( + ont_id="did:ont:AGVn4NZNEQ7RawHTDxjaTjZ3R8h8q1aq9h", + public_key=unhexlify( + "03a8269b0dad311d98195e76729bc57003348a315fd17b6bf4f90ba8b86735fa33" + ), + ont_id_attributes=[ + messages.OntologyOntIdAttribute( + key="firstName", type="json", value="John Sheppard" + ) + ], + ) + + self.client.debug.swipe_down() + # not using ontology.sign_add_attr() because of swiping + signature = self._ontology_sign( + 2, parse_path("m/44'/1024'/0'/0/0"), transaction, ont_id_add_attributes + ) + + assert signature.payload == unhexlify( + "bd00c66b2a6469643a6f6e743a4147566e344e5a4e455137526177485444786a61546a5a33523868387131617139686a7cc8516a7cc80966697273744e616d656a7cc8046a736f6e6a7cc80d4a6f686e2053686570706172646a7cc82103a8269b0dad311d98195e76729bc57003348a315fd17b6bf4f90ba8b86735fa336a7cc86c0d616464417474726962757465731400000000000000000000000000000000000000030068164f6e746f6c6f67792e4e61746976652e496e766f6b65" + ) + assert signature.signature == unhexlify( + "01c256dc16d88685fd6652d69b808059f7ed30edadb0ccfe51802702b94b65500922f9ea80e0fd7b77b5c51515e3bc43a495b3e98fb3adb82a0ab5dd47169fcf4e" + ) + + def _ontology_sign( + self, num_of_swipes, address_n, transaction, ont_id_add_attributes + ): + # Sending Ontology message + msg = messages.OntologySignOntIdAddAttributes( + address_n=address_n, + transaction=transaction, + ont_id_add_attributes=ont_id_add_attributes, + ) + + self.client.transport.write(msg) + ret = self.client.transport.read() + + # Confirm action + assert isinstance(ret, messages.ButtonRequest) + self.client.debug.press_yes() + self.client.transport.write(messages.ButtonAck()) + time.sleep(1) + for _ in range(num_of_swipes): + self.client.debug.swipe_down() + time.sleep(1) + self.client.debug.press_yes() + return self.client.transport.read() diff --git a/trezorlib/tests/device_tests/test_msg_ontology_sign_ont_id_register.py b/trezorlib/tests/device_tests/test_msg_ontology_sign_ont_id_register.py new file mode 100644 index 000000000..dcf125e0d --- /dev/null +++ b/trezorlib/tests/device_tests/test_msg_ontology_sign_ont_id_register.py @@ -0,0 +1,83 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import time +from binascii import unhexlify + +import pytest + +from trezorlib import messages +from trezorlib.tools import parse_path + +from .common import TrezorTest + + +@pytest.mark.xfail +@pytest.mark.ontology +@pytest.mark.skip_t1 +class TestMsgOntologySignOntIdRegister(TrezorTest): + def test_ontology_sign_ont_id_register(self): + self.setup_mnemonic_nopin_nopassphrase() + + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7f7f1ceb, + type=0xd1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + ont_id_register = messages.OntologyOntIdRegister( + ont_id="did:ont:AGVn4NZNEQ7RawHTDxjaTjZ3R8h8q1aq9h", + public_key=unhexlify( + "03a8269b0dad311d98195e76729bc57003348a315fd17b6bf4f90ba8b86735fa33" + ), + ) + + # not using ontology.sign_register() because of swiping + signature = self._ontology_sign( + 1, parse_path("m/44'/1024'/0'/0/0"), transaction, ont_id_register + ) + assert signature.payload == unhexlify( + "9800c66b2a6469643a6f6e743a4147566e344e5a4e455137526177485444786a61546a5a33523868387131617139686a7cc82103a8269b0dad311d98195e76729bc57003348a315fd17b6bf4f90ba8b86735fa336a7cc86c127265674944576974685075626c69634b65791400000000000000000000000000000000000000030068164f6e746f6c6f67792e4e61746976652e496e766f6b65" + ) + assert signature.signature == unhexlify( + "015d6abe231352d1ab32f0b0de0222cfb9a7a13f467a2bf8a369b61aa1f933dc3a6a2ba7831c8a15984fe0958d24cbca05d8e0736510c1734d773145ce3eac9e9b" + ) + + def _ontology_sign(self, num_of_swipes, address_n, transaction, ont_id_register): + # Sending Ontology message + msg = messages.OntologySignOntIdRegister( + address_n=address_n, + transaction=transaction, + ont_id_register=ont_id_register, + ) + + self.client.transport.write(msg) + ret = self.client.transport.read() + + # Confirm action + assert isinstance(ret, messages.ButtonRequest) + self.client.debug.press_yes() + self.client.transport.write(messages.ButtonAck()) + time.sleep(1) + for _ in range(num_of_swipes): + self.client.debug.swipe_down() + time.sleep(1) + self.client.debug.press_yes() + return self.client.transport.read() diff --git a/trezorlib/tests/device_tests/test_msg_ontology_sign_tx.py b/trezorlib/tests/device_tests/test_msg_ontology_sign_tx.py new file mode 100644 index 000000000..46f5265fa --- /dev/null +++ b/trezorlib/tests/device_tests/test_msg_ontology_sign_tx.py @@ -0,0 +1,86 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +from binascii import unhexlify + +import pytest + +from trezorlib import messages, ontology +from trezorlib.tools import parse_path + +from .common import TrezorTest + + +@pytest.mark.xfail +@pytest.mark.ontology +@pytest.mark.skip_t1 +class TestMsgOntologySigntx(TrezorTest): + def test_ontology_sign_transfer_ont(self): + self.setup_mnemonic_nopin_nopassphrase() + + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7f7f1ceb, + type=0xd1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + transfer = messages.OntologyTransfer( + asset=1, + amount=100, + from_address="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + to_address="AcyLq3tokVpkMBMLALVMWRdVJ83TTgBUwU", + ) + + signature = ontology.sign_transfer( + self.client, parse_path("m/44'/1024'/0'/0/0"), transaction, transfer + ) + assert signature.payload == unhexlify( + "7900c66b140b045b101bc9fabaf181e251a38e76b73962111b6a7cc814e885e849e7f545ea84e8c555b86c70e4f751c4ec6a7cc80864000000000000006a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b65" + ) + assert signature.signature == unhexlify( + "0102f9b0c43b2ed35aa89b0927a60e692cb8a74280c2da819a909150c8b3fd2b0b401806c97797fcc4b93d34f210ad01740cfd13b720a389a80f384c1f94fb749e" + ) + + def test_ontology_sign_transfer_ong(self): + self.setup_mnemonic_nopin_nopassphrase() + + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7f7f1ceb, + type=0xd1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + transfer = messages.OntologyTransfer( + asset=2, + amount=12000000, + from_address="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + to_address="AcyLq3tokVpkMBMLALVMWRdVJ83TTgBUwU", + ) + + signature = ontology.sign_transfer( + self.client, parse_path("m/44'/1024'/0'/0/0"), transaction, transfer + ) + assert signature.signature == unhexlify( + "01ad88061a6cf5f4960cf9d311adb6dec4925d368b0fa9b7f56269f2a4078bea2367469af50c70260142d2ce3cc2d1e7fd0b2923df659c994412ff18f138438e9d" + ) diff --git a/trezorlib/tests/device_tests/test_msg_ontology_sign_withdraw_ong.py b/trezorlib/tests/device_tests/test_msg_ontology_sign_withdraw_ong.py new file mode 100644 index 000000000..759f576b4 --- /dev/null +++ b/trezorlib/tests/device_tests/test_msg_ontology_sign_withdraw_ong.py @@ -0,0 +1,58 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +from binascii import unhexlify + +import pytest + +from trezorlib import messages, ontology +from trezorlib.tools import parse_path + +from .common import TrezorTest + + +@pytest.mark.xfail +@pytest.mark.ontology +@pytest.mark.skip_t1 +class TestMsgOntologySignWithdraw(TrezorTest): + def test_ontology_sign_withdraw_ong(self): + self.setup_mnemonic_nopin_nopassphrase() + + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7f7f1ceb, + type=0xd1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + withdraw_ong = messages.OntologyWithdrawOng( + amount=12000000, + from_address="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + to_address="AcyLq3tokVpkMBMLALVMWRdVJ83TTgBUwU", + ) + + signature = ontology.sign_withdrawal( + self.client, parse_path("m/44'/1024'/0'/0/0"), transaction, withdraw_ong + ) + assert signature.payload == unhexlify( + "9300c66b140b045b101bc9fabaf181e251a38e76b73962111b6a7cc81400000000000000000000000000000000000000016a7cc814e885e849e7f545ea84e8c555b86c70e4f751c4ec6a7cc808001bb700000000006a7cc86c0c7472616e7366657246726f6d1400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b65" + ) + assert signature.signature == unhexlify( + "01a44355ac4549a021ecc571eb85ffb6ae4ff50cffc416ec55df40cad538fa55c64386167df2fb6b3fa9e698ebe265088839667b88da7e599ce7df679b0d5dfe60" + )