From 88ef4257cdc5259783cd9a3d1eb32402697e5ff0 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 9 Mar 2018 11:37:06 +0100 Subject: [PATCH 01/53] wallet/nem: init, get_address --- SConscript.firmware | 3 +++ SConscript.unix | 3 +++ .../modtrezorcrypto/modtrezorcrypto-bip32.h | 16 +++++++++++++++ mocks/generated/trezorconfig.py | 6 ++++++ mocks/generated/trezorcrypto.py | 5 +++++ mocks/generated/trezorio.py | 7 +++++-- mocks/generated/trezorui.py | 4 ++-- mocks/generated/trezorutils.py | 18 +++++++++++++++++ src/apps/nem/__init__.py | 13 ++++++++++++ src/apps/nem/get_address.py | 20 +++++++++++++++++++ src/apps/nem/helpers.py | 14 +++++++++++++ src/main.py | 2 ++ 12 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 src/apps/nem/__init__.py create mode 100644 src/apps/nem/get_address.py create mode 100644 src/apps/nem/helpers.py diff --git a/SConscript.firmware b/SConscript.firmware index 805a98378..be4bb8f42 100644 --- a/SConscript.firmware +++ b/SConscript.firmware @@ -27,6 +27,7 @@ CPPDEFINES_MOD += [ 'RAND_PLATFORM_INDEPENDENT', ('USE_KECCAK', '1'), ('USE_ETHEREUM', '1'), + ('USE_NEM', '1'), ] SOURCE_MOD += [ 'embed/extmod/modtrezorcrypto/modtrezorcrypto.c', @@ -38,6 +39,7 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/aes/aes_modes.c', 'vendor/trezor-crypto/aes/aestab.c', 'vendor/trezor-crypto/base58.c', + 'vendor/trezor-crypto/base32.c', 'vendor/trezor-crypto/bignum.c', 'vendor/trezor-crypto/bip32.c', 'vendor/trezor-crypto/bip39.c', @@ -63,6 +65,7 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/hasher.c', 'vendor/trezor-crypto/hmac.c', 'vendor/trezor-crypto/memzero.c', + 'vendor/trezor-crypto/nem.c', 'vendor/trezor-crypto/nist256p1.c', 'vendor/trezor-crypto/pbkdf2.c', 'vendor/trezor-crypto/rand.c', diff --git a/SConscript.unix b/SConscript.unix index 01fdf7d18..818d511ac 100644 --- a/SConscript.unix +++ b/SConscript.unix @@ -25,6 +25,7 @@ CPPDEFINES_MOD += [ 'AES_192', ('USE_KECCAK', '1'), ('USE_ETHEREUM', '1'), + ('USE_NEM', '1'), ] SOURCE_MOD += [ 'embed/extmod/modtrezorcrypto/modtrezorcrypto.c', @@ -35,6 +36,7 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/aes/aes_modes.c', 'vendor/trezor-crypto/aes/aestab.c', 'vendor/trezor-crypto/base58.c', + 'vendor/trezor-crypto/base32.c', 'vendor/trezor-crypto/bignum.c', 'vendor/trezor-crypto/bip32.c', 'vendor/trezor-crypto/bip39.c', @@ -67,6 +69,7 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/secp256k1.c', 'vendor/trezor-crypto/sha2.c', 'vendor/trezor-crypto/sha3.c', + 'vendor/trezor-crypto/nem.c', ] # modtrezorio diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h index 6fc81aa1c..7dacea55c 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h @@ -39,6 +39,7 @@ STATIC const mp_obj_type_t mod_trezorcrypto_HDNode_type; #define XPUB_MAXLEN 128 #define ADDRESS_MAXLEN 36 +#define NEM_ADDRESS_SIZE 40 /// def __init__(self, /// depth: int, @@ -313,6 +314,20 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_address(mp_obj_t self, mp_obj_t version) } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_address_obj, mod_trezorcrypto_HDNode_address); +/// def nem_address(self, network: int) -> str: +/// ''' +/// Compute a NEM address string from the HD node. +/// ''' +STATIC mp_obj_t mod_trezorcrypto_HDNode_nem_address(mp_obj_t self, mp_obj_t network) { + mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(self); + + uint8_t n = mp_obj_get_int_truncated(network); + char address[NEM_ADDRESS_SIZE]; + hdnode_get_nem_address(&o->hdnode, n, address); + return mp_obj_new_str(address, strlen(address), false); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_nem_address_obj, mod_trezorcrypto_HDNode_nem_address); + /// def ethereum_pubkeyhash(self) -> bytes: /// ''' /// Compute an Ethereum pubkeyhash (aka address) from the HD node. @@ -340,6 +355,7 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_HDNode_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_private_key), MP_ROM_PTR(&mod_trezorcrypto_HDNode_private_key_obj) }, { MP_ROM_QSTR(MP_QSTR_public_key), MP_ROM_PTR(&mod_trezorcrypto_HDNode_public_key_obj) }, { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&mod_trezorcrypto_HDNode_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_nem_address), MP_ROM_PTR(&mod_trezorcrypto_HDNode_nem_address_obj) }, { MP_ROM_QSTR(MP_QSTR_ethereum_pubkeyhash), MP_ROM_PTR(&mod_trezorcrypto_HDNode_ethereum_pubkeyhash_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_HDNode_locals_dict, mod_trezorcrypto_HDNode_locals_dict_table); diff --git a/mocks/generated/trezorconfig.py b/mocks/generated/trezorconfig.py index ab1462b94..376b5184e 100644 --- a/mocks/generated/trezorconfig.py +++ b/mocks/generated/trezorconfig.py @@ -7,6 +7,12 @@ def init() -> None: called from this module! ''' +# extmod/modtrezorconfig/modtrezorconfig.c +def check_pin(pin: int, waitcallback: (int, int -> None)) -> bool: + ''' + Check the given PIN. Returns True on success, False on failure. + ''' + # extmod/modtrezorconfig/modtrezorconfig.c def unlock(pin: int, waitcallback: (int, int -> None)) -> bool: ''' diff --git a/mocks/generated/trezorcrypto.py b/mocks/generated/trezorcrypto.py index af87455a7..a110d65af 100644 --- a/mocks/generated/trezorcrypto.py +++ b/mocks/generated/trezorcrypto.py @@ -93,6 +93,11 @@ class HDNode: Compute a base58-encoded address string from the HD node. ''' + def nem_address(self, network: int) -> str: + ''' + Compute a NEM address string from the HD node. + ''' + def ethereum_pubkeyhash(self) -> bytes: ''' Compute an Ethereum pubkeyhash (aka address) from the HD node. diff --git a/mocks/generated/trezorio.py b/mocks/generated/trezorio.py index 0c79c832c..e816c89c0 100644 --- a/mocks/generated/trezorio.py +++ b/mocks/generated/trezorio.py @@ -128,14 +128,17 @@ class USB: ''' def __init__(self, + device_class: int=0, + device_subclass: int=0, + device_protocol: int=0, vendor_id: int, product_id: int, release_num: int, manufacturer: str='', product: str='', serial_number: str='', - configuration: str='', - interface: str='') -> None: + interface: str='', + usb21_enabled: bool=True) -> None: ''' ''' diff --git a/mocks/generated/trezorui.py b/mocks/generated/trezorui.py index acec41492..16392fd67 100644 --- a/mocks/generated/trezorui.py +++ b/mocks/generated/trezorui.py @@ -112,7 +112,7 @@ class Display: Call without the xy parameter to just perform the read of the value. ''' - def save(self, filename: str) -> None: + def save(self, prefix: str) -> None: ''' - Saves current display contents to file filename. + Saves current display contents to PNG file with given prefix. ''' diff --git a/mocks/generated/trezorutils.py b/mocks/generated/trezorutils.py index fefe5d3ff..8c7a3763c 100644 --- a/mocks/generated/trezorutils.py +++ b/mocks/generated/trezorutils.py @@ -24,3 +24,21 @@ def halt(msg: str = None) -> None: ''' Halts execution. ''' + +# extmod/modtrezorutils/modtrezorutils.c +def set_mode_unprivileged() -> None: + ''' + Set unprivileged mode. + ''' + +# extmod/modtrezorutils/modtrezorutils.c +def symbol(name: str) -> str/int/None: + ''' + Retrieve internal symbol. + ''' + +# extmod/modtrezorutils/modtrezorutils.c +def model() -> str: + ''' + Return which hardware model we are running on. + ''' diff --git a/src/apps/nem/__init__.py b/src/apps/nem/__init__.py new file mode 100644 index 000000000..02ffd256c --- /dev/null +++ b/src/apps/nem/__init__.py @@ -0,0 +1,13 @@ +from trezor.wire import register, protobuf_workflow +from trezor.utils import unimport +from trezor.messages.wire_types import NEMGetAddress + + +@unimport +def dispatch_NemGetAddress(*args, **kwargs): + from .get_address import nem_get_address + return nem_get_address(*args, **kwargs) + + +def boot(): + register(NEMGetAddress, protobuf_workflow, dispatch_NemGetAddress) diff --git a/src/apps/nem/get_address.py b/src/apps/nem/get_address.py new file mode 100644 index 000000000..46bbc2548 --- /dev/null +++ b/src/apps/nem/get_address.py @@ -0,0 +1,20 @@ +from apps.wallet.get_address import _show_address +from apps.common import seed +from trezor.messages.NEMAddress import NEMAddress +from .helpers import * + +_NEM_CURVE = 'ed25519-keccak' + + +async def nem_get_address(ctx, msg): + network = nem_validate_network(msg.network) + node = await seed.derive_node(ctx, msg.address_n, _NEM_CURVE) + address = node.nem_address(network) + + if msg.show_display: + while True: + if await _show_address(ctx, address): + break + + return NEMAddress(address=address) + diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py new file mode 100644 index 000000000..1ac0bcb1f --- /dev/null +++ b/src/apps/nem/helpers.py @@ -0,0 +1,14 @@ + +from micropython import const + +_NEM_NETWORK_MAINNET = const(0x68) +_NEM_NETWORK_TESTNET = const(0x98) +_NEM_NETWORK_MIJIN = const(0x60) + + +def nem_validate_network(network): + if network in (_NEM_NETWORK_MAINNET, _NEM_NETWORK_TESTNET, _NEM_NETWORK_MIJIN): + return network + if network is None: + return _NEM_NETWORK_MAINNET + raise ValueError('Invalid NEM network') diff --git a/src/main.py b/src/main.py index 5b1fe06c2..0c239327c 100644 --- a/src/main.py +++ b/src/main.py @@ -12,6 +12,7 @@ import apps.management import apps.wallet import apps.ethereum import apps.lisk +import apps.nem if __debug__: import apps.debug else: @@ -23,6 +24,7 @@ apps.management.boot() apps.wallet.boot() apps.ethereum.boot() apps.lisk.boot() +apps.nem.boot() if __debug__: apps.debug.boot() else: From 33ac5de8486642e25c6b4ccf69577568f08dbc0d Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 12 Mar 2018 13:21:41 +0100 Subject: [PATCH 02/53] wallet/nem: basic modtrezorcrypto for nem --- .../modtrezorcrypto/modtrezorcrypto-nem.h | 47 +++++++++++++++++++ .../extmod/modtrezorcrypto/modtrezorcrypto.c | 2 + mocks/generated/trezorcrypto.py | 6 +++ src/trezor/crypto/__init__.py | 2 +- 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h new file mode 100644 index 000000000..67e35a3b7 --- /dev/null +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h @@ -0,0 +1,47 @@ +/* + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "py/objstr.h" +#include "nem.h" + +/// def validate_address(address: str, network: int) -> bool: +/// ''' +/// Validate a NEM address +/// ''' +STATIC mp_obj_t mod_trezorcrypto_nem_validate_address(mp_obj_t address, mp_obj_t network) { + + mp_buffer_info_t addr; + mp_get_buffer_raise(address, &addr, MP_BUFFER_READ); + + uint32_t n = mp_obj_get_int_truncated(network); + return mp_obj_new_bool(nem_validate_address(addr.buf, n)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_nem_validate_address_obj, mod_trezorcrypto_nem_validate_address); + +// objects definition +STATIC const mp_rom_map_elem_t mod_trezorcrypto_nem_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_validate_address), MP_ROM_PTR(&mod_trezorcrypto_nem_validate_address_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_nem_globals, mod_trezorcrypto_nem_globals_table); + +// module definition +STATIC const mp_obj_module_t mod_trezorcrypto_nem_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mod_trezorcrypto_nem_globals, +}; diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto.c b/embed/extmod/modtrezorcrypto/modtrezorcrypto.c index 466a409b4..de107b881 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto.c +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto.c @@ -36,6 +36,7 @@ #include "modtrezorcrypto-curve25519.h" #include "modtrezorcrypto-ed25519.h" #include "modtrezorcrypto-nist256p1.h" +#include "modtrezorcrypto-nem.h" #include "modtrezorcrypto-pbkdf2.h" #include "modtrezorcrypto-random.h" #include "modtrezorcrypto-rfc6979.h" @@ -60,6 +61,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorcrypto_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_curve25519), MP_ROM_PTR(&mod_trezorcrypto_curve25519_module) }, { MP_ROM_QSTR(MP_QSTR_ed25519), MP_ROM_PTR(&mod_trezorcrypto_ed25519_module) }, { MP_ROM_QSTR(MP_QSTR_nist256p1), MP_ROM_PTR(&mod_trezorcrypto_nist256p1_module) }, + { MP_ROM_QSTR(MP_QSTR_nem), MP_ROM_PTR(&mod_trezorcrypto_nem_module) }, { MP_ROM_QSTR(MP_QSTR_pbkdf2), MP_ROM_PTR(&mod_trezorcrypto_Pbkdf2_type) }, { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mod_trezorcrypto_random_module) }, { MP_ROM_QSTR(MP_QSTR_rfc6979), MP_ROM_PTR(&mod_trezorcrypto_Rfc6979_type) }, diff --git a/mocks/generated/trezorcrypto.py b/mocks/generated/trezorcrypto.py index a110d65af..ddba1f1c6 100644 --- a/mocks/generated/trezorcrypto.py +++ b/mocks/generated/trezorcrypto.py @@ -309,6 +309,12 @@ def cosi_sign(secret_key: bytes, message: bytes, nonce: bytes, sigR: bytes, comb Produce signature of message using COSI cosigning scheme. ''' +# extmod/modtrezorcrypto/modtrezorcrypto-nem.h +def validate_address(address: str, network: int) -> bool: + ''' + Validate a NEM address + ''' + # extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h def generate_secret() -> bytes: ''' diff --git a/src/trezor/crypto/__init__.py b/src/trezor/crypto/__init__.py index 0cb5c1497..1041d98cf 100644 --- a/src/trezor/crypto/__init__.py +++ b/src/trezor/crypto/__init__.py @@ -1 +1 @@ -from trezorcrypto import bip32, bip39, chacha20poly1305, crc, pbkdf2, random, rfc6979 # noqa: F401 +from trezorcrypto import bip32, bip39, chacha20poly1305, crc, pbkdf2, random, rfc6979, nem # noqa: F401 From a18b39874733b6e5940a1f874df101c094379671 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 14 Mar 2018 14:53:11 +0100 Subject: [PATCH 03/53] nem: unit test for address --- src/apps/nem/get_address.py | 4 +- src/apps/nem/helpers.py | 11 ++-- tests/test_apps.nem.address.py | 93 ++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 tests/test_apps.nem.address.py diff --git a/src/apps/nem/get_address.py b/src/apps/nem/get_address.py index 46bbc2548..a223fdb14 100644 --- a/src/apps/nem/get_address.py +++ b/src/apps/nem/get_address.py @@ -3,12 +3,10 @@ from apps.common import seed from trezor.messages.NEMAddress import NEMAddress from .helpers import * -_NEM_CURVE = 'ed25519-keccak' - async def nem_get_address(ctx, msg): network = nem_validate_network(msg.network) - node = await seed.derive_node(ctx, msg.address_n, _NEM_CURVE) + node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE) address = node.nem_address(network) if msg.show_display: diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index 1ac0bcb1f..8e543681b 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -1,14 +1,15 @@ from micropython import const -_NEM_NETWORK_MAINNET = const(0x68) -_NEM_NETWORK_TESTNET = const(0x98) -_NEM_NETWORK_MIJIN = const(0x60) +NEM_NETWORK_MAINNET = const(0x68) +NEM_NETWORK_TESTNET = const(0x98) +NEM_NETWORK_MIJIN = const(0x60) +NEM_CURVE = 'ed25519-keccak' def nem_validate_network(network): - if network in (_NEM_NETWORK_MAINNET, _NEM_NETWORK_TESTNET, _NEM_NETWORK_MIJIN): + if network in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN): return network if network is None: - return _NEM_NETWORK_MAINNET + return NEM_NETWORK_MAINNET raise ValueError('Invalid NEM network') diff --git a/tests/test_apps.nem.address.py b/tests/test_apps.nem.address.py new file mode 100644 index 000000000..580ea5a29 --- /dev/null +++ b/tests/test_apps.nem.address.py @@ -0,0 +1,93 @@ +from common import * +from ubinascii import unhexlify +from trezor.crypto import bip32 +from apps.nem.helpers import NEM_NETWORK_MAINNET, NEM_CURVE + + +class TestNemAddress(unittest.TestCase): + + # test vectors from https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/1.test-keys.dat + def test_addresses(self): + # private key, public key, address + test_cases = [ + ('575dbb3062267eff57c970a336ebbc8fbcfe12c5bd3ed7bc11eb0481d7704ced', + 'c5f54ba980fcbb657dbaaa42700539b207873e134d2375efeab5f1ab52f87844', + 'NDD2CT6LQLIYQ56KIXI3ENTM6EK3D44P5JFXJ4R4'), + ('5b0e3fa5d3b49a79022d7c1e121ba1cbbf4db5821f47ab8c708ef88defc29bfe', + '96eb2a145211b1b7ab5f0d4b14f8abc8d695c7aee31a3cfc2d4881313c68eea3', + 'NABHFGE5ORQD3LE4O6B7JUFN47ECOFBFASC3SCAC'), + ('738ba9bb9110aea8f15caa353aca5653b4bdfca1db9f34d0efed2ce1325aeeda', + '2d8425e4ca2d8926346c7a7ca39826acd881a8639e81bd68820409c6e30d142a', + 'NAVOZX4HDVOAR4W6K4WJHWPD3MOFU27DFHC7KZOZ'), + ('e8bf9bc0f35c12d8c8bf94dd3a8b5b4034f1063948e3cc5304e55e31aa4b95a6', + '4feed486777ed38e44c489c7c4e93a830e4c4a907fa19a174e630ef0f6ed0409', + 'NBZ6JK5YOCU6UPSSZ5D3G27UHAPHTY5HDQMGE6TT'), + ('c325ea529674396db5675939e7988883d59a5fc17a28ca977e3ba85370232a83', + '83ee32e4e145024d29bca54f71fa335a98b3e68283f1a3099c4d4ae113b53e54', + 'NCQW2P5DNZ5BBXQVGS367DQ4AHC3RXOEVGRCLY6V'), + ('a811cb7a80a7227ae61f6da536534ee3c2744e3c7e4b85f3e0df3c6a9c5613df', + '6d34c04f3a0e42f0c3c6f50e475ae018cfa2f56df58c481ad4300424a6270cbb', + 'NA5IG3XFXZHIPJ5QLKX2FBJPEZYPMBPPK2ZRC3EH'), + ('9c66de1ec77f4dfaaebdf9c8bc599ca7e8e6f0bc71390ffee2c9dd3f3619242a', + 'a8fefd72a3b833dc7c7ed7d57ed86906dac22f88f1f4331873eb2da3152a3e77', + 'NAABHVFJDBM74XMJJ52R7QN2MTTG2ZUXPQS62QZ7'), + ('c56bc16ecf727878c15e24f4ae68569600ac7b251218a44ef50ce54175776edc', + 'c92f761e6d83d20068fd46fe4bd5b97f4c6ba05d23180679b718d1f3e4fb066e', + 'NCLK3OLMHR3F2E3KSBUIZ4K5PNWUDN37MLSJBJZP'), + ('9dd73599283882fa1561ddfc9be5830b5dd453c90465d3fe5eeb646a3606374e', + 'eaf16a4833e59370a04ccd5c63395058de34877b48c17174c71db5ed37b537ed', + 'ND3AHW4VTI5R5QE5V44KIGPRU5FBJ5AFUCJXOY5H'), + ('d9639dc6f49dad02a42fd8c217f1b1b4f8ce31ccd770388b645e639c72ff24fa', + '0f74a2f537cd9c986df018994dde75bdeee05e35eb9fe27adf506ca8475064f7', + 'NCTZ4YAP43ONK3UYTASQVNDMBO24ZHJE65F3QPYE'), + ('efc1992cd50b70ca55ac12c07aa5d026a8b78ffe28a7dbffc9228b26e02c38c1', + '2ebff201255f6cf948c78f528658b99a7c13ac791942fa22d59af610558111f5', + 'NDQ2TMCMXBSFPZQPE2YKH6XLC24HD6LUMN6Z4GIC'), + ('143a815e92e43f3ed1a921ee48cd143931b88b7c3d8e1e981f743c2a5be3c5ba', + '419ed11d48730e4ae2c93f0ea4df853b8d578713a36dab227517cf965861af4e', + 'NA32IDDW2C53BDSBJNFL3Z6UU3J5CJZJMCZDXCF4'), + ('bc1a082f5ac6fdd3a83ade211e5986ac0551bad6c7da96727ec744e5df963e2a', + 'a160e6f9112233a7ce94202ed7a4443e1dac444b5095f9fecbb965fba3f92cac', + 'NADUCEQLC3FTGB25GTA5HOUTB53CBVQNVOIP7NTJ'), + ('4e47b4c6f4c7886e49ec109c61f4af5cfbb1637283218941d55a7f9fe1053f72', + 'fbb91b16df828e21a9802980a44fc757c588bc1382a4cea429d6fa2ae0333f56', + 'NBAF3BFLLPWH33MYE6VUPP5T6DQBZBKIDEQKZQOE'), + ('efc4389da48ce49f85365cfa578c746530e9eac42db1b64ec346119b1becd347', + '2232f24dda0f2ded3ecd831210d4e8521a096b50cadd5a34f3f7083374e1ec12', + 'NBOGTK2I2ATOGGD7ZFJHROG5MWL7XCKAUKSWIVSA'), + ('bdba57c78ca7da16a3360efd13f06276284db8c40351de7fcd38ba0c35ac754d', + 'c334c6c0dad5aaa2a0d0fb4c6032cb6a0edd96bf61125b5ea9062d5a00ee0eee', + 'NCLERTEFYXKLK7RA4MVACEFMXMK3P7QMWTM7FBW2'), + ('20694c1ec3c4a311bcdb29ed2edc428f6d4f9a4c429ad6a5bf3222084e35695f', + '518c4de412efa93de06a55947d11f697639443916ec8fcf04ebc3e6d17d0bd93', + 'NB5V4BPIJHXVONO7UGMJDPFARMFA73BOBNOOYCOV'), + ('e0d4f3760ac107b33c22c2cac24ab2f520b282684f5f66a4212ff95d926323ce', + 'b3d16f4ead9de67c290144da535a0ed2504b03c05e5f1ceb8c7863762f786857', + 'NC4PBAO5TPCAVQKBVOC4F6DMZP3CFSQBU46PSKBD'), + ('efa9afc617412093c9c7a7c211a5332dd556f941e1a88c494ec860608610eea2', + '7e7716e4cebceb731d6f1fd28676f34888e9a0000fcfa1471db1c616c2ddf559', + 'NCFW2LPXIWLBWAQN2QVIWEOD7IVDO3HQBD2OU56K'), + ('d98499b3db61944684ce06a91735af4e14105338473fcf6ebe2b0bcada3dfd21', + '114171230ad6f8522a000cdc73fbc5c733b30bb71f2b146ccbdf34499f79a810', + 'NCUKWDY3J3THKQHAKOK5ALF6ANJQABZHCH7VN6DP') + ] + + for test in test_cases: + private_key = bytearray(reversed(unhexlify(test[0]))) + + node = bip32.HDNode( + depth=0, + fingerprint=0, + child_num=0, + chain_code=bytearray(32), + private_key=private_key, + curve_name=NEM_CURVE + ) + + self.assertEqual(node.nem_address(NEM_NETWORK_MAINNET), test[2]) + # public key is prepended with 1, removing + self.assertEqual(node.public_key()[1:], unhexlify(test[1])) + + +if __name__ == '__main__': + unittest.main() From b5cc678caa1fbbfa0f2edb8d776cca815e879849 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 15 Mar 2018 14:52:34 +0100 Subject: [PATCH 04/53] nem: encrypt --- .../modtrezorcrypto/modtrezorcrypto-bip32.h | 39 +++++ mocks/generated/trezorcrypto.py | 5 + tests/test_apps.nem.address.py | 146 +++++++++++++++++- 3 files changed, 189 insertions(+), 1 deletion(-) diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h index 7dacea55c..c5e509a8f 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h @@ -24,6 +24,7 @@ #include "bip32.h" #include "curves.h" #include "memzero.h" +#include "nem.h" /// class HDNode: /// ''' @@ -328,6 +329,43 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_nem_address(mp_obj_t self, mp_obj_t netw } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_nem_address_obj, mod_trezorcrypto_HDNode_nem_address); +/// def nem_encrypt(self, transfer_public_key: bytes, iv: bytes, salt: bytes, payload: bytes) -> bytes: +/// ''' +/// Encrypts payload using the transfer's public key +/// ''' +STATIC mp_obj_t mod_trezorcrypto_HDNode_nem_encrypt(size_t n_args, const mp_obj_t *args) { + mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(args[0]); + + mp_buffer_info_t transfer_pk; + mp_get_buffer_raise(args[1], &transfer_pk, MP_BUFFER_READ); + if (transfer_pk.len != 32) { + mp_raise_ValueError("transfer_public_key has invalid length"); + } + + mp_buffer_info_t iv; + mp_get_buffer_raise(args[2], &iv, MP_BUFFER_READ); + if (iv.len != 16) { + mp_raise_ValueError("IV has invalid length"); + } + mp_buffer_info_t salt; + mp_get_buffer_raise(args[3], &salt, MP_BUFFER_READ); + if (salt.len != NEM_SALT_SIZE) { + mp_raise_ValueError("Salt has invalid length"); + } + mp_buffer_info_t payload; + mp_get_buffer_raise(args[4], &payload, MP_BUFFER_READ); + if (payload.len == 0) { + mp_raise_ValueError("Payload is empty"); + } + + uint8_t buffer[NEM_ENCRYPTED_SIZE(payload.len)]; + + hdnode_nem_encrypt(&o->hdnode, *(const ed25519_public_key *)transfer_pk.buf, iv.buf, salt.buf, payload.buf, payload.len, buffer); + return mp_obj_new_bytes(buffer, sizeof(buffer)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorcrypto_HDNode_nem_encrypt_obj, 5, 5, mod_trezorcrypto_HDNode_nem_encrypt); + + /// def ethereum_pubkeyhash(self) -> bytes: /// ''' /// Compute an Ethereum pubkeyhash (aka address) from the HD node. @@ -356,6 +394,7 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_HDNode_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_public_key), MP_ROM_PTR(&mod_trezorcrypto_HDNode_public_key_obj) }, { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&mod_trezorcrypto_HDNode_address_obj) }, { MP_ROM_QSTR(MP_QSTR_nem_address), MP_ROM_PTR(&mod_trezorcrypto_HDNode_nem_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_nem_encrypt), MP_ROM_PTR(&mod_trezorcrypto_HDNode_nem_encrypt_obj) }, { MP_ROM_QSTR(MP_QSTR_ethereum_pubkeyhash), MP_ROM_PTR(&mod_trezorcrypto_HDNode_ethereum_pubkeyhash_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_HDNode_locals_dict, mod_trezorcrypto_HDNode_locals_dict_table); diff --git a/mocks/generated/trezorcrypto.py b/mocks/generated/trezorcrypto.py index ddba1f1c6..5eaeed8bd 100644 --- a/mocks/generated/trezorcrypto.py +++ b/mocks/generated/trezorcrypto.py @@ -98,6 +98,11 @@ class HDNode: Compute a NEM address string from the HD node. ''' + def nem_encrypt(self, transfer_public_key: bytes, iv: bytes, salt: bytes, payload: bytes) -> bytes: + ''' + Encrypts payload using the transfer's public key + ''' + def ethereum_pubkeyhash(self) -> bytes: ''' Compute an Ethereum pubkeyhash (aka address) from the HD node. diff --git a/tests/test_apps.nem.address.py b/tests/test_apps.nem.address.py index 580ea5a29..ee35a93df 100644 --- a/tests/test_apps.nem.address.py +++ b/tests/test_apps.nem.address.py @@ -6,8 +6,8 @@ from apps.nem.helpers import NEM_NETWORK_MAINNET, NEM_CURVE class TestNemAddress(unittest.TestCase): - # test vectors from https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/1.test-keys.dat def test_addresses(self): + # test vectors from https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/1.test-keys.dat # private key, public key, address test_cases = [ ('575dbb3062267eff57c970a336ebbc8fbcfe12c5bd3ed7bc11eb0481d7704ced', @@ -88,6 +88,150 @@ class TestNemAddress(unittest.TestCase): # public key is prepended with 1, removing self.assertEqual(node.public_key()[1:], unhexlify(test[1])) + def test_encryption(self): + # test vectors from https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/4.test-cipher.dat + # private key, transfer public key, salt, iv, plain text, cipher text + test_cases = [ + {'private': '3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6', + 'public': '57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21', + 'salt': '83616c67f076d356fd1288a6e0fd7a60488ba312a3adf0088b1b33c7655c3e6a', + 'iv': 'a73ff5c32f8fd055b09775817a6a3f95', + 'input': '86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c', + 'output': '70815da779b1b954d7a7f00c16940e9917a0412a06a444b539bf147603eef87f'}, + {'private': '3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6', + 'public': '57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21', + 'salt': '703ce0b1d276b10eef35672df03234385a903460db18ba9d4e05b3ad31abb284', + 'iv': '91246c2d5493867c4fa3e78f85963677', + 'input': '86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c', + 'output': '564b2f40d42c0efc1bd6f057115a5abd1564cae36d7ccacf5d825d38401aa894'}, + {'private': '3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6', + 'public': '57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21', + 'salt': 'b22e8e8e7373ac31ca7f0f6eb8b93130aba5266772a658593f3a11792e7e8d92', + 'iv': '9f8e33d82374dad6aac0e3dbe7aea704', + 'input': '86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c', + 'output': '7cab88d00a3fc656002eccbbd966e1d5d14a3090d92cf502cdbf843515625dcf'}, + {'private': '3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6', + 'public': '57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21', + 'salt': 'af646c54cd153dffe453b60efbceeb85c1e95a414ea0036c4da94afb3366f5d9', + 'iv': '6acdf8e01acc8074ddc807281b6af888', + 'input': '86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c', + 'output': 'aa70543a485b63a4dd141bb7fd78019092ac6fad731e914280a287c7467bae1a'}, + {'private': '3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6', + 'public': '57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21', + 'salt': 'd9c0d386636c8a024935c024589f9cd39e820a16485b14951e690a967830e269', + 'iv': 'f2e9f18aeb374965f54d2f4e31189a8f', + 'input': '86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c', + 'output': '33d97c216ea6498dfddabf94c2e2403d73efc495e9b284d9d90aaff840217d25'}, + {'private': 'd5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a', + 'public': '66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04', + 'salt': '06c227baac1ae3b0b1dc583f4850f13f9ba5d53be4a98fa5c3ea16217847530d', + 'iv': '3735123e78c44895df6ea33fa57e9a72', + 'input': '86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c', + 'output': 'd5b5d66ba8cee0eb7ecf95b143fa77a46d6de13749e12eff40f5a7e649167ccb'}, + {'private': 'd5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a', + 'public': '66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04', + 'salt': '92f55ba5bc6fc2f23e3eedc299357c71518e36ba2447a4da7a9dfe9dfeb107b5', + 'iv': '1cbc4982e53e370052af97ab088fa942', + 'input': '86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c', + 'output': 'd48ef1ef526d805656cfc932aff259eadb17aa3391dde1877a722cba31d935b2'}, + {'private': 'd5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a', + 'public': '66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04', + 'salt': '10f15a39ba49866292a43b7781bc71ca8bbd4889f1616461caf056bcb91b0158', + 'iv': 'c40d531d92bfee969dce91417346c892', + 'input': '49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a4', + 'output': 'e6d75afdb542785669b42198577c5b358d95397d71ec6f5835dca46d332cc08dbf73ea790b7bcb169a65719c0d55054c'}, + {'private': 'd5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a', + 'public': '66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04', + 'salt': '9c01ed42b219b3bbe1a43ae9d7af5c1dd09363baacfdba8f4d03d1046915e26e', + 'iv': '059a35d5f83249e632790015ed6518b9', + 'input': '49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a4', + 'output': '5ef11aadff2eccee8b712dab968fa842eb770818ec0e6663ed242ea8b6bbc1c66d6285ee5b5f03d55dfee382fb4fa25d'}, + {'private': 'd5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a', + 'public': '66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04', + 'salt': 'bc1067e2a7415ea45ff1ca9894338c591ff15f2e57ae2789ae31b9d5bea0f11e', + 'iv': '8c73f0d6613898daeefa3cf8b0686d37', + 'input': '49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a4', + 'output': '6d220213b1878cd40a458f2a1e6e3b48040455fdf504dcd857f4f2ca1ad642e3a44fc401d04e339d302f66a9fad3d919'}, + {'private': '9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921', + 'public': '441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094', + 'salt': 'cf4a21cb790552165827b678ca9695fcaf77566d382325112ff79483455de667', + 'iv': 'bfbf5482e06f55b88bdd9e053b7eee6e', + 'input': '49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a4', + 'output': '1198a78c29c215d5c450f7b8513ead253160bc9fde80d9cc8e6bee2efe9713cf5a09d6293c41033271c9e8c22036a28b'}, + {'private': '9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921', + 'public': '441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094', + 'salt': 'eba5eae8aef79114082c3e70baef95bb02edf13b3897e8be7a70272962ef8838', + 'iv': 'af9a56da3da18e2fbd2948a16332532b', + 'input': '49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a4', + 'output': '1062ab5fbbdee9042ad35bdadfd3047c0a2127fe0f001da1be1b0582185edfc9687be8d68f85795833bb04af9cedd3bb'}, + {'private': '9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921', + 'public': '441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094', + 'salt': '518f8dfd0c138f1ffb4ea8029db15441d70abd893c3d767dc668f23ba7770e27', + 'iv': '42d28307974a1b2a2d921d270cfce03b', + 'input': '49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a4', + 'output': '005e49fb7c5da540a84b034c853fc9f78a6b901ea495aed0c2abd4f08f1a96f9ffefc6a57f1ac09e0aea95ca0f03ffd8'}, + {'private': '9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921', + 'public': '441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094', + 'salt': '582fdf58b53715c26e10ba809e8f2ab70502e5a3d4e9a81100b7227732ab0bbc', + 'iv': '91f2aad3189bb2edc93bc891e73911ba', + 'input': '49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a4', + 'output': '821a69cb16c57f0cb866e590b38069e35faec3ae18f158bb067db83a11237d29ab1e6b868b3147236a0958f15c2e2167'}, + {'private': '9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921', + 'public': '441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094', + 'salt': 'a415b4c006118fb72fc37b2746ef288e23ac45c8ff7ade5f368a31557b6ac93a', + 'iv': '2b7c5f75606c0b8106c6489ea5657a9e', + 'input': '24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85', + 'output': '2781d5ee8ef1cb1596f8902b33dfae5045f84a987ca58173af5830dbce386062'}, + {'private': 'ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc', + 'public': '5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541', + 'salt': '47e73ec362ea82d3a7c5d55532ad51d2cdf5316b981b2b2bd542b0efa027e8ea', + 'iv': 'b2193f59030c8d05a7d3577b7f64dd33', + 'input': '24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85', + 'output': '3f43912db8dd6672b9996e5272e18c4b88fec9d7e8372db9c5f4709a4af1d86f'}, + {'private': 'ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc', + 'public': '5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541', + 'salt': 'aaa006c57b6d1e402650577fe9787d8d285f4bacd7c01f998be49c766f8860c7', + 'iv': '130304ddb9adc8870cf56bcae9487b7f', + 'input': '24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85', + 'output': '878cc7d8c0ef8dac0182a78eedc8080a402f59d8062a6b4ca8f4a74f3c3b3de7'}, + {'private': 'ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc', + 'public': '5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541', + 'salt': '28dc7ccd6c2a939eef64b8be7b9ae248295e7fcd8471c22fa2f98733fea97611', + 'iv': 'cb13890d3a11bc0a7433738263006710', + 'input': '24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85', + 'output': 'e74ded846bebfa912fa1720e4c1415e6e5df7e7a1a7fedb5665d68f1763209a4'}, + {'private': 'ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc', + 'public': '5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541', + 'salt': '79974fa2cad95154d0873902c153ccc3e7d54b17f2eeb3f29b6344cad9365a9a', + 'iv': '22123357979d20f44cc8eb0263d84e0e', + 'input': '24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85', + 'output': 'eb14dec7b8b64d81a2ee4db07b0adf144d4f79a519bbf332b823583fa2d45405'}, + {'private': 'ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc', + 'public': '5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541', + 'salt': '3409a6f8c4dcd9bd04144eb67e55a98696b674735b01bf1196191f29871ef966', + 'iv': 'a823a0965969380ea1f8659ea5fd8fdd', + 'input': '24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85', + 'output': '00a7eb708eae745847173f8217efb05be13059710aee632e3f471ac3c6202b51'}, + ] + + for test in test_cases: + private_key = bytearray(reversed(unhexlify(test['private']))) + node = bip32.HDNode( + depth=0, + fingerprint=0, + child_num=0, + chain_code=bytearray(32), + private_key=private_key, + curve_name=NEM_CURVE + ) + + encrypted = node.nem_encrypt(unhexlify(test['public']), + unhexlify(test['iv']), + unhexlify(test['salt']), + unhexlify(test['input'])) + + self.assertEqual(encrypted, unhexlify(test['output'])) + if __name__ == '__main__': unittest.main() From 4827ff3a036c6041e2431173820a608682f3bb02 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 16 Mar 2018 13:55:37 +0100 Subject: [PATCH 05/53] nem: create transfer (including mosaics) --- src/apps/nem/helpers.py | 9 ++ src/apps/nem/sign_tx.py | 64 +++++++++++++ src/apps/nem/writers.py | 26 ++++++ ...nem.address.py => test_apps.nem.hdnode.py} | 0 tests/test_apps.nem.transfer.py | 92 +++++++++++++++++++ 5 files changed, 191 insertions(+) create mode 100644 src/apps/nem/sign_tx.py create mode 100644 src/apps/nem/writers.py rename tests/{test_apps.nem.address.py => test_apps.nem.hdnode.py} (100%) create mode 100644 tests/test_apps.nem.transfer.py diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index 8e543681b..39d48aba5 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -6,6 +6,15 @@ NEM_NETWORK_TESTNET = const(0x98) NEM_NETWORK_MIJIN = const(0x60) NEM_CURVE = 'ed25519-keccak' +NEM_TRANSACTION_TYPE_TRANSFER = const(0x0101) +NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER = const(0x0801) +NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION = const(0x1001) +NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE = const(0x1002) +NEM_TRANSACTION_TYPE_MULTISIG = const(0x1004) +NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE = const(0x2001) +NEM_TRANSACTION_TYPE_MOSAIC_CREATION = const(0x4001) +NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE = const(0x4002) + def nem_validate_network(network): if network in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN): diff --git a/src/apps/nem/sign_tx.py b/src/apps/nem/sign_tx.py new file mode 100644 index 000000000..4c1294a88 --- /dev/null +++ b/src/apps/nem/sign_tx.py @@ -0,0 +1,64 @@ + +from .helpers import * +from .writers import * + + +def nem_transaction_create_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, + recipient: str, amount: int, payload: bytearray = None, encrypted: bool = False, + mosaics: int = 0) -> bytearray: + + tx = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_TRANSFER, + _nem_get_version(network, mosaics), + timestamp, + signer_public_key, + fee, + deadline) + + write_bytes_with_length(tx, bytearray(recipient)) + write_uint64(tx, amount) + + if payload: + # payload + payload size (u32) + encryption flag (u32) + write_uint32(tx, len(payload) + 2 * 4) + if encrypted: + write_uint32(tx, 0x02) + else: + write_uint32(tx, 0x01) + write_bytes_with_length(tx, payload) + else: + write_uint32(tx, 0) + + if mosaics: + write_uint32(tx, mosaics) + + return tx + + +def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): + identifier_length = 4 + len(namespace) + 4 + len(mosaic) + # indentifier length (u32) + quantity (u64) + identifier size + write_uint32(w, 4 + 8 + identifier_length) + write_uint32(w, identifier_length) + write_bytes_with_length(w, bytearray(namespace)) + write_bytes_with_length(w, bytearray(mosaic)) + write_uint64(w, quantity) + + +def _nem_transaction_write_common(tx_type: int, version: int, timestamp: int, signer: bytes, fee: int, deadline: int)\ + -> bytearray: + ret = bytearray() + write_uint32(ret, tx_type) + write_uint32(ret, version) + write_uint32(ret, timestamp) + + write_bytes_with_length(ret, bytearray(signer)) + write_uint64(ret, fee) + write_uint32(ret, deadline) + + return ret + + +def _nem_get_version(network, mosaics) -> int: + if mosaics: + return network << 24 | 2 + return network << 24 | 1 diff --git a/src/apps/nem/writers.py b/src/apps/nem/writers.py new file mode 100644 index 000000000..4708fa6a9 --- /dev/null +++ b/src/apps/nem/writers.py @@ -0,0 +1,26 @@ + +def write_uint32(w, n: int): + w.append(n & 0xFF) + w.append((n >> 8) & 0xFF) + w.append((n >> 16) & 0xFF) + w.append((n >> 24) & 0xFF) + + +def write_uint64(w, n: int): + w.append(n & 0xFF) + w.append((n >> 8) & 0xFF) + w.append((n >> 16) & 0xFF) + w.append((n >> 24) & 0xFF) + w.append((n >> 32) & 0xFF) + w.append((n >> 40) & 0xFF) + w.append((n >> 48) & 0xFF) + w.append((n >> 56) & 0xFF) + + +def write_bytes(w, buf: bytearray): + w.extend(buf) + + +def write_bytes_with_length(w, buf: bytearray): + write_uint32(w, len(buf)) + write_bytes(w, buf) diff --git a/tests/test_apps.nem.address.py b/tests/test_apps.nem.hdnode.py similarity index 100% rename from tests/test_apps.nem.address.py rename to tests/test_apps.nem.hdnode.py diff --git a/tests/test_apps.nem.transfer.py b/tests/test_apps.nem.transfer.py new file mode 100644 index 000000000..a03d037a1 --- /dev/null +++ b/tests/test_apps.nem.transfer.py @@ -0,0 +1,92 @@ +from common import * + +from apps.nem.sign_tx import * +from trezor.crypto import hashlib + + +class TestNemTransfer(unittest.TestCase): + + def test_create_transfer(self): + + # http://bob.nem.ninja:8765/#/transfer/0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f + t = nem_transaction_create_transfer(NEM_NETWORK_TESTNET, + 0, + unhexlify('e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f'), + 0, + 0, + 'TBGIMRE4SBFRUJXMH7DVF2IBY36L2EDWZ37GVSC4', + 50000000000000) + + self.assertEqual(t, unhexlify('01010000010000980000000020000000e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f00000000000000000000000028000000544247494d52453453424652554a584d48374456463249425933364c324544575a3337475653433400203d88792d000000000000')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f')) + + def test_create_transfer_with_payload(self): + + # http://chain.nem.ninja/#/transfer/e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb + t = nem_transaction_create_transfer(NEM_NETWORK_MAINNET, + 0, + unhexlify( + '8d07f90fb4bbe7715fa327c926770166a11be2e494a970605f2e12557f66c9b9'), + 0, + 0, + 'NBT3WHA2YXG2IR4PWKFFMO772JWOITTD2V4PECSB', + 5175000000000, + bytearray('Good luck!')) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb')) + + def test_create_transfer_with_mosaic(self): + + # http://bob.nem.ninja:8765/#/transfer/3409d9ece28d6296d6d5e220a7e3cb8641a3fb235ffcbd20c95da64f003ace6c + t = nem_transaction_create_transfer(NEM_NETWORK_TESTNET, + 14072100, + unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324'), + 194000000, + 14075700, + 'TBLOODPLWOWMZ2TARX4RFPOSOWLULHXMROBN2WXI', + 3000000, + bytearray('sending you 3 pairs of paddles\n'), + False, + 2) + self.assertEqual(t, unhexlify('010100000200009824b9d60020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd3248034900b0000000034c7d6002800000054424c4f4f44504c574f574d5a3254415258345246504f534f574c554c48584d524f424e32575849c0c62d000000000027000000010000001f00000073656e64696e6720796f752033207061697273206f6620706164646c65730a02000000')) + + nem_transaction_write_mosaic(t, 'gimre.games.pong', 'paddles', 2) + nem_transaction_write_mosaic(t, 'nem', 'xem', 44000000) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('3409d9ece28d6296d6d5e220a7e3cb8641a3fb235ffcbd20c95da64f003ace6c')) + + # http://chain.nem.ninja/#/transfer/882dca18dcbe075e15e0ec5a1d7e6ccd69cc0f1309ffd3fde227bfbc107b3f6e + t = nem_transaction_create_transfer(NEM_NETWORK_MAINNET, + 26730750, + unhexlify( + 'f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01'), + 179500000, + 26734350, + 'NBE223WPKEBHQPCYUC4U4CDUQCRRFMPZLOQLB5OP', + 1000000, + bytearray('enjoy! :)'), + False, + 1) + nem_transaction_write_mosaic(t, 'imre.g', 'tokens', 1) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('882dca18dcbe075e15e0ec5a1d7e6ccd69cc0f1309ffd3fde227bfbc107b3f6e')) + + def test_create_transfer_with_encrypted_payload(self): + + # http://chain.nem.ninja/#/transfer/40e89160e6f83d37f7c82defc0afe2c1605ae8c919134570a51dd27ea1bb516c + + t = nem_transaction_create_transfer(NEM_NETWORK_MAINNET, + 77229, + unhexlify('f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01'), + 30000000, + 80829, + 'NALICEPFLZQRZGPRIJTMJOCPWDNECXTNNG7QLSG3', + 30000000, + unhexlify('4d9dcf9186967d30be93d6d5404ded22812dbbae7c3f0de501bcd7228cba45bded13000eec7b4c6215fc4d3588168c9218167cec98e6977359153a4132e050f594548e61e0dc61c153f0f53c5e65c595239c9eb7c4e7d48e0f4bb8b1dd2f5ddc'), + True) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('40e89160e6f83d37f7c82defc0afe2c1605ae8c919134570a51dd27ea1bb516c')) + + +if __name__ == '__main__': + unittest.main() From 5dd3d1e7e5d78e07f059abac25ea749fb61586ec Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 16 Mar 2018 14:25:48 +0100 Subject: [PATCH 06/53] nem: provision namespaces --- src/apps/nem/sign_tx.py | 24 ++++++++- tests/test_apps.nem.transaction.provision.py | 49 +++++++++++++++++++ ... => test_apps.nem.transaction.transfer.py} | 2 +- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/test_apps.nem.transaction.provision.py rename tests/{test_apps.nem.transfer.py => test_apps.nem.transaction.transfer.py} (99%) diff --git a/src/apps/nem/sign_tx.py b/src/apps/nem/sign_tx.py index 4c1294a88..90cac69f7 100644 --- a/src/apps/nem/sign_tx.py +++ b/src/apps/nem/sign_tx.py @@ -34,6 +34,28 @@ def nem_transaction_create_transfer(network: int, timestamp: int, signer_public_ return tx +def nem_transaction_create_provision_namespace(network: int, timestamp: int, signer_public_key: bytes, fee: int, + deadline: int, namespace: str, parent: str, rental_sink: str, + rental_fee: int) -> bytearray: + + tx = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE, + _nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + write_bytes_with_length(tx, bytearray(rental_sink)) + write_uint64(tx, rental_fee) + write_bytes_with_length(tx, bytearray(namespace)) + if parent: + write_bytes_with_length(tx, bytearray(parent)) + else: + write_uint32(tx, 0xffffffff) + + return tx + + def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): identifier_length = 4 + len(namespace) + 4 + len(mosaic) # indentifier length (u32) + quantity (u64) + identifier size @@ -58,7 +80,7 @@ def _nem_transaction_write_common(tx_type: int, version: int, timestamp: int, si return ret -def _nem_get_version(network, mosaics) -> int: +def _nem_get_version(network, mosaics=None) -> int: if mosaics: return network << 24 | 2 return network << 24 | 1 diff --git a/tests/test_apps.nem.transaction.provision.py b/tests/test_apps.nem.transaction.provision.py new file mode 100644 index 000000000..a2c6aa87f --- /dev/null +++ b/tests/test_apps.nem.transaction.provision.py @@ -0,0 +1,49 @@ +from common import * + +from apps.nem.sign_tx import * +from trezor.crypto import hashlib + + +class TestNemTransactionProvisition(unittest.TestCase): + + def test_create_provision_namespace(self): + + # http://bob.nem.ninja:8765/#/transfer/0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f + t = nem_transaction_create_provision_namespace(NEM_NETWORK_TESTNET, + 56999445, + unhexlify('84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5'), + 20000000, + 57003045, + 'gimre', + '', + 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35', + 5000000000) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('f7cab28da57204d01a907c697836577a4ae755e6c9bac60dcc318494a22debb3')) + + # http://bob.nem.ninja:8765/#/namespace/7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d + t = nem_transaction_create_provision_namespace(NEM_NETWORK_TESTNET, + 21496797, + unhexlify('244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc'), + 108000000, + 21500397, + 'misc', + 'alice', + 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35', + 5000000000) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d')) + + # http://chain.nem.ninja/#/namespace/57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569 + t = nem_transaction_create_provision_namespace(NEM_NETWORK_MAINNET, + 26699717, + unhexlify('9f3c14f304309c8b72b2821339c4428793b1518bea72d58dd01f19d523518614'), + 108000000, + 26703317, + 'sex', + '', + 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA', + 50000000000) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569')) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_apps.nem.transfer.py b/tests/test_apps.nem.transaction.transfer.py similarity index 99% rename from tests/test_apps.nem.transfer.py rename to tests/test_apps.nem.transaction.transfer.py index a03d037a1..c5e7201c4 100644 --- a/tests/test_apps.nem.transfer.py +++ b/tests/test_apps.nem.transaction.transfer.py @@ -4,7 +4,7 @@ from apps.nem.sign_tx import * from trezor.crypto import hashlib -class TestNemTransfer(unittest.TestCase): +class TestNemTransactionTransfer(unittest.TestCase): def test_create_transfer(self): From 8e7ee89b027e788a43655f2e9c8f7327a95a6004 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 19 Mar 2018 14:49:28 +0100 Subject: [PATCH 07/53] nem: transaction serialization --- src/apps/nem/transaction.py | 86 ++++++++++++++++++++ tests/test_apps.nem.transaction.provision.py | 2 +- tests/test_apps.nem.transaction.transfer.py | 2 +- 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/apps/nem/transaction.py diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py new file mode 100644 index 000000000..90cac69f7 --- /dev/null +++ b/src/apps/nem/transaction.py @@ -0,0 +1,86 @@ + +from .helpers import * +from .writers import * + + +def nem_transaction_create_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, + recipient: str, amount: int, payload: bytearray = None, encrypted: bool = False, + mosaics: int = 0) -> bytearray: + + tx = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_TRANSFER, + _nem_get_version(network, mosaics), + timestamp, + signer_public_key, + fee, + deadline) + + write_bytes_with_length(tx, bytearray(recipient)) + write_uint64(tx, amount) + + if payload: + # payload + payload size (u32) + encryption flag (u32) + write_uint32(tx, len(payload) + 2 * 4) + if encrypted: + write_uint32(tx, 0x02) + else: + write_uint32(tx, 0x01) + write_bytes_with_length(tx, payload) + else: + write_uint32(tx, 0) + + if mosaics: + write_uint32(tx, mosaics) + + return tx + + +def nem_transaction_create_provision_namespace(network: int, timestamp: int, signer_public_key: bytes, fee: int, + deadline: int, namespace: str, parent: str, rental_sink: str, + rental_fee: int) -> bytearray: + + tx = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE, + _nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + write_bytes_with_length(tx, bytearray(rental_sink)) + write_uint64(tx, rental_fee) + write_bytes_with_length(tx, bytearray(namespace)) + if parent: + write_bytes_with_length(tx, bytearray(parent)) + else: + write_uint32(tx, 0xffffffff) + + return tx + + +def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): + identifier_length = 4 + len(namespace) + 4 + len(mosaic) + # indentifier length (u32) + quantity (u64) + identifier size + write_uint32(w, 4 + 8 + identifier_length) + write_uint32(w, identifier_length) + write_bytes_with_length(w, bytearray(namespace)) + write_bytes_with_length(w, bytearray(mosaic)) + write_uint64(w, quantity) + + +def _nem_transaction_write_common(tx_type: int, version: int, timestamp: int, signer: bytes, fee: int, deadline: int)\ + -> bytearray: + ret = bytearray() + write_uint32(ret, tx_type) + write_uint32(ret, version) + write_uint32(ret, timestamp) + + write_bytes_with_length(ret, bytearray(signer)) + write_uint64(ret, fee) + write_uint32(ret, deadline) + + return ret + + +def _nem_get_version(network, mosaics=None) -> int: + if mosaics: + return network << 24 | 2 + return network << 24 | 1 diff --git a/tests/test_apps.nem.transaction.provision.py b/tests/test_apps.nem.transaction.provision.py index a2c6aa87f..9ef13edf5 100644 --- a/tests/test_apps.nem.transaction.provision.py +++ b/tests/test_apps.nem.transaction.provision.py @@ -1,6 +1,6 @@ from common import * -from apps.nem.sign_tx import * +from apps.nem.transaction import * from trezor.crypto import hashlib diff --git a/tests/test_apps.nem.transaction.transfer.py b/tests/test_apps.nem.transaction.transfer.py index c5e7201c4..6bb9791c1 100644 --- a/tests/test_apps.nem.transaction.transfer.py +++ b/tests/test_apps.nem.transaction.transfer.py @@ -1,6 +1,6 @@ from common import * -from apps.nem.sign_tx import * +from apps.nem.transaction import * from trezor.crypto import hashlib From fb15e993f1528fe8b3493b336e7e1b1beb079ff1 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 19 Mar 2018 14:37:23 +0100 Subject: [PATCH 08/53] embed/crypto: ed25519 keccak signing to be discussed whether not to provide a hash func instead of string --- .../modtrezorcrypto/modtrezorcrypto-ed25519.h | 29 ++++++++++++++----- mocks/generated/trezorcrypto.py | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h index 450d36668..683815f77 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h @@ -20,6 +20,7 @@ #include "py/objstr.h" #include "ed25519-donna/ed25519.h" +#include "ed25519-donna/ed25519-keccak.h" #include "rand.h" @@ -54,14 +55,14 @@ STATIC mp_obj_t mod_trezorcrypto_ed25519_publickey(mp_obj_t secret_key) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_ed25519_publickey_obj, mod_trezorcrypto_ed25519_publickey); -/// def sign(secret_key: bytes, message: bytes) -> bytes: +/// def sign(secret_key: bytes, message: bytes, hasher: str='') -> bytes: /// ''' /// Uses secret key to produce the signature of message. /// ''' -STATIC mp_obj_t mod_trezorcrypto_ed25519_sign(mp_obj_t secret_key, mp_obj_t message) { +STATIC mp_obj_t mod_trezorcrypto_ed25519_sign(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t sk, msg; - mp_get_buffer_raise(secret_key, &sk, MP_BUFFER_READ); - mp_get_buffer_raise(message, &msg, MP_BUFFER_READ); + mp_get_buffer_raise(args[0], &sk, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &msg, MP_BUFFER_READ); if (sk.len != 32) { mp_raise_ValueError("Invalid length of secret key"); } @@ -69,12 +70,26 @@ STATIC mp_obj_t mod_trezorcrypto_ed25519_sign(mp_obj_t secret_key, mp_obj_t mess mp_raise_ValueError("Empty data to sign"); } ed25519_public_key pk; - ed25519_publickey(*(const ed25519_secret_key *)sk.buf, pk); uint8_t out[64]; - ed25519_sign(msg.buf, msg.len, *(const ed25519_secret_key *)sk.buf, pk, *(ed25519_signature *)out); + mp_buffer_info_t hash_func; + + if (n_args == 3) { + mp_get_buffer_raise(args[2], &hash_func, MP_BUFFER_READ); + // if hash_func == 'keccak': + if (memcmp(hash_func.buf, "keccak", sizeof("keccak")) == 0) { + ed25519_publickey_keccak(*(const ed25519_secret_key *)sk.buf, pk); + ed25519_sign_keccak(msg.buf, msg.len, *(const ed25519_secret_key *)sk.buf, pk, *(ed25519_signature *)out); + } else { + mp_raise_ValueError("Unknown hash function"); + } + } else { + ed25519_publickey(*(const ed25519_secret_key *)sk.buf, pk); + ed25519_sign(msg.buf, msg.len, *(const ed25519_secret_key *)sk.buf, pk, *(ed25519_signature *)out); + } + return mp_obj_new_bytes(out, sizeof(out)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_ed25519_sign_obj, mod_trezorcrypto_ed25519_sign); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorcrypto_ed25519_sign_obj, 2, 3, mod_trezorcrypto_ed25519_sign); /// def verify(public_key: bytes, signature: bytes, message: bytes) -> bool: /// ''' diff --git a/mocks/generated/trezorcrypto.py b/mocks/generated/trezorcrypto.py index 5eaeed8bd..6023d51cc 100644 --- a/mocks/generated/trezorcrypto.py +++ b/mocks/generated/trezorcrypto.py @@ -284,7 +284,7 @@ def publickey(secret_key: bytes) -> bytes: ''' # extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h -def sign(secret_key: bytes, message: bytes) -> bytes: +def sign(secret_key: bytes, message: bytes, hasher: str='') -> bytes: ''' Uses secret key to produce the signature of message. ''' From d28a3ca5cff0e3b1f93795d776c7561bbbe046ff Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 19 Mar 2018 14:50:10 +0100 Subject: [PATCH 09/53] nem: first signed transaction --- src/apps/nem/__init__.py | 9 ++++- src/apps/nem/layout.py | 29 ++++++++++++++ src/apps/nem/sign_tx.py | 86 ---------------------------------------- src/apps/nem/signing.py | 43 ++++++++++++++++++++ 4 files changed, 80 insertions(+), 87 deletions(-) create mode 100644 src/apps/nem/layout.py delete mode 100644 src/apps/nem/sign_tx.py create mode 100644 src/apps/nem/signing.py diff --git a/src/apps/nem/__init__.py b/src/apps/nem/__init__.py index 02ffd256c..90a04f153 100644 --- a/src/apps/nem/__init__.py +++ b/src/apps/nem/__init__.py @@ -1,6 +1,6 @@ from trezor.wire import register, protobuf_workflow from trezor.utils import unimport -from trezor.messages.wire_types import NEMGetAddress +from trezor.messages.wire_types import NEMGetAddress, NEMSignTx @unimport @@ -9,5 +9,12 @@ def dispatch_NemGetAddress(*args, **kwargs): return nem_get_address(*args, **kwargs) +@unimport +def dispatch_NemSignTx(*args, **kwargs): + from .signing import nem_sign_tx + return nem_sign_tx(*args, **kwargs) + + def boot(): register(NEMGetAddress, protobuf_workflow, dispatch_NemGetAddress) + register(NEMSignTx, protobuf_workflow, dispatch_NemSignTx) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py new file mode 100644 index 000000000..9573b49ab --- /dev/null +++ b/src/apps/nem/layout.py @@ -0,0 +1,29 @@ +from apps.common.confirm import * +from trezor import ui +from trezor.messages import ButtonRequestType +from trezor.ui.text import Text + +# todo wording, ui + + +async def require_confirm_tx(ctx, recipient, value): + content = Text('Confirm sending', ui.ICON_SEND, + ui.BOLD, value, + ui.NORMAL, 'to', + ui.MONO, recipient, + icon_color=ui.GREEN) + await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 + + +async def require_confirm_fee(ctx, value, fee): + content = Text('Confirm transaction', ui.ICON_SEND, + ui.BOLD, value, + ui.NORMAL, 'fee:', + ui.BOLD, fee, + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def require_confirm_action(ctx): + content = Text('Send unencrypted transaction?', ui.ICON_SEND) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) diff --git a/src/apps/nem/sign_tx.py b/src/apps/nem/sign_tx.py deleted file mode 100644 index 90cac69f7..000000000 --- a/src/apps/nem/sign_tx.py +++ /dev/null @@ -1,86 +0,0 @@ - -from .helpers import * -from .writers import * - - -def nem_transaction_create_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, - recipient: str, amount: int, payload: bytearray = None, encrypted: bool = False, - mosaics: int = 0) -> bytearray: - - tx = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_TRANSFER, - _nem_get_version(network, mosaics), - timestamp, - signer_public_key, - fee, - deadline) - - write_bytes_with_length(tx, bytearray(recipient)) - write_uint64(tx, amount) - - if payload: - # payload + payload size (u32) + encryption flag (u32) - write_uint32(tx, len(payload) + 2 * 4) - if encrypted: - write_uint32(tx, 0x02) - else: - write_uint32(tx, 0x01) - write_bytes_with_length(tx, payload) - else: - write_uint32(tx, 0) - - if mosaics: - write_uint32(tx, mosaics) - - return tx - - -def nem_transaction_create_provision_namespace(network: int, timestamp: int, signer_public_key: bytes, fee: int, - deadline: int, namespace: str, parent: str, rental_sink: str, - rental_fee: int) -> bytearray: - - tx = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE, - _nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) - - write_bytes_with_length(tx, bytearray(rental_sink)) - write_uint64(tx, rental_fee) - write_bytes_with_length(tx, bytearray(namespace)) - if parent: - write_bytes_with_length(tx, bytearray(parent)) - else: - write_uint32(tx, 0xffffffff) - - return tx - - -def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): - identifier_length = 4 + len(namespace) + 4 + len(mosaic) - # indentifier length (u32) + quantity (u64) + identifier size - write_uint32(w, 4 + 8 + identifier_length) - write_uint32(w, identifier_length) - write_bytes_with_length(w, bytearray(namespace)) - write_bytes_with_length(w, bytearray(mosaic)) - write_uint64(w, quantity) - - -def _nem_transaction_write_common(tx_type: int, version: int, timestamp: int, signer: bytes, fee: int, deadline: int)\ - -> bytearray: - ret = bytearray() - write_uint32(ret, tx_type) - write_uint32(ret, version) - write_uint32(ret, timestamp) - - write_bytes_with_length(ret, bytearray(signer)) - write_uint64(ret, fee) - write_uint32(ret, deadline) - - return ret - - -def _nem_get_version(network, mosaics=None) -> int: - if mosaics: - return network << 24 | 2 - return network << 24 | 1 diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py new file mode 100644 index 000000000..2f07ef5a2 --- /dev/null +++ b/src/apps/nem/signing.py @@ -0,0 +1,43 @@ +from apps.nem.transaction import * +from apps.nem.layout import * +from trezor.messages.NEMSignTx import NEMSignTx +from trezor.messages.NEMSignedTx import NEMSignedTx + + +async def nem_sign_tx(ctx, msg: NEMSignTx): + from ..common import seed + from trezor.crypto.curve import ed25519 + + # if len(msg.transfer.public_key): + # todo encrypt + + node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) + # 0x01 prefix is not part of the actual public key, hence removed + public_key = node.public_key()[1:] + + tx = nem_transaction_create_transfer( + msg.transaction.network, + msg.transaction.timestamp, + public_key, + msg.transaction.fee, + msg.transaction.deadline, + msg.transfer.recipient, + msg.transfer.amount, + msg.transfer.payload, # todo might require encryption + msg.transfer.public_key is not None, + len(msg.transfer.mosaics) + ) + + for mosaic in msg.transfer.mosaics: + nem_transaction_write_mosaic(tx, mosaic.namespace, mosaic.mosaic, mosaic.quantity) + + await require_confirm_action(ctx) + await require_confirm_fee(ctx, msg.transfer.amount, msg.transaction.fee) + await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) + + signature = ed25519.sign(node.private_key(), tx, 'keccak') + + resp = NEMSignedTx() + resp.data = tx + resp.signature = signature + return resp From 9674a58db036962a2b2913819914a81cc22ab9b3 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 21 Mar 2018 14:30:28 +0100 Subject: [PATCH 10/53] nem: encrypted payload --- src/apps/nem/helpers.py | 4 ++++ src/apps/nem/signing.py | 40 +++++++++++++++++++++++++++---------- src/apps/nem/transaction.py | 1 + 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index 39d48aba5..091498e6f 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -15,6 +15,10 @@ NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE = const(0x2001) NEM_TRANSACTION_TYPE_MOSAIC_CREATION = const(0x4001) NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE = const(0x4002) +NEM_SALT_SIZE = const(32) +AES_BLOCK_SIZE = const(16) +NEM_HASH_ALG = 'keccak' + def nem_validate_network(network): if network in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN): diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 2f07ef5a2..4973a57a3 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -1,17 +1,25 @@ from apps.nem.transaction import * from apps.nem.layout import * +from apps.nem import helpers +from apps.common import seed from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMSignedTx import NEMSignedTx +from trezor.crypto.curve import ed25519 +from trezor.crypto import random async def nem_sign_tx(ctx, msg: NEMSignTx): - from ..common import seed - from trezor.crypto.curve import ed25519 - - # if len(msg.transfer.public_key): - # todo encrypt node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) + + payload = msg.transfer.payload + encrypted = False + if msg.transfer.public_key is not None: + if payload is None: + raise ValueError("Public key provided but no payload to encrypt") + payload = _nem_encrypt(node, msg.transfer.public_key, msg.transfer.payload) + encrypted = True + # 0x01 prefix is not part of the actual public key, hence removed public_key = node.public_key()[1:] @@ -23,21 +31,31 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): msg.transaction.deadline, msg.transfer.recipient, msg.transfer.amount, - msg.transfer.payload, # todo might require encryption - msg.transfer.public_key is not None, + payload, + encrypted, len(msg.transfer.mosaics) ) for mosaic in msg.transfer.mosaics: nem_transaction_write_mosaic(tx, mosaic.namespace, mosaic.mosaic, mosaic.quantity) - await require_confirm_action(ctx) - await require_confirm_fee(ctx, msg.transfer.amount, msg.transaction.fee) - await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) + if payload: # confirm unencrypted + # todo encrypted vs unencrypted + await require_confirm_action(ctx) # todo - signature = ed25519.sign(node.private_key(), tx, 'keccak') + await require_confirm_fee(ctx, msg.transfer.amount, msg.transaction.fee) # todo + await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) # todo + + signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) resp = NEMSignedTx() resp.data = tx resp.signature = signature return resp + + +def _nem_encrypt(node, public_key: bytes, payload: bytes) -> bytes: + salt = random.bytes(helpers.NEM_SALT_SIZE) + iv = random.bytes(helpers.AES_BLOCK_SIZE) + encrypted = node.nem_encrypt(public_key, iv, salt, payload) + return iv + salt + encrypted diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index 90cac69f7..7fc44c236 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -1,6 +1,7 @@ from .helpers import * from .writers import * +from ubinascii import hexlify def nem_transaction_create_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, From 302ec82d3d57876402c7cda736cb7438dde628f4 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 21 Mar 2018 15:39:32 +0100 Subject: [PATCH 11/53] nem/layout: confirm payload (todo) --- src/apps/nem/layout.py | 10 ++++++++-- src/apps/nem/signing.py | 3 +-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index 9573b49ab..be3167c85 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -24,6 +24,12 @@ async def require_confirm_fee(ctx, value, fee): await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) -async def require_confirm_action(ctx): - content = Text('Send unencrypted transaction?', ui.ICON_SEND) +async def require_confirm_action(ctx, payload, encrypt=False): + if encrypt: + content = Text("Send payload encrypted?", ui.ICON_SEND, + ui.NORMAL, payload) + else: + content = Text("Send payload unencrypted?", ui.ICON_SEND, + ui.NORMAL, payload, + icon_color=ui.RED) await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 4973a57a3..1ddc6f6b3 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -40,8 +40,7 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): nem_transaction_write_mosaic(tx, mosaic.namespace, mosaic.mosaic, mosaic.quantity) if payload: # confirm unencrypted - # todo encrypted vs unencrypted - await require_confirm_action(ctx) # todo + await require_confirm_action(ctx, msg.transfer.payload, encrypted) await require_confirm_fee(ctx, msg.transfer.amount, msg.transaction.fee) # todo await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) # todo From 26969688944a2f165089269a0dd528ccbf8e827b Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 22 Mar 2018 13:27:43 +0100 Subject: [PATCH 12/53] nem: transaction mosaic creation --- src/apps/nem/transaction.py | 64 +++++++++++++++++++ ...st_apps.nem.transaction.mosaic.creation.py | 36 +++++++++++ 2 files changed, 100 insertions(+) create mode 100644 tests/test_apps.nem.transaction.mosaic.creation.py diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index 7fc44c236..ac353c322 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -57,6 +57,70 @@ def nem_transaction_create_provision_namespace(network: int, timestamp: int, sig return tx +def nem_transaction_create_mosaic_creation(network: int, timestamp: int, signer_public_key: bytes, fee:int, + deadline: int, namespace: str, mosaic: str, description: str, + divisibility: int, supply: int, mutable_supply: bool, transferable: bool, + levy_type: int, levy_fee: int, levy_address: str, levy_namespace: str, + levy_mosaic: str, creation_sink: str, creation_fee: int): + + w = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_CREATION, + _nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + mosaics_w = bytearray() + write_bytes_with_length(mosaics_w, bytearray(signer_public_key)) + identifier_length = 4 + len(namespace) + 4 + len(mosaic) + write_uint32(mosaics_w, identifier_length) + write_bytes_with_length(mosaics_w, bytearray(namespace)) + write_bytes_with_length(mosaics_w, bytearray(mosaic)) + write_bytes_with_length(mosaics_w, bytearray(description)) + write_uint32(mosaics_w, 4) # number of properties + + nem_write_mosaic(mosaics_w, "divisibility", divisibility) + nem_write_mosaic(mosaics_w, "initialSupply", supply) + nem_write_mosaic(mosaics_w, "supplyMutable", mutable_supply) + nem_write_mosaic(mosaics_w, "transferable", transferable) + + if levy_type: + levy_identifier_length = 4 + len(levy_namespace) + 4 + len(levy_mosaic) + write_uint32(mosaics_w, 4 + 4 + len(levy_address) + 4 + levy_identifier_length + 8) + write_uint32(mosaics_w, levy_type) + write_bytes_with_length(mosaics_w, bytearray(levy_address)) + write_uint32(mosaics_w, levy_identifier_length) + write_bytes_with_length(mosaics_w, bytearray(levy_namespace)) + write_bytes_with_length(mosaics_w, bytearray(levy_mosaic)) + write_uint64(mosaics_w, levy_fee) + else: + write_uint32(mosaics_w, 0) + + # write mosaic bytes with length + write_bytes_with_length(w, mosaics_w) + + write_bytes_with_length(w, bytearray(creation_sink)) + write_uint64(w, creation_fee) + + return w + + +def nem_write_mosaic(w: bytearray, name: str, value): + if type(value) == bool: + if value: + value = "true" + else: + value = "false" + elif type(value) == int: + # todo might need more formatting + value = str(value) + elif type(value) != str: + raise ValueError('Incompatible value type') + write_uint32(w, 4 + len(name) + 4 + len(value)) + write_bytes_with_length(w, bytearray(name)) + write_bytes_with_length(w, bytearray(value)) + + def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): identifier_length = 4 + len(namespace) + 4 + len(mosaic) # indentifier length (u32) + quantity (u64) + identifier size diff --git a/tests/test_apps.nem.transaction.mosaic.creation.py b/tests/test_apps.nem.transaction.mosaic.creation.py new file mode 100644 index 000000000..cd62536cc --- /dev/null +++ b/tests/test_apps.nem.transaction.mosaic.creation.py @@ -0,0 +1,36 @@ +from common import * + +from apps.nem.transaction import * +from trezor.crypto import hashlib + + +class TestNemTransactionMosaicCreation(unittest.TestCase): + + def test_nem_transaction_mosaic_creation(self): + + t = nem_transaction_create_mosaic_creation(NEM_NETWORK_TESTNET, + 14070896, + unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324'), + 108000000, + 14074496, + 'gimre.games.pong', + 'paddles', + 'Paddles for the bong game.\n', + 0, + 10000, + True, + True, + 0, + 0, + '', + '', + '', + 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC', + 50000000000) + + self.assertEqual(t, unhexlify('014000000100009870b4d60020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd32400f36f060000000080c2d600de00000020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd3241f0000001000000067696d72652e67616d65732e706f6e6707000000706164646c65731b000000506164646c657320666f722074686520626f6e672067616d652e0a04000000150000000c00000064697669736962696c69747901000000301a0000000d000000696e697469616c537570706c79050000003130303030190000000d000000737570706c794d757461626c650400000074727565180000000c0000007472616e7366657261626c650400000074727565000000002800000054424d4f534149434f443446353445453543444d523233434342474f414d3258534a4252354f4c4300743ba40b000000')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa')) + + +if __name__ == '__main__': + unittest.main() From a6c406abeac033734c6846d2556a381119e1d38a Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 22 Mar 2018 14:54:25 +0100 Subject: [PATCH 13/53] nem: transaction mosaic creation test --- ...st_apps.nem.transaction.mosaic.creation.py | 113 +++++++++++++++--- 1 file changed, 94 insertions(+), 19 deletions(-) diff --git a/tests/test_apps.nem.transaction.mosaic.creation.py b/tests/test_apps.nem.transaction.mosaic.creation.py index cd62536cc..ff37aa6e2 100644 --- a/tests/test_apps.nem.transaction.mosaic.creation.py +++ b/tests/test_apps.nem.transaction.mosaic.creation.py @@ -8,29 +8,104 @@ class TestNemTransactionMosaicCreation(unittest.TestCase): def test_nem_transaction_mosaic_creation(self): + # http://bob.nem.ninja:8765/#/mosaic/68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa/0 t = nem_transaction_create_mosaic_creation(NEM_NETWORK_TESTNET, - 14070896, - unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324'), - 108000000, - 14074496, - 'gimre.games.pong', - 'paddles', - 'Paddles for the bong game.\n', - 0, - 10000, - True, - True, - 0, - 0, - '', - '', - '', - 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC', - 50000000000) + 14070896, + unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324'), + 108000000, + 14074496, + 'gimre.games.pong', + 'paddles', + 'Paddles for the bong game.\n', + 0, + 10000, + True, + True, + 0, + 0, + '', + '', + '', + 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC', + 50000000000) self.assertEqual(t, unhexlify('014000000100009870b4d60020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd32400f36f060000000080c2d600de00000020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd3241f0000001000000067696d72652e67616d65732e706f6e6707000000706164646c65731b000000506164646c657320666f722074686520626f6e672067616d652e0a04000000150000000c00000064697669736962696c69747901000000301a0000000d000000696e697469616c537570706c79050000003130303030190000000d000000737570706c794d757461626c650400000074727565180000000c0000007472616e7366657261626c650400000074727565000000002800000054424d4f534149434f443446353445453543444d523233434342474f414d3258534a4252354f4c4300743ba40b000000')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa')) - + def test_nem_transaction_mosaic_creation_with_levy(self): + # http://bob.nem.ninja:8765/#/mosaic/b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55/0 + t = nem_transaction_create_mosaic_creation(NEM_NETWORK_TESTNET, + 21497248, + unhexlify("244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc"), + 108000000, + 21500848, + "alice.misc", + "bar", + "Special offer: get one bar extra by bying one foo!", + 0, + 1000, + False, + True, + 1, + 1, + "TALICE2GMA34CXHD7XLJQ536NM5UNKQHTORNNT2J", + "nem", + "xem", + "TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC", + 50000000000) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55')) + + # http://chain.nem.ninja/#/mosaic/e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6/0 + t = nem_transaction_create_mosaic_creation(NEM_NETWORK_MAINNET, + 69251020, + unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a"), + 20000000, + 69337420, + "dim", + "coin", + "DIM COIN", + 6, + 9000000000, + False, + True, + 2, + 10, + "NCGGLVO2G3CUACVI5GNX2KRBJSQCN4RDL2ZWJ4DP", + "dim", + "coin", + "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", + 500000000) + self.assertEqual(t, unhexlify('0140000001000068ccaf200420000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a002d3101000000004c0122040c01000020000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a0f0000000300000064696d04000000636f696e0800000044494d20434f494e04000000150000000c00000064697669736962696c69747901000000361f0000000d000000696e697469616c537570706c790a000000393030303030303030301a0000000d000000737570706c794d757461626c650500000066616c7365180000000c0000007472616e7366657261626c6504000000747275654b00000002000000280000004e4347474c564f32473343554143564935474e58324b52424a5351434e3452444c325a574a3444500f0000000300000064696d04000000636f696e0a00000000000000280000004e424d4f534149434f443446353445453543444d523233434342474f414d325853495558365452530065cd1d00000000')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6')) + + def test_nem_transaction_mosaic_creation_with_description(self): + # http://chain.nem.ninja/#/mosaic/269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b/0 + t = nem_transaction_create_mosaic_creation(NEM_NETWORK_MAINNET, + 26729938, + unhexlify("58956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f23"), + 108000000, + 26733538, + "jabo38", + "red_token", + "This token is to celebrate the release of Namespaces and Mosaics " + "on the NEM system. This token was the fist ever mosaic created " + "other than nem.xem. There are only 10,000 Red Tokens that will " + "ever be created. It has no levy and can be traded freely among " + "third parties.", + 2, + 10000, + False, + True, + 0, + 0, + "", + "", + "", + "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", + 50000000000) + self.assertEqual(t, unhexlify('0140000001000068d2dd97012000000058956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f2300f36f0600000000e2eb9701c80100002000000058956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f2317000000060000006a61626f3338090000007265645f746f6b656e0c0100005468697320746f6b656e20697320746f2063656c656272617465207468652072656c65617365206f66204e616d6573706163657320616e64204d6f7361696373206f6e20746865204e454d2073797374656d2e205468697320746f6b656e207761732074686520666973742065766572206d6f736169632063726561746564206f74686572207468616e206e656d2e78656d2e20546865726520617265206f6e6c792031302c3030302052656420546f6b656e7320746861742077696c6c206576657220626520637265617465642e20497420686173206e6f206c65767920616e642063616e2062652074726164656420667265656c7920616d6f6e6720746869726420706172746965732e04000000150000000c00000064697669736962696c69747901000000321a0000000d000000696e697469616c537570706c790500000031303030301a0000000d000000737570706c794d757461626c650500000066616c7365180000000c0000007472616e7366657261626c65040000007472756500000000280000004e424d4f534149434f443446353445453543444d523233434342474f414d3258534955583654525300743ba40b000000')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b')) + if __name__ == '__main__': unittest.main() From 2e6587ec6c312b7937acef6cdaa07ece5415f48a Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 22 Mar 2018 15:31:10 +0100 Subject: [PATCH 14/53] nem: transaction create mosaic supply change --- src/apps/nem/transaction.py | 21 ++++++ ...t_apps.nem.transaction.mosaic_creation.py} | 0 ...ps.nem.transaction.mosaic_supply_change.py | 69 +++++++++++++++++++ 3 files changed, 90 insertions(+) rename tests/{test_apps.nem.transaction.mosaic.creation.py => test_apps.nem.transaction.mosaic_creation.py} (100%) create mode 100644 tests/test_apps.nem.transaction.mosaic_supply_change.py diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index ac353c322..d8fdf33d9 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -105,6 +105,27 @@ def nem_transaction_create_mosaic_creation(network: int, timestamp: int, signer_ return w +def nem_transaction_create_mosaic_supply_change(network: int, timestamp: int, signer_public_key: bytes, fee: int, + deadline: int, namespace: str, mosaic: str, type: int, delta: int): + + w = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, + _nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + identifier_length = 4 + len(namespace) + 4 + len(mosaic) + write_uint32(w, identifier_length) + write_bytes_with_length(w, bytearray(namespace)) + write_bytes_with_length(w, bytearray(mosaic)) + + write_uint32(w, type) + write_uint64(w, delta) + + return w + + def nem_write_mosaic(w: bytearray, name: str, value): if type(value) == bool: if value: diff --git a/tests/test_apps.nem.transaction.mosaic.creation.py b/tests/test_apps.nem.transaction.mosaic_creation.py similarity index 100% rename from tests/test_apps.nem.transaction.mosaic.creation.py rename to tests/test_apps.nem.transaction.mosaic_creation.py diff --git a/tests/test_apps.nem.transaction.mosaic_supply_change.py b/tests/test_apps.nem.transaction.mosaic_supply_change.py new file mode 100644 index 000000000..c5e3affba --- /dev/null +++ b/tests/test_apps.nem.transaction.mosaic_supply_change.py @@ -0,0 +1,69 @@ +from common import * + +from apps.nem.transaction import * +from trezor.crypto import hashlib + + +class TestNemTransactionMosaicCreation(unittest.TestCase): + + def test_nem_transaction_create_mosaic_supply_change(self): + + # http://bigalice2.nem.ninja:7890/transaction/get?hash=33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50 + t = nem_transaction_create_mosaic_supply_change(NEM_NETWORK_TESTNET, + 14071648, + unhexlify("994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324"), + 108000000, + 14075248, + "gimre.games.pong", + "paddles", + 1, + 1234) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify('33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50')) + + # http://bigalice2.nem.ninja:7890/transaction/get?hash=1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2 + t = nem_transaction_create_mosaic_supply_change(NEM_NETWORK_TESTNET, + 14126909, + unhexlify("84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5"), + 108000000, + 14130509, + "jabo38_ltd.fuzzy_kittens_cafe", + "coupons", + 2, + 1) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify('1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2')) + + # http://bigalice3.nem.ninja:7890/transaction/get?hash=694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff + t = nem_transaction_create_mosaic_supply_change(NEM_NETWORK_MAINNET, + 53377685, + unhexlify("b7ccc27b21ba6cf5c699a8dc86ba6ba98950442597ff9fa30e0abe0f5f4dd05d"), + 20000000, + 53464085, + "abvapp", + "abv", + 1, + 9000000) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify('694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff')) + + # http://bigalice3.nem.ninja:7890/transaction/get?hash=09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185 + t = nem_transaction_create_mosaic_supply_change(NEM_NETWORK_MAINNET, + 55176304, + unhexlify("75f001a8641e2ce5c4386883dda561399ed346177411b492a677b73899502f13"), + 20000000, + 55262704, + "sushi", + "wasabi", + 2, + 20) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify('09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185')) + + +if __name__ == '__main__': + unittest.main() From 48c6686dcaf6388c4297d76d4e31420a8bd73372 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 22 Mar 2018 15:55:55 +0100 Subject: [PATCH 15/53] nem: aggregate modification --- src/apps/nem/transaction.py | 26 ++++++- ....nem.transaction.aggregate_modification.py | 75 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tests/test_apps.nem.transaction.aggregate_modification.py diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index d8fdf33d9..6736f16e3 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -1,7 +1,6 @@ from .helpers import * from .writers import * -from ubinascii import hexlify def nem_transaction_create_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, @@ -126,6 +125,31 @@ def nem_transaction_create_mosaic_supply_change(network: int, timestamp: int, si return w +def nem_transaction_create_aggregate_modification(network: int, timestamp: int, signer_public_key: bytes, fee: int, + deadline: int, modifications: int, relative_change: bool): + + w = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, + _nem_get_version(network, relative_change), + timestamp, + signer_public_key, + fee, + deadline) + write_uint32(w, modifications) + return w + + +def nem_transaction_write_cosignatory_modification(w: bytearray, type: int, cosignatory: bytes): + write_uint32(w, 4 + 4 + len(cosignatory)) + write_uint32(w, type) + write_bytes_with_length(w, bytearray(cosignatory)) + return w + + +def nem_transaction_write_minimum_cosignatories(w: bytearray, relative_change: int): + write_uint32(w, 4) + write_uint32(w, relative_change) + + def nem_write_mosaic(w: bytearray, name: str, value): if type(value) == bool: if value: diff --git a/tests/test_apps.nem.transaction.aggregate_modification.py b/tests/test_apps.nem.transaction.aggregate_modification.py new file mode 100644 index 000000000..e0d58b70f --- /dev/null +++ b/tests/test_apps.nem.transaction.aggregate_modification.py @@ -0,0 +1,75 @@ +from common import * + +from apps.nem.transaction import * +from trezor.crypto import hashlib + + +class TestNemTransactionMosaicCreation(unittest.TestCase): + + def test_nem_transaction_aggregate_modification(self): + # http://bob.nem.ninja:8765/#/aggregate/6a55471b17159e5b6cd579c421e95a4e39d92e3f78b0a55ee337e785a601d3a2 + t = nem_transaction_create_aggregate_modification(NEM_NETWORK_TESTNET, + 0, + unhexlify("462ee976890916e54fa825d26bdd0235f5eb5b6a143c199ab0ae5ee9328e08ce"), + 22000000, + 0, + 2, + False) + + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324")) + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "c54d6e33ed1446eedd7f7a80a588dd01857f723687a09200c1917d5524752f8b")) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify("6a55471b17159e5b6cd579c421e95a4e39d92e3f78b0a55ee337e785a601d3a2")) + + # http://chain.nem.ninja/#/aggregate/cc64ca69bfa95db2ff7ac1e21fe6d27ece189c603200ebc9778d8bb80ca25c3c + t = nem_transaction_create_aggregate_modification(NEM_NETWORK_MAINNET, + 0, + unhexlify("f41b99320549741c5cce42d9e4bb836d98c50ed5415d0c3c2912d1bb50e6a0e5"), + 40000000, + 0, + 5, + False) + + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "1fbdbdde28daf828245e4533765726f0b7790e0b7146e2ce205df3e86366980b")) + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "f94e8702eb1943b23570b1b83be1b81536df35538978820e98bfce8f999e2d37")) + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "826cedee421ff66e708858c17815fcd831a4bb68e3d8956299334e9e24380ba8")) + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "719862cd7d0f4e875a6a0274c9a1738f38f40ad9944179006a54c34724c1274d")) + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "43aa69177018fc3e2bdbeb259c81cddf24be50eef9c5386db51d82386c41475a")) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify("cc64ca69bfa95db2ff7ac1e21fe6d27ece189c603200ebc9778d8bb80ca25c3c")) + + def test_nem_transaction_aggregate_modification_relative_change(self): + # http://bob.nem.ninja:8765/#/aggregate/1fbdae5ba753e68af270930413ae90f671eb8ab58988116684bac0abd5726584 + t = nem_transaction_create_aggregate_modification(NEM_NETWORK_TESTNET, + 6542254, + unhexlify("6bf7849c1eec6a2002995cc457dc00c4e29bad5c88de63f51e42dfdcd7b2131d"), + 40000000, + 6545854, + 4, + True) + + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "5f53d076c8c3ec3110b98364bc423092c3ec2be2b1b3c40fd8ab68d54fa39295")) + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "9eb199c2b4d406f64cb7aa5b2b0815264b56ba8fe44d558a6cb423a31a33c4c2")) + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "94b2323dab23a3faba24fa6ddda0ece4fbb06acfedd74e76ad9fae38d006882b")) + nem_transaction_write_cosignatory_modification(t, 1, unhexlify( + "d88c6ee2a2cd3929d0d76b6b14ecb549d21296ab196a2b3a4cb2536bcce32e87")) + nem_transaction_write_minimum_cosignatories(t, 2) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify("1fbdae5ba753e68af270930413ae90f671eb8ab58988116684bac0abd5726584")) + + +if __name__ == '__main__': + unittest.main() From 667585041cccbdfb2df24d041957787a09cb2393 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 22 Mar 2018 16:05:14 +0100 Subject: [PATCH 16/53] nem: transaction importance transfer --- src/apps/nem/transaction.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index 6736f16e3..8c7bffc16 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -125,6 +125,20 @@ def nem_transaction_create_mosaic_supply_change(network: int, timestamp: int, si return w +def nem_transaction_create_importance_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, + deadline: int, mode: int, remote: bytes): + + w = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER, + _nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + write_uint32(w, mode) + write_bytes_with_length(w, bytearray(remote)) + + def nem_transaction_create_aggregate_modification(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, modifications: int, relative_change: bool): From 0c01c4f4dda2278724414c43823eb48f6ecb5d55 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 26 Mar 2018 11:10:15 +0200 Subject: [PATCH 17/53] nem: small cosmetics in signing --- src/apps/nem/signing.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 1ddc6f6b3..d7a2ebbb1 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -1,5 +1,5 @@ -from apps.nem.transaction import * from apps.nem.layout import * +from apps.nem.transaction import * from apps.nem import helpers from apps.common import seed from trezor.messages.NEMSignTx import NEMSignTx @@ -12,13 +12,7 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) - payload = msg.transfer.payload - encrypted = False - if msg.transfer.public_key is not None: - if payload is None: - raise ValueError("Public key provided but no payload to encrypt") - payload = _nem_encrypt(node, msg.transfer.public_key, msg.transfer.payload) - encrypted = True + payload, encrypted = _get_payload(msg, node) # 0x01 prefix is not part of the actual public key, hence removed public_key = node.public_key()[1:] @@ -53,6 +47,18 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): return resp +def _get_payload(msg: NEMSignTx, node) -> [bytes, bool]: + payload = msg.transfer.payload + encrypted = False + if msg.transfer.public_key is not None: + if payload is None: + raise ValueError("Public key provided but no payload to encrypt") + payload = _nem_encrypt(node, msg.transfer.public_key, msg.transfer.payload) + encrypted = True + + return payload, encrypted + + def _nem_encrypt(node, public_key: bytes, payload: bytes) -> bytes: salt = random.bytes(helpers.NEM_SALT_SIZE) iv = random.bytes(helpers.AES_BLOCK_SIZE) From 10a52d8688285117f6702b328bff89f504d69512 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 26 Mar 2018 12:05:47 +0200 Subject: [PATCH 18/53] nem: multisig (basic functions, unit tests) --- src/apps/nem/multisig.py | 38 ++++++++++++++ src/apps/nem/transaction.py | 92 +++++++++++++-------------------- src/apps/nem/writers.py | 20 +++++++ tests/test_apps.nem.multisig.py | 86 ++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 56 deletions(-) create mode 100644 src/apps/nem/multisig.py create mode 100644 tests/test_apps.nem.multisig.py diff --git a/src/apps/nem/multisig.py b/src/apps/nem/multisig.py new file mode 100644 index 000000000..6942d859c --- /dev/null +++ b/src/apps/nem/multisig.py @@ -0,0 +1,38 @@ + +from .helpers import * +from .writers import * +from trezor.crypto import hashlib + + +def nem_transaction_create_multisig(network: int, timestamp: int, signer_public_key: bytes, + fee: int, deadline: int, inner: bytes): + + w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MULTISIG, + nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + write_bytes_with_length(w, bytearray(inner)) + + return w + + +def nem_transaction_create_multisig_signature(network: int, timestamp: int, signer_public_key: bytes, + fee: int, deadline: int, inner: bytes, address: str): + + w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE, + nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + hash = hashlib.sha3_256(inner).digest(True) + + write_uint32(w, 4 + len(hash)) + write_bytes_with_length(w, hash) + write_bytes_with_length(w, address) + + return w diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index 8c7bffc16..f0f8b9485 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -7,12 +7,12 @@ def nem_transaction_create_transfer(network: int, timestamp: int, signer_public_ recipient: str, amount: int, payload: bytearray = None, encrypted: bool = False, mosaics: int = 0) -> bytearray: - tx = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_TRANSFER, - _nem_get_version(network, mosaics), - timestamp, - signer_public_key, - fee, - deadline) + tx = nem_transaction_write_common(NEM_TRANSACTION_TYPE_TRANSFER, + nem_get_version(network, mosaics), + timestamp, + signer_public_key, + fee, + deadline) write_bytes_with_length(tx, bytearray(recipient)) write_uint64(tx, amount) @@ -38,12 +38,12 @@ def nem_transaction_create_provision_namespace(network: int, timestamp: int, sig deadline: int, namespace: str, parent: str, rental_sink: str, rental_fee: int) -> bytearray: - tx = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE, - _nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) + tx = nem_transaction_write_common(NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE, + nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) write_bytes_with_length(tx, bytearray(rental_sink)) write_uint64(tx, rental_fee) @@ -62,12 +62,12 @@ def nem_transaction_create_mosaic_creation(network: int, timestamp: int, signer_ levy_type: int, levy_fee: int, levy_address: str, levy_namespace: str, levy_mosaic: str, creation_sink: str, creation_fee: int): - w = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_CREATION, - _nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) + w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_CREATION, + nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) mosaics_w = bytearray() write_bytes_with_length(mosaics_w, bytearray(signer_public_key)) @@ -107,12 +107,12 @@ def nem_transaction_create_mosaic_creation(network: int, timestamp: int, signer_ def nem_transaction_create_mosaic_supply_change(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, namespace: str, mosaic: str, type: int, delta: int): - w = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, - _nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) + w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, + nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) identifier_length = 4 + len(namespace) + 4 + len(mosaic) write_uint32(w, identifier_length) @@ -128,12 +128,12 @@ def nem_transaction_create_mosaic_supply_change(network: int, timestamp: int, si def nem_transaction_create_importance_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, mode: int, remote: bytes): - w = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER, - _nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) + w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER, + nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) write_uint32(w, mode) write_bytes_with_length(w, bytearray(remote)) @@ -142,12 +142,12 @@ def nem_transaction_create_importance_transfer(network: int, timestamp: int, sig def nem_transaction_create_aggregate_modification(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, modifications: int, relative_change: bool): - w = _nem_transaction_write_common(NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, - _nem_get_version(network, relative_change), - timestamp, - signer_public_key, - fee, - deadline) + w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, + nem_get_version(network, relative_change), + timestamp, + signer_public_key, + fee, + deadline) write_uint32(w, modifications) return w @@ -188,23 +188,3 @@ def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quan write_bytes_with_length(w, bytearray(namespace)) write_bytes_with_length(w, bytearray(mosaic)) write_uint64(w, quantity) - - -def _nem_transaction_write_common(tx_type: int, version: int, timestamp: int, signer: bytes, fee: int, deadline: int)\ - -> bytearray: - ret = bytearray() - write_uint32(ret, tx_type) - write_uint32(ret, version) - write_uint32(ret, timestamp) - - write_bytes_with_length(ret, bytearray(signer)) - write_uint64(ret, fee) - write_uint32(ret, deadline) - - return ret - - -def _nem_get_version(network, mosaics=None) -> int: - if mosaics: - return network << 24 | 2 - return network << 24 | 1 diff --git a/src/apps/nem/writers.py b/src/apps/nem/writers.py index 4708fa6a9..0f3544328 100644 --- a/src/apps/nem/writers.py +++ b/src/apps/nem/writers.py @@ -24,3 +24,23 @@ def write_bytes(w, buf: bytearray): def write_bytes_with_length(w, buf: bytearray): write_uint32(w, len(buf)) write_bytes(w, buf) + + +def nem_transaction_write_common(tx_type: int, version: int, timestamp: int, signer: bytes, fee: int, deadline: int)\ + -> bytearray: + ret = bytearray() + write_uint32(ret, tx_type) + write_uint32(ret, version) + write_uint32(ret, timestamp) + + write_bytes_with_length(ret, bytearray(signer)) + write_uint64(ret, fee) + write_uint32(ret, deadline) + + return ret + + +def nem_get_version(network, mosaics=None) -> int: + if mosaics: + return network << 24 | 2 + return network << 24 | 1 diff --git a/tests/test_apps.nem.multisig.py b/tests/test_apps.nem.multisig.py new file mode 100644 index 000000000..061838f23 --- /dev/null +++ b/tests/test_apps.nem.multisig.py @@ -0,0 +1,86 @@ +from common import * + +from apps.nem.transaction import * +from apps.nem.multisig import * + + +class TestNemMultisig(unittest.TestCase): + + def test_nem_multisig(self): + # http://bob.nem.ninja:8765/#/multisig/7d3a7087023ee29005262016706818579a2b5499eb9ca76bad98c1e6f4c46642 + + base_tx = nem_transaction_create_aggregate_modification(NEM_NETWORK_TESTNET, + 3939039, + unhexlify("abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac"), + 16000000, + 3960639, + 1, + False) + + base_tx = nem_transaction_write_cosignatory_modification(base_tx, 2, unhexlify("e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3")) + multisig = nem_transaction_create_multisig(NEM_NETWORK_TESTNET, + 3939039, + unhexlify("59d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e"), + 6000000, + 3960639, + base_tx) + + self.assertEqual(multisig, unhexlify("0410000001000098df1a3c002000000059d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e808d5b00000000003f6f3c006c0000000110000001000098df1a3c0020000000abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac0024f400000000003f6f3c0001000000280000000200000020000000e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3")) + + address = "TCRXYUQIMFA7AOGL5LF3YWLC7VABLYUMJ5ACBUNL" + multisig = nem_transaction_create_multisig_signature(NEM_NETWORK_TESTNET, + 3939891, + unhexlify("71cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4"), + 6000000, + 3961491, + base_tx, + address) + + self.assertEqual(multisig, unhexlify("0210000001000098331e3c002000000071cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4808d5b000000000093723c0024000000200000008ec165580bdabfd31ce6007a1748ce5bdf30eab7a214743097de3bc822ac7e002800000054435258595551494d464137414f474c354c463359574c43375641424c59554d4a35414342554e4c")) + + def test_nem_multisig_2(self): + # http://chain.nem.ninja/#/multisig/1016cf3bdd61bd57b9b2b07b6ff2dee390279d8d899265bdc23d42360abe2e6c + + base_tx = nem_transaction_create_provision_namespace(NEM_NETWORK_MAINNET, + 59414272, + unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a"), + 20000000, + 59500672, + "dim", + "", + "NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA", + 5000000000) + + multisig = nem_transaction_create_multisig(NEM_NETWORK_MAINNET, + 59414272, + unhexlify("cfe58463f0eaebceb5d00717f8aead49171a5d7c08f6b1299bd534f11715acc9"), + 6000000, + 59500672, + base_tx) + + self.assertEqual(multisig, unhexlify("041000000100006800978a0320000000cfe58463f0eaebceb5d00717f8aead49171a5d7c08f6b1299bd534f11715acc9808d5b000000000080e88b037b000000012000000100006800978a0320000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a002d31010000000080e88b03280000004e414d4553504143455748344d4b464d42435646455244504f4f5034464b374d54425844505a5a4100f2052a010000000300000064696dffffffff")) + + address = "NDDRG3UEB5LZZZMDWBE4RTKZK73JBHPAIWBHCFMV" + multisig = nem_transaction_create_multisig_signature(NEM_NETWORK_MAINNET, + 59414342, + unhexlify("1b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2"), + 6000000, + 59500742, + base_tx, + address) + + self.assertEqual(multisig, unhexlify("021000000100006846978a03200000001b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2808d5b0000000000c6e88b032400000020000000bfa2088f7720f89dd4664d650e321dabd02fab61b7355bc88a391a848a49786a280000004e4444524733554542354c5a5a5a4d445742453452544b5a4b37334a424850414957424843464d56")) + + multisig = nem_transaction_create_multisig_signature(NEM_NETWORK_MAINNET, + 59414381, + unhexlify("7ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87"), + 6000000, + 59500781, + base_tx, + address) + + self.assertEqual(multisig, unhexlify("02100000010000686d978a03200000007ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87808d5b0000000000ede88b032400000020000000bfa2088f7720f89dd4664d650e321dabd02fab61b7355bc88a391a848a49786a280000004e4444524733554542354c5a5a5a4d445742453452544b5a4b37334a424850414957424843464d56")) + + +if __name__ == '__main__': + unittest.main() From 1f7ab29613bc45624276468d983c6d3e016f2034 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 26 Mar 2018 13:25:46 +0200 Subject: [PATCH 19/53] nem: typos --- src/apps/nem/signing.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index d7a2ebbb1..5962cb7c4 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -13,9 +13,9 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) payload, encrypted = _get_payload(msg, node) + public_key = _get_public_key(node) - # 0x01 prefix is not part of the actual public key, hence removed - public_key = node.public_key()[1:] + _validate_network(msg.transaction.network) tx = nem_transaction_create_transfer( msg.transaction.network, @@ -59,8 +59,18 @@ def _get_payload(msg: NEMSignTx, node) -> [bytes, bool]: return payload, encrypted +def _get_public_key(node) -> bytes: + # 0x01 prefix is not part of the actual public key, hence removed + return node.public_key()[1:] + + def _nem_encrypt(node, public_key: bytes, payload: bytes) -> bytes: salt = random.bytes(helpers.NEM_SALT_SIZE) iv = random.bytes(helpers.AES_BLOCK_SIZE) encrypted = node.nem_encrypt(public_key, iv, salt, payload) return iv + salt + encrypted + + +def _validate_network(network): + if network not in [NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN]: + raise ValueError('Invalid NEM network') From d30d6859ba810f7700fd0c3b54dbefeb220f1809 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 26 Mar 2018 15:03:57 +0200 Subject: [PATCH 20/53] nem: validators This commit introduces a lot of boundary checking validating if the NEM transaction has all required fields. It is based solely on the T1 mcu code. --- src/apps/nem/get_address.py | 5 +- src/apps/nem/helpers.py | 12 +-- src/apps/nem/signing.py | 11 +- src/apps/nem/validators.py | 210 ++++++++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 19 deletions(-) create mode 100644 src/apps/nem/validators.py diff --git a/src/apps/nem/get_address.py b/src/apps/nem/get_address.py index a223fdb14..d7660f2f3 100644 --- a/src/apps/nem/get_address.py +++ b/src/apps/nem/get_address.py @@ -1,11 +1,11 @@ from apps.wallet.get_address import _show_address from apps.common import seed from trezor.messages.NEMAddress import NEMAddress -from .helpers import * +from .validators import * async def nem_get_address(ctx, msg): - network = nem_validate_network(msg.network) + network = validate_network(msg.network) node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE) address = node.nem_address(network) @@ -15,4 +15,3 @@ async def nem_get_address(ctx, msg): break return NEMAddress(address=address) - diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index 091498e6f..a68961aea 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -15,14 +15,10 @@ NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE = const(0x2001) NEM_TRANSACTION_TYPE_MOSAIC_CREATION = const(0x4001) NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE = const(0x4002) +NEM_MAX_DIVISIBILITY = const(6) +NEM_MAX_SUPPLY = const(9000000000) + NEM_SALT_SIZE = const(32) AES_BLOCK_SIZE = const(16) NEM_HASH_ALG = 'keccak' - - -def nem_validate_network(network): - if network in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN): - return network - if network is None: - return NEM_NETWORK_MAINNET - raise ValueError('Invalid NEM network') +NEM_PUBLIC_KEY_SIZE = const(32) # ed25519 public key diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 5962cb7c4..44b99c5ce 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -1,5 +1,6 @@ from apps.nem.layout import * from apps.nem.transaction import * +from apps.nem.validators import validate from apps.nem import helpers from apps.common import seed from trezor.messages.NEMSignTx import NEMSignTx @@ -10,13 +11,12 @@ from trezor.crypto import random async def nem_sign_tx(ctx, msg: NEMSignTx): - node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) + validate(msg) + node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) payload, encrypted = _get_payload(msg, node) public_key = _get_public_key(node) - _validate_network(msg.transaction.network) - tx = nem_transaction_create_transfer( msg.transaction.network, msg.transaction.timestamp, @@ -69,8 +69,3 @@ def _nem_encrypt(node, public_key: bytes, payload: bytes) -> bytes: iv = random.bytes(helpers.AES_BLOCK_SIZE) encrypted = node.nem_encrypt(public_key, iv, salt, payload) return iv + salt + encrypted - - -def _validate_network(network): - if network not in [NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN]: - raise ValueError('Invalid NEM network') diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py new file mode 100644 index 000000000..e98beef8f --- /dev/null +++ b/src/apps/nem/validators.py @@ -0,0 +1,210 @@ +from apps.nem.helpers import * +from trezor.messages.NEMModificationType import CosignatoryModification_Delete +from trezor.messages.NEMSignTx import NEMAggregateModification +from trezor.messages.NEMSignTx import NEMImportanceTransfer +from trezor.messages.NEMSignTx import NEMMosaicCreation +from trezor.messages.NEMSignTx import NEMMosaicSupplyChange +from trezor.messages.NEMSignTx import NEMProvisionNamespace +from trezor.messages.NEMSignTx import NEMSignTx +from trezor.messages.NEMSignTx import NEMTransactionCommon +from trezor.messages.NEMSignTx import NEMTransfer +from trezor.crypto import nem + + +def validate(msg: NEMSignTx): + + if msg.transaction is None: + raise ValueError('No common provided') + + _validate_single_tx(msg) + _validate_common(msg.transaction) + + if msg.transfer: + _validate_transfer(msg.transfer, msg.transaction.network) + if msg.provision_namespace: + _validate_provision_namespace(msg.provision_namespace, msg.transaction.network) + if msg.mosaic_creation: + _validate_mosaic_creation(msg.mosaic_creation, msg.transaction.network) + if msg.supply_change: + _validate_supply_change(msg.supply_change) + if msg.aggregate_modification: + _validate_aggregate_modification(msg.aggregate_modification, not len(msg.multisig)) + if msg.importance_transfer: + _validate_importance_transfer(msg.importance_transfer) + + +def validate_network(network: int) -> int: + if network is None: + return NEM_NETWORK_MAINNET + _validate_network(network) + return network + + +def _validate_network(network: int): + if network not in [NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN]: + raise ValueError('Invalid NEM network') + + +def _validate_single_tx(msg: NEMSignTx): + # ensure exactly one transaction is provided + tx_count = bool(msg.transfer) + \ + bool(msg.provision_namespace) + \ + bool(msg.mosaic_creation) + \ + bool(msg.supply_change) + \ + bool(msg.aggregate_modification) + \ + bool(msg.importance_transfer) + if tx_count == 0: + raise ValueError('No transaction provided') + if tx_count > 1: + raise ValueError('More than one transaction provided') + + +def _validate_common(common: NEMTransactionCommon, inner: bool=False): + + common.network = validate_network(common.network) + + err = None + if not common.timestamp: + err = 'timestamp' + if not common.fee: + err = 'fee' + if not common.deadline: + err = 'deadline' + + is_signer = common.signer is not None + if inner != is_signer: + if not inner: + raise ValueError('Signer not allowed in outer transaction') + err = 'signer' + + if err: + if inner: + raise ValueError('No ' + err + ' provided in inner transaction') + else: + raise ValueError('No ' + err + ' provided') + + if common.signer: + _validate_public_key(common.signer, 'Invalid signer public key in inner transaction') + + +def _validate_public_key(public_key: bytes, err_msg): + if not public_key: + raise ValueError(err_msg + ' (none provided)') + if len(public_key) != NEM_PUBLIC_KEY_SIZE: + raise ValueError(err_msg + ' (invalid length)') + + +def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer): + if not importance_transfer.mode: + raise ValueError('No mode provided') + _validate_public_key(importance_transfer.public_key, 'Invalid remote account public key provided') + + +def _validate_aggregate_modification(aggregate_modification: NEMAggregateModification, creation: bool=False): + + if creation and len(aggregate_modification.modifications) == 0: + raise ValueError('No modifications provided') + + for m in aggregate_modification.modifications: + if not m.type: + raise ValueError('No modification type provided') + if creation and m.type == CosignatoryModification_Delete: + raise ValueError('Cannot remove cosignatory when converting account') + _validate_public_key(m.public_key, 'Invalid cosignatory public key provided') + + +def _validate_supply_change(supply_change: NEMMosaicSupplyChange): + if not supply_change.namespace: + raise ValueError('No namespace provided') + if not supply_change.mosaic: + raise ValueError('No mosaic provided') + if not supply_change.type: + raise ValueError('No type provided') + if not supply_change.delta: + raise ValueError('No delta provided') + + +def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int): + if not mosaic_creation.definition: + raise ValueError('No mosaic definition provided') + if not mosaic_creation.sink: + raise ValueError('No creation sink provided') + if not mosaic_creation.fee: + raise ValueError('No creation sink fee provided') + + if not nem.validate_address(mosaic_creation.sink, network): + raise ValueError('Invalid creation sink address') + + if mosaic_creation.definition.name: + raise ValueError('Name not allowed in mosaic creation transactions') + if mosaic_creation.definition.ticker: + raise ValueError('Ticker not allowed in mosaic creation transactions') + if len(mosaic_creation.definition.networks): + raise ValueError('Networks not allowed in mosaic creation transactions') + + if not mosaic_creation.definition.namespace: + raise ValueError('No mosaic namespace provided') + if not mosaic_creation.definition.mosaic: + raise ValueError('No mosaic name provided') + + if mosaic_creation.definition.levy: + if not mosaic_creation.definition.fee: + raise ValueError('No levy fee provided') + if not mosaic_creation.definition.levy_address: + raise ValueError('No levy address provided') + if not mosaic_creation.definition.levy_namespace: + raise ValueError('No levy namespace provided') + if not mosaic_creation.definition.levy_mosaic: + raise ValueError('No levy mosaic name provided') + + if not mosaic_creation.definition.divisibility: + raise ValueError('No divisibility provided') + if not mosaic_creation.definition.supply: + raise ValueError('No supply provided') + if not mosaic_creation.definition.mutable_supply: + raise ValueError('No supply mutability provided') + if not mosaic_creation.definition.transferable: + raise ValueError('No mosaic transferability provided') + if not mosaic_creation.definition.description: + raise ValueError('No description provided') + + if mosaic_creation.definition.divisibility > NEM_MAX_DIVISIBILITY: + raise ValueError('Invalid divisibility provided') + if mosaic_creation.definition.supply > NEM_MAX_SUPPLY: + raise ValueError('Invalid supply provided') + + if not nem.validate_address(mosaic_creation.definition.levy_address, network): + raise ValueError('Invalid levy address') + + +def _validate_provision_namespace(provision_namespace: NEMProvisionNamespace, network: int): + if not provision_namespace.namespace: + raise ValueError('No namespace provided') + if not provision_namespace.sink: + raise ValueError('No rental sink provided') + if not provision_namespace.fee: + raise ValueError('No rental sink fee provided') + + if not nem.validate_address(provision_namespace.sink, network): + raise ValueError('Invalid rental sink address') + + +def _validate_transfer(transfer: NEMTransfer, network: int): + if transfer.recipient is None: + raise ValueError('No recipient provided') + if transfer.amount is None: + raise ValueError('No amount provided') + + if transfer.public_key: + _validate_public_key(transfer.public_key, 'Invalid recipient public key') + + if not nem.validate_address(transfer.recipient, network): + raise ValueError('Invalid recipient address') + + for m in transfer.mosaics: + if not m.namespace: + raise ValueError('No mosaic namespace provided') + if not m.mosaic: + raise ValueError('No mosaic name provided') + if not m.quantity: + raise ValueError('No mosaic quantity provided') From 0e1b3aa904b1a82217feefd70807848ee1e54dae Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 26 Mar 2018 16:39:29 +0200 Subject: [PATCH 21/53] nem: layout improved --- src/apps/nem/layout.py | 32 ++++++++++++++++++-------------- src/apps/nem/signing.py | 4 ++-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index be3167c85..19714b692 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -2,34 +2,38 @@ from apps.common.confirm import * from trezor import ui from trezor.messages import ButtonRequestType from trezor.ui.text import Text - -# todo wording, ui +from trezor.utils import chunks, format_amount, split_words +from .helpers import * async def require_confirm_tx(ctx, recipient, value): content = Text('Confirm sending', ui.ICON_SEND, - ui.BOLD, value, + ui.BOLD, format_amount(value, NEM_MAX_DIVISIBILITY) + ' NEM', ui.NORMAL, 'to', - ui.MONO, recipient, + ui.MONO, *split_address(recipient), icon_color=ui.GREEN) await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 -async def require_confirm_fee(ctx, value, fee): - content = Text('Confirm transaction', ui.ICON_SEND, - ui.BOLD, value, - ui.NORMAL, 'fee:', - ui.BOLD, fee, +async def require_confirm_fee(ctx, fee): + content = Text('Confirm fee', ui.ICON_SEND, + 'Pay ', ui.BOLD, format_amount(fee, NEM_MAX_DIVISIBILITY) + ' NEM', + ui.NORMAL, 'for transaction fee?', icon_color=ui.GREEN) await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) -async def require_confirm_action(ctx, payload, encrypt=False): +async def require_confirm_action(ctx, payload: bytes, encrypt=False): + payload = str(payload, 'utf-8') if encrypt: - content = Text("Send payload encrypted?", ui.ICON_SEND, - ui.NORMAL, payload) + content = Text('Send encrypted?', ui.ICON_SEND, + ui.NORMAL, *split_words(payload, 18)) else: - content = Text("Send payload unencrypted?", ui.ICON_SEND, - ui.NORMAL, payload, + content = Text('Send unencrypted?', ui.ICON_SEND, + ui.NORMAL, *split_words(payload, 18), icon_color=ui.RED) await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +def split_address(data): + return chunks(data, 17) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 44b99c5ce..e4f4d948c 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -36,8 +36,8 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): if payload: # confirm unencrypted await require_confirm_action(ctx, msg.transfer.payload, encrypted) - await require_confirm_fee(ctx, msg.transfer.amount, msg.transaction.fee) # todo - await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) # todo + await require_confirm_fee(ctx, msg.transaction.fee) + await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) From 3fc96805dd294be961a1dc5fb890870d83731dd9 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 27 Mar 2018 10:50:58 +0200 Subject: [PATCH 22/53] nem: modtrezorcrypto overflow fix and err check --- embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h index c5e509a8f..da3fae52f 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h @@ -323,8 +323,10 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_nem_address(mp_obj_t self, mp_obj_t netw mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(self); uint8_t n = mp_obj_get_int_truncated(network); - char address[NEM_ADDRESS_SIZE]; - hdnode_get_nem_address(&o->hdnode, n, address); + char address[NEM_ADDRESS_SIZE + 1]; + if (!hdnode_get_nem_address(&o->hdnode, n, address)) { + mp_raise_ValueError("Failed to compute a NEM address"); + } return mp_obj_new_str(address, strlen(address), false); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_nem_address_obj, mod_trezorcrypto_HDNode_nem_address); @@ -360,7 +362,9 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_nem_encrypt(size_t n_args, const mp_obj_ uint8_t buffer[NEM_ENCRYPTED_SIZE(payload.len)]; - hdnode_nem_encrypt(&o->hdnode, *(const ed25519_public_key *)transfer_pk.buf, iv.buf, salt.buf, payload.buf, payload.len, buffer); + if (!hdnode_nem_encrypt(&o->hdnode, *(const ed25519_public_key *)transfer_pk.buf, iv.buf, salt.buf, payload.buf, payload.len, buffer)) { + mp_raise_ValueError("HDNode nem encrypt failed"); + } return mp_obj_new_bytes(buffer, sizeof(buffer)); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorcrypto_HDNode_nem_encrypt_obj, 5, 5, mod_trezorcrypto_HDNode_nem_encrypt); From 93ff3f074b6e8ba2091ee64c9899ad05ab456f54 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 27 Mar 2018 13:52:56 +0200 Subject: [PATCH 23/53] nem: error on not yet implemented functions --- src/apps/nem/validators.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index e98beef8f..5dc748baa 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -16,6 +16,10 @@ def validate(msg: NEMSignTx): if msg.transaction is None: raise ValueError('No common provided') + if msg.provision_namespace or msg.mosaic_creation or msg.supply_change or msg.aggregate_modification \ + or msg.importance_transfer: + raise ValueError('Not yet implemented') + _validate_single_tx(msg) _validate_common(msg.transaction) From 105ba853afe0e11ea7ce48f51e84d8698d49315e Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 27 Mar 2018 14:30:57 +0200 Subject: [PATCH 24/53] nem: get address formatting --- src/apps/nem/get_address.py | 38 ++++++++++++++++++++++++++++++++++--- src/apps/nem/validators.py | 2 +- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/apps/nem/get_address.py b/src/apps/nem/get_address.py index d7660f2f3..14c155485 100644 --- a/src/apps/nem/get_address.py +++ b/src/apps/nem/get_address.py @@ -1,7 +1,11 @@ -from apps.wallet.get_address import _show_address +from .validators import * from apps.common import seed +from apps.common.confirm import confirm +from trezor import ui from trezor.messages.NEMAddress import NEMAddress -from .validators import * +from trezor.messages import ButtonRequestType +from trezor.ui.text import Text +from trezor.utils import chunks async def nem_get_address(ctx, msg): @@ -11,7 +15,35 @@ async def nem_get_address(ctx, msg): if msg.show_display: while True: - if await _show_address(ctx, address): + if await _show_address(ctx, address, network): break return NEMAddress(address=address) + + +async def _show_address(ctx, address: str, network: int): + lines = _split_address(address) + print(network) + content = Text('Export NEM address', ui.ICON_RECEIVE, + ui.MONO, _get_network_str(network) + ' network', + ui.MONO, *lines, + icon_color=ui.GREEN) + + return await confirm( + ctx, + content, + code=ButtonRequestType.Address, + cancel_style=ui.BTN_KEY) + + +def _split_address(address: str): + return chunks(address, 17) + + +def _get_network_str(network: int) -> str: + if network == NEM_NETWORK_MAINNET: + return 'Mainnet' + elif network == NEM_NETWORK_TESTNET: + return 'Testnet' + elif network == NEM_NETWORK_MIJIN: + return 'Mijin' diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index 5dc748baa..677cce8e4 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -18,7 +18,7 @@ def validate(msg: NEMSignTx): if msg.provision_namespace or msg.mosaic_creation or msg.supply_change or msg.aggregate_modification \ or msg.importance_transfer: - raise ValueError('Not yet implemented') + raise ValueError('Not yet implemented') # todo _validate_single_tx(msg) _validate_common(msg.transaction) From 381d2da1eaf368202f9422e1d92f0ffd4b999594 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 28 Mar 2018 10:14:53 +0200 Subject: [PATCH 25/53] nem: provision namespace signing --- src/apps/nem/layout.py | 9 ++++++- src/apps/nem/signing.py | 48 ++++++++++++++++++++++++++++---------- src/apps/nem/validators.py | 2 +- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index 19714b692..91fb5028b 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -23,7 +23,14 @@ async def require_confirm_fee(ctx, fee): await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) -async def require_confirm_action(ctx, payload: bytes, encrypt=False): +async def require_confirm_final(ctx, action: str): + content = Text('Confirm sending', ui.ICON_SEND, + ui.NORMAL, *split_words(action, 18), + icon_color=ui.GREEN) + await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 + + +async def require_confirm_payload(ctx, payload: bytes, encrypt=False): payload = str(payload, 'utf-8') if encrypt: content = Text('Send encrypted?', ui.ICON_SEND, diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index e4f4d948c..601715605 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -10,17 +10,46 @@ from trezor.crypto import random async def nem_sign_tx(ctx, msg: NEMSignTx): - validate(msg) - node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) - payload, encrypted = _get_payload(msg, node) - public_key = _get_public_key(node) + tx = bytearray() + + if msg.transfer: + tx = await _transfer(ctx, node, msg) + # todo msg.transfer.mosaics = canonicalize_mosaics(msg.transfer.mosaics) + + elif msg.provision_namespace: # todo are those disjunctive? + tx = await _provision_namespace(ctx, node, msg) + + signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) + resp = NEMSignedTx() + resp.data = tx + resp.signature = signature + return resp + + +async def _provision_namespace(ctx, node, msg: NEMSignTx) -> bytearray: + await require_confirm_fee(ctx, msg.transaction.fee) + await require_confirm_final(ctx, 'Create provision namespace "' + msg.provision_namespace.namespace + '"?') + return nem_transaction_create_provision_namespace( + msg.transaction.network, + msg.transaction.timestamp, + _get_public_key(node), + msg.transaction.fee, + msg.transaction.deadline, + msg.provision_namespace.namespace, + msg.provision_namespace.parent, + msg.provision_namespace.sink, + msg.provision_namespace.fee) + + +async def _transfer(ctx, node, msg: NEMSignTx) -> bytes: + payload, encrypted = _get_payload(msg, node) tx = nem_transaction_create_transfer( msg.transaction.network, msg.transaction.timestamp, - public_key, + _get_public_key(node), msg.transaction.fee, msg.transaction.deadline, msg.transfer.recipient, @@ -34,17 +63,12 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): nem_transaction_write_mosaic(tx, mosaic.namespace, mosaic.mosaic, mosaic.quantity) if payload: # confirm unencrypted - await require_confirm_action(ctx, msg.transfer.payload, encrypted) + await require_confirm_payload(ctx, msg.transfer.payload, encrypted) await require_confirm_fee(ctx, msg.transaction.fee) await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) - signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) - - resp = NEMSignedTx() - resp.data = tx - resp.signature = signature - return resp + return tx def _get_payload(msg: NEMSignTx, node) -> [bytes, bool]: diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index 677cce8e4..bcd1d9a59 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -16,7 +16,7 @@ def validate(msg: NEMSignTx): if msg.transaction is None: raise ValueError('No common provided') - if msg.provision_namespace or msg.mosaic_creation or msg.supply_change or msg.aggregate_modification \ + if msg.mosaic_creation or msg.supply_change or msg.aggregate_modification \ or msg.importance_transfer: raise ValueError('Not yet implemented') # todo From 448ce35c2b919305b8a0cff1dd5ed6f4a49f2816 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 28 Mar 2018 11:53:47 +0200 Subject: [PATCH 26/53] nem: mosaic creation signing --- src/apps/nem/layout.py | 13 +++++------ src/apps/nem/signing.py | 43 ++++++++++++++++++++++++++++++++++--- src/apps/nem/transaction.py | 6 +++++- src/apps/nem/validators.py | 2 +- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index 91fb5028b..0f62e3a47 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -15,17 +15,18 @@ async def require_confirm_tx(ctx, recipient, value): await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 -async def require_confirm_fee(ctx, fee): - content = Text('Confirm fee', ui.ICON_SEND, - 'Pay ', ui.BOLD, format_amount(fee, NEM_MAX_DIVISIBILITY) + ' NEM', - ui.NORMAL, 'for transaction fee?', +async def require_confirm_action(ctx, action: str): + content = Text('Confirm sending', ui.ICON_SEND, + ui.NORMAL, *split_words(action, 18), icon_color=ui.GREEN) await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) -async def require_confirm_final(ctx, action: str): +async def require_confirm_final(ctx, action: str, fee: int): content = Text('Confirm sending', ui.ICON_SEND, - ui.NORMAL, *split_words(action, 18), + ui.NORMAL, 'Create ', action, + ui.BOLD, 'paying ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' NEM', + ui.NORMAL, 'for transaction fee?', icon_color=ui.GREEN) await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 601715605..40e491e84 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -21,6 +21,9 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): elif msg.provision_namespace: # todo are those disjunctive? tx = await _provision_namespace(ctx, node, msg) + elif msg.mosaic_creation: + tx = await _mosaic_creation(ctx, node, msg) + signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) resp = NEMSignedTx() @@ -29,9 +32,43 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): return resp +async def _mosaic_creation(ctx, node, msg: NEMSignTx) -> bytearray: + await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' + + msg.mosaic_creation.definition.namespace + '"?') + if msg.mosaic_creation.definition.description: + await require_confirm_action(ctx, 'Create mosaic with description: ' + + msg.mosaic_creation.definition.description) + if msg.mosaic_creation.definition.mutable_supply: + await require_confirm_action(ctx, 'Create mosaic with mutable supply') + else: + await require_confirm_action(ctx, 'Create mosaic with immutable supply') + await require_confirm_final(ctx, 'mosaic', msg.transaction.fee) + + return nem_transaction_create_mosaic_creation( + msg.transaction.network, + msg.transaction.timestamp, + _get_public_key(node), + msg.transaction.fee, + msg.transaction.deadline, + msg.mosaic_creation.definition.namespace, + msg.mosaic_creation.definition.mosaic, + msg.mosaic_creation.definition.description, + msg.mosaic_creation.definition.divisibility, + msg.mosaic_creation.definition.supply, + msg.mosaic_creation.definition.mutable_supply, + msg.mosaic_creation.definition.transferable, + msg.mosaic_creation.definition.levy, + msg.mosaic_creation.definition.fee, + msg.mosaic_creation.definition.levy_address, + msg.mosaic_creation.definition.levy_namespace, + msg.mosaic_creation.definition.levy_mosaic, + msg.mosaic_creation.sink, + msg.mosaic_creation.fee) + + async def _provision_namespace(ctx, node, msg: NEMSignTx) -> bytearray: - await require_confirm_fee(ctx, msg.transaction.fee) - await require_confirm_final(ctx, 'Create provision namespace "' + msg.provision_namespace.namespace + '"?') + await require_confirm_action(ctx, 'Create provision namespace "' + msg.provision_namespace.namespace + '"?') + await require_confirm_final(ctx, 'provision namespace', msg.transaction.fee) return nem_transaction_create_provision_namespace( msg.transaction.network, msg.transaction.timestamp, @@ -65,7 +102,7 @@ async def _transfer(ctx, node, msg: NEMSignTx) -> bytes: if payload: # confirm unencrypted await require_confirm_payload(ctx, msg.transfer.payload, encrypted) - await require_confirm_fee(ctx, msg.transaction.fee) + await require_confirm_action(ctx, 'todo') # todo! await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) return tx diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index f0f8b9485..a94c0a3b7 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -165,13 +165,17 @@ def nem_transaction_write_minimum_cosignatories(w: bytearray, relative_change: i def nem_write_mosaic(w: bytearray, name: str, value): + if value is None: + if name in ['divisibility', 'initialSupply']: + value = 0 + elif name in ['supplyMutable', 'transferable']: + value = False if type(value) == bool: if value: value = "true" else: value = "false" elif type(value) == int: - # todo might need more formatting value = str(value) elif type(value) != str: raise ValueError('Incompatible value type') diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index bcd1d9a59..6374dc794 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -16,7 +16,7 @@ def validate(msg: NEMSignTx): if msg.transaction is None: raise ValueError('No common provided') - if msg.mosaic_creation or msg.supply_change or msg.aggregate_modification \ + if msg.supply_change or msg.aggregate_modification \ or msg.importance_transfer: raise ValueError('Not yet implemented') # todo From 9f4cef923bcd4caa284d69dbfdd1d81ea4941eb7 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 28 Mar 2018 16:47:22 +0200 Subject: [PATCH 27/53] nem: validators fix --- src/apps/nem/validators.py | 62 +++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index 6374dc794..667057a54 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -68,11 +68,11 @@ def _validate_common(common: NEMTransactionCommon, inner: bool=False): common.network = validate_network(common.network) err = None - if not common.timestamp: + if common.timestamp is None: err = 'timestamp' - if not common.fee: + if common.fee is None: err = 'fee' - if not common.deadline: + if common.deadline is None: err = 'deadline' is_signer = common.signer is not None @@ -99,7 +99,7 @@ def _validate_public_key(public_key: bytes, err_msg): def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer): - if not importance_transfer.mode: + if importance_transfer.mode is None: raise ValueError('No mode provided') _validate_public_key(importance_transfer.public_key, 'Invalid remote account public key provided') @@ -118,58 +118,58 @@ def _validate_aggregate_modification(aggregate_modification: NEMAggregateModific def _validate_supply_change(supply_change: NEMMosaicSupplyChange): - if not supply_change.namespace: + if supply_change.namespace is None: raise ValueError('No namespace provided') - if not supply_change.mosaic: + if supply_change.mosaic is None: raise ValueError('No mosaic provided') - if not supply_change.type: + if supply_change.type is None: raise ValueError('No type provided') - if not supply_change.delta: + if supply_change.delta is None: raise ValueError('No delta provided') def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int): - if not mosaic_creation.definition: + if mosaic_creation.definition is None: raise ValueError('No mosaic definition provided') - if not mosaic_creation.sink: + if mosaic_creation.sink is None: raise ValueError('No creation sink provided') - if not mosaic_creation.fee: + if mosaic_creation.fee is None: raise ValueError('No creation sink fee provided') if not nem.validate_address(mosaic_creation.sink, network): raise ValueError('Invalid creation sink address') - if mosaic_creation.definition.name: + if mosaic_creation.definition.name is not None: raise ValueError('Name not allowed in mosaic creation transactions') - if mosaic_creation.definition.ticker: + if mosaic_creation.definition.ticker is not None: raise ValueError('Ticker not allowed in mosaic creation transactions') if len(mosaic_creation.definition.networks): raise ValueError('Networks not allowed in mosaic creation transactions') - if not mosaic_creation.definition.namespace: + if mosaic_creation.definition.namespace is None: raise ValueError('No mosaic namespace provided') - if not mosaic_creation.definition.mosaic: + if mosaic_creation.definition.mosaic is None: raise ValueError('No mosaic name provided') - if mosaic_creation.definition.levy: - if not mosaic_creation.definition.fee: + if mosaic_creation.definition.levy is not None: + if mosaic_creation.definition.fee is None: raise ValueError('No levy fee provided') - if not mosaic_creation.definition.levy_address: + if mosaic_creation.definition.levy_address is None: raise ValueError('No levy address provided') - if not mosaic_creation.definition.levy_namespace: + if mosaic_creation.definition.levy_namespace is None: raise ValueError('No levy namespace provided') - if not mosaic_creation.definition.levy_mosaic: + if mosaic_creation.definition.levy_mosaic is None: raise ValueError('No levy mosaic name provided') - if not mosaic_creation.definition.divisibility: + if mosaic_creation.definition.divisibility is None: raise ValueError('No divisibility provided') - if not mosaic_creation.definition.supply: + if mosaic_creation.definition.supply is None: raise ValueError('No supply provided') - if not mosaic_creation.definition.mutable_supply: + if mosaic_creation.definition.mutable_supply is None: raise ValueError('No supply mutability provided') - if not mosaic_creation.definition.transferable: + if mosaic_creation.definition.transferable is None: raise ValueError('No mosaic transferability provided') - if not mosaic_creation.definition.description: + if mosaic_creation.definition.description is None: raise ValueError('No description provided') if mosaic_creation.definition.divisibility > NEM_MAX_DIVISIBILITY: @@ -182,11 +182,11 @@ def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int): def _validate_provision_namespace(provision_namespace: NEMProvisionNamespace, network: int): - if not provision_namespace.namespace: + if provision_namespace.namespace is None: raise ValueError('No namespace provided') - if not provision_namespace.sink: + if provision_namespace.sink is None: raise ValueError('No rental sink provided') - if not provision_namespace.fee: + if provision_namespace.fee is None: raise ValueError('No rental sink fee provided') if not nem.validate_address(provision_namespace.sink, network): @@ -206,9 +206,9 @@ def _validate_transfer(transfer: NEMTransfer, network: int): raise ValueError('Invalid recipient address') for m in transfer.mosaics: - if not m.namespace: + if m.namespace is None: raise ValueError('No mosaic namespace provided') - if not m.mosaic: + if m.mosaic is None: raise ValueError('No mosaic name provided') - if not m.quantity: + if m.quantity is None: raise ValueError('No mosaic quantity provided') From c425a8baf928d996ffad19e218581241908a98df Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 28 Mar 2018 16:58:41 +0200 Subject: [PATCH 28/53] nem: properties in swiping pages --- src/apps/nem/layout.py | 49 +++++++++++++++++++++++++++++++++++++- src/apps/nem/signing.py | 8 +------ src/apps/nem/validators.py | 9 +++++-- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index 0f62e3a47..e9f921696 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -1,7 +1,9 @@ from apps.common.confirm import * from trezor import ui from trezor.messages import ButtonRequestType +from trezor.messages.NEMMosaicDefinition import NEMMosaicDefinition from trezor.ui.text import Text +from trezor.ui.scroll import Scrollpage, animate_swipe, paginate from trezor.utils import chunks, format_amount, split_words from .helpers import * @@ -22,10 +24,55 @@ async def require_confirm_action(ctx, action: str): await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) +@ui.layout +async def _show_page(page: int, page_count: int, content): + content = Scrollpage(content[page], page, page_count) + if page + 1 == page_count: + await ConfirmDialog(content) + else: + content.render() + await animate_swipe() + + +async def require_confirm_properties(ctx, definition: NEMMosaicDefinition): + properties = _get_mosaic_properties(definition) + first_page = const(0) + paginator = paginate(_show_page, len(properties), first_page, properties) + await ctx.wait(paginator) + + +def _get_mosaic_properties(definition: NEMMosaicDefinition): + properties = [] + if definition.description: + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Description:', + ui.NORMAL, definition.description) + properties.append(t) + if definition.transferable: + transferable = 'Yes' + else: + transferable = 'No' + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Transferable?', + ui.NORMAL, transferable) + properties.append(t) + if definition.mutable_supply: + imm = 'mutable' + else: + imm = 'immutable' + if definition.supply: + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Initial supply:', + ui.NORMAL, format_amount(definition.supply, definition.divisibility), + ui.NORMAL, imm) + properties.append(t) + return properties + + async def require_confirm_final(ctx, action: str, fee: int): content = Text('Confirm sending', ui.ICON_SEND, ui.NORMAL, 'Create ', action, - ui.BOLD, 'paying ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' NEM', + ui.BOLD, 'paying ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM', ui.NORMAL, 'for transaction fee?', icon_color=ui.GREEN) await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 40e491e84..b8d53396e 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -35,13 +35,7 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): async def _mosaic_creation(ctx, node, msg: NEMSignTx) -> bytearray: await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' + msg.mosaic_creation.definition.namespace + '"?') - if msg.mosaic_creation.definition.description: - await require_confirm_action(ctx, 'Create mosaic with description: ' - + msg.mosaic_creation.definition.description) - if msg.mosaic_creation.definition.mutable_supply: - await require_confirm_action(ctx, 'Create mosaic with mutable supply') - else: - await require_confirm_action(ctx, 'Create mosaic with immutable supply') + await require_confirm_properties(ctx, msg.mosaic_creation.definition) await require_confirm_final(ctx, 'mosaic', msg.transaction.fee) return nem_transaction_create_mosaic_creation( diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index 667057a54..baf759f6b 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -87,7 +87,7 @@ def _validate_common(common: NEMTransactionCommon, inner: bool=False): else: raise ValueError('No ' + err + ' provided') - if common.signer: + if common.signer is not None: _validate_public_key(common.signer, 'Invalid signer public key in inner transaction') @@ -151,6 +151,11 @@ def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int): if mosaic_creation.definition.mosaic is None: raise ValueError('No mosaic name provided') + if mosaic_creation.definition.supply is not None and mosaic_creation.definition.divisibility is None: + raise ValueError('Definition divisibility needs to be provided when supply is') + if mosaic_creation.definition.supply is None and mosaic_creation.definition.divisibility is not None: + raise ValueError('Definition supply needs to be provided when divisibility is') + if mosaic_creation.definition.levy is not None: if mosaic_creation.definition.fee is None: raise ValueError('No levy fee provided') @@ -199,7 +204,7 @@ def _validate_transfer(transfer: NEMTransfer, network: int): if transfer.amount is None: raise ValueError('No amount provided') - if transfer.public_key: + if transfer.public_key is not None: _validate_public_key(transfer.public_key, 'Invalid recipient public key') if not nem.validate_address(transfer.recipient, network): From d4b2bee47ec255e7e4ce78bb73bc53f0bf8a25f1 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 29 Mar 2018 10:44:22 +0200 Subject: [PATCH 29/53] nem: supply change signing --- src/apps/nem/signing.py | 19 +++++++++++++++++++ src/apps/nem/validators.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index b8d53396e..915db90a0 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -24,6 +24,9 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): elif msg.mosaic_creation: tx = await _mosaic_creation(ctx, node, msg) + elif msg.supply_change: + tx = await _supply_change(ctx, node, msg) + signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) resp = NEMSignedTx() @@ -32,9 +35,25 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): return resp +async def _supply_change(ctx, node, msg: NEMSignTx): + # todo confirms! + return nem_transaction_create_mosaic_supply_change( + msg.transaction.network, + msg.transaction.timestamp, + _get_public_key(node), + msg.transaction.fee, + msg.transaction.deadline, + msg.supply_change.namespace, + msg.supply_change.mosaic, + msg.supply_change.type, + msg.supply_change.delta + ) + + async def _mosaic_creation(ctx, node, msg: NEMSignTx) -> bytearray: await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' + msg.mosaic_creation.definition.namespace + '"?') + # todo confirm levy! await require_confirm_properties(ctx, msg.mosaic_creation.definition) await require_confirm_final(ctx, 'mosaic', msg.transaction.fee) diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index baf759f6b..3b327916c 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -16,7 +16,7 @@ def validate(msg: NEMSignTx): if msg.transaction is None: raise ValueError('No common provided') - if msg.supply_change or msg.aggregate_modification \ + if msg.aggregate_modification \ or msg.importance_transfer: raise ValueError('Not yet implemented') # todo From 52affe2897274d0b58c249c89b9ae8a3000973da Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 29 Mar 2018 11:43:38 +0200 Subject: [PATCH 30/53] nem: aggregate modification signing --- src/apps/nem/signing.py | 28 ++++++++++++++++++++++++---- src/apps/nem/validators.py | 14 +++++++++----- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 915db90a0..27bd96bcd 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -17,15 +17,14 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): if msg.transfer: tx = await _transfer(ctx, node, msg) # todo msg.transfer.mosaics = canonicalize_mosaics(msg.transfer.mosaics) - - elif msg.provision_namespace: # todo are those disjunctive? + elif msg.provision_namespace: tx = await _provision_namespace(ctx, node, msg) - elif msg.mosaic_creation: tx = await _mosaic_creation(ctx, node, msg) - elif msg.supply_change: tx = await _supply_change(ctx, node, msg) + elif msg.aggregate_modification: + tx = await _aggregate_modification(ctx, node, msg) signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) @@ -35,6 +34,27 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): return resp +async def _aggregate_modification(ctx, node, msg: NEMSignTx): + # todo confirms! + w = nem_transaction_create_aggregate_modification( + msg.transaction.network, + msg.transaction.timestamp, + _get_public_key(node), + msg.transaction.fee, + msg.transaction.deadline, + len(msg.aggregate_modification.modifications), + msg.aggregate_modification.relative_change + ) + + for m in msg.aggregate_modification.modifications: + nem_transaction_write_cosignatory_modification(w, m.type, m.public_key) + + if msg.aggregate_modification.relative_change: + nem_transaction_write_minimum_cosignatories(w, msg.aggregate_modification.relative_change) + + return w + + async def _supply_change(ctx, node, msg: NEMSignTx): # todo confirms! return nem_transaction_create_mosaic_supply_change( diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index 3b327916c..32fdc51e0 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -1,5 +1,5 @@ from apps.nem.helpers import * -from trezor.messages.NEMModificationType import CosignatoryModification_Delete +from trezor.messages import NEMModificationType from trezor.messages.NEMSignTx import NEMAggregateModification from trezor.messages.NEMSignTx import NEMImportanceTransfer from trezor.messages.NEMSignTx import NEMMosaicCreation @@ -16,8 +16,7 @@ def validate(msg: NEMSignTx): if msg.transaction is None: raise ValueError('No common provided') - if msg.aggregate_modification \ - or msg.importance_transfer: + if msg.importance_transfer: raise ValueError('Not yet implemented') # todo _validate_single_tx(msg) @@ -32,7 +31,7 @@ def validate(msg: NEMSignTx): if msg.supply_change: _validate_supply_change(msg.supply_change) if msg.aggregate_modification: - _validate_aggregate_modification(msg.aggregate_modification, not len(msg.multisig)) + _validate_aggregate_modification(msg.aggregate_modification, msg.multisig is not None) if msg.importance_transfer: _validate_importance_transfer(msg.importance_transfer) @@ -112,7 +111,12 @@ def _validate_aggregate_modification(aggregate_modification: NEMAggregateModific for m in aggregate_modification.modifications: if not m.type: raise ValueError('No modification type provided') - if creation and m.type == CosignatoryModification_Delete: + if m.type not in [ + NEMModificationType.CosignatoryModification_Add, + NEMModificationType.CosignatoryModification_Delete + ]: + raise ValueError('Unknown aggregate modification ') + if creation and m.type == NEMModificationType.CosignatoryModification_Delete: raise ValueError('Cannot remove cosignatory when converting account') _validate_public_key(m.public_key, 'Invalid cosignatory public key provided') From 368b979a8aa11012bff5f9d0076a9753db72e384 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 29 Mar 2018 12:40:13 +0200 Subject: [PATCH 31/53] nem: importance transfer signing --- src/apps/nem/signing.py | 19 ++++++++++++++++++- src/apps/nem/transaction.py | 1 + src/apps/nem/validators.py | 3 --- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 27bd96bcd..d5b66c78c 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -12,7 +12,6 @@ from trezor.crypto import random async def nem_sign_tx(ctx, msg: NEMSignTx): validate(msg) node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) - tx = bytearray() if msg.transfer: tx = await _transfer(ctx, node, msg) @@ -25,6 +24,10 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): tx = await _supply_change(ctx, node, msg) elif msg.aggregate_modification: tx = await _aggregate_modification(ctx, node, msg) + elif msg.importance_transfer: + tx = await _importance_transfer(ctx, node, msg) + else: + raise ValueError('No transaction provided') signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) @@ -34,6 +37,20 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): return resp +async def _importance_transfer(ctx, node, msg: NEMSignTx): + # todo confirms! + w = nem_transaction_create_importance_transfer( + msg.transaction.network, + msg.transaction.timestamp, + _get_public_key(node), + msg.transaction.fee, + msg.transaction.deadline, + msg.importance_transfer.mode, + msg.importance_transfer.public_key + ) + return w + + async def _aggregate_modification(ctx, node, msg: NEMSignTx): # todo confirms! w = nem_transaction_create_aggregate_modification( diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index a94c0a3b7..4a776b22d 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -137,6 +137,7 @@ def nem_transaction_create_importance_transfer(network: int, timestamp: int, sig write_uint32(w, mode) write_bytes_with_length(w, bytearray(remote)) + return w def nem_transaction_create_aggregate_modification(network: int, timestamp: int, signer_public_key: bytes, fee: int, diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index 32fdc51e0..f8b407b62 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -16,9 +16,6 @@ def validate(msg: NEMSignTx): if msg.transaction is None: raise ValueError('No common provided') - if msg.importance_transfer: - raise ValueError('Not yet implemented') # todo - _validate_single_tx(msg) _validate_common(msg.transaction) From d07deecc7ea029f44c37d45d6d0c2dfa99a61976 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 29 Mar 2018 13:35:40 +0200 Subject: [PATCH 32/53] nem: mosaics moved to seperate file --- src/apps/nem/mosaic.py | 102 ++++++++++++++++++ src/apps/nem/signing.py | 1 + src/apps/nem/transaction.py | 99 ----------------- ...st_apps.nem.transaction.mosaic_creation.py | 2 +- ...ps.nem.transaction.mosaic_supply_change.py | 2 +- tests/test_apps.nem.transaction.transfer.py | 1 + 6 files changed, 106 insertions(+), 101 deletions(-) create mode 100644 src/apps/nem/mosaic.py diff --git a/src/apps/nem/mosaic.py b/src/apps/nem/mosaic.py new file mode 100644 index 000000000..719b60f96 --- /dev/null +++ b/src/apps/nem/mosaic.py @@ -0,0 +1,102 @@ + +from .helpers import * +from .writers import * + + +def nem_transaction_create_mosaic_creation(network: int, timestamp: int, signer_public_key: bytes, fee:int, + deadline: int, namespace: str, mosaic: str, description: str, + divisibility: int, supply: int, mutable_supply: bool, transferable: bool, + levy_type: int, levy_fee: int, levy_address: str, levy_namespace: str, + levy_mosaic: str, creation_sink: str, creation_fee: int): + + w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_CREATION, + nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + mosaics_w = bytearray() + write_bytes_with_length(mosaics_w, bytearray(signer_public_key)) + identifier_length = 4 + len(namespace) + 4 + len(mosaic) + write_uint32(mosaics_w, identifier_length) + write_bytes_with_length(mosaics_w, bytearray(namespace)) + write_bytes_with_length(mosaics_w, bytearray(mosaic)) + write_bytes_with_length(mosaics_w, bytearray(description)) + write_uint32(mosaics_w, 4) # number of properties + + nem_write_mosaic(mosaics_w, "divisibility", divisibility) + nem_write_mosaic(mosaics_w, "initialSupply", supply) + nem_write_mosaic(mosaics_w, "supplyMutable", mutable_supply) + nem_write_mosaic(mosaics_w, "transferable", transferable) + + if levy_type: + levy_identifier_length = 4 + len(levy_namespace) + 4 + len(levy_mosaic) + write_uint32(mosaics_w, 4 + 4 + len(levy_address) + 4 + levy_identifier_length + 8) + write_uint32(mosaics_w, levy_type) + write_bytes_with_length(mosaics_w, bytearray(levy_address)) + write_uint32(mosaics_w, levy_identifier_length) + write_bytes_with_length(mosaics_w, bytearray(levy_namespace)) + write_bytes_with_length(mosaics_w, bytearray(levy_mosaic)) + write_uint64(mosaics_w, levy_fee) + else: + write_uint32(mosaics_w, 0) + + # write mosaic bytes with length + write_bytes_with_length(w, mosaics_w) + + write_bytes_with_length(w, bytearray(creation_sink)) + write_uint64(w, creation_fee) + + return w + + +def nem_transaction_create_mosaic_supply_change(network: int, timestamp: int, signer_public_key: bytes, fee: int, + deadline: int, namespace: str, mosaic: str, type: int, delta: int): + + w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, + nem_get_version(network), + timestamp, + signer_public_key, + fee, + deadline) + + identifier_length = 4 + len(namespace) + 4 + len(mosaic) + write_uint32(w, identifier_length) + write_bytes_with_length(w, bytearray(namespace)) + write_bytes_with_length(w, bytearray(mosaic)) + + write_uint32(w, type) + write_uint64(w, delta) + + return w + + +def nem_write_mosaic(w: bytearray, name: str, value): + if value is None: + if name in ['divisibility', 'initialSupply']: + value = 0 + elif name in ['supplyMutable', 'transferable']: + value = False + if type(value) == bool: + if value: + value = "true" + else: + value = "false" + elif type(value) == int: + value = str(value) + elif type(value) != str: + raise ValueError('Incompatible value type') + write_uint32(w, 4 + len(name) + 4 + len(value)) + write_bytes_with_length(w, bytearray(name)) + write_bytes_with_length(w, bytearray(value)) + + +def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): + identifier_length = 4 + len(namespace) + 4 + len(mosaic) + # indentifier length (u32) + quantity (u64) + identifier size + write_uint32(w, 4 + 8 + identifier_length) + write_uint32(w, identifier_length) + write_bytes_with_length(w, bytearray(namespace)) + write_bytes_with_length(w, bytearray(mosaic)) + write_uint64(w, quantity) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index d5b66c78c..2cef2a45e 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -1,5 +1,6 @@ from apps.nem.layout import * from apps.nem.transaction import * +from apps.nem.mosaic import * from apps.nem.validators import validate from apps.nem import helpers from apps.common import seed diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py index 4a776b22d..97e6ef11f 100644 --- a/src/apps/nem/transaction.py +++ b/src/apps/nem/transaction.py @@ -56,75 +56,6 @@ def nem_transaction_create_provision_namespace(network: int, timestamp: int, sig return tx -def nem_transaction_create_mosaic_creation(network: int, timestamp: int, signer_public_key: bytes, fee:int, - deadline: int, namespace: str, mosaic: str, description: str, - divisibility: int, supply: int, mutable_supply: bool, transferable: bool, - levy_type: int, levy_fee: int, levy_address: str, levy_namespace: str, - levy_mosaic: str, creation_sink: str, creation_fee: int): - - w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_CREATION, - nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) - - mosaics_w = bytearray() - write_bytes_with_length(mosaics_w, bytearray(signer_public_key)) - identifier_length = 4 + len(namespace) + 4 + len(mosaic) - write_uint32(mosaics_w, identifier_length) - write_bytes_with_length(mosaics_w, bytearray(namespace)) - write_bytes_with_length(mosaics_w, bytearray(mosaic)) - write_bytes_with_length(mosaics_w, bytearray(description)) - write_uint32(mosaics_w, 4) # number of properties - - nem_write_mosaic(mosaics_w, "divisibility", divisibility) - nem_write_mosaic(mosaics_w, "initialSupply", supply) - nem_write_mosaic(mosaics_w, "supplyMutable", mutable_supply) - nem_write_mosaic(mosaics_w, "transferable", transferable) - - if levy_type: - levy_identifier_length = 4 + len(levy_namespace) + 4 + len(levy_mosaic) - write_uint32(mosaics_w, 4 + 4 + len(levy_address) + 4 + levy_identifier_length + 8) - write_uint32(mosaics_w, levy_type) - write_bytes_with_length(mosaics_w, bytearray(levy_address)) - write_uint32(mosaics_w, levy_identifier_length) - write_bytes_with_length(mosaics_w, bytearray(levy_namespace)) - write_bytes_with_length(mosaics_w, bytearray(levy_mosaic)) - write_uint64(mosaics_w, levy_fee) - else: - write_uint32(mosaics_w, 0) - - # write mosaic bytes with length - write_bytes_with_length(w, mosaics_w) - - write_bytes_with_length(w, bytearray(creation_sink)) - write_uint64(w, creation_fee) - - return w - - -def nem_transaction_create_mosaic_supply_change(network: int, timestamp: int, signer_public_key: bytes, fee: int, - deadline: int, namespace: str, mosaic: str, type: int, delta: int): - - w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, - nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) - - identifier_length = 4 + len(namespace) + 4 + len(mosaic) - write_uint32(w, identifier_length) - write_bytes_with_length(w, bytearray(namespace)) - write_bytes_with_length(w, bytearray(mosaic)) - - write_uint32(w, type) - write_uint64(w, delta) - - return w - - def nem_transaction_create_importance_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, mode: int, remote: bytes): @@ -163,33 +94,3 @@ def nem_transaction_write_cosignatory_modification(w: bytearray, type: int, cosi def nem_transaction_write_minimum_cosignatories(w: bytearray, relative_change: int): write_uint32(w, 4) write_uint32(w, relative_change) - - -def nem_write_mosaic(w: bytearray, name: str, value): - if value is None: - if name in ['divisibility', 'initialSupply']: - value = 0 - elif name in ['supplyMutable', 'transferable']: - value = False - if type(value) == bool: - if value: - value = "true" - else: - value = "false" - elif type(value) == int: - value = str(value) - elif type(value) != str: - raise ValueError('Incompatible value type') - write_uint32(w, 4 + len(name) + 4 + len(value)) - write_bytes_with_length(w, bytearray(name)) - write_bytes_with_length(w, bytearray(value)) - - -def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): - identifier_length = 4 + len(namespace) + 4 + len(mosaic) - # indentifier length (u32) + quantity (u64) + identifier size - write_uint32(w, 4 + 8 + identifier_length) - write_uint32(w, identifier_length) - write_bytes_with_length(w, bytearray(namespace)) - write_bytes_with_length(w, bytearray(mosaic)) - write_uint64(w, quantity) diff --git a/tests/test_apps.nem.transaction.mosaic_creation.py b/tests/test_apps.nem.transaction.mosaic_creation.py index ff37aa6e2..725db6f05 100644 --- a/tests/test_apps.nem.transaction.mosaic_creation.py +++ b/tests/test_apps.nem.transaction.mosaic_creation.py @@ -1,6 +1,6 @@ from common import * -from apps.nem.transaction import * +from apps.nem.mosaic import * from trezor.crypto import hashlib diff --git a/tests/test_apps.nem.transaction.mosaic_supply_change.py b/tests/test_apps.nem.transaction.mosaic_supply_change.py index c5e3affba..0f72f21e6 100644 --- a/tests/test_apps.nem.transaction.mosaic_supply_change.py +++ b/tests/test_apps.nem.transaction.mosaic_supply_change.py @@ -1,6 +1,6 @@ from common import * -from apps.nem.transaction import * +from apps.nem.mosaic import * from trezor.crypto import hashlib diff --git a/tests/test_apps.nem.transaction.transfer.py b/tests/test_apps.nem.transaction.transfer.py index 6bb9791c1..75648ff80 100644 --- a/tests/test_apps.nem.transaction.transfer.py +++ b/tests/test_apps.nem.transaction.transfer.py @@ -1,6 +1,7 @@ from common import * from apps.nem.transaction import * +from apps.nem.mosaic import * from trezor.crypto import hashlib From 73415049df081cdf16bc1d28bef6f2422f2f8e83 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 4 Apr 2018 12:01:44 +0200 Subject: [PATCH 33/53] nem: fix unused imports as in 9b9183c4f77370390ec39b8ac4eb200dffcd5b9b --- src/apps/nem/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/apps/nem/__init__.py b/src/apps/nem/__init__.py index 90a04f153..e0c9f3b27 100644 --- a/src/apps/nem/__init__.py +++ b/src/apps/nem/__init__.py @@ -1,15 +1,12 @@ from trezor.wire import register, protobuf_workflow -from trezor.utils import unimport from trezor.messages.wire_types import NEMGetAddress, NEMSignTx -@unimport def dispatch_NemGetAddress(*args, **kwargs): from .get_address import nem_get_address return nem_get_address(*args, **kwargs) -@unimport def dispatch_NemSignTx(*args, **kwargs): from .signing import nem_sign_tx return nem_sign_tx(*args, **kwargs) From 4cd87d3a0145ea3901974e200004e0acec5f1ff2 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 29 Mar 2018 14:16:58 +0200 Subject: [PATCH 34/53] nem: mosaics canonicalization --- src/apps/nem/mosaic.py | 33 ++++ src/apps/nem/signing.py | 2 +- ...nem.transaction.mosaic.canonicalization.py | 147 ++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 tests/test_apps.nem.transaction.mosaic.canonicalization.py diff --git a/src/apps/nem/mosaic.py b/src/apps/nem/mosaic.py index 719b60f96..c9d5b4a1d 100644 --- a/src/apps/nem/mosaic.py +++ b/src/apps/nem/mosaic.py @@ -1,6 +1,7 @@ from .helpers import * from .writers import * +from trezor.messages.NEMMosaic import NEMMosaic def nem_transaction_create_mosaic_creation(network: int, timestamp: int, signer_public_key: bytes, fee:int, @@ -100,3 +101,35 @@ def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quan write_bytes_with_length(w, bytearray(namespace)) write_bytes_with_length(w, bytearray(mosaic)) write_uint64(w, quantity) + + +def nem_canonicalize_mosaics(mosaics: list): + if len(mosaics) <= 1: + return mosaics + mosaics = nem_merge_mosaics(mosaics) + return nem_sort_mosaics(mosaics) + + +def are_mosaics_equal(a: NEMMosaic, b: NEMMosaic) -> bool: + if a.namespace == b.namespace and a.mosaic == b.mosaic: + return True + return False + + +def nem_merge_mosaics(mosaics: list) -> list: + if not len(mosaics): + return list() + ret = list() + for i in mosaics: + found = False + for k, y in enumerate(ret): + if are_mosaics_equal(i, y): + ret[k].quantity += i.quantity + found = True + if not found: + ret.append(i) + return ret + + +def nem_sort_mosaics(mosaics: list) -> list: + return sorted(mosaics, key=lambda m: (m.namespace, m.mosaic)) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 2cef2a45e..f68e71e2c 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -15,8 +15,8 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) if msg.transfer: + msg.transfer.mosaics = nem_canonicalize_mosaics(msg.transfer.mosaics) tx = await _transfer(ctx, node, msg) - # todo msg.transfer.mosaics = canonicalize_mosaics(msg.transfer.mosaics) elif msg.provision_namespace: tx = await _provision_namespace(ctx, node, msg) elif msg.mosaic_creation: diff --git a/tests/test_apps.nem.transaction.mosaic.canonicalization.py b/tests/test_apps.nem.transaction.mosaic.canonicalization.py new file mode 100644 index 000000000..c5d3b676d --- /dev/null +++ b/tests/test_apps.nem.transaction.mosaic.canonicalization.py @@ -0,0 +1,147 @@ +from common import * + +from apps.nem.mosaic import * + + +class TestNemTransactionMosaicCanonicalization(unittest.TestCase): + + def test_nem_transaction_mosaic_canonicalization(self): + a = NEMMosaic() + a.namespace = 'abc' + a.quantity = 3 + a.mosaic = 'mosaic' + b = NEMMosaic() + b.namespace = 'abc' + b.quantity = 4 + b.mosaic = 'a' + c = NEMMosaic() + c.namespace = 'zzz' + c.quantity = 3 + c.mosaic = 'mosaic' + d = NEMMosaic() + d.namespace = 'abc' + d.quantity = 8 + d.mosaic = 'mosaic' + e = NEMMosaic() + e.namespace = 'aaa' + e.quantity = 1 + e.mosaic = 'mosaic' + f = NEMMosaic() + f.namespace = 'aaa' + f.quantity = 1 + f.mosaic = 'mosaicz' + g = NEMMosaic() + g.namespace = 'zzz' + g.quantity = 30 + g.mosaic = 'mosaic' + + res = nem_canonicalize_mosaics([a, b, c, d, e, f, g]) + self.assertEqual(res, [e, f, b, a, c]) + self.assertEqual(res[2].quantity, b.quantity) + self.assertEqual(res[3].quantity, 3 + 8) # a + d + self.assertEqual(res[4].quantity, 3 + 30) # c + g + + def test_nem_transaction_mosaic_merge(self): + a = NEMMosaic() + a.namespace = 'abc' + a.quantity = 1 + a.mosaic = 'mosaic' + b = NEMMosaic() + b.namespace = 'abc' + b.quantity = 1 + b.mosaic = 'mosaic' + + merged = nem_merge_mosaics([a, b]) + self.assertEqual(merged[0].quantity, 2) + self.assertEqual(len(merged), 1) + + a.quantity = 1 + b.quantity = 10 + merged = nem_merge_mosaics([a, b]) + self.assertEqual(merged[0].quantity, 11) + + a.namespace = 'abcdef' + merged = nem_merge_mosaics([a, b]) + self.assertEqual(len(merged), 2) + + c = NEMMosaic() + c.namespace = 'abc' + c.mosaic = 'xxx' + c.quantity = 2 + merged = nem_merge_mosaics([a, b, c]) + self.assertEqual(len(merged), 3) + + a.namespace = 'abcdef' + a.quantity = 1 + a.mosaic = 'mosaic' + b.namespace = 'abc' + b.quantity = 2 + b.mosaic = 'mosaic' + c.namespace = 'abc' + c.mosaic = 'mosaic' + c.quantity = 3 + merged = nem_merge_mosaics([a, b, c]) + self.assertEqual(merged[0].quantity, 1) + self.assertEqual(merged[1].quantity, 5) + self.assertEqual(len(merged), 2) + + a.namespace = 'abc' + a.quantity = 1 + a.mosaic = 'mosaic' + b.namespace = 'abc' + b.quantity = 2 + b.mosaic = 'mosaic' + c.namespace = 'abc' + c.mosaic = 'mosaic' + c.quantity = 3 + merged = nem_merge_mosaics([a, b, c]) + self.assertEqual(merged[0].quantity, 6) + self.assertEqual(len(merged), 1) + + def test_nem_transaction_mosaic_sort(self): + a = NEMMosaic() + a.namespace = 'abcz' + a.quantity = 1 + a.mosaic = 'mosaic' + b = NEMMosaic() + b.namespace = 'abca' + b.quantity = 1 + b.mosaic = 'mosaic' + res = nem_sort_mosaics([a, b]) + self.assertEqual(res, [b, a]) + + a.namespace = '' + b.namespace = 'a.b.c' + res = nem_sort_mosaics([a, b]) + self.assertEqual(res, [a, b]) + + a.namespace = 'z.z.z' + b.namespace = 'a.b.c' + res = nem_sort_mosaics([a, b]) + self.assertEqual(res, [b, a]) + + a.namespace = 'a' + b.namespace = 'a' + a.mosaic = 'mosaic' + b.mosaic = 'mosaic' + res = nem_sort_mosaics([a, b]) + self.assertEqual(res, [a, b]) + + a.mosaic = 'www' + b.mosaic = 'aaa' + res = nem_sort_mosaics([a, b]) + self.assertEqual(res, [b, a]) + + c = NEMMosaic() + c.namespace = 'a' + c.mosaic = 'zzz' + res = nem_sort_mosaics([a, b, c]) + self.assertEqual(res, [b, a, c]) + + c.mosaic = 'bbb' + res = nem_sort_mosaics([a, b, c]) + self.assertEqual(res, [b, c, a]) + + +if __name__ == '__main__': + unittest.main() From 6aef64d367d7a2c705d84b77501cf854d3794696 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 4 Apr 2018 21:06:37 +0200 Subject: [PATCH 35/53] nem: compute address from pubkey --- .../modtrezorcrypto/modtrezorcrypto-nem.h | 19 ++++++++++ mocks/generated/trezorcrypto.py | 6 +++ tests/test_apps.nem.address.py | 37 +++++++++++++++++++ tests/test_apps.nem.hdnode.py | 2 +- 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/test_apps.nem.address.py diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h index 67e35a3b7..d3dfb7dd2 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h @@ -34,9 +34,28 @@ STATIC mp_obj_t mod_trezorcrypto_nem_validate_address(mp_obj_t address, mp_obj_t } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_nem_validate_address_obj, mod_trezorcrypto_nem_validate_address); +/// def compute_address(public_key: bytes, network: int) -> str: +/// ''' +/// Compute a NEM address from a public key +/// ''' +STATIC mp_obj_t mod_trezorcrypto_nem_compute_address(mp_obj_t public_key, mp_obj_t network) { + + mp_buffer_info_t p; + mp_get_buffer_raise(public_key, &p, MP_BUFFER_READ); + uint32_t n = mp_obj_get_int_truncated(network); + + char address[NEM_ADDRESS_SIZE + 1]; + if (!nem_get_address(p.buf, n, address)) { + mp_raise_ValueError("Failed to compute a NEM address from a provided public key"); + } + return mp_obj_new_str(address, strlen(address), false); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_nem_compute_address_obj, mod_trezorcrypto_nem_compute_address); + // objects definition STATIC const mp_rom_map_elem_t mod_trezorcrypto_nem_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_validate_address), MP_ROM_PTR(&mod_trezorcrypto_nem_validate_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_compute_address), MP_ROM_PTR(&mod_trezorcrypto_nem_compute_address_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_nem_globals, mod_trezorcrypto_nem_globals_table); diff --git a/mocks/generated/trezorcrypto.py b/mocks/generated/trezorcrypto.py index 6023d51cc..62d89b923 100644 --- a/mocks/generated/trezorcrypto.py +++ b/mocks/generated/trezorcrypto.py @@ -320,6 +320,12 @@ def validate_address(address: str, network: int) -> bool: Validate a NEM address ''' +# extmod/modtrezorcrypto/modtrezorcrypto-nem.h +def compute_address(public_key: bytes, network: int) -> str: + ''' + Compute a NEM address from a public key + ''' + # extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h def generate_secret() -> bytes: ''' diff --git a/tests/test_apps.nem.address.py b/tests/test_apps.nem.address.py new file mode 100644 index 000000000..dab6faa8e --- /dev/null +++ b/tests/test_apps.nem.address.py @@ -0,0 +1,37 @@ +from common import * +from ubinascii import unhexlify +from trezor.crypto import nem +from apps.nem.helpers import NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET + + +class TestNemAddress(unittest.TestCase): + + def test_addresses(self): + pubkey = unhexlify('c5f54ba980fcbb657dbaaa42700539b207873e134d2375efeab5f1ab52f87844') + address = nem.compute_address(pubkey, NEM_NETWORK_MAINNET) + self.assertEqual(address, 'NDD2CT6LQLIYQ56KIXI3ENTM6EK3D44P5JFXJ4R4') + + pubkey = unhexlify('114171230ad6f8522a000cdc73fbc5c733b30bb71f2b146ccbdf34499f79a810') + address = nem.compute_address(pubkey, NEM_NETWORK_MAINNET) + self.assertEqual(address, 'NCUKWDY3J3THKQHAKOK5ALF6ANJQABZHCH7VN6DP') + + def test_validate_address(self): + validity = nem.validate_address('NDD2CT6LQLIYQ56KIXI3ENTM6EK3D44P5JFXJ4R4', NEM_NETWORK_MAINNET) + self.assertTrue(validity) + + validity = nem.validate_address('NCUKWDY3J3THKQHAKOK5ALF6ANJQABZHCH7VN6DP', NEM_NETWORK_MAINNET) + self.assertTrue(validity) + + validity = nem.validate_address('TAU5HO3DRQZNELFEMZZTUKQEZGQ7IUAHKPO7OOLK', NEM_NETWORK_TESTNET) + self.assertTrue(validity) + + validity = nem.validate_address('nope', NEM_NETWORK_TESTNET) + self.assertFalse(validity) + + # not valid on testnet + validity = nem.validate_address('NCUKWDY3J3THKQHAKOK5ALF6ANJQABZHCH7VN6DP', NEM_NETWORK_TESTNET) + self.assertFalse(validity) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_apps.nem.hdnode.py b/tests/test_apps.nem.hdnode.py index ee35a93df..ea109c655 100644 --- a/tests/test_apps.nem.hdnode.py +++ b/tests/test_apps.nem.hdnode.py @@ -4,7 +4,7 @@ from trezor.crypto import bip32 from apps.nem.helpers import NEM_NETWORK_MAINNET, NEM_CURVE -class TestNemAddress(unittest.TestCase): +class TestNemHDNode(unittest.TestCase): def test_addresses(self): # test vectors from https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/1.test-keys.dat From 561ca35a08eb4fbba589ba01ca7416c4a4eed0f2 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 4 Apr 2018 16:23:44 +0200 Subject: [PATCH 36/53] nem: correct layout and confirms --- src/apps/nem/helpers.py | 3 + src/apps/nem/layout.py | 120 ++++++++++++++++++++++++++----------- src/apps/nem/signing.py | 82 ++++++++++++++++++------- src/apps/nem/validators.py | 18 +++++- 4 files changed, 166 insertions(+), 57 deletions(-) diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index a68961aea..22a23fd86 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -22,3 +22,6 @@ NEM_SALT_SIZE = const(32) AES_BLOCK_SIZE = const(16) NEM_HASH_ALG = 'keccak' NEM_PUBLIC_KEY_SIZE = const(32) # ed25519 public key + +NEM_MAX_PLAIN_PAYLOAD_SIZE = const(1024) +NEM_MAX_ENCRYPTED_PAYLOAD_SIZE = const(960) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index e9f921696..a972399ee 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -2,36 +2,71 @@ from apps.common.confirm import * from trezor import ui from trezor.messages import ButtonRequestType from trezor.messages.NEMMosaicDefinition import NEMMosaicDefinition +from trezor.messages import NEMMosaicLevy from trezor.ui.text import Text from trezor.ui.scroll import Scrollpage, animate_swipe, paginate from trezor.utils import chunks, format_amount, split_words from .helpers import * -async def require_confirm_tx(ctx, recipient, value): - content = Text('Confirm sending', ui.ICON_SEND, - ui.BOLD, format_amount(value, NEM_MAX_DIVISIBILITY) + ' NEM', +async def require_confirm_action(ctx, action: str): + content = Text('Confirm action', ui.ICON_SEND, + ui.NORMAL, *split_words(action, 18), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def require_confirm_fee(ctx, action: str, fee: int): + content = Text('Confirm fee', ui.ICON_SEND, + ui.NORMAL, action, + ui.BOLD, format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM', + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def require_confirm_transfer(ctx, recipient, value): + content = Text('Confirm transfer', ui.ICON_SEND, + ui.BOLD, 'Send ' + format_amount(value, NEM_MAX_DIVISIBILITY) + ' XEM', ui.NORMAL, 'to', ui.MONO, *split_address(recipient), icon_color=ui.GREEN) - await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) -async def require_confirm_action(ctx, action: str): - content = Text('Confirm sending', ui.ICON_SEND, - ui.NORMAL, *split_words(action, 18), +async def require_confirm_address(ctx, action: str, address: str): + content = Text('Confirm address', ui.ICON_SEND, + ui.NORMAL, action, + ui.MONO, *split_address(address), icon_color=ui.GREEN) await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) -@ui.layout -async def _show_page(page: int, page_count: int, content): - content = Scrollpage(content[page], page, page_count) - if page + 1 == page_count: - await ConfirmDialog(content) +async def require_confirm_final(ctx, fee: int): + content = Text('Final confirm', ui.ICON_SEND, + ui.NORMAL, 'Sign this transaction', + ui.BOLD, 'and pay ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM', + ui.NORMAL, 'for transaction fee?', + icon_color=ui.GREEN) + await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 + + +async def require_confirm_payload(ctx, payload: bytes, encrypt=False): + payload = str(payload, 'utf-8') + + if len(payload) > 48: + payload = payload[:48] + '..' + print(len(payload)) + if encrypt: + content = Text('Confirm payload', ui.ICON_SEND, + ui.BOLD, 'Encrypted:', + ui.NORMAL, *split_words(payload, 22), + icon_color=ui.GREEN) else: - content.render() - await animate_swipe() + content = Text('Confirm payload', ui.ICON_SEND, + ui.BOLD, 'Unencrypted:', + ui.NORMAL, *split_words(payload, 22), + icon_color=ui.RED) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) async def require_confirm_properties(ctx, definition: NEMMosaicDefinition): @@ -41,6 +76,16 @@ async def require_confirm_properties(ctx, definition: NEMMosaicDefinition): await ctx.wait(paginator) +@ui.layout +async def _show_page(page: int, page_count: int, content): + content = Scrollpage(content[page], page, page_count) + if page + 1 == page_count: + await ConfirmDialog(content) + else: + content.render() + await animate_swipe() + + def _get_mosaic_properties(definition: NEMMosaicDefinition): properties = [] if definition.description: @@ -63,31 +108,34 @@ def _get_mosaic_properties(definition: NEMMosaicDefinition): if definition.supply: t = Text('Confirm properties', ui.ICON_SEND, ui.BOLD, 'Initial supply:', - ui.NORMAL, format_amount(definition.supply, definition.divisibility), + ui.NORMAL, str(definition.supply), ui.NORMAL, imm) + else: + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Initial supply:', + ui.NORMAL, imm) + properties.append(t) + if definition.levy: + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Levy recipient:', + ui.MONO, *split_address(definition.levy_address)) + properties.append(t) + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Levy namespace:', + ui.NORMAL, definition.levy_namespace, + ui.BOLD, 'Levy mosaic:', + ui.NORMAL, definition.levy_mosaic) + properties.append(t) + if definition.levy == NEMMosaicLevy.MosaicLevy_Absolute: + levy_type = 'absolute' + else: + levy_type = 'percentile' + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Levy type:', + ui.NORMAL, levy_type) properties.append(t) - return properties - - -async def require_confirm_final(ctx, action: str, fee: int): - content = Text('Confirm sending', ui.ICON_SEND, - ui.NORMAL, 'Create ', action, - ui.BOLD, 'paying ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM', - ui.NORMAL, 'for transaction fee?', - icon_color=ui.GREEN) - await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 - -async def require_confirm_payload(ctx, payload: bytes, encrypt=False): - payload = str(payload, 'utf-8') - if encrypt: - content = Text('Send encrypted?', ui.ICON_SEND, - ui.NORMAL, *split_words(payload, 18)) - else: - content = Text('Send unencrypted?', ui.ICON_SEND, - ui.NORMAL, *split_words(payload, 18), - icon_color=ui.RED) - await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + return properties def split_address(data): diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index f68e71e2c..af2304b87 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -4,10 +4,14 @@ from apps.nem.mosaic import * from apps.nem.validators import validate from apps.nem import helpers from apps.common import seed +from trezor.messages import NEMSupplyChangeType +from trezor.messages import NEMModificationType +from trezor.messages import NEMImportanceTransferMode from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMSignedTx import NEMSignedTx from trezor.crypto.curve import ed25519 from trezor.crypto import random +from trezor.crypto import nem async def nem_sign_tx(ctx, msg: NEMSignTx): @@ -39,7 +43,13 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): async def _importance_transfer(ctx, node, msg: NEMSignTx): - # todo confirms! + if msg.importance_transfer.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: + m = 'Activate' + else: + m = 'Deactivate' + await require_confirm_action(ctx, m + ' remote harvesting?') + await require_confirm_final(ctx, msg.transaction.fee) + w = nem_transaction_create_importance_transfer( msg.transaction.network, msg.transaction.timestamp, @@ -53,28 +63,52 @@ async def _importance_transfer(ctx, node, msg: NEMSignTx): async def _aggregate_modification(ctx, node, msg: NEMSignTx): - # todo confirms! - w = nem_transaction_create_aggregate_modification( - msg.transaction.network, - msg.transaction.timestamp, - _get_public_key(node), - msg.transaction.fee, - msg.transaction.deadline, - len(msg.aggregate_modification.modifications), - msg.aggregate_modification.relative_change - ) + if not msg.multisig: + await require_confirm_action(ctx, 'Convert account to multisig account?') + w = nem_transaction_create_aggregate_modification( + msg.transaction.network, + msg.transaction.timestamp, + _get_public_key(node), + msg.transaction.fee, + msg.transaction.deadline, + len(msg.aggregate_modification.modifications), + msg.aggregate_modification.relative_change + ) for m in msg.aggregate_modification.modifications: + if m.type == NEMModificationType.CosignatoryModification_Add: + action = 'Add' + else: + action = 'Remove' + address = nem.compute_address(m.public_key, msg.transaction.network) + await require_confirm_address(ctx, action + ' cosignatory?', address) nem_transaction_write_cosignatory_modification(w, m.type, m.public_key) if msg.aggregate_modification.relative_change: + if not msg.multisig: + action = 'Set minimum cosignatories to ' + else: + action = 'Modify the number of cosignatories by ' + await require_confirm_action(ctx, action + str(msg.aggregate_modification.relative_change) + '?') + nem_transaction_write_minimum_cosignatories(w, msg.aggregate_modification.relative_change) + await require_confirm_final(ctx, msg.transaction.fee) return w async def _supply_change(ctx, node, msg: NEMSignTx): - # todo confirms! + await require_confirm_action(ctx, 'Modify supply for "' + msg.supply_change.mosaic + '" under namespace "' + + msg.supply_change.namespace + '"?') + if msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Decrease: + ask_msg = 'Decrease supply by ' + str(msg.supply_change.delta) + ' whole units?' + elif msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Increase: + ask_msg = 'Increase supply by ' + str(msg.supply_change.delta) + ' whole units?' + else: + raise ValueError('Invalid supply change type') + await require_confirm_action(ctx, ask_msg) + + await require_confirm_final(ctx, msg.transaction.fee) return nem_transaction_create_mosaic_supply_change( msg.transaction.network, msg.transaction.timestamp, @@ -91,10 +125,10 @@ async def _supply_change(ctx, node, msg: NEMSignTx): async def _mosaic_creation(ctx, node, msg: NEMSignTx) -> bytearray: await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' + msg.mosaic_creation.definition.namespace + '"?') - # todo confirm levy! await require_confirm_properties(ctx, msg.mosaic_creation.definition) - await require_confirm_final(ctx, 'mosaic', msg.transaction.fee) + await require_confirm_fee(ctx, 'Confirm creation fee', msg.mosaic_creation.fee) + await require_confirm_final(ctx, msg.transaction.fee) return nem_transaction_create_mosaic_creation( msg.transaction.network, msg.transaction.timestamp, @@ -118,8 +152,14 @@ async def _mosaic_creation(ctx, node, msg: NEMSignTx) -> bytearray: async def _provision_namespace(ctx, node, msg: NEMSignTx) -> bytearray: - await require_confirm_action(ctx, 'Create provision namespace "' + msg.provision_namespace.namespace + '"?') - await require_confirm_final(ctx, 'provision namespace', msg.transaction.fee) + if msg.provision_namespace.parent: + await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"' + + 'under namespace "' + msg.provision_namespace.parent + '"?') + else: + await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"?') + await require_confirm_fee(ctx, 'Confirm rental fee', msg.provision_namespace.fee) + + await require_confirm_final(ctx, msg.transaction.fee) return nem_transaction_create_provision_namespace( msg.transaction.network, msg.transaction.timestamp, @@ -148,14 +188,16 @@ async def _transfer(ctx, node, msg: NEMSignTx) -> bytes: ) for mosaic in msg.transfer.mosaics: + await require_confirm_action(ctx, 'Confirm transfer of ' + str(mosaic.quantity) + + ' raw units of ' + mosaic.namespace + '.' + mosaic.mosaic) nem_transaction_write_mosaic(tx, mosaic.namespace, mosaic.mosaic, mosaic.quantity) - if payload: # confirm unencrypted - await require_confirm_payload(ctx, msg.transfer.payload, encrypted) + await require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount) - await require_confirm_action(ctx, 'todo') # todo! - await require_confirm_tx(ctx, msg.transfer.recipient, msg.transfer.amount) + if payload: + await require_confirm_payload(ctx, msg.transfer.payload, encrypted) + await require_confirm_final(ctx, msg.transaction.fee) return tx diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index f8b407b62..216e15053 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -19,6 +19,11 @@ def validate(msg: NEMSignTx): _validate_single_tx(msg) _validate_common(msg.transaction) + if msg.multisig: + _validate_multisig(msg.multisig, msg.transaction.network) + if not msg.multisig and msg.cosigning: + raise ValueError('No multisig transaction to cosign') + if msg.transfer: _validate_transfer(msg.transfer, msg.transaction.network) if msg.provision_namespace: @@ -28,7 +33,7 @@ def validate(msg: NEMSignTx): if msg.supply_change: _validate_supply_change(msg.supply_change) if msg.aggregate_modification: - _validate_aggregate_modification(msg.aggregate_modification, msg.multisig is not None) + _validate_aggregate_modification(msg.aggregate_modification, msg.multisig is None) if msg.importance_transfer: _validate_importance_transfer(msg.importance_transfer) @@ -100,6 +105,11 @@ def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer): _validate_public_key(importance_transfer.public_key, 'Invalid remote account public key provided') +def _validate_multisig(multisig: NEMTransactionCommon, network: int): + if multisig.network != network: + raise ValueError('Inner transaction network is different') + + def _validate_aggregate_modification(aggregate_modification: NEMAggregateModification, creation: bool=False): if creation and len(aggregate_modification.modifications) == 0: @@ -208,6 +218,12 @@ def _validate_transfer(transfer: NEMTransfer, network: int): if transfer.public_key is not None: _validate_public_key(transfer.public_key, 'Invalid recipient public key') + if transfer.payload: + if len(transfer.payload) > NEM_MAX_PLAIN_PAYLOAD_SIZE: + raise ValueError('Payload too large') + if transfer.public_key and len(transfer.payload) > NEM_MAX_ENCRYPTED_PAYLOAD_SIZE: + raise ValueError('Payload too large') + if not nem.validate_address(transfer.recipient, network): raise ValueError('Invalid recipient address') From d54d33df3a52683a322f826525690cf812d1f59e Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 6 Apr 2018 10:40:46 +0200 Subject: [PATCH 37/53] nem: big refactoring of serialization protocol classes are now used; reflected in unit tests --- src/apps/nem/__init__.py | 8 +- src/apps/nem/get_address.py | 3 +- src/apps/nem/helpers.py | 2 + src/apps/nem/layout.py | 1 - src/apps/nem/mosaic.py | 113 ++++----- src/apps/nem/multisig.py | 81 +++++-- src/apps/nem/namespace.py | 29 +++ src/apps/nem/signing.py | 228 ++++-------------- src/apps/nem/transaction.py | 96 -------- src/apps/nem/transfer.py | 85 +++++++ src/apps/nem/validators.py | 14 +- src/apps/nem/writers.py | 24 +- ... test_apps.nem.mosaic.canonicalization.py} | 37 ++- tests/test_apps.nem.mosaic_creation.py | 150 ++++++++++++ tests/test_apps.nem.mosaic_supply_change.py | 87 +++++++ ...pps.nem.multisig.aggregate_modification.py | 94 ++++++++ tests/test_apps.nem.multisig.py | 143 ++++++----- tests/test_apps.nem.namespace.py | 69 ++++++ ....nem.transaction.aggregate_modification.py | 75 ------ ...st_apps.nem.transaction.mosaic_creation.py | 111 --------- ...ps.nem.transaction.mosaic_supply_change.py | 69 ------ tests/test_apps.nem.transaction.provision.py | 49 ---- tests/test_apps.nem.transaction.transfer.py | 93 ------- tests/test_apps.nem.transfer.py | 116 +++++++++ 24 files changed, 924 insertions(+), 853 deletions(-) create mode 100644 src/apps/nem/namespace.py delete mode 100644 src/apps/nem/transaction.py create mode 100644 src/apps/nem/transfer.py rename tests/{test_apps.nem.transaction.mosaic.canonicalization.py => test_apps.nem.mosaic.canonicalization.py} (79%) create mode 100644 tests/test_apps.nem.mosaic_creation.py create mode 100644 tests/test_apps.nem.mosaic_supply_change.py create mode 100644 tests/test_apps.nem.multisig.aggregate_modification.py create mode 100644 tests/test_apps.nem.namespace.py delete mode 100644 tests/test_apps.nem.transaction.aggregate_modification.py delete mode 100644 tests/test_apps.nem.transaction.mosaic_creation.py delete mode 100644 tests/test_apps.nem.transaction.mosaic_supply_change.py delete mode 100644 tests/test_apps.nem.transaction.provision.py delete mode 100644 tests/test_apps.nem.transaction.transfer.py create mode 100644 tests/test_apps.nem.transfer.py diff --git a/src/apps/nem/__init__.py b/src/apps/nem/__init__.py index e0c9f3b27..483d795f8 100644 --- a/src/apps/nem/__init__.py +++ b/src/apps/nem/__init__.py @@ -3,13 +3,13 @@ from trezor.messages.wire_types import NEMGetAddress, NEMSignTx def dispatch_NemGetAddress(*args, **kwargs): - from .get_address import nem_get_address - return nem_get_address(*args, **kwargs) + from .get_address import get_address + return get_address(*args, **kwargs) def dispatch_NemSignTx(*args, **kwargs): - from .signing import nem_sign_tx - return nem_sign_tx(*args, **kwargs) + from .signing import sign_tx + return sign_tx(*args, **kwargs) def boot(): diff --git a/src/apps/nem/get_address.py b/src/apps/nem/get_address.py index 14c155485..6dce87eaf 100644 --- a/src/apps/nem/get_address.py +++ b/src/apps/nem/get_address.py @@ -8,7 +8,7 @@ from trezor.ui.text import Text from trezor.utils import chunks -async def nem_get_address(ctx, msg): +async def get_address(ctx, msg): network = validate_network(msg.network) node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE) address = node.nem_address(network) @@ -23,7 +23,6 @@ async def nem_get_address(ctx, msg): async def _show_address(ctx, address: str, network: int): lines = _split_address(address) - print(network) content = Text('Export NEM address', ui.ICON_RECEIVE, ui.MONO, _get_network_str(network) + ' network', ui.MONO, *lines, diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index 22a23fd86..9e619d00e 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -1,5 +1,7 @@ from micropython import const +from trezor.messages.NEMSignTx import NEMSignTx + NEM_NETWORK_MAINNET = const(0x68) NEM_NETWORK_TESTNET = const(0x98) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index a972399ee..ea82683a8 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -55,7 +55,6 @@ async def require_confirm_payload(ctx, payload: bytes, encrypt=False): if len(payload) > 48: payload = payload[:48] + '..' - print(len(payload)) if encrypt: content = Text('Confirm payload', ui.ICON_SEND, ui.BOLD, 'Encrypted:', diff --git a/src/apps/nem/mosaic.py b/src/apps/nem/mosaic.py index c9d5b4a1d..77b370c90 100644 --- a/src/apps/nem/mosaic.py +++ b/src/apps/nem/mosaic.py @@ -1,79 +1,84 @@ - -from .helpers import * from .writers import * from trezor.messages.NEMMosaic import NEMMosaic +from trezor.messages import NEMSupplyChangeType +from apps.nem.layout import * + + +async def ask_mosaic_creation(ctx, msg: NEMSignTx): + await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' + + msg.mosaic_creation.definition.namespace + '"?') + await require_confirm_properties(ctx, msg.mosaic_creation.definition) + await require_confirm_fee(ctx, 'Confirm creation fee', msg.mosaic_creation.fee) + + await require_confirm_final(ctx, msg.transaction.fee) + + +async def ask_mosaic_supply_change(ctx, msg: NEMSignTx): + await require_confirm_action(ctx, 'Modify supply for "' + msg.supply_change.mosaic + '" under namespace "' + + msg.supply_change.namespace + '"?') + if msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Decrease: + ask_msg = 'Decrease supply by ' + str(msg.supply_change.delta) + ' whole units?' + elif msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Increase: + ask_msg = 'Increase supply by ' + str(msg.supply_change.delta) + ' whole units?' + else: + raise ValueError('Invalid supply change type') + await require_confirm_action(ctx, ask_msg) + await require_confirm_final(ctx, msg.transaction.fee) -def nem_transaction_create_mosaic_creation(network: int, timestamp: int, signer_public_key: bytes, fee:int, - deadline: int, namespace: str, mosaic: str, description: str, - divisibility: int, supply: int, mutable_supply: bool, transferable: bool, - levy_type: int, levy_fee: int, levy_address: str, levy_namespace: str, - levy_mosaic: str, creation_sink: str, creation_fee: int): - w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_CREATION, - nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) +def serialize_mosaic_creation(msg: NEMSignTx, public_key: bytes): + w = write_common(msg.transaction, bytearray(public_key), NEM_TRANSACTION_TYPE_MOSAIC_CREATION) mosaics_w = bytearray() - write_bytes_with_length(mosaics_w, bytearray(signer_public_key)) - identifier_length = 4 + len(namespace) + 4 + len(mosaic) + write_bytes_with_length(mosaics_w, bytearray(public_key)) + identifier_length = 4 + len(msg.mosaic_creation.definition.namespace) + 4 + len(msg.mosaic_creation.definition.mosaic) write_uint32(mosaics_w, identifier_length) - write_bytes_with_length(mosaics_w, bytearray(namespace)) - write_bytes_with_length(mosaics_w, bytearray(mosaic)) - write_bytes_with_length(mosaics_w, bytearray(description)) + write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.namespace)) + write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.mosaic)) + write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.description)) write_uint32(mosaics_w, 4) # number of properties - nem_write_mosaic(mosaics_w, "divisibility", divisibility) - nem_write_mosaic(mosaics_w, "initialSupply", supply) - nem_write_mosaic(mosaics_w, "supplyMutable", mutable_supply) - nem_write_mosaic(mosaics_w, "transferable", transferable) + _write_property(mosaics_w, "divisibility", msg.mosaic_creation.definition.divisibility) + _write_property(mosaics_w, "initialSupply", msg.mosaic_creation.definition.supply) + _write_property(mosaics_w, "supplyMutable", msg.mosaic_creation.definition.mutable_supply) + _write_property(mosaics_w, "transferable", msg.mosaic_creation.definition.transferable) - if levy_type: - levy_identifier_length = 4 + len(levy_namespace) + 4 + len(levy_mosaic) - write_uint32(mosaics_w, 4 + 4 + len(levy_address) + 4 + levy_identifier_length + 8) - write_uint32(mosaics_w, levy_type) - write_bytes_with_length(mosaics_w, bytearray(levy_address)) + if msg.mosaic_creation.definition.levy: + levy_identifier_length = 4 + len(msg.mosaic_creation.definition.levy_namespace) + 4 + len(msg.mosaic_creation.definition.levy_mosaic) + write_uint32(mosaics_w, 4 + 4 + len(msg.mosaic_creation.definition.levy_address) + 4 + levy_identifier_length + 8) + write_uint32(mosaics_w, msg.mosaic_creation.definition.levy) + write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.levy_address)) write_uint32(mosaics_w, levy_identifier_length) - write_bytes_with_length(mosaics_w, bytearray(levy_namespace)) - write_bytes_with_length(mosaics_w, bytearray(levy_mosaic)) - write_uint64(mosaics_w, levy_fee) + write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.levy_namespace)) + write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.levy_mosaic)) + write_uint64(mosaics_w, msg.mosaic_creation.definition.fee) else: write_uint32(mosaics_w, 0) # write mosaic bytes with length write_bytes_with_length(w, mosaics_w) - write_bytes_with_length(w, bytearray(creation_sink)) - write_uint64(w, creation_fee) + write_bytes_with_length(w, bytearray(msg.mosaic_creation.sink)) + write_uint64(w, msg.mosaic_creation.fee) return w -def nem_transaction_create_mosaic_supply_change(network: int, timestamp: int, signer_public_key: bytes, fee: int, - deadline: int, namespace: str, mosaic: str, type: int, delta: int): - - w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, - nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) +def serialize_mosaic_supply_change(msg: NEMSignTx, public_key: bytes): + w = write_common(msg.transaction, bytearray(public_key), NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE) - identifier_length = 4 + len(namespace) + 4 + len(mosaic) + identifier_length = 4 + len(msg.supply_change.namespace) + 4 + len(msg.supply_change.mosaic) write_uint32(w, identifier_length) - write_bytes_with_length(w, bytearray(namespace)) - write_bytes_with_length(w, bytearray(mosaic)) - - write_uint32(w, type) - write_uint64(w, delta) + write_bytes_with_length(w, bytearray(msg.supply_change.namespace)) + write_bytes_with_length(w, bytearray(msg.supply_change.mosaic)) + write_uint32(w, msg.supply_change.type) + write_uint64(w, msg.supply_change.delta) return w -def nem_write_mosaic(w: bytearray, name: str, value): +def _write_property(w: bytearray, name: str, value): if value is None: if name in ['divisibility', 'initialSupply']: value = 0 @@ -93,7 +98,7 @@ def nem_write_mosaic(w: bytearray, name: str, value): write_bytes_with_length(w, bytearray(value)) -def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): +def serialize_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): identifier_length = 4 + len(namespace) + 4 + len(mosaic) # indentifier length (u32) + quantity (u64) + identifier size write_uint32(w, 4 + 8 + identifier_length) @@ -103,11 +108,11 @@ def nem_transaction_write_mosaic(w: bytearray, namespace: str, mosaic: str, quan write_uint64(w, quantity) -def nem_canonicalize_mosaics(mosaics: list): +def canonicalize_mosaics(mosaics: list): if len(mosaics) <= 1: return mosaics - mosaics = nem_merge_mosaics(mosaics) - return nem_sort_mosaics(mosaics) + mosaics = merge_mosaics(mosaics) + return sort_mosaics(mosaics) def are_mosaics_equal(a: NEMMosaic, b: NEMMosaic) -> bool: @@ -116,7 +121,7 @@ def are_mosaics_equal(a: NEMMosaic, b: NEMMosaic) -> bool: return False -def nem_merge_mosaics(mosaics: list) -> list: +def merge_mosaics(mosaics: list) -> list: if not len(mosaics): return list() ret = list() @@ -131,5 +136,5 @@ def nem_merge_mosaics(mosaics: list) -> list: return ret -def nem_sort_mosaics(mosaics: list) -> list: +def sort_mosaics(mosaics: list) -> list: return sorted(mosaics, key=lambda m: (m.namespace, m.mosaic)) diff --git a/src/apps/nem/multisig.py b/src/apps/nem/multisig.py index 6942d859c..1058cfd8e 100644 --- a/src/apps/nem/multisig.py +++ b/src/apps/nem/multisig.py @@ -1,38 +1,73 @@ - -from .helpers import * from .writers import * +from apps.nem.layout import * from trezor.crypto import hashlib +from trezor.messages import NEMModificationType +from trezor.crypto import nem -def nem_transaction_create_multisig(network: int, timestamp: int, signer_public_key: bytes, - fee: int, deadline: int, inner: bytes): +async def ask_multisig(ctx, msg: NEMSignTx): + # todo + await require_confirm_action(ctx, 'Multisig?') - w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MULTISIG, - nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) - write_bytes_with_length(w, bytearray(inner)) +async def ask_aggregate_modification(ctx, msg: NEMSignTx): + if not msg.multisig: + await require_confirm_action(ctx, 'Convert account to multisig account?') - return w + for m in msg.aggregate_modification.modifications: + if m.type == NEMModificationType.CosignatoryModification_Add: + action = 'Add' + else: + action = 'Remove' + address = nem.compute_address(m.public_key, msg.transaction.network) + await require_confirm_address(ctx, action + ' cosignatory?', address) + + if msg.aggregate_modification.relative_change: + if not msg.multisig: + action = 'Set minimum cosignatories to ' + else: + action = 'Modify the number of cosignatories by ' + await require_confirm_action(ctx, action + str(msg.aggregate_modification.relative_change) + '?') + await require_confirm_final(ctx, msg.transaction.fee) -def nem_transaction_create_multisig_signature(network: int, timestamp: int, signer_public_key: bytes, - fee: int, deadline: int, inner: bytes, address: str): - w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE, - nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) +def serialize_multisig(msg: NEMTransactionCommon, public_key: bytes, inner: bytes): + w = write_common(msg, bytearray(public_key), NEM_TRANSACTION_TYPE_MULTISIG) + write_bytes_with_length(w, bytearray(inner)) + return w - hash = hashlib.sha3_256(inner).digest(True) - write_uint32(w, 4 + len(hash)) - write_bytes_with_length(w, hash) +def serialize_multisig_signature(msg: NEMTransactionCommon, public_key: bytes, inner: bytes, address: str): + w = write_common(msg, bytearray(public_key), NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE) + digest = hashlib.sha3_256(inner).digest(True) + + write_uint32(w, 4 + len(digest)) + write_bytes_with_length(w, digest) write_bytes_with_length(w, address) + return w + +def serialize_aggregate_modification(msg: NEMSignTx, public_key: bytes): + version = msg.transaction.network << 24 | 1 + if msg.aggregate_modification.relative_change: + version = msg.transaction.network << 24 | 2 + + w = write_common(msg.transaction, + bytearray(public_key), + NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, + version) + write_uint32(w, len(msg.aggregate_modification.modifications)) return w + + +def serialize_cosignatory_modification(w: bytearray, type: int, cosignatory_pubkey: bytes): + write_uint32(w, 4 + 4 + len(cosignatory_pubkey)) + write_uint32(w, type) + write_bytes_with_length(w, bytearray(cosignatory_pubkey)) + return w + + +def serialize_minimum_cosignatories(w: bytearray, relative_change: int): + write_uint32(w, 4) + write_uint32(w, relative_change) diff --git a/src/apps/nem/namespace.py b/src/apps/nem/namespace.py new file mode 100644 index 000000000..cc720ad90 --- /dev/null +++ b/src/apps/nem/namespace.py @@ -0,0 +1,29 @@ +from .writers import * +from apps.nem.layout import * + + +async def ask_provision_namespace(ctx, msg: NEMSignTx): + if msg.provision_namespace.parent: + await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"' + + 'under namespace "' + msg.provision_namespace.parent + '"?') + else: + await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"?') + await require_confirm_fee(ctx, 'Confirm rental fee', msg.provision_namespace.fee) + + await require_confirm_final(ctx, msg.transaction.fee) + + +def serialize_provision_namespace(msg: NEMSignTx, public_key: bytes) -> bytearray: + tx = write_common(msg.transaction, + bytearray(public_key), + NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE) + + write_bytes_with_length(tx, bytearray(msg.provision_namespace.sink)) + write_uint64(tx, msg.provision_namespace.fee) + write_bytes_with_length(tx, bytearray(msg.provision_namespace.namespace)) + if msg.provision_namespace.parent: + write_bytes_with_length(tx, bytearray(msg.provision_namespace.parent)) + else: + write_uint32(tx, 0xffffffff) + + return tx diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index af2304b87..f56f52857 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -1,36 +1,37 @@ -from apps.nem.layout import * -from apps.nem.transaction import * +from apps.nem.transfer import * +from apps.nem.multisig import * +from apps.nem.namespace import * from apps.nem.mosaic import * from apps.nem.validators import validate from apps.nem import helpers from apps.common import seed -from trezor.messages import NEMSupplyChangeType -from trezor.messages import NEMModificationType -from trezor.messages import NEMImportanceTransferMode from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMSignedTx import NEMSignedTx from trezor.crypto.curve import ed25519 -from trezor.crypto import random -from trezor.crypto import nem -async def nem_sign_tx(ctx, msg: NEMSignTx): +async def sign_tx(ctx, msg: NEMSignTx): validate(msg) node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE) + if msg.multisig: + public_key = msg.multisig.signer + else: + public_key = _get_public_key(node) + if msg.transfer: - msg.transfer.mosaics = nem_canonicalize_mosaics(msg.transfer.mosaics) - tx = await _transfer(ctx, node, msg) + msg.transfer.mosaics = canonicalize_mosaics(msg.transfer.mosaics) + tx = await _transfer(ctx, public_key, msg, node) elif msg.provision_namespace: - tx = await _provision_namespace(ctx, node, msg) + tx = await _provision_namespace(ctx, public_key, msg) elif msg.mosaic_creation: - tx = await _mosaic_creation(ctx, node, msg) + tx = await _mosaic_creation(ctx, public_key, msg) elif msg.supply_change: - tx = await _supply_change(ctx, node, msg) + tx = await _supply_change(ctx, public_key, msg) elif msg.aggregate_modification: - tx = await _aggregate_modification(ctx, node, msg) + tx = await _aggregate_modification(ctx, public_key, msg) elif msg.importance_transfer: - tx = await _importance_transfer(ctx, node, msg) + tx = await _importance_transfer(ctx, public_key, msg) else: raise ValueError('No transaction provided') @@ -42,184 +43,51 @@ async def nem_sign_tx(ctx, msg: NEMSignTx): return resp -async def _importance_transfer(ctx, node, msg: NEMSignTx): - if msg.importance_transfer.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: - m = 'Activate' - else: - m = 'Deactivate' - await require_confirm_action(ctx, m + ' remote harvesting?') - await require_confirm_final(ctx, msg.transaction.fee) - - w = nem_transaction_create_importance_transfer( - msg.transaction.network, - msg.transaction.timestamp, - _get_public_key(node), - msg.transaction.fee, - msg.transaction.deadline, - msg.importance_transfer.mode, - msg.importance_transfer.public_key - ) - return w - - -async def _aggregate_modification(ctx, node, msg: NEMSignTx): - if not msg.multisig: - await require_confirm_action(ctx, 'Convert account to multisig account?') - w = nem_transaction_create_aggregate_modification( - msg.transaction.network, - msg.transaction.timestamp, - _get_public_key(node), - msg.transaction.fee, - msg.transaction.deadline, - len(msg.aggregate_modification.modifications), - msg.aggregate_modification.relative_change - ) - - for m in msg.aggregate_modification.modifications: - if m.type == NEMModificationType.CosignatoryModification_Add: - action = 'Add' - else: - action = 'Remove' - address = nem.compute_address(m.public_key, msg.transaction.network) - await require_confirm_address(ctx, action + ' cosignatory?', address) - nem_transaction_write_cosignatory_modification(w, m.type, m.public_key) +def _get_public_key(node) -> bytes: + # 0x01 prefix is not part of the actual public key, hence removed + return node.public_key()[1:] - if msg.aggregate_modification.relative_change: - if not msg.multisig: - action = 'Set minimum cosignatories to ' - else: - action = 'Modify the number of cosignatories by ' - await require_confirm_action(ctx, action + str(msg.aggregate_modification.relative_change) + '?') - nem_transaction_write_minimum_cosignatories(w, msg.aggregate_modification.relative_change) +async def _transfer(ctx, public_key: bytes, msg: NEMSignTx, node) -> bytes: + payload, encrypted = get_transfer_payload(msg, node) + await ask_transfer(ctx, msg, payload, encrypted) - await require_confirm_final(ctx, msg.transaction.fee) + w = serialize_transfer(msg, public_key, payload, encrypted) + for mosaic in msg.transfer.mosaics: + serialize_mosaic(w, mosaic.namespace, mosaic.mosaic, mosaic.quantity) return w -async def _supply_change(ctx, node, msg: NEMSignTx): - await require_confirm_action(ctx, 'Modify supply for "' + msg.supply_change.mosaic + '" under namespace "' - + msg.supply_change.namespace + '"?') - if msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Decrease: - ask_msg = 'Decrease supply by ' + str(msg.supply_change.delta) + ' whole units?' - elif msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Increase: - ask_msg = 'Increase supply by ' + str(msg.supply_change.delta) + ' whole units?' - else: - raise ValueError('Invalid supply change type') - await require_confirm_action(ctx, ask_msg) - - await require_confirm_final(ctx, msg.transaction.fee) - return nem_transaction_create_mosaic_supply_change( - msg.transaction.network, - msg.transaction.timestamp, - _get_public_key(node), - msg.transaction.fee, - msg.transaction.deadline, - msg.supply_change.namespace, - msg.supply_change.mosaic, - msg.supply_change.type, - msg.supply_change.delta - ) - - -async def _mosaic_creation(ctx, node, msg: NEMSignTx) -> bytearray: - await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' - + msg.mosaic_creation.definition.namespace + '"?') - await require_confirm_properties(ctx, msg.mosaic_creation.definition) - await require_confirm_fee(ctx, 'Confirm creation fee', msg.mosaic_creation.fee) - - await require_confirm_final(ctx, msg.transaction.fee) - return nem_transaction_create_mosaic_creation( - msg.transaction.network, - msg.transaction.timestamp, - _get_public_key(node), - msg.transaction.fee, - msg.transaction.deadline, - msg.mosaic_creation.definition.namespace, - msg.mosaic_creation.definition.mosaic, - msg.mosaic_creation.definition.description, - msg.mosaic_creation.definition.divisibility, - msg.mosaic_creation.definition.supply, - msg.mosaic_creation.definition.mutable_supply, - msg.mosaic_creation.definition.transferable, - msg.mosaic_creation.definition.levy, - msg.mosaic_creation.definition.fee, - msg.mosaic_creation.definition.levy_address, - msg.mosaic_creation.definition.levy_namespace, - msg.mosaic_creation.definition.levy_mosaic, - msg.mosaic_creation.sink, - msg.mosaic_creation.fee) - - -async def _provision_namespace(ctx, node, msg: NEMSignTx) -> bytearray: - if msg.provision_namespace.parent: - await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"' + - 'under namespace "' + msg.provision_namespace.parent + '"?') - else: - await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"?') - await require_confirm_fee(ctx, 'Confirm rental fee', msg.provision_namespace.fee) - - await require_confirm_final(ctx, msg.transaction.fee) - return nem_transaction_create_provision_namespace( - msg.transaction.network, - msg.transaction.timestamp, - _get_public_key(node), - msg.transaction.fee, - msg.transaction.deadline, - msg.provision_namespace.namespace, - msg.provision_namespace.parent, - msg.provision_namespace.sink, - msg.provision_namespace.fee) - - -async def _transfer(ctx, node, msg: NEMSignTx) -> bytes: - payload, encrypted = _get_payload(msg, node) - tx = nem_transaction_create_transfer( - msg.transaction.network, - msg.transaction.timestamp, - _get_public_key(node), - msg.transaction.fee, - msg.transaction.deadline, - msg.transfer.recipient, - msg.transfer.amount, - payload, - encrypted, - len(msg.transfer.mosaics) - ) +async def _provision_namespace(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: + await ask_provision_namespace(ctx, msg) + return serialize_provision_namespace(msg, public_key) - for mosaic in msg.transfer.mosaics: - await require_confirm_action(ctx, 'Confirm transfer of ' + str(mosaic.quantity) + - ' raw units of ' + mosaic.namespace + '.' + mosaic.mosaic) - nem_transaction_write_mosaic(tx, mosaic.namespace, mosaic.mosaic, mosaic.quantity) - await require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount) +async def _mosaic_creation(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: + await ask_mosaic_creation(ctx, msg) + return serialize_mosaic_creation(msg, public_key) - if payload: - await require_confirm_payload(ctx, msg.transfer.payload, encrypted) - await require_confirm_final(ctx, msg.transaction.fee) - return tx +async def _supply_change(ctx, public_key: bytes, msg: NEMSignTx): + await ask_mosaic_supply_change(ctx, msg) + return serialize_mosaic_supply_change(msg, public_key) -def _get_payload(msg: NEMSignTx, node) -> [bytes, bool]: - payload = msg.transfer.payload - encrypted = False - if msg.transfer.public_key is not None: - if payload is None: - raise ValueError("Public key provided but no payload to encrypt") - payload = _nem_encrypt(node, msg.transfer.public_key, msg.transfer.payload) - encrypted = True - - return payload, encrypted +async def _aggregate_modification(ctx, public_key: bytes, msg: NEMSignTx): + await ask_aggregate_modification(ctx, msg) + if not msg.multisig: + w = serialize_aggregate_modification(msg, public_key) + else: + w = bytearray() # todo + for m in msg.aggregate_modification.modifications: + serialize_cosignatory_modification(w, m.type, m.public_key) -def _get_public_key(node) -> bytes: - # 0x01 prefix is not part of the actual public key, hence removed - return node.public_key()[1:] + if msg.aggregate_modification.relative_change: + serialize_minimum_cosignatories(w, msg.aggregate_modification.relative_change) + return w -def _nem_encrypt(node, public_key: bytes, payload: bytes) -> bytes: - salt = random.bytes(helpers.NEM_SALT_SIZE) - iv = random.bytes(helpers.AES_BLOCK_SIZE) - encrypted = node.nem_encrypt(public_key, iv, salt, payload) - return iv + salt + encrypted +async def _importance_transfer(ctx, public_key: bytes, msg: NEMSignTx): + await ask_importance_transfer(ctx, msg) + return serialize_importance_transfer(msg, public_key) diff --git a/src/apps/nem/transaction.py b/src/apps/nem/transaction.py deleted file mode 100644 index 97e6ef11f..000000000 --- a/src/apps/nem/transaction.py +++ /dev/null @@ -1,96 +0,0 @@ - -from .helpers import * -from .writers import * - - -def nem_transaction_create_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, deadline: int, - recipient: str, amount: int, payload: bytearray = None, encrypted: bool = False, - mosaics: int = 0) -> bytearray: - - tx = nem_transaction_write_common(NEM_TRANSACTION_TYPE_TRANSFER, - nem_get_version(network, mosaics), - timestamp, - signer_public_key, - fee, - deadline) - - write_bytes_with_length(tx, bytearray(recipient)) - write_uint64(tx, amount) - - if payload: - # payload + payload size (u32) + encryption flag (u32) - write_uint32(tx, len(payload) + 2 * 4) - if encrypted: - write_uint32(tx, 0x02) - else: - write_uint32(tx, 0x01) - write_bytes_with_length(tx, payload) - else: - write_uint32(tx, 0) - - if mosaics: - write_uint32(tx, mosaics) - - return tx - - -def nem_transaction_create_provision_namespace(network: int, timestamp: int, signer_public_key: bytes, fee: int, - deadline: int, namespace: str, parent: str, rental_sink: str, - rental_fee: int) -> bytearray: - - tx = nem_transaction_write_common(NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE, - nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) - - write_bytes_with_length(tx, bytearray(rental_sink)) - write_uint64(tx, rental_fee) - write_bytes_with_length(tx, bytearray(namespace)) - if parent: - write_bytes_with_length(tx, bytearray(parent)) - else: - write_uint32(tx, 0xffffffff) - - return tx - - -def nem_transaction_create_importance_transfer(network: int, timestamp: int, signer_public_key: bytes, fee: int, - deadline: int, mode: int, remote: bytes): - - w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER, - nem_get_version(network), - timestamp, - signer_public_key, - fee, - deadline) - - write_uint32(w, mode) - write_bytes_with_length(w, bytearray(remote)) - return w - - -def nem_transaction_create_aggregate_modification(network: int, timestamp: int, signer_public_key: bytes, fee: int, - deadline: int, modifications: int, relative_change: bool): - - w = nem_transaction_write_common(NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, - nem_get_version(network, relative_change), - timestamp, - signer_public_key, - fee, - deadline) - write_uint32(w, modifications) - return w - - -def nem_transaction_write_cosignatory_modification(w: bytearray, type: int, cosignatory: bytes): - write_uint32(w, 4 + 4 + len(cosignatory)) - write_uint32(w, type) - write_bytes_with_length(w, bytearray(cosignatory)) - return w - - -def nem_transaction_write_minimum_cosignatories(w: bytearray, relative_change: int): - write_uint32(w, 4) - write_uint32(w, relative_change) diff --git a/src/apps/nem/transfer.py b/src/apps/nem/transfer.py new file mode 100644 index 000000000..81b87a399 --- /dev/null +++ b/src/apps/nem/transfer.py @@ -0,0 +1,85 @@ +from .writers import * +from apps.nem.layout import * +from trezor.messages import NEMImportanceTransferMode +from trezor.crypto import random + + +async def ask_transfer(ctx, msg: NEMSignTx, payload, encrypted): + if payload: + await require_confirm_payload(ctx, msg.transfer.payload, encrypted) + + for mosaic in msg.transfer.mosaics: + await require_confirm_action(ctx, 'Confirm transfer of ' + str(mosaic.quantity) + + ' raw units of ' + mosaic.namespace + '.' + mosaic.mosaic) + + await require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount) + + await require_confirm_final(ctx, msg.transaction.fee) + + +async def ask_importance_transfer(ctx, msg: NEMSignTx): + if msg.importance_transfer.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: + m = 'Activate' + else: + m = 'Deactivate' + await require_confirm_action(ctx, m + ' remote harvesting?') + await require_confirm_final(ctx, msg.transaction.fee) + + +def serialize_transfer(msg: NEMSignTx, public_key: bytes, payload: bytes=None, encrypted: bool=False) -> bytearray: + tx = write_common(msg.transaction, + bytearray(public_key), + NEM_TRANSACTION_TYPE_TRANSFER, + _get_version(msg.transaction.network, msg.transfer.mosaics)) + + write_bytes_with_length(tx, bytearray(msg.transfer.recipient)) + write_uint64(tx, msg.transfer.amount) + + if payload: + # payload + payload size (u32) + encryption flag (u32) + write_uint32(tx, len(payload) + 2 * 4) + if encrypted: + write_uint32(tx, 0x02) + else: + write_uint32(tx, 0x01) + write_bytes_with_length(tx, bytearray(payload)) + else: + write_uint32(tx, 0) + + if msg.transfer.mosaics: + write_uint32(tx, len(msg.transfer.mosaics)) + + return tx + + +def serialize_importance_transfer(msg: NEMSignTx, public_key: bytes) -> bytearray: + w = write_common(msg.transaction, bytearray(public_key), NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER) + + write_uint32(w, msg.importance_transfer.mode) + write_bytes_with_length(w, bytearray(msg.importance_transfer.public_key)) + return w + + +def get_transfer_payload(msg: NEMSignTx, node) -> [bytes, bool]: + payload = msg.transfer.payload + encrypted = False + if msg.transfer.public_key is not None: + if payload is None: + raise ValueError("Public key provided but no payload to encrypt") + payload = _encrypt(node, msg.transfer.public_key, msg.transfer.payload) + encrypted = True + + return payload, encrypted + + +def _encrypt(node, public_key: bytes, payload: bytes) -> bytes: + salt = random.bytes(NEM_SALT_SIZE) + iv = random.bytes(AES_BLOCK_SIZE) + encrypted = node.nem_encrypt(public_key, iv, salt, payload) + return iv + salt + encrypted + + +def _get_version(network, mosaics=None) -> int: + if mosaics: + return network << 24 | 2 + return network << 24 | 1 diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index 216e15053..1ac4502e5 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -76,12 +76,12 @@ def _validate_common(common: NEMTransactionCommon, inner: bool=False): if common.deadline is None: err = 'deadline' - is_signer = common.signer is not None - if inner != is_signer: - if not inner: - raise ValueError('Signer not allowed in outer transaction') - err = 'signer' - + # is_signer = common.signer is not None todo !! + # if inner != is_signer: + # if not inner: + # raise ValueError('Signer not allowed in outer transaction') + # err = 'signer' + # if err: if inner: raise ValueError('No ' + err + ' provided in inner transaction') @@ -106,6 +106,8 @@ def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer): def _validate_multisig(multisig: NEMTransactionCommon, network: int): + _validate_common(multisig) + _validate_public_key(multisig.signer, 'Invalid multisig signer public key provided') if multisig.network != network: raise ValueError('Inner transaction network is different') diff --git a/src/apps/nem/writers.py b/src/apps/nem/writers.py index 0f3544328..dfd601551 100644 --- a/src/apps/nem/writers.py +++ b/src/apps/nem/writers.py @@ -1,4 +1,7 @@ +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon + + def write_uint32(w, n: int): w.append(n & 0xFF) w.append((n >> 8) & 0xFF) @@ -26,21 +29,16 @@ def write_bytes_with_length(w, buf: bytearray): write_bytes(w, buf) -def nem_transaction_write_common(tx_type: int, version: int, timestamp: int, signer: bytes, fee: int, deadline: int)\ - -> bytearray: +def write_common(common: NEMTransactionCommon, public_key: bytearray, transaction_type: int, version: int=None) -> bytearray: ret = bytearray() - write_uint32(ret, tx_type) + write_uint32(ret, transaction_type) + if version is None: + version = common.network << 24 | 1 write_uint32(ret, version) - write_uint32(ret, timestamp) + write_uint32(ret, common.timestamp) - write_bytes_with_length(ret, bytearray(signer)) - write_uint64(ret, fee) - write_uint32(ret, deadline) + write_bytes_with_length(ret, public_key) + write_uint64(ret, common.fee) + write_uint32(ret, common.deadline) return ret - - -def nem_get_version(network, mosaics=None) -> int: - if mosaics: - return network << 24 | 2 - return network << 24 | 1 diff --git a/tests/test_apps.nem.transaction.mosaic.canonicalization.py b/tests/test_apps.nem.mosaic.canonicalization.py similarity index 79% rename from tests/test_apps.nem.transaction.mosaic.canonicalization.py rename to tests/test_apps.nem.mosaic.canonicalization.py index c5d3b676d..3de8ab89f 100644 --- a/tests/test_apps.nem.transaction.mosaic.canonicalization.py +++ b/tests/test_apps.nem.mosaic.canonicalization.py @@ -1,11 +1,10 @@ from common import * - from apps.nem.mosaic import * -class TestNemTransactionMosaicCanonicalization(unittest.TestCase): +class TestNemMosaicCanonicalization(unittest.TestCase): - def test_nem_transaction_mosaic_canonicalization(self): + def test_mosaic_canonicalization(self): a = NEMMosaic() a.namespace = 'abc' a.quantity = 3 @@ -35,13 +34,13 @@ class TestNemTransactionMosaicCanonicalization(unittest.TestCase): g.quantity = 30 g.mosaic = 'mosaic' - res = nem_canonicalize_mosaics([a, b, c, d, e, f, g]) + res = canonicalize_mosaics([a, b, c, d, e, f, g]) self.assertEqual(res, [e, f, b, a, c]) self.assertEqual(res[2].quantity, b.quantity) self.assertEqual(res[3].quantity, 3 + 8) # a + d self.assertEqual(res[4].quantity, 3 + 30) # c + g - def test_nem_transaction_mosaic_merge(self): + def test_mosaic_merge(self): a = NEMMosaic() a.namespace = 'abc' a.quantity = 1 @@ -51,24 +50,24 @@ class TestNemTransactionMosaicCanonicalization(unittest.TestCase): b.quantity = 1 b.mosaic = 'mosaic' - merged = nem_merge_mosaics([a, b]) + merged = merge_mosaics([a, b]) self.assertEqual(merged[0].quantity, 2) self.assertEqual(len(merged), 1) a.quantity = 1 b.quantity = 10 - merged = nem_merge_mosaics([a, b]) + merged = merge_mosaics([a, b]) self.assertEqual(merged[0].quantity, 11) a.namespace = 'abcdef' - merged = nem_merge_mosaics([a, b]) + merged = merge_mosaics([a, b]) self.assertEqual(len(merged), 2) c = NEMMosaic() c.namespace = 'abc' c.mosaic = 'xxx' c.quantity = 2 - merged = nem_merge_mosaics([a, b, c]) + merged = merge_mosaics([a, b, c]) self.assertEqual(len(merged), 3) a.namespace = 'abcdef' @@ -80,7 +79,7 @@ class TestNemTransactionMosaicCanonicalization(unittest.TestCase): c.namespace = 'abc' c.mosaic = 'mosaic' c.quantity = 3 - merged = nem_merge_mosaics([a, b, c]) + merged = merge_mosaics([a, b, c]) self.assertEqual(merged[0].quantity, 1) self.assertEqual(merged[1].quantity, 5) self.assertEqual(len(merged), 2) @@ -94,11 +93,11 @@ class TestNemTransactionMosaicCanonicalization(unittest.TestCase): c.namespace = 'abc' c.mosaic = 'mosaic' c.quantity = 3 - merged = nem_merge_mosaics([a, b, c]) + merged = merge_mosaics([a, b, c]) self.assertEqual(merged[0].quantity, 6) self.assertEqual(len(merged), 1) - def test_nem_transaction_mosaic_sort(self): + def test_mosaic_sort(self): a = NEMMosaic() a.namespace = 'abcz' a.quantity = 1 @@ -107,39 +106,39 @@ class TestNemTransactionMosaicCanonicalization(unittest.TestCase): b.namespace = 'abca' b.quantity = 1 b.mosaic = 'mosaic' - res = nem_sort_mosaics([a, b]) + res = sort_mosaics([a, b]) self.assertEqual(res, [b, a]) a.namespace = '' b.namespace = 'a.b.c' - res = nem_sort_mosaics([a, b]) + res = sort_mosaics([a, b]) self.assertEqual(res, [a, b]) a.namespace = 'z.z.z' b.namespace = 'a.b.c' - res = nem_sort_mosaics([a, b]) + res = sort_mosaics([a, b]) self.assertEqual(res, [b, a]) a.namespace = 'a' b.namespace = 'a' a.mosaic = 'mosaic' b.mosaic = 'mosaic' - res = nem_sort_mosaics([a, b]) + res = sort_mosaics([a, b]) self.assertEqual(res, [a, b]) a.mosaic = 'www' b.mosaic = 'aaa' - res = nem_sort_mosaics([a, b]) + res = sort_mosaics([a, b]) self.assertEqual(res, [b, a]) c = NEMMosaic() c.namespace = 'a' c.mosaic = 'zzz' - res = nem_sort_mosaics([a, b, c]) + res = sort_mosaics([a, b, c]) self.assertEqual(res, [b, a, c]) c.mosaic = 'bbb' - res = nem_sort_mosaics([a, b, c]) + res = sort_mosaics([a, b, c]) self.assertEqual(res, [b, c, a]) diff --git a/tests/test_apps.nem.mosaic_creation.py b/tests/test_apps.nem.mosaic_creation.py new file mode 100644 index 000000000..7085dad48 --- /dev/null +++ b/tests/test_apps.nem.mosaic_creation.py @@ -0,0 +1,150 @@ +from common import * + +from apps.nem.mosaic import * +from trezor.crypto import hashlib +from trezor.messages.NEMMosaicCreation import NEMMosaicCreation +from trezor.messages.NEMMosaicDefinition import NEMMosaicDefinition + + +class TestNemMosaicCreation(unittest.TestCase): + + def test_nem_transaction_mosaic_creation(self): + + # http://bob.nem.ninja:8765/#/mosaic/68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa/0 + m = _create_msg(NEM_NETWORK_TESTNET, + 14070896, + 108000000, + 14074496, + 'gimre.games.pong', + 'paddles', + 'Paddles for the bong game.\n', + 0, + 10000, + True, + True, + 0, + 0, + '', + '', + '', + 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC', + 50000000000) + + t = serialize_mosaic_creation(m, unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324')) + + self.assertEqual(t, unhexlify('014000000100009870b4d60020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd32400f36f060000000080c2d600de00000020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd3241f0000001000000067696d72652e67616d65732e706f6e6707000000706164646c65731b000000506164646c657320666f722074686520626f6e672067616d652e0a04000000150000000c00000064697669736962696c69747901000000301a0000000d000000696e697469616c537570706c79050000003130303030190000000d000000737570706c794d757461626c650400000074727565180000000c0000007472616e7366657261626c650400000074727565000000002800000054424d4f534149434f443446353445453543444d523233434342474f414d3258534a4252354f4c4300743ba40b000000')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa')) + + def test_nem_transaction_mosaic_creation_with_levy(self): + # http://bob.nem.ninja:8765/#/mosaic/b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55/0 + m = _create_msg(NEM_NETWORK_TESTNET, + 21497248, + 108000000, + 21500848, + "alice.misc", + "bar", + "Special offer: get one bar extra by bying one foo!", + 0, + 1000, + False, + True, + 1, + 1, + "TALICE2GMA34CXHD7XLJQ536NM5UNKQHTORNNT2J", + "nem", + "xem", + "TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC", + 50000000000) + + t = serialize_mosaic_creation(m, unhexlify("244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc"),) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55')) + + # http://chain.nem.ninja/#/mosaic/e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6/0 + m = _create_msg(NEM_NETWORK_MAINNET, + 69251020, + 20000000, + 69337420, + "dim", + "coin", + "DIM COIN", + 6, + 9000000000, + False, + True, + 2, + 10, + "NCGGLVO2G3CUACVI5GNX2KRBJSQCN4RDL2ZWJ4DP", + "dim", + "coin", + "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", + 500000000) + + t = serialize_mosaic_creation(m, unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a")) + self.assertEqual(t, unhexlify('0140000001000068ccaf200420000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a002d3101000000004c0122040c01000020000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a0f0000000300000064696d04000000636f696e0800000044494d20434f494e04000000150000000c00000064697669736962696c69747901000000361f0000000d000000696e697469616c537570706c790a000000393030303030303030301a0000000d000000737570706c794d757461626c650500000066616c7365180000000c0000007472616e7366657261626c6504000000747275654b00000002000000280000004e4347474c564f32473343554143564935474e58324b52424a5351434e3452444c325a574a3444500f0000000300000064696d04000000636f696e0a00000000000000280000004e424d4f534149434f443446353445453543444d523233434342474f414d325853495558365452530065cd1d00000000')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6')) + + def test_nem_transaction_mosaic_creation_with_description(self): + # http://chain.nem.ninja/#/mosaic/269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b/0 + m = _create_msg(NEM_NETWORK_MAINNET, + 26729938, + 108000000, + 26733538, + "jabo38", + "red_token", + "This token is to celebrate the release of Namespaces and Mosaics " + "on the NEM system. This token was the fist ever mosaic created " + "other than nem.xem. There are only 10,000 Red Tokens that will " + "ever be created. It has no levy and can be traded freely among " + "third parties.", + 2, + 10000, + False, + True, + 0, + 0, + "", + "", + "", + "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", + 50000000000) + t = serialize_mosaic_creation(m, unhexlify("58956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f23")) + + self.assertEqual(t, unhexlify('0140000001000068d2dd97012000000058956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f2300f36f0600000000e2eb9701c80100002000000058956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f2317000000060000006a61626f3338090000007265645f746f6b656e0c0100005468697320746f6b656e20697320746f2063656c656272617465207468652072656c65617365206f66204e616d6573706163657320616e64204d6f7361696373206f6e20746865204e454d2073797374656d2e205468697320746f6b656e207761732074686520666973742065766572206d6f736169632063726561746564206f74686572207468616e206e656d2e78656d2e20546865726520617265206f6e6c792031302c3030302052656420546f6b656e7320746861742077696c6c206576657220626520637265617465642e20497420686173206e6f206c65767920616e642063616e2062652074726164656420667265656c7920616d6f6e6720746869726420706172746965732e04000000150000000c00000064697669736962696c69747901000000321a0000000d000000696e697469616c537570706c790500000031303030301a0000000d000000737570706c794d757461626c650500000066616c7365180000000c0000007472616e7366657261626c65040000007472756500000000280000004e424d4f534149434f443446353445453543444d523233434342474f414d3258534955583654525300743ba40b000000')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b')) + + +def _create_msg(network: int, timestamp: int, fee: int, deadline: int, + namespace: str, mosaic: str, description: str, + divisibility: int, supply: int, mutable_supply: bool, transferable: bool, + levy_type: int, levy_fee: int, levy_address: str, levy_namespace: str, + levy_mosaic: str, creation_sink: str, creation_fee: int): + m = NEMSignTx() + m.transaction = NEMTransactionCommon() + m.transaction.network = network + m.transaction.timestamp = timestamp + m.transaction.fee = fee + m.transaction.deadline = deadline + + m.mosaic_creation = NEMMosaicCreation() + m.mosaic_creation.sink = creation_sink + m.mosaic_creation.fee = creation_fee + + m.mosaic_creation.definition = NEMMosaicDefinition() + m.mosaic_creation.definition.namespace = namespace + m.mosaic_creation.definition.mosaic = mosaic + m.mosaic_creation.definition.description = description + m.mosaic_creation.definition.divisibility = divisibility + m.mosaic_creation.definition.supply = supply + m.mosaic_creation.definition.mutable_supply = mutable_supply + m.mosaic_creation.definition.transferable = transferable + m.mosaic_creation.definition.levy = levy_type + m.mosaic_creation.definition.fee = levy_fee + m.mosaic_creation.definition.levy_address = levy_address + m.mosaic_creation.definition.levy_namespace = levy_namespace + m.mosaic_creation.definition.levy_mosaic = levy_mosaic + return m + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_apps.nem.mosaic_supply_change.py b/tests/test_apps.nem.mosaic_supply_change.py new file mode 100644 index 000000000..48a77c142 --- /dev/null +++ b/tests/test_apps.nem.mosaic_supply_change.py @@ -0,0 +1,87 @@ +from common import * + +from apps.nem.mosaic import * +from trezor.crypto import hashlib +from trezor.messages.NEMMosaicSupplyChange import NEMMosaicSupplyChange + + +class TestNemMosaicSupplyChange(unittest.TestCase): + + def test_nem_transaction_create_mosaic_supply_change(self): + + # http://bigalice2.nem.ninja:7890/transaction/get?hash=33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50 + m = _create_msg(NEM_NETWORK_TESTNET, + 14071648, + 108000000, + 14075248, + "gimre.games.pong", + "paddles", + 1, + 1234) + t = serialize_mosaic_supply_change(m, unhexlify("994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324")) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify('33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50')) + + # http://bigalice2.nem.ninja:7890/transaction/get?hash=1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2 + m = _create_msg(NEM_NETWORK_TESTNET, + 14126909, + 108000000, + 14130509, + "jabo38_ltd.fuzzy_kittens_cafe", + "coupons", + 2, + 1) + t = serialize_mosaic_supply_change(m, unhexlify("84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5")) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify('1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2')) + + # http://bigalice3.nem.ninja:7890/transaction/get?hash=694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff + m = _create_msg(NEM_NETWORK_MAINNET, + 53377685, + 20000000, + 53464085, + "abvapp", + "abv", + 1, + 9000000) + t = serialize_mosaic_supply_change(m, unhexlify("b7ccc27b21ba6cf5c699a8dc86ba6ba98950442597ff9fa30e0abe0f5f4dd05d")) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify('694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff')) + + # http://bigalice3.nem.ninja:7890/transaction/get?hash=09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185 + m = _create_msg(NEM_NETWORK_MAINNET, + 55176304, + 20000000, + 55262704, + "sushi", + "wasabi", + 2, + 20) + t = serialize_mosaic_supply_change(m, unhexlify("75f001a8641e2ce5c4386883dda561399ed346177411b492a677b73899502f13")) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify('09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185')) + + +def _create_msg(network: int, timestamp: int, fee: int, deadline: int, + namespace: str, mosaic: str, mod_type: int, delta: int): + m = NEMSignTx() + m.transaction = NEMTransactionCommon() + m.transaction.network = network + m.transaction.timestamp = timestamp + m.transaction.fee = fee + m.transaction.deadline = deadline + + m.supply_change = NEMMosaicSupplyChange() + m.supply_change.namespace = namespace + m.supply_change.mosaic = mosaic + m.supply_change.type = mod_type + m.supply_change.delta = delta + return m + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_apps.nem.multisig.aggregate_modification.py b/tests/test_apps.nem.multisig.aggregate_modification.py new file mode 100644 index 000000000..2c3007f9d --- /dev/null +++ b/tests/test_apps.nem.multisig.aggregate_modification.py @@ -0,0 +1,94 @@ +from common import * + +from apps.nem.multisig import * +from trezor.crypto import hashlib +from trezor.messages.NEMSignTx import NEMSignTx +from trezor.messages.NEMAggregateModification import NEMAggregateModification +from trezor.messages.NEMCosignatoryModification import NEMCosignatoryModification + + +class TestNemMultisigAggregateModification(unittest.TestCase): + + def test_nem_transaction_aggregate_modification(self): + # http://bob.nem.ninja:8765/#/aggregate/6a55471b17159e5b6cd579c421e95a4e39d92e3f78b0a55ee337e785a601d3a2 + m = _create_msg(NEM_NETWORK_TESTNET, + 0, + 22000000, + 0, + 2, + False) + t = serialize_aggregate_modification(m, unhexlify("462ee976890916e54fa825d26bdd0235f5eb5b6a143c199ab0ae5ee9328e08ce")) + + serialize_cosignatory_modification(t, 1, unhexlify( + "994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324")) + serialize_cosignatory_modification(t, 1, unhexlify( + "c54d6e33ed1446eedd7f7a80a588dd01857f723687a09200c1917d5524752f8b")) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify("6a55471b17159e5b6cd579c421e95a4e39d92e3f78b0a55ee337e785a601d3a2")) + + # http://chain.nem.ninja/#/aggregate/cc64ca69bfa95db2ff7ac1e21fe6d27ece189c603200ebc9778d8bb80ca25c3c + m = _create_msg(NEM_NETWORK_MAINNET, + 0, + 40000000, + 0, + 5, + False) + t = serialize_aggregate_modification(m, unhexlify("f41b99320549741c5cce42d9e4bb836d98c50ed5415d0c3c2912d1bb50e6a0e5")) + + serialize_cosignatory_modification(t, 1, unhexlify( + "1fbdbdde28daf828245e4533765726f0b7790e0b7146e2ce205df3e86366980b")) + serialize_cosignatory_modification(t, 1, unhexlify( + "f94e8702eb1943b23570b1b83be1b81536df35538978820e98bfce8f999e2d37")) + serialize_cosignatory_modification(t, 1, unhexlify( + "826cedee421ff66e708858c17815fcd831a4bb68e3d8956299334e9e24380ba8")) + serialize_cosignatory_modification(t, 1, unhexlify( + "719862cd7d0f4e875a6a0274c9a1738f38f40ad9944179006a54c34724c1274d")) + serialize_cosignatory_modification(t, 1, unhexlify( + "43aa69177018fc3e2bdbeb259c81cddf24be50eef9c5386db51d82386c41475a")) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify("cc64ca69bfa95db2ff7ac1e21fe6d27ece189c603200ebc9778d8bb80ca25c3c")) + + def test_nem_transaction_aggregate_modification_relative_change(self): + # http://bob.nem.ninja:8765/#/aggregate/1fbdae5ba753e68af270930413ae90f671eb8ab58988116684bac0abd5726584 + m = _create_msg(NEM_NETWORK_TESTNET, + 6542254, + 40000000, + 6545854, + 4, + True) + t = serialize_aggregate_modification(m, unhexlify("6bf7849c1eec6a2002995cc457dc00c4e29bad5c88de63f51e42dfdcd7b2131d")) + + serialize_cosignatory_modification(t, 1, unhexlify( + "5f53d076c8c3ec3110b98364bc423092c3ec2be2b1b3c40fd8ab68d54fa39295")) + serialize_cosignatory_modification(t, 1, unhexlify( + "9eb199c2b4d406f64cb7aa5b2b0815264b56ba8fe44d558a6cb423a31a33c4c2")) + serialize_cosignatory_modification(t, 1, unhexlify( + "94b2323dab23a3faba24fa6ddda0ece4fbb06acfedd74e76ad9fae38d006882b")) + serialize_cosignatory_modification(t, 1, unhexlify( + "d88c6ee2a2cd3929d0d76b6b14ecb549d21296ab196a2b3a4cb2536bcce32e87")) + serialize_minimum_cosignatories(t, 2) + + self.assertEqual(hashlib.sha3_256(t).digest(True), + unhexlify("1fbdae5ba753e68af270930413ae90f671eb8ab58988116684bac0abd5726584")) + + +def _create_msg(network: int, timestamp: int, fee: int, deadline: int, + modifications: int, relative_change: bool): + m = NEMSignTx() + m.transaction = NEMTransactionCommon() + m.transaction.network = network + m.transaction.timestamp = timestamp + m.transaction.fee = fee + m.transaction.deadline = deadline + + m.aggregate_modification = NEMAggregateModification() + for i in range(modifications): + m.aggregate_modification.modifications.append(NEMCosignatoryModification()) + m.aggregate_modification.relative_change = relative_change + return m + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_apps.nem.multisig.py b/tests/test_apps.nem.multisig.py index 061838f23..caa3da37a 100644 --- a/tests/test_apps.nem.multisig.py +++ b/tests/test_apps.nem.multisig.py @@ -1,86 +1,113 @@ from common import * -from apps.nem.transaction import * from apps.nem.multisig import * +from apps.nem.namespace import * +from trezor.messages.NEMSignTx import NEMSignTx +from trezor.messages.NEMAggregateModification import NEMAggregateModification +from trezor.messages.NEMProvisionNamespace import NEMProvisionNamespace +from trezor.messages.NEMCosignatoryModification import NEMCosignatoryModification class TestNemMultisig(unittest.TestCase): def test_nem_multisig(self): # http://bob.nem.ninja:8765/#/multisig/7d3a7087023ee29005262016706818579a2b5499eb9ca76bad98c1e6f4c46642 - - base_tx = nem_transaction_create_aggregate_modification(NEM_NETWORK_TESTNET, - 3939039, - unhexlify("abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac"), - 16000000, - 3960639, - 1, - False) - - base_tx = nem_transaction_write_cosignatory_modification(base_tx, 2, unhexlify("e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3")) - multisig = nem_transaction_create_multisig(NEM_NETWORK_TESTNET, - 3939039, - unhexlify("59d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e"), - 6000000, - 3960639, - base_tx) + m = _create_msg(NEM_NETWORK_TESTNET, + 3939039, + 16000000, + 3960639, + 1, + False) + base_tx = serialize_aggregate_modification(m, unhexlify("abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac")) + + base_tx = serialize_cosignatory_modification(base_tx, 2, unhexlify("e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3")) + m = _create_common_msg(NEM_NETWORK_TESTNET, + 3939039, + 6000000, + 3960639) + multisig = serialize_multisig(m, unhexlify("59d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e"), base_tx) self.assertEqual(multisig, unhexlify("0410000001000098df1a3c002000000059d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e808d5b00000000003f6f3c006c0000000110000001000098df1a3c0020000000abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac0024f400000000003f6f3c0001000000280000000200000020000000e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3")) address = "TCRXYUQIMFA7AOGL5LF3YWLC7VABLYUMJ5ACBUNL" - multisig = nem_transaction_create_multisig_signature(NEM_NETWORK_TESTNET, - 3939891, - unhexlify("71cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4"), - 6000000, - 3961491, - base_tx, - address) + m = _create_common_msg(NEM_NETWORK_TESTNET, + 3939891, + 6000000, + 3961491) + multisig = serialize_multisig_signature(m, unhexlify("71cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4"), base_tx, address) self.assertEqual(multisig, unhexlify("0210000001000098331e3c002000000071cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4808d5b000000000093723c0024000000200000008ec165580bdabfd31ce6007a1748ce5bdf30eab7a214743097de3bc822ac7e002800000054435258595551494d464137414f474c354c463359574c43375641424c59554d4a35414342554e4c")) def test_nem_multisig_2(self): # http://chain.nem.ninja/#/multisig/1016cf3bdd61bd57b9b2b07b6ff2dee390279d8d899265bdc23d42360abe2e6c - - base_tx = nem_transaction_create_provision_namespace(NEM_NETWORK_MAINNET, - 59414272, - unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a"), - 20000000, - 59500672, - "dim", - "", - "NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA", - 5000000000) - - multisig = nem_transaction_create_multisig(NEM_NETWORK_MAINNET, - 59414272, - unhexlify("cfe58463f0eaebceb5d00717f8aead49171a5d7c08f6b1299bd534f11715acc9"), - 6000000, - 59500672, - base_tx) - + m = _create_provision_msg(NEM_NETWORK_MAINNET, + 59414272, + 20000000, + 59500672, + "dim", + "", + "NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA", + 5000000000) + base_tx = serialize_provision_namespace(m, unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a")) + + m = _create_common_msg(NEM_NETWORK_MAINNET, + 59414272, + 6000000, + 59500672) + + multisig = serialize_multisig(m, unhexlify("cfe58463f0eaebceb5d00717f8aead49171a5d7c08f6b1299bd534f11715acc9"), base_tx) self.assertEqual(multisig, unhexlify("041000000100006800978a0320000000cfe58463f0eaebceb5d00717f8aead49171a5d7c08f6b1299bd534f11715acc9808d5b000000000080e88b037b000000012000000100006800978a0320000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a002d31010000000080e88b03280000004e414d4553504143455748344d4b464d42435646455244504f4f5034464b374d54425844505a5a4100f2052a010000000300000064696dffffffff")) + m = _create_common_msg(NEM_NETWORK_MAINNET, + 59414342, + 6000000, + 59500742) address = "NDDRG3UEB5LZZZMDWBE4RTKZK73JBHPAIWBHCFMV" - multisig = nem_transaction_create_multisig_signature(NEM_NETWORK_MAINNET, - 59414342, - unhexlify("1b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2"), - 6000000, - 59500742, - base_tx, - address) - + multisig = serialize_multisig_signature(m, unhexlify("1b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2"), base_tx, address) self.assertEqual(multisig, unhexlify("021000000100006846978a03200000001b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2808d5b0000000000c6e88b032400000020000000bfa2088f7720f89dd4664d650e321dabd02fab61b7355bc88a391a848a49786a280000004e4444524733554542354c5a5a5a4d445742453452544b5a4b37334a424850414957424843464d56")) - multisig = nem_transaction_create_multisig_signature(NEM_NETWORK_MAINNET, - 59414381, - unhexlify("7ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87"), - 6000000, - 59500781, - base_tx, - address) - + m = _create_common_msg(NEM_NETWORK_MAINNET, + 59414381, + 6000000, + 59500781) + address = "NDDRG3UEB5LZZZMDWBE4RTKZK73JBHPAIWBHCFMV" + multisig = serialize_multisig_signature(m, unhexlify("7ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87"), base_tx, address) self.assertEqual(multisig, unhexlify("02100000010000686d978a03200000007ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87808d5b0000000000ede88b032400000020000000bfa2088f7720f89dd4664d650e321dabd02fab61b7355bc88a391a848a49786a280000004e4444524733554542354c5a5a5a4d445742453452544b5a4b37334a424850414957424843464d56")) +def _create_common_msg(network: int, timestamp: int, fee: int, deadline: int): + m = NEMTransactionCommon() + m.network = network + m.timestamp = timestamp + m.fee = fee + m.deadline = deadline + return m + + +def _create_msg(network: int, timestamp: int, fee: int, deadline: int, + modifications: int, relative_change: bool): + m = NEMSignTx() + m.transaction = _create_common_msg(network, timestamp, fee, deadline) + + m.aggregate_modification = NEMAggregateModification() + for i in range(modifications): + m.aggregate_modification.modifications.append(NEMCosignatoryModification()) + m.aggregate_modification.relative_change = relative_change + return m + + +def _create_provision_msg(network: int, timestamp: int, fee: int, deadline: int, + name: str, parent: str, sink: str, rental_fee: int): + m = NEMSignTx() + m.transaction = _create_common_msg(network, timestamp, fee, deadline) + + m.provision_namespace = NEMProvisionNamespace() + m.provision_namespace.namespace = name + m.provision_namespace.parent = parent + m.provision_namespace.sink = sink + m.provision_namespace.fee = rental_fee + return m + + if __name__ == '__main__': unittest.main() diff --git a/tests/test_apps.nem.namespace.py b/tests/test_apps.nem.namespace.py new file mode 100644 index 000000000..b84bf6729 --- /dev/null +++ b/tests/test_apps.nem.namespace.py @@ -0,0 +1,69 @@ +from common import * + +from apps.nem.namespace import * +from trezor.crypto import hashlib +from trezor.messages.NEMProvisionNamespace import NEMProvisionNamespace +from trezor.messages.NEMSignTx import NEMSignTx + + +class TestNemNamespace(unittest.TestCase): + + def test_create_provision_namespace(self): + + # http://bob.nem.ninja:8765/#/transfer/0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f + m = _create_msg(NEM_NETWORK_TESTNET, + 56999445, + 20000000, + 57003045, + 'gimre', + '', + 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35', + 5000000000) + t = serialize_provision_namespace(m, unhexlify('84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('f7cab28da57204d01a907c697836577a4ae755e6c9bac60dcc318494a22debb3')) + + # http://bob.nem.ninja:8765/#/namespace/7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d + m = _create_msg(NEM_NETWORK_TESTNET, + 21496797, + 108000000, + 21500397, + 'misc', + 'alice', + 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35', + 5000000000) + t = serialize_provision_namespace(m, unhexlify('244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc')) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d')) + + # http://chain.nem.ninja/#/namespace/57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569 + m = _create_msg(NEM_NETWORK_MAINNET, + 26699717, + 108000000, + 26703317, + 'sex', + '', + 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA', + 50000000000) + t = serialize_provision_namespace(m, unhexlify('9f3c14f304309c8b72b2821339c4428793b1518bea72d58dd01f19d523518614')) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569')) + + +def _create_msg(network: int, timestamp: int, fee: int, deadline: int, + name: str, parent: str, sink: str, rental_fee: int): + m = NEMSignTx() + m.transaction = NEMTransactionCommon() + m.transaction.network = network + m.transaction.timestamp = timestamp + m.transaction.fee = fee + m.transaction.deadline = deadline + m.provision_namespace = NEMProvisionNamespace() + m.provision_namespace.namespace = name + m.provision_namespace.parent = parent + m.provision_namespace.sink = sink + m.provision_namespace.fee = rental_fee + return m + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_apps.nem.transaction.aggregate_modification.py b/tests/test_apps.nem.transaction.aggregate_modification.py deleted file mode 100644 index e0d58b70f..000000000 --- a/tests/test_apps.nem.transaction.aggregate_modification.py +++ /dev/null @@ -1,75 +0,0 @@ -from common import * - -from apps.nem.transaction import * -from trezor.crypto import hashlib - - -class TestNemTransactionMosaicCreation(unittest.TestCase): - - def test_nem_transaction_aggregate_modification(self): - # http://bob.nem.ninja:8765/#/aggregate/6a55471b17159e5b6cd579c421e95a4e39d92e3f78b0a55ee337e785a601d3a2 - t = nem_transaction_create_aggregate_modification(NEM_NETWORK_TESTNET, - 0, - unhexlify("462ee976890916e54fa825d26bdd0235f5eb5b6a143c199ab0ae5ee9328e08ce"), - 22000000, - 0, - 2, - False) - - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324")) - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "c54d6e33ed1446eedd7f7a80a588dd01857f723687a09200c1917d5524752f8b")) - - self.assertEqual(hashlib.sha3_256(t).digest(True), - unhexlify("6a55471b17159e5b6cd579c421e95a4e39d92e3f78b0a55ee337e785a601d3a2")) - - # http://chain.nem.ninja/#/aggregate/cc64ca69bfa95db2ff7ac1e21fe6d27ece189c603200ebc9778d8bb80ca25c3c - t = nem_transaction_create_aggregate_modification(NEM_NETWORK_MAINNET, - 0, - unhexlify("f41b99320549741c5cce42d9e4bb836d98c50ed5415d0c3c2912d1bb50e6a0e5"), - 40000000, - 0, - 5, - False) - - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "1fbdbdde28daf828245e4533765726f0b7790e0b7146e2ce205df3e86366980b")) - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "f94e8702eb1943b23570b1b83be1b81536df35538978820e98bfce8f999e2d37")) - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "826cedee421ff66e708858c17815fcd831a4bb68e3d8956299334e9e24380ba8")) - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "719862cd7d0f4e875a6a0274c9a1738f38f40ad9944179006a54c34724c1274d")) - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "43aa69177018fc3e2bdbeb259c81cddf24be50eef9c5386db51d82386c41475a")) - - self.assertEqual(hashlib.sha3_256(t).digest(True), - unhexlify("cc64ca69bfa95db2ff7ac1e21fe6d27ece189c603200ebc9778d8bb80ca25c3c")) - - def test_nem_transaction_aggregate_modification_relative_change(self): - # http://bob.nem.ninja:8765/#/aggregate/1fbdae5ba753e68af270930413ae90f671eb8ab58988116684bac0abd5726584 - t = nem_transaction_create_aggregate_modification(NEM_NETWORK_TESTNET, - 6542254, - unhexlify("6bf7849c1eec6a2002995cc457dc00c4e29bad5c88de63f51e42dfdcd7b2131d"), - 40000000, - 6545854, - 4, - True) - - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "5f53d076c8c3ec3110b98364bc423092c3ec2be2b1b3c40fd8ab68d54fa39295")) - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "9eb199c2b4d406f64cb7aa5b2b0815264b56ba8fe44d558a6cb423a31a33c4c2")) - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "94b2323dab23a3faba24fa6ddda0ece4fbb06acfedd74e76ad9fae38d006882b")) - nem_transaction_write_cosignatory_modification(t, 1, unhexlify( - "d88c6ee2a2cd3929d0d76b6b14ecb549d21296ab196a2b3a4cb2536bcce32e87")) - nem_transaction_write_minimum_cosignatories(t, 2) - - self.assertEqual(hashlib.sha3_256(t).digest(True), - unhexlify("1fbdae5ba753e68af270930413ae90f671eb8ab58988116684bac0abd5726584")) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_apps.nem.transaction.mosaic_creation.py b/tests/test_apps.nem.transaction.mosaic_creation.py deleted file mode 100644 index 725db6f05..000000000 --- a/tests/test_apps.nem.transaction.mosaic_creation.py +++ /dev/null @@ -1,111 +0,0 @@ -from common import * - -from apps.nem.mosaic import * -from trezor.crypto import hashlib - - -class TestNemTransactionMosaicCreation(unittest.TestCase): - - def test_nem_transaction_mosaic_creation(self): - - # http://bob.nem.ninja:8765/#/mosaic/68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa/0 - t = nem_transaction_create_mosaic_creation(NEM_NETWORK_TESTNET, - 14070896, - unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324'), - 108000000, - 14074496, - 'gimre.games.pong', - 'paddles', - 'Paddles for the bong game.\n', - 0, - 10000, - True, - True, - 0, - 0, - '', - '', - '', - 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC', - 50000000000) - - self.assertEqual(t, unhexlify('014000000100009870b4d60020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd32400f36f060000000080c2d600de00000020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd3241f0000001000000067696d72652e67616d65732e706f6e6707000000706164646c65731b000000506164646c657320666f722074686520626f6e672067616d652e0a04000000150000000c00000064697669736962696c69747901000000301a0000000d000000696e697469616c537570706c79050000003130303030190000000d000000737570706c794d757461626c650400000074727565180000000c0000007472616e7366657261626c650400000074727565000000002800000054424d4f534149434f443446353445453543444d523233434342474f414d3258534a4252354f4c4300743ba40b000000')) - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa')) - - def test_nem_transaction_mosaic_creation_with_levy(self): - # http://bob.nem.ninja:8765/#/mosaic/b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55/0 - t = nem_transaction_create_mosaic_creation(NEM_NETWORK_TESTNET, - 21497248, - unhexlify("244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc"), - 108000000, - 21500848, - "alice.misc", - "bar", - "Special offer: get one bar extra by bying one foo!", - 0, - 1000, - False, - True, - 1, - 1, - "TALICE2GMA34CXHD7XLJQ536NM5UNKQHTORNNT2J", - "nem", - "xem", - "TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC", - 50000000000) - - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55')) - - # http://chain.nem.ninja/#/mosaic/e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6/0 - t = nem_transaction_create_mosaic_creation(NEM_NETWORK_MAINNET, - 69251020, - unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a"), - 20000000, - 69337420, - "dim", - "coin", - "DIM COIN", - 6, - 9000000000, - False, - True, - 2, - 10, - "NCGGLVO2G3CUACVI5GNX2KRBJSQCN4RDL2ZWJ4DP", - "dim", - "coin", - "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", - 500000000) - self.assertEqual(t, unhexlify('0140000001000068ccaf200420000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a002d3101000000004c0122040c01000020000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a0f0000000300000064696d04000000636f696e0800000044494d20434f494e04000000150000000c00000064697669736962696c69747901000000361f0000000d000000696e697469616c537570706c790a000000393030303030303030301a0000000d000000737570706c794d757461626c650500000066616c7365180000000c0000007472616e7366657261626c6504000000747275654b00000002000000280000004e4347474c564f32473343554143564935474e58324b52424a5351434e3452444c325a574a3444500f0000000300000064696d04000000636f696e0a00000000000000280000004e424d4f534149434f443446353445453543444d523233434342474f414d325853495558365452530065cd1d00000000')) - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6')) - - def test_nem_transaction_mosaic_creation_with_description(self): - # http://chain.nem.ninja/#/mosaic/269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b/0 - t = nem_transaction_create_mosaic_creation(NEM_NETWORK_MAINNET, - 26729938, - unhexlify("58956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f23"), - 108000000, - 26733538, - "jabo38", - "red_token", - "This token is to celebrate the release of Namespaces and Mosaics " - "on the NEM system. This token was the fist ever mosaic created " - "other than nem.xem. There are only 10,000 Red Tokens that will " - "ever be created. It has no levy and can be traded freely among " - "third parties.", - 2, - 10000, - False, - True, - 0, - 0, - "", - "", - "", - "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", - 50000000000) - self.assertEqual(t, unhexlify('0140000001000068d2dd97012000000058956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f2300f36f0600000000e2eb9701c80100002000000058956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f2317000000060000006a61626f3338090000007265645f746f6b656e0c0100005468697320746f6b656e20697320746f2063656c656272617465207468652072656c65617365206f66204e616d6573706163657320616e64204d6f7361696373206f6e20746865204e454d2073797374656d2e205468697320746f6b656e207761732074686520666973742065766572206d6f736169632063726561746564206f74686572207468616e206e656d2e78656d2e20546865726520617265206f6e6c792031302c3030302052656420546f6b656e7320746861742077696c6c206576657220626520637265617465642e20497420686173206e6f206c65767920616e642063616e2062652074726164656420667265656c7920616d6f6e6720746869726420706172746965732e04000000150000000c00000064697669736962696c69747901000000321a0000000d000000696e697469616c537570706c790500000031303030301a0000000d000000737570706c794d757461626c650500000066616c7365180000000c0000007472616e7366657261626c65040000007472756500000000280000004e424d4f534149434f443446353445453543444d523233434342474f414d3258534955583654525300743ba40b000000')) - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b')) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_apps.nem.transaction.mosaic_supply_change.py b/tests/test_apps.nem.transaction.mosaic_supply_change.py deleted file mode 100644 index 0f72f21e6..000000000 --- a/tests/test_apps.nem.transaction.mosaic_supply_change.py +++ /dev/null @@ -1,69 +0,0 @@ -from common import * - -from apps.nem.mosaic import * -from trezor.crypto import hashlib - - -class TestNemTransactionMosaicCreation(unittest.TestCase): - - def test_nem_transaction_create_mosaic_supply_change(self): - - # http://bigalice2.nem.ninja:7890/transaction/get?hash=33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50 - t = nem_transaction_create_mosaic_supply_change(NEM_NETWORK_TESTNET, - 14071648, - unhexlify("994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324"), - 108000000, - 14075248, - "gimre.games.pong", - "paddles", - 1, - 1234) - - self.assertEqual(hashlib.sha3_256(t).digest(True), - unhexlify('33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50')) - - # http://bigalice2.nem.ninja:7890/transaction/get?hash=1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2 - t = nem_transaction_create_mosaic_supply_change(NEM_NETWORK_TESTNET, - 14126909, - unhexlify("84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5"), - 108000000, - 14130509, - "jabo38_ltd.fuzzy_kittens_cafe", - "coupons", - 2, - 1) - - self.assertEqual(hashlib.sha3_256(t).digest(True), - unhexlify('1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2')) - - # http://bigalice3.nem.ninja:7890/transaction/get?hash=694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff - t = nem_transaction_create_mosaic_supply_change(NEM_NETWORK_MAINNET, - 53377685, - unhexlify("b7ccc27b21ba6cf5c699a8dc86ba6ba98950442597ff9fa30e0abe0f5f4dd05d"), - 20000000, - 53464085, - "abvapp", - "abv", - 1, - 9000000) - - self.assertEqual(hashlib.sha3_256(t).digest(True), - unhexlify('694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff')) - - # http://bigalice3.nem.ninja:7890/transaction/get?hash=09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185 - t = nem_transaction_create_mosaic_supply_change(NEM_NETWORK_MAINNET, - 55176304, - unhexlify("75f001a8641e2ce5c4386883dda561399ed346177411b492a677b73899502f13"), - 20000000, - 55262704, - "sushi", - "wasabi", - 2, - 20) - - self.assertEqual(hashlib.sha3_256(t).digest(True), - unhexlify('09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185')) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_apps.nem.transaction.provision.py b/tests/test_apps.nem.transaction.provision.py deleted file mode 100644 index 9ef13edf5..000000000 --- a/tests/test_apps.nem.transaction.provision.py +++ /dev/null @@ -1,49 +0,0 @@ -from common import * - -from apps.nem.transaction import * -from trezor.crypto import hashlib - - -class TestNemTransactionProvisition(unittest.TestCase): - - def test_create_provision_namespace(self): - - # http://bob.nem.ninja:8765/#/transfer/0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f - t = nem_transaction_create_provision_namespace(NEM_NETWORK_TESTNET, - 56999445, - unhexlify('84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5'), - 20000000, - 57003045, - 'gimre', - '', - 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35', - 5000000000) - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('f7cab28da57204d01a907c697836577a4ae755e6c9bac60dcc318494a22debb3')) - - # http://bob.nem.ninja:8765/#/namespace/7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d - t = nem_transaction_create_provision_namespace(NEM_NETWORK_TESTNET, - 21496797, - unhexlify('244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc'), - 108000000, - 21500397, - 'misc', - 'alice', - 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35', - 5000000000) - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d')) - - # http://chain.nem.ninja/#/namespace/57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569 - t = nem_transaction_create_provision_namespace(NEM_NETWORK_MAINNET, - 26699717, - unhexlify('9f3c14f304309c8b72b2821339c4428793b1518bea72d58dd01f19d523518614'), - 108000000, - 26703317, - 'sex', - '', - 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA', - 50000000000) - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569')) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_apps.nem.transaction.transfer.py b/tests/test_apps.nem.transaction.transfer.py deleted file mode 100644 index 75648ff80..000000000 --- a/tests/test_apps.nem.transaction.transfer.py +++ /dev/null @@ -1,93 +0,0 @@ -from common import * - -from apps.nem.transaction import * -from apps.nem.mosaic import * -from trezor.crypto import hashlib - - -class TestNemTransactionTransfer(unittest.TestCase): - - def test_create_transfer(self): - - # http://bob.nem.ninja:8765/#/transfer/0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f - t = nem_transaction_create_transfer(NEM_NETWORK_TESTNET, - 0, - unhexlify('e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f'), - 0, - 0, - 'TBGIMRE4SBFRUJXMH7DVF2IBY36L2EDWZ37GVSC4', - 50000000000000) - - self.assertEqual(t, unhexlify('01010000010000980000000020000000e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f00000000000000000000000028000000544247494d52453453424652554a584d48374456463249425933364c324544575a3337475653433400203d88792d000000000000')) - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f')) - - def test_create_transfer_with_payload(self): - - # http://chain.nem.ninja/#/transfer/e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb - t = nem_transaction_create_transfer(NEM_NETWORK_MAINNET, - 0, - unhexlify( - '8d07f90fb4bbe7715fa327c926770166a11be2e494a970605f2e12557f66c9b9'), - 0, - 0, - 'NBT3WHA2YXG2IR4PWKFFMO772JWOITTD2V4PECSB', - 5175000000000, - bytearray('Good luck!')) - - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb')) - - def test_create_transfer_with_mosaic(self): - - # http://bob.nem.ninja:8765/#/transfer/3409d9ece28d6296d6d5e220a7e3cb8641a3fb235ffcbd20c95da64f003ace6c - t = nem_transaction_create_transfer(NEM_NETWORK_TESTNET, - 14072100, - unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324'), - 194000000, - 14075700, - 'TBLOODPLWOWMZ2TARX4RFPOSOWLULHXMROBN2WXI', - 3000000, - bytearray('sending you 3 pairs of paddles\n'), - False, - 2) - self.assertEqual(t, unhexlify('010100000200009824b9d60020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd3248034900b0000000034c7d6002800000054424c4f4f44504c574f574d5a3254415258345246504f534f574c554c48584d524f424e32575849c0c62d000000000027000000010000001f00000073656e64696e6720796f752033207061697273206f6620706164646c65730a02000000')) - - nem_transaction_write_mosaic(t, 'gimre.games.pong', 'paddles', 2) - nem_transaction_write_mosaic(t, 'nem', 'xem', 44000000) - - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('3409d9ece28d6296d6d5e220a7e3cb8641a3fb235ffcbd20c95da64f003ace6c')) - - # http://chain.nem.ninja/#/transfer/882dca18dcbe075e15e0ec5a1d7e6ccd69cc0f1309ffd3fde227bfbc107b3f6e - t = nem_transaction_create_transfer(NEM_NETWORK_MAINNET, - 26730750, - unhexlify( - 'f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01'), - 179500000, - 26734350, - 'NBE223WPKEBHQPCYUC4U4CDUQCRRFMPZLOQLB5OP', - 1000000, - bytearray('enjoy! :)'), - False, - 1) - nem_transaction_write_mosaic(t, 'imre.g', 'tokens', 1) - - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('882dca18dcbe075e15e0ec5a1d7e6ccd69cc0f1309ffd3fde227bfbc107b3f6e')) - - def test_create_transfer_with_encrypted_payload(self): - - # http://chain.nem.ninja/#/transfer/40e89160e6f83d37f7c82defc0afe2c1605ae8c919134570a51dd27ea1bb516c - - t = nem_transaction_create_transfer(NEM_NETWORK_MAINNET, - 77229, - unhexlify('f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01'), - 30000000, - 80829, - 'NALICEPFLZQRZGPRIJTMJOCPWDNECXTNNG7QLSG3', - 30000000, - unhexlify('4d9dcf9186967d30be93d6d5404ded22812dbbae7c3f0de501bcd7228cba45bded13000eec7b4c6215fc4d3588168c9218167cec98e6977359153a4132e050f594548e61e0dc61c153f0f53c5e65c595239c9eb7c4e7d48e0f4bb8b1dd2f5ddc'), - True) - - self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('40e89160e6f83d37f7c82defc0afe2c1605ae8c919134570a51dd27ea1bb516c')) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_apps.nem.transfer.py b/tests/test_apps.nem.transfer.py new file mode 100644 index 000000000..bf37ca8dd --- /dev/null +++ b/tests/test_apps.nem.transfer.py @@ -0,0 +1,116 @@ +from common import * + +from apps.nem.transfer import * +from apps.nem.mosaic import * +from trezor.crypto import hashlib +from trezor.messages.NEMTransfer import NEMTransfer +from trezor.messages.NEMSignTx import NEMSignTx + + +class TestNemTransfer(unittest.TestCase): + + def test_create_transfer(self): + + # http://bob.nem.ninja:8765/#/transfer/0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f + m = _create_msg(NEM_NETWORK_TESTNET, + 0, + 0, + 0, + 'TBGIMRE4SBFRUJXMH7DVF2IBY36L2EDWZ37GVSC4', + 50000000000000) + + t = serialize_transfer(m, unhexlify('e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f')) + self.assertEqual(t, unhexlify('01010000010000980000000020000000e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f00000000000000000000000028000000544247494d52453453424652554a584d48374456463249425933364c324544575a3337475653433400203d88792d000000000000')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f')) + + def test_create_transfer_with_payload(self): + + # http://chain.nem.ninja/#/transfer/e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb + m = _create_msg(NEM_NETWORK_MAINNET, + 0, + 0, + 0, + 'NBT3WHA2YXG2IR4PWKFFMO772JWOITTD2V4PECSB', + 5175000000000) + + t = serialize_transfer(m, + unhexlify('8d07f90fb4bbe7715fa327c926770166a11be2e494a970605f2e12557f66c9b9'), + bytearray('Good luck!')) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb')) + + def test_create_transfer_with_encrypted_payload(self): + + # http://chain.nem.ninja/#/transfer/40e89160e6f83d37f7c82defc0afe2c1605ae8c919134570a51dd27ea1bb516c + m = _create_msg(NEM_NETWORK_MAINNET, + 77229, + 30000000, + 80829, + 'NALICEPFLZQRZGPRIJTMJOCPWDNECXTNNG7QLSG3', + 30000000) + + t = serialize_transfer(m, + unhexlify('f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01'), + unhexlify('4d9dcf9186967d30be93d6d5404ded22812dbbae7c3f0de501bcd7228cba45bded13000eec7b4c6215fc4d3588168c9218167cec98e6977359153a4132e050f594548e61e0dc61c153f0f53c5e65c595239c9eb7c4e7d48e0f4bb8b1dd2f5ddc'), + True) + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('40e89160e6f83d37f7c82defc0afe2c1605ae8c919134570a51dd27ea1bb516c')) + + def test_create_transfer_with_mosaic(self): + + # http://bob.nem.ninja:8765/#/transfer/3409d9ece28d6296d6d5e220a7e3cb8641a3fb235ffcbd20c95da64f003ace6c + m = _create_msg(NEM_NETWORK_TESTNET, + 14072100, + 194000000, + 14075700, + 'TBLOODPLWOWMZ2TARX4RFPOSOWLULHXMROBN2WXI', + 3000000, + 2) + + t = serialize_transfer(m, + unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324'), + bytearray('sending you 3 pairs of paddles\n'), + False) + + self.assertEqual(t, unhexlify('010100000200009824b9d60020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd3248034900b0000000034c7d6002800000054424c4f4f44504c574f574d5a3254415258345246504f534f574c554c48584d524f424e32575849c0c62d000000000027000000010000001f00000073656e64696e6720796f752033207061697273206f6620706164646c65730a02000000')) + + serialize_mosaic(t, 'gimre.games.pong', 'paddles', 2) + serialize_mosaic(t, 'nem', 'xem', 44000000) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('3409d9ece28d6296d6d5e220a7e3cb8641a3fb235ffcbd20c95da64f003ace6c')) + + # http://chain.nem.ninja/#/transfer/882dca18dcbe075e15e0ec5a1d7e6ccd69cc0f1309ffd3fde227bfbc107b3f6e + m = _create_msg(NEM_NETWORK_MAINNET, + 26730750, + 179500000, + 26734350, + 'NBE223WPKEBHQPCYUC4U4CDUQCRRFMPZLOQLB5OP', + 1000000, + 1) + + t = serialize_transfer(m, + unhexlify('f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01'), + bytearray('enjoy! :)'), + False) + serialize_mosaic(t, 'imre.g', 'tokens', 1) + + self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('882dca18dcbe075e15e0ec5a1d7e6ccd69cc0f1309ffd3fde227bfbc107b3f6e')) + + +def _create_msg(network: int, timestamp: int, fee: int, deadline: int, + recipient: str, amount: int, mosaics: int = 0): + m = NEMSignTx() + m.transaction = NEMTransactionCommon() + m.transaction.network = network + m.transaction.timestamp = timestamp + m.transaction.fee = fee + m.transaction.deadline = deadline + m.transfer = NEMTransfer() + m.transfer.recipient = recipient + m.transfer.amount = amount + m.transfer.mosaics = list() + for i in range(mosaics): + m.transfer.mosaics.append(NEMMosaic()) + return m + + +if __name__ == '__main__': + unittest.main() From b80a8022d04aef41f7b95ab822df1c2296098bdd Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 10 Apr 2018 14:45:13 +0200 Subject: [PATCH 38/53] nem: multisig --- src/apps/nem/multisig.py | 14 ++++++++++---- src/apps/nem/signing.py | 20 ++++++++++++++++---- tests/test_apps.nem.multisig.py | 11 +++++------ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/apps/nem/multisig.py b/src/apps/nem/multisig.py index 1058cfd8e..c3e51383f 100644 --- a/src/apps/nem/multisig.py +++ b/src/apps/nem/multisig.py @@ -6,8 +6,12 @@ from trezor.crypto import nem async def ask_multisig(ctx, msg: NEMSignTx): - # todo - await require_confirm_action(ctx, 'Multisig?') + address = nem.compute_address(msg.multisig.signer, msg.transaction.network) + if msg.cosigning: + await require_confirm_address(ctx, 'Cosign transaction for', address) + else: + await require_confirm_address(ctx, 'Initiate transaction for', address) + await require_confirm_fee(ctx, 'Confirm multisig fee', msg.multisig.fee) async def ask_aggregate_modification(ctx, msg: NEMSignTx): @@ -38,8 +42,10 @@ def serialize_multisig(msg: NEMTransactionCommon, public_key: bytes, inner: byte return w -def serialize_multisig_signature(msg: NEMTransactionCommon, public_key: bytes, inner: bytes, address: str): - w = write_common(msg, bytearray(public_key), NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE) +def serialize_multisig_signature(common: NEMTransactionCommon, public_key: bytes, + inner: bytes, address_public_key: bytes): + address = nem.compute_address(address_public_key, common.network) + w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE) digest = hashlib.sha3_256(inner).digest(True) write_uint32(w, 4 + len(digest)) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index f56f52857..7ff52a596 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -16,6 +16,7 @@ async def sign_tx(ctx, msg: NEMSignTx): if msg.multisig: public_key = msg.multisig.signer + await ask_multisig(ctx, msg) else: public_key = _get_public_key(node) @@ -35,6 +36,10 @@ async def sign_tx(ctx, msg: NEMSignTx): else: raise ValueError('No transaction provided') + if msg.multisig: + # wrap transaction in multisig wrapper + tx = _multisig(node, msg, tx) + signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) resp = NEMSignedTx() @@ -43,6 +48,16 @@ async def sign_tx(ctx, msg: NEMSignTx): return resp +def _multisig(node, msg: NEMSignTx, inner_tx: bytes) -> bytes: + if msg.cosigning: + return serialize_multisig_signature(msg.multisig, + _get_public_key(node), + inner_tx, + msg.multisig.signer) + else: + return serialize_multisig(msg.multisig, _get_public_key(node), inner_tx) + + def _get_public_key(node) -> bytes: # 0x01 prefix is not part of the actual public key, hence removed return node.public_key()[1:] @@ -75,10 +90,7 @@ async def _supply_change(ctx, public_key: bytes, msg: NEMSignTx): async def _aggregate_modification(ctx, public_key: bytes, msg: NEMSignTx): await ask_aggregate_modification(ctx, msg) - if not msg.multisig: - w = serialize_aggregate_modification(msg, public_key) - else: - w = bytearray() # todo + w = serialize_aggregate_modification(msg, public_key) for m in msg.aggregate_modification.modifications: serialize_cosignatory_modification(w, m.type, m.public_key) diff --git a/tests/test_apps.nem.multisig.py b/tests/test_apps.nem.multisig.py index caa3da37a..e816e6062 100644 --- a/tests/test_apps.nem.multisig.py +++ b/tests/test_apps.nem.multisig.py @@ -29,12 +29,12 @@ class TestNemMultisig(unittest.TestCase): self.assertEqual(multisig, unhexlify("0410000001000098df1a3c002000000059d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e808d5b00000000003f6f3c006c0000000110000001000098df1a3c0020000000abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac0024f400000000003f6f3c0001000000280000000200000020000000e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3")) - address = "TCRXYUQIMFA7AOGL5LF3YWLC7VABLYUMJ5ACBUNL" + address_pubkey = unhexlify("abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac") m = _create_common_msg(NEM_NETWORK_TESTNET, 3939891, 6000000, 3961491) - multisig = serialize_multisig_signature(m, unhexlify("71cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4"), base_tx, address) + multisig = serialize_multisig_signature(m, unhexlify("71cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4"), base_tx, address_pubkey) self.assertEqual(multisig, unhexlify("0210000001000098331e3c002000000071cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4808d5b000000000093723c0024000000200000008ec165580bdabfd31ce6007a1748ce5bdf30eab7a214743097de3bc822ac7e002800000054435258595551494d464137414f474c354c463359574c43375641424c59554d4a35414342554e4c")) @@ -62,16 +62,15 @@ class TestNemMultisig(unittest.TestCase): 59414342, 6000000, 59500742) - address = "NDDRG3UEB5LZZZMDWBE4RTKZK73JBHPAIWBHCFMV" - multisig = serialize_multisig_signature(m, unhexlify("1b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2"), base_tx, address) + address_pubkey = unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a") + multisig = serialize_multisig_signature(m, unhexlify("1b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2"), base_tx, address_pubkey) self.assertEqual(multisig, unhexlify("021000000100006846978a03200000001b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2808d5b0000000000c6e88b032400000020000000bfa2088f7720f89dd4664d650e321dabd02fab61b7355bc88a391a848a49786a280000004e4444524733554542354c5a5a5a4d445742453452544b5a4b37334a424850414957424843464d56")) m = _create_common_msg(NEM_NETWORK_MAINNET, 59414381, 6000000, 59500781) - address = "NDDRG3UEB5LZZZMDWBE4RTKZK73JBHPAIWBHCFMV" - multisig = serialize_multisig_signature(m, unhexlify("7ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87"), base_tx, address) + multisig = serialize_multisig_signature(m, unhexlify("7ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87"), base_tx, address_pubkey) self.assertEqual(multisig, unhexlify("02100000010000686d978a03200000007ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87808d5b0000000000ede88b032400000020000000bfa2088f7720f89dd4664d650e321dabd02fab61b7355bc88a391a848a49786a280000004e4444524733554542354c5a5a5a4d445742453452544b5a4b37334a424850414957424843464d56")) From 32681972f16389910f8374e96f136295802d2995 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 10 Apr 2018 16:01:42 +0200 Subject: [PATCH 39/53] nem: aggregate modification relative_change is int --- tests/test_apps.nem.multisig.aggregate_modification.py | 8 ++++---- tests/test_apps.nem.multisig.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_apps.nem.multisig.aggregate_modification.py b/tests/test_apps.nem.multisig.aggregate_modification.py index 2c3007f9d..01daf6519 100644 --- a/tests/test_apps.nem.multisig.aggregate_modification.py +++ b/tests/test_apps.nem.multisig.aggregate_modification.py @@ -16,7 +16,7 @@ class TestNemMultisigAggregateModification(unittest.TestCase): 22000000, 0, 2, - False) + 0) t = serialize_aggregate_modification(m, unhexlify("462ee976890916e54fa825d26bdd0235f5eb5b6a143c199ab0ae5ee9328e08ce")) serialize_cosignatory_modification(t, 1, unhexlify( @@ -33,7 +33,7 @@ class TestNemMultisigAggregateModification(unittest.TestCase): 40000000, 0, 5, - False) + 0) t = serialize_aggregate_modification(m, unhexlify("f41b99320549741c5cce42d9e4bb836d98c50ed5415d0c3c2912d1bb50e6a0e5")) serialize_cosignatory_modification(t, 1, unhexlify( @@ -57,7 +57,7 @@ class TestNemMultisigAggregateModification(unittest.TestCase): 40000000, 6545854, 4, - True) + 2) t = serialize_aggregate_modification(m, unhexlify("6bf7849c1eec6a2002995cc457dc00c4e29bad5c88de63f51e42dfdcd7b2131d")) serialize_cosignatory_modification(t, 1, unhexlify( @@ -75,7 +75,7 @@ class TestNemMultisigAggregateModification(unittest.TestCase): def _create_msg(network: int, timestamp: int, fee: int, deadline: int, - modifications: int, relative_change: bool): + modifications: int, relative_change: int): m = NEMSignTx() m.transaction = NEMTransactionCommon() m.transaction.network = network diff --git a/tests/test_apps.nem.multisig.py b/tests/test_apps.nem.multisig.py index e816e6062..bd37ef65b 100644 --- a/tests/test_apps.nem.multisig.py +++ b/tests/test_apps.nem.multisig.py @@ -17,7 +17,7 @@ class TestNemMultisig(unittest.TestCase): 16000000, 3960639, 1, - False) + 0) base_tx = serialize_aggregate_modification(m, unhexlify("abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac")) base_tx = serialize_cosignatory_modification(base_tx, 2, unhexlify("e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3")) @@ -84,7 +84,7 @@ def _create_common_msg(network: int, timestamp: int, fee: int, deadline: int): def _create_msg(network: int, timestamp: int, fee: int, deadline: int, - modifications: int, relative_change: bool): + modifications: int, relative_change: int): m = NEMSignTx() m.transaction = _create_common_msg(network, timestamp, fee, deadline) From 8de3cd7cacf57dd40fd6b82559c559c55e3b9687 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 12 Apr 2018 14:53:10 +0200 Subject: [PATCH 40/53] nem: refactored to directories --- src/apps/nem/helpers.py | 3 - src/apps/nem/mosaic/__init__.py | 12 +++ src/apps/nem/mosaic/layout.py | 26 ++++++ .../nem/{mosaic.py => mosaic/serialize.py} | 72 +--------------- src/apps/nem/multisig/__init__.py | 29 +++++++ src/apps/nem/multisig/layout.py | 36 ++++++++ .../{multisig.py => multisig/serialize.py} | 37 +------- src/apps/nem/namespace.py | 29 ------- src/apps/nem/namespace/__init__.py | 7 ++ src/apps/nem/namespace/layout.py | 13 +++ src/apps/nem/namespace/serialize.py | 19 +++++ src/apps/nem/signing.py | 84 ++++--------------- src/apps/nem/transfer/__init__.py | 19 +++++ src/apps/nem/transfer/layout.py | 25 ++++++ .../{transfer.py => transfer/serialize.py} | 71 ++++++++++------ .../test_apps.nem.mosaic.canonicalization.py | 2 +- 16 files changed, 256 insertions(+), 228 deletions(-) create mode 100644 src/apps/nem/mosaic/__init__.py create mode 100644 src/apps/nem/mosaic/layout.py rename src/apps/nem/{mosaic.py => mosaic/serialize.py} (56%) create mode 100644 src/apps/nem/multisig/__init__.py create mode 100644 src/apps/nem/multisig/layout.py rename src/apps/nem/{multisig.py => multisig/serialize.py} (54%) delete mode 100644 src/apps/nem/namespace.py create mode 100644 src/apps/nem/namespace/__init__.py create mode 100644 src/apps/nem/namespace/layout.py create mode 100644 src/apps/nem/namespace/serialize.py create mode 100644 src/apps/nem/transfer/__init__.py create mode 100644 src/apps/nem/transfer/layout.py rename src/apps/nem/{transfer.py => transfer/serialize.py} (59%) diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index 9e619d00e..c7f9f65a1 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -1,7 +1,4 @@ - from micropython import const -from trezor.messages.NEMSignTx import NEMSignTx - NEM_NETWORK_MAINNET = const(0x68) NEM_NETWORK_TESTNET = const(0x98) diff --git a/src/apps/nem/mosaic/__init__.py b/src/apps/nem/mosaic/__init__.py new file mode 100644 index 000000000..ed5c5c9b4 --- /dev/null +++ b/src/apps/nem/mosaic/__init__.py @@ -0,0 +1,12 @@ +from .layout import * +from .serialize import * + + +async def mosaic_creation(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: + await ask_mosaic_creation(ctx, msg) + return serialize_mosaic_creation(msg, public_key) + + +async def supply_change(ctx, public_key: bytes, msg: NEMSignTx): + await ask_mosaic_supply_change(ctx, msg) + return serialize_mosaic_supply_change(msg, public_key) diff --git a/src/apps/nem/mosaic/layout.py b/src/apps/nem/mosaic/layout.py new file mode 100644 index 000000000..55a02f39a --- /dev/null +++ b/src/apps/nem/mosaic/layout.py @@ -0,0 +1,26 @@ +from apps.nem.layout import * +from trezor.messages import NEMSignTx +from trezor.messages import NEMSupplyChangeType + + +async def ask_mosaic_creation(ctx, msg: NEMSignTx): + await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' + + msg.mosaic_creation.definition.namespace + '"?') + await require_confirm_properties(ctx, msg.mosaic_creation.definition) + await require_confirm_fee(ctx, 'Confirm creation fee', msg.mosaic_creation.fee) + + await require_confirm_final(ctx, msg.transaction.fee) + + +async def ask_mosaic_supply_change(ctx, msg: NEMSignTx): + await require_confirm_action(ctx, 'Modify supply for "' + msg.supply_change.mosaic + '" under namespace "' + + msg.supply_change.namespace + '"?') + if msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Decrease: + ask_msg = 'Decrease supply by ' + str(msg.supply_change.delta) + ' whole units?' + elif msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Increase: + ask_msg = 'Increase supply by ' + str(msg.supply_change.delta) + ' whole units?' + else: + raise ValueError('Invalid supply change type') + await require_confirm_action(ctx, ask_msg) + + await require_confirm_final(ctx, msg.transaction.fee) diff --git a/src/apps/nem/mosaic.py b/src/apps/nem/mosaic/serialize.py similarity index 56% rename from src/apps/nem/mosaic.py rename to src/apps/nem/mosaic/serialize.py index 77b370c90..2300923a0 100644 --- a/src/apps/nem/mosaic.py +++ b/src/apps/nem/mosaic/serialize.py @@ -1,30 +1,6 @@ -from .writers import * -from trezor.messages.NEMMosaic import NEMMosaic -from trezor.messages import NEMSupplyChangeType -from apps.nem.layout import * - - -async def ask_mosaic_creation(ctx, msg: NEMSignTx): - await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' - + msg.mosaic_creation.definition.namespace + '"?') - await require_confirm_properties(ctx, msg.mosaic_creation.definition) - await require_confirm_fee(ctx, 'Confirm creation fee', msg.mosaic_creation.fee) - - await require_confirm_final(ctx, msg.transaction.fee) - - -async def ask_mosaic_supply_change(ctx, msg: NEMSignTx): - await require_confirm_action(ctx, 'Modify supply for "' + msg.supply_change.mosaic + '" under namespace "' - + msg.supply_change.namespace + '"?') - if msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Decrease: - ask_msg = 'Decrease supply by ' + str(msg.supply_change.delta) + ' whole units?' - elif msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Increase: - ask_msg = 'Increase supply by ' + str(msg.supply_change.delta) + ' whole units?' - else: - raise ValueError('Invalid supply change type') - await require_confirm_action(ctx, ask_msg) - - await require_confirm_final(ctx, msg.transaction.fee) +from apps.nem.writers import * +from apps.nem.helpers import * +from trezor.messages.NEMSignTx import NEMSignTx def serialize_mosaic_creation(msg: NEMSignTx, public_key: bytes): @@ -96,45 +72,3 @@ def _write_property(w: bytearray, name: str, value): write_uint32(w, 4 + len(name) + 4 + len(value)) write_bytes_with_length(w, bytearray(name)) write_bytes_with_length(w, bytearray(value)) - - -def serialize_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): - identifier_length = 4 + len(namespace) + 4 + len(mosaic) - # indentifier length (u32) + quantity (u64) + identifier size - write_uint32(w, 4 + 8 + identifier_length) - write_uint32(w, identifier_length) - write_bytes_with_length(w, bytearray(namespace)) - write_bytes_with_length(w, bytearray(mosaic)) - write_uint64(w, quantity) - - -def canonicalize_mosaics(mosaics: list): - if len(mosaics) <= 1: - return mosaics - mosaics = merge_mosaics(mosaics) - return sort_mosaics(mosaics) - - -def are_mosaics_equal(a: NEMMosaic, b: NEMMosaic) -> bool: - if a.namespace == b.namespace and a.mosaic == b.mosaic: - return True - return False - - -def merge_mosaics(mosaics: list) -> list: - if not len(mosaics): - return list() - ret = list() - for i in mosaics: - found = False - for k, y in enumerate(ret): - if are_mosaics_equal(i, y): - ret[k].quantity += i.quantity - found = True - if not found: - ret.append(i) - return ret - - -def sort_mosaics(mosaics: list) -> list: - return sorted(mosaics, key=lambda m: (m.namespace, m.mosaic)) diff --git a/src/apps/nem/multisig/__init__.py b/src/apps/nem/multisig/__init__.py new file mode 100644 index 000000000..d31fd25a5 --- /dev/null +++ b/src/apps/nem/multisig/__init__.py @@ -0,0 +1,29 @@ +from .serialize import * +from .layout import * + + +async def ask(ctx, msg: NEMSignTx): + await ask_multisig(ctx, msg) + + +def initiate(public_key, msg: NEMSignTx, inner_tx: bytes) -> bytes: + return serialize_multisig(msg.multisig, public_key, inner_tx) + + +def cosign(public_key, msg: NEMSignTx, inner_tx: bytes) -> bytes: + return serialize_multisig_signature(msg.multisig, + public_key, + inner_tx, + msg.multisig.signer) + + +async def aggregate_modification(ctx, public_key: bytes, msg: NEMSignTx): + await ask_aggregate_modification(ctx, msg) + w = serialize_aggregate_modification(msg, public_key) + + for m in msg.aggregate_modification.modifications: + serialize_cosignatory_modification(w, m.type, m.public_key) + + if msg.aggregate_modification.relative_change: + serialize_minimum_cosignatories(w, msg.aggregate_modification.relative_change) + return w diff --git a/src/apps/nem/multisig/layout.py b/src/apps/nem/multisig/layout.py new file mode 100644 index 000000000..199eca4e7 --- /dev/null +++ b/src/apps/nem/multisig/layout.py @@ -0,0 +1,36 @@ +from apps.nem.layout import * +from trezor.messages import NEMModificationType +from trezor.messages import NEMSignTx +from trezor.crypto import nem + + +async def ask_multisig(ctx, msg: NEMSignTx): + address = nem.compute_address(msg.multisig.signer, msg.transaction.network) + if msg.cosigning: + await require_confirm_address(ctx, 'Cosign transaction for', address) + else: + await require_confirm_address(ctx, 'Initiate transaction for', address) + await require_confirm_fee(ctx, 'Confirm multisig fee', msg.multisig.fee) + + +async def ask_aggregate_modification(ctx, msg: NEMSignTx): + if not msg.multisig: + await require_confirm_action(ctx, 'Convert account to multisig account?') + + for m in msg.aggregate_modification.modifications: + if m.type == NEMModificationType.CosignatoryModification_Add: + action = 'Add' + else: + action = 'Remove' + address = nem.compute_address(m.public_key, msg.transaction.network) + await require_confirm_address(ctx, action + ' cosignatory?', address) + + if msg.aggregate_modification.relative_change: + if not msg.multisig: + action = 'Set minimum cosignatories to ' + else: + action = 'Modify the number of cosignatories by ' + await require_confirm_action(ctx, action + str(msg.aggregate_modification.relative_change) + '?') + + await require_confirm_final(ctx, msg.transaction.fee) + diff --git a/src/apps/nem/multisig.py b/src/apps/nem/multisig/serialize.py similarity index 54% rename from src/apps/nem/multisig.py rename to src/apps/nem/multisig/serialize.py index c3e51383f..6ba49336d 100644 --- a/src/apps/nem/multisig.py +++ b/src/apps/nem/multisig/serialize.py @@ -1,41 +1,10 @@ -from .writers import * -from apps.nem.layout import * +from apps.nem.writers import * +from apps.nem.helpers import * +from trezor.messages.NEMSignTx import NEMSignTx from trezor.crypto import hashlib -from trezor.messages import NEMModificationType from trezor.crypto import nem -async def ask_multisig(ctx, msg: NEMSignTx): - address = nem.compute_address(msg.multisig.signer, msg.transaction.network) - if msg.cosigning: - await require_confirm_address(ctx, 'Cosign transaction for', address) - else: - await require_confirm_address(ctx, 'Initiate transaction for', address) - await require_confirm_fee(ctx, 'Confirm multisig fee', msg.multisig.fee) - - -async def ask_aggregate_modification(ctx, msg: NEMSignTx): - if not msg.multisig: - await require_confirm_action(ctx, 'Convert account to multisig account?') - - for m in msg.aggregate_modification.modifications: - if m.type == NEMModificationType.CosignatoryModification_Add: - action = 'Add' - else: - action = 'Remove' - address = nem.compute_address(m.public_key, msg.transaction.network) - await require_confirm_address(ctx, action + ' cosignatory?', address) - - if msg.aggregate_modification.relative_change: - if not msg.multisig: - action = 'Set minimum cosignatories to ' - else: - action = 'Modify the number of cosignatories by ' - await require_confirm_action(ctx, action + str(msg.aggregate_modification.relative_change) + '?') - - await require_confirm_final(ctx, msg.transaction.fee) - - def serialize_multisig(msg: NEMTransactionCommon, public_key: bytes, inner: bytes): w = write_common(msg, bytearray(public_key), NEM_TRANSACTION_TYPE_MULTISIG) write_bytes_with_length(w, bytearray(inner)) diff --git a/src/apps/nem/namespace.py b/src/apps/nem/namespace.py deleted file mode 100644 index cc720ad90..000000000 --- a/src/apps/nem/namespace.py +++ /dev/null @@ -1,29 +0,0 @@ -from .writers import * -from apps.nem.layout import * - - -async def ask_provision_namespace(ctx, msg: NEMSignTx): - if msg.provision_namespace.parent: - await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"' + - 'under namespace "' + msg.provision_namespace.parent + '"?') - else: - await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"?') - await require_confirm_fee(ctx, 'Confirm rental fee', msg.provision_namespace.fee) - - await require_confirm_final(ctx, msg.transaction.fee) - - -def serialize_provision_namespace(msg: NEMSignTx, public_key: bytes) -> bytearray: - tx = write_common(msg.transaction, - bytearray(public_key), - NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE) - - write_bytes_with_length(tx, bytearray(msg.provision_namespace.sink)) - write_uint64(tx, msg.provision_namespace.fee) - write_bytes_with_length(tx, bytearray(msg.provision_namespace.namespace)) - if msg.provision_namespace.parent: - write_bytes_with_length(tx, bytearray(msg.provision_namespace.parent)) - else: - write_uint32(tx, 0xffffffff) - - return tx diff --git a/src/apps/nem/namespace/__init__.py b/src/apps/nem/namespace/__init__.py new file mode 100644 index 000000000..f6ae3cc70 --- /dev/null +++ b/src/apps/nem/namespace/__init__.py @@ -0,0 +1,7 @@ +from .layout import * +from .serialize import * + + +async def namespace(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: + await ask_provision_namespace(ctx, msg) + return serialize_provision_namespace(msg, public_key) diff --git a/src/apps/nem/namespace/layout.py b/src/apps/nem/namespace/layout.py new file mode 100644 index 000000000..dcaa3d2fe --- /dev/null +++ b/src/apps/nem/namespace/layout.py @@ -0,0 +1,13 @@ +from apps.nem.layout import * +from trezor.messages import NEMSignTx + + +async def ask_provision_namespace(ctx, msg: NEMSignTx): + if msg.provision_namespace.parent: + await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"' + + 'under namespace "' + msg.provision_namespace.parent + '"?') + else: + await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"?') + await require_confirm_fee(ctx, 'Confirm rental fee', msg.provision_namespace.fee) + + await require_confirm_final(ctx, msg.transaction.fee) diff --git a/src/apps/nem/namespace/serialize.py b/src/apps/nem/namespace/serialize.py new file mode 100644 index 000000000..9deeddf33 --- /dev/null +++ b/src/apps/nem/namespace/serialize.py @@ -0,0 +1,19 @@ +from apps.nem.helpers import * +from apps.nem.writers import * +from trezor.messages.NEMSignTx import NEMSignTx + + +def serialize_provision_namespace(msg: NEMSignTx, public_key: bytes) -> bytearray: + tx = write_common(msg.transaction, + bytearray(public_key), + NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE) + + write_bytes_with_length(tx, bytearray(msg.provision_namespace.sink)) + write_uint64(tx, msg.provision_namespace.fee) + write_bytes_with_length(tx, bytearray(msg.provision_namespace.namespace)) + if msg.provision_namespace.parent: + write_bytes_with_length(tx, bytearray(msg.provision_namespace.parent)) + else: + write_uint32(tx, 0xffffffff) + + return tx diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 7ff52a596..d96b49456 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -1,9 +1,9 @@ -from apps.nem.transfer import * -from apps.nem.multisig import * -from apps.nem.namespace import * -from apps.nem.mosaic import * +from apps.nem import namespace +from apps.nem import transfer +from apps.nem import mosaic +from apps.nem import multisig from apps.nem.validators import validate -from apps.nem import helpers +from apps.nem.helpers import * from apps.common import seed from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMSignedTx import NEMSignedTx @@ -16,31 +16,33 @@ async def sign_tx(ctx, msg: NEMSignTx): if msg.multisig: public_key = msg.multisig.signer - await ask_multisig(ctx, msg) + await multisig.ask(ctx, msg) else: public_key = _get_public_key(node) if msg.transfer: - msg.transfer.mosaics = canonicalize_mosaics(msg.transfer.mosaics) - tx = await _transfer(ctx, public_key, msg, node) + tx = await transfer.transfer(ctx, public_key, msg, node) elif msg.provision_namespace: - tx = await _provision_namespace(ctx, public_key, msg) + tx = await namespace.namespace(ctx, public_key, msg) elif msg.mosaic_creation: - tx = await _mosaic_creation(ctx, public_key, msg) + tx = await mosaic.mosaic_creation(ctx, public_key, msg) elif msg.supply_change: - tx = await _supply_change(ctx, public_key, msg) + tx = await mosaic.supply_change(ctx, public_key, msg) elif msg.aggregate_modification: - tx = await _aggregate_modification(ctx, public_key, msg) + tx = await multisig.aggregate_modification(ctx, public_key, msg) elif msg.importance_transfer: - tx = await _importance_transfer(ctx, public_key, msg) + tx = await transfer.importance_transfer(ctx, public_key, msg) else: raise ValueError('No transaction provided') if msg.multisig: # wrap transaction in multisig wrapper - tx = _multisig(node, msg, tx) + if msg.cosigning: + tx = multisig.cosign(_get_public_key(node), msg, tx) + else: + tx = multisig.initiate(_get_public_key(node), msg, tx) - signature = ed25519.sign(node.private_key(), tx, helpers.NEM_HASH_ALG) + signature = ed25519.sign(node.private_key(), tx, NEM_HASH_ALG) resp = NEMSignedTx() resp.data = tx @@ -48,58 +50,6 @@ async def sign_tx(ctx, msg: NEMSignTx): return resp -def _multisig(node, msg: NEMSignTx, inner_tx: bytes) -> bytes: - if msg.cosigning: - return serialize_multisig_signature(msg.multisig, - _get_public_key(node), - inner_tx, - msg.multisig.signer) - else: - return serialize_multisig(msg.multisig, _get_public_key(node), inner_tx) - - def _get_public_key(node) -> bytes: # 0x01 prefix is not part of the actual public key, hence removed return node.public_key()[1:] - - -async def _transfer(ctx, public_key: bytes, msg: NEMSignTx, node) -> bytes: - payload, encrypted = get_transfer_payload(msg, node) - await ask_transfer(ctx, msg, payload, encrypted) - - w = serialize_transfer(msg, public_key, payload, encrypted) - for mosaic in msg.transfer.mosaics: - serialize_mosaic(w, mosaic.namespace, mosaic.mosaic, mosaic.quantity) - return w - - -async def _provision_namespace(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: - await ask_provision_namespace(ctx, msg) - return serialize_provision_namespace(msg, public_key) - - -async def _mosaic_creation(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: - await ask_mosaic_creation(ctx, msg) - return serialize_mosaic_creation(msg, public_key) - - -async def _supply_change(ctx, public_key: bytes, msg: NEMSignTx): - await ask_mosaic_supply_change(ctx, msg) - return serialize_mosaic_supply_change(msg, public_key) - - -async def _aggregate_modification(ctx, public_key: bytes, msg: NEMSignTx): - await ask_aggregate_modification(ctx, msg) - w = serialize_aggregate_modification(msg, public_key) - - for m in msg.aggregate_modification.modifications: - serialize_cosignatory_modification(w, m.type, m.public_key) - - if msg.aggregate_modification.relative_change: - serialize_minimum_cosignatories(w, msg.aggregate_modification.relative_change) - return w - - -async def _importance_transfer(ctx, public_key: bytes, msg: NEMSignTx): - await ask_importance_transfer(ctx, msg) - return serialize_importance_transfer(msg, public_key) diff --git a/src/apps/nem/transfer/__init__.py b/src/apps/nem/transfer/__init__.py new file mode 100644 index 000000000..7c9cb0d91 --- /dev/null +++ b/src/apps/nem/transfer/__init__.py @@ -0,0 +1,19 @@ +from .layout import * +from .serialize import * + + +async def transfer(ctx, public_key: bytes, msg: NEMSignTx, node): + msg.transfer.mosaics = canonicalize_mosaics(msg.transfer.mosaics) + + payload, encrypted = get_transfer_payload(msg, node) + await ask_transfer(ctx, msg, payload, encrypted) + + w = serialize_transfer(msg, public_key, payload, encrypted) + for mosaic in msg.transfer.mosaics: + serialize_mosaic(w, mosaic.namespace, mosaic.mosaic, mosaic.quantity) + return w + + +async def importance_transfer(ctx, public_key: bytes, msg: NEMSignTx): + await ask_importance_transfer(ctx, msg) + return serialize_importance_transfer(msg, public_key) diff --git a/src/apps/nem/transfer/layout.py b/src/apps/nem/transfer/layout.py new file mode 100644 index 000000000..bf95c50d2 --- /dev/null +++ b/src/apps/nem/transfer/layout.py @@ -0,0 +1,25 @@ +from apps.nem.layout import * +from trezor.messages import NEMImportanceTransferMode +from trezor.messages import NEMSignTx + + +async def ask_transfer(ctx, msg: NEMSignTx, payload, encrypted): + if payload: + await require_confirm_payload(ctx, msg.transfer.payload, encrypted) + + for mosaic in msg.transfer.mosaics: + await require_confirm_action(ctx, 'Confirm transfer of ' + str(mosaic.quantity) + + ' raw units of ' + mosaic.namespace + '.' + mosaic.mosaic) + + await require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount) + + await require_confirm_final(ctx, msg.transaction.fee) + + +async def ask_importance_transfer(ctx, msg: NEMSignTx): + if msg.importance_transfer.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: + m = 'Activate' + else: + m = 'Deactivate' + await require_confirm_action(ctx, m + ' remote harvesting?') + await require_confirm_final(ctx, msg.transaction.fee) diff --git a/src/apps/nem/transfer.py b/src/apps/nem/transfer/serialize.py similarity index 59% rename from src/apps/nem/transfer.py rename to src/apps/nem/transfer/serialize.py index 81b87a399..6bc7986cf 100644 --- a/src/apps/nem/transfer.py +++ b/src/apps/nem/transfer/serialize.py @@ -1,31 +1,10 @@ -from .writers import * -from apps.nem.layout import * -from trezor.messages import NEMImportanceTransferMode +from apps.nem.writers import * +from apps.nem.helpers import * +from trezor.messages.NEMMosaic import NEMMosaic +from trezor.messages.NEMSignTx import NEMSignTx from trezor.crypto import random -async def ask_transfer(ctx, msg: NEMSignTx, payload, encrypted): - if payload: - await require_confirm_payload(ctx, msg.transfer.payload, encrypted) - - for mosaic in msg.transfer.mosaics: - await require_confirm_action(ctx, 'Confirm transfer of ' + str(mosaic.quantity) + - ' raw units of ' + mosaic.namespace + '.' + mosaic.mosaic) - - await require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount) - - await require_confirm_final(ctx, msg.transaction.fee) - - -async def ask_importance_transfer(ctx, msg: NEMSignTx): - if msg.importance_transfer.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: - m = 'Activate' - else: - m = 'Deactivate' - await require_confirm_action(ctx, m + ' remote harvesting?') - await require_confirm_final(ctx, msg.transaction.fee) - - def serialize_transfer(msg: NEMSignTx, public_key: bytes, payload: bytes=None, encrypted: bool=False) -> bytearray: tx = write_common(msg.transaction, bytearray(public_key), @@ -52,6 +31,16 @@ def serialize_transfer(msg: NEMSignTx, public_key: bytes, payload: bytes=None, e return tx +def serialize_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): + identifier_length = 4 + len(namespace) + 4 + len(mosaic) + # indentifier length (u32) + quantity (u64) + identifier size + write_uint32(w, 4 + 8 + identifier_length) + write_uint32(w, identifier_length) + write_bytes_with_length(w, bytearray(namespace)) + write_bytes_with_length(w, bytearray(mosaic)) + write_uint64(w, quantity) + + def serialize_importance_transfer(msg: NEMSignTx, public_key: bytes) -> bytearray: w = write_common(msg.transaction, bytearray(public_key), NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER) @@ -83,3 +72,35 @@ def _get_version(network, mosaics=None) -> int: if mosaics: return network << 24 | 2 return network << 24 | 1 + + +def canonicalize_mosaics(mosaics: list): + if len(mosaics) <= 1: + return mosaics + mosaics = merge_mosaics(mosaics) + return sort_mosaics(mosaics) + + +def are_mosaics_equal(a: NEMMosaic, b: NEMMosaic) -> bool: + if a.namespace == b.namespace and a.mosaic == b.mosaic: + return True + return False + + +def merge_mosaics(mosaics: list) -> list: + if not len(mosaics): + return list() + ret = list() + for i in mosaics: + found = False + for k, y in enumerate(ret): + if are_mosaics_equal(i, y): + ret[k].quantity += i.quantity + found = True + if not found: + ret.append(i) + return ret + + +def sort_mosaics(mosaics: list) -> list: + return sorted(mosaics, key=lambda m: (m.namespace, m.mosaic)) diff --git a/tests/test_apps.nem.mosaic.canonicalization.py b/tests/test_apps.nem.mosaic.canonicalization.py index 3de8ab89f..01c95f1c4 100644 --- a/tests/test_apps.nem.mosaic.canonicalization.py +++ b/tests/test_apps.nem.mosaic.canonicalization.py @@ -1,5 +1,5 @@ from common import * -from apps.nem.mosaic import * +from apps.nem.transfer import * class TestNemMosaicCanonicalization(unittest.TestCase): From 85c904cbf725f0d92cf9bfcb2312c87ab47e044a Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 12 Apr 2018 14:59:31 +0200 Subject: [PATCH 41/53] nem: layout refactoring and fixes --- src/apps/nem/layout.py | 125 +++---------------------------- src/apps/nem/mosaic/__init__.py | 2 +- src/apps/nem/mosaic/layout.py | 98 ++++++++++++++++++++++-- src/apps/nem/multisig/layout.py | 17 +++-- src/apps/nem/namespace/layout.py | 12 ++- src/apps/nem/transfer/layout.py | 43 +++++++++-- 6 files changed, 161 insertions(+), 136 deletions(-) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index ea82683a8..83f525b75 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -1,44 +1,26 @@ from apps.common.confirm import * from trezor import ui from trezor.messages import ButtonRequestType -from trezor.messages.NEMMosaicDefinition import NEMMosaicDefinition -from trezor.messages import NEMMosaicLevy from trezor.ui.text import Text -from trezor.ui.scroll import Scrollpage, animate_swipe, paginate from trezor.utils import chunks, format_amount, split_words from .helpers import * -async def require_confirm_action(ctx, action: str): - content = Text('Confirm action', ui.ICON_SEND, - ui.NORMAL, *split_words(action, 18), - icon_color=ui.GREEN) - await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) +async def require_confirm_text(ctx, action: str): + await require_confirm_content(ctx, 'Confirm action', split_words(action, 18)) async def require_confirm_fee(ctx, action: str, fee: int): - content = Text('Confirm fee', ui.ICON_SEND, - ui.NORMAL, action, - ui.BOLD, format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM', - icon_color=ui.GREEN) - await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) - - -async def require_confirm_transfer(ctx, recipient, value): - content = Text('Confirm transfer', ui.ICON_SEND, - ui.BOLD, 'Send ' + format_amount(value, NEM_MAX_DIVISIBILITY) + ' XEM', - ui.NORMAL, 'to', - ui.MONO, *split_address(recipient), - icon_color=ui.GREEN) - await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + content = [ui.NORMAL, action, + ui.BOLD, format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM'] + await require_confirm_content(ctx, 'Confirm fee', content) -async def require_confirm_address(ctx, action: str, address: str): - content = Text('Confirm address', ui.ICON_SEND, - ui.NORMAL, action, - ui.MONO, *split_address(address), - icon_color=ui.GREEN) - await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) +async def require_confirm_content(ctx, headline: str, content: []): + text = Text(headline, ui.ICON_SEND, + *content, + icon_color=ui.GREEN) + await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput) async def require_confirm_final(ctx, fee: int): @@ -50,92 +32,5 @@ async def require_confirm_final(ctx, fee: int): await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 -async def require_confirm_payload(ctx, payload: bytes, encrypt=False): - payload = str(payload, 'utf-8') - - if len(payload) > 48: - payload = payload[:48] + '..' - if encrypt: - content = Text('Confirm payload', ui.ICON_SEND, - ui.BOLD, 'Encrypted:', - ui.NORMAL, *split_words(payload, 22), - icon_color=ui.GREEN) - else: - content = Text('Confirm payload', ui.ICON_SEND, - ui.BOLD, 'Unencrypted:', - ui.NORMAL, *split_words(payload, 22), - icon_color=ui.RED) - await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) - - -async def require_confirm_properties(ctx, definition: NEMMosaicDefinition): - properties = _get_mosaic_properties(definition) - first_page = const(0) - paginator = paginate(_show_page, len(properties), first_page, properties) - await ctx.wait(paginator) - - -@ui.layout -async def _show_page(page: int, page_count: int, content): - content = Scrollpage(content[page], page, page_count) - if page + 1 == page_count: - await ConfirmDialog(content) - else: - content.render() - await animate_swipe() - - -def _get_mosaic_properties(definition: NEMMosaicDefinition): - properties = [] - if definition.description: - t = Text('Confirm properties', ui.ICON_SEND, - ui.BOLD, 'Description:', - ui.NORMAL, definition.description) - properties.append(t) - if definition.transferable: - transferable = 'Yes' - else: - transferable = 'No' - t = Text('Confirm properties', ui.ICON_SEND, - ui.BOLD, 'Transferable?', - ui.NORMAL, transferable) - properties.append(t) - if definition.mutable_supply: - imm = 'mutable' - else: - imm = 'immutable' - if definition.supply: - t = Text('Confirm properties', ui.ICON_SEND, - ui.BOLD, 'Initial supply:', - ui.NORMAL, str(definition.supply), - ui.NORMAL, imm) - else: - t = Text('Confirm properties', ui.ICON_SEND, - ui.BOLD, 'Initial supply:', - ui.NORMAL, imm) - properties.append(t) - if definition.levy: - t = Text('Confirm properties', ui.ICON_SEND, - ui.BOLD, 'Levy recipient:', - ui.MONO, *split_address(definition.levy_address)) - properties.append(t) - t = Text('Confirm properties', ui.ICON_SEND, - ui.BOLD, 'Levy namespace:', - ui.NORMAL, definition.levy_namespace, - ui.BOLD, 'Levy mosaic:', - ui.NORMAL, definition.levy_mosaic) - properties.append(t) - if definition.levy == NEMMosaicLevy.MosaicLevy_Absolute: - levy_type = 'absolute' - else: - levy_type = 'percentile' - t = Text('Confirm properties', ui.ICON_SEND, - ui.BOLD, 'Levy type:', - ui.NORMAL, levy_type) - properties.append(t) - - return properties - - def split_address(data): return chunks(data, 17) diff --git a/src/apps/nem/mosaic/__init__.py b/src/apps/nem/mosaic/__init__.py index ed5c5c9b4..04010fb6c 100644 --- a/src/apps/nem/mosaic/__init__.py +++ b/src/apps/nem/mosaic/__init__.py @@ -8,5 +8,5 @@ async def mosaic_creation(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: async def supply_change(ctx, public_key: bytes, msg: NEMSignTx): - await ask_mosaic_supply_change(ctx, msg) + await ask_supply_change(ctx, msg) return serialize_mosaic_supply_change(msg, public_key) diff --git a/src/apps/nem/mosaic/layout.py b/src/apps/nem/mosaic/layout.py index 55a02f39a..1e3506976 100644 --- a/src/apps/nem/mosaic/layout.py +++ b/src/apps/nem/mosaic/layout.py @@ -1,26 +1,110 @@ from apps.nem.layout import * from trezor.messages import NEMSignTx from trezor.messages import NEMSupplyChangeType +from trezor.messages import NEMMosaicDefinition +from trezor.messages import NEMMosaicLevy +from trezor.ui.scroll import Scrollpage, animate_swipe, paginate async def ask_mosaic_creation(ctx, msg: NEMSignTx): - await require_confirm_action(ctx, 'Create mosaic "' + msg.mosaic_creation.definition.mosaic + '" under namespace "' - + msg.mosaic_creation.definition.namespace + '"?') - await require_confirm_properties(ctx, msg.mosaic_creation.definition) + await require_confirm_content(ctx, 'Create mosaic', _creation_message(msg.mosaic_creation)) + await _require_confirm_properties(ctx, msg.mosaic_creation.definition) await require_confirm_fee(ctx, 'Confirm creation fee', msg.mosaic_creation.fee) await require_confirm_final(ctx, msg.transaction.fee) -async def ask_mosaic_supply_change(ctx, msg: NEMSignTx): - await require_confirm_action(ctx, 'Modify supply for "' + msg.supply_change.mosaic + '" under namespace "' - + msg.supply_change.namespace + '"?') +async def ask_supply_change(ctx, msg: NEMSignTx): + await require_confirm_content(ctx, 'Supply change', _supply_message(msg.supply_change)) if msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Decrease: ask_msg = 'Decrease supply by ' + str(msg.supply_change.delta) + ' whole units?' elif msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Increase: ask_msg = 'Increase supply by ' + str(msg.supply_change.delta) + ' whole units?' else: raise ValueError('Invalid supply change type') - await require_confirm_action(ctx, ask_msg) + await require_confirm_text(ctx, ask_msg) await require_confirm_final(ctx, msg.transaction.fee) + + +def _creation_message(mosaic_creation): + return [ui.NORMAL, 'Create mosaic', + ui.BOLD, mosaic_creation.definition.mosaic, + ui.NORMAL, 'under namespace', + ui.BOLD, mosaic_creation.definition.namespace] + + +def _supply_message(supply_change): + return [ui.NORMAL, 'Modify supply for', + ui.BOLD, supply_change.mosaic, + ui.NORMAL, 'under namespace', + ui.BOLD, supply_change.namespace] + + +async def _require_confirm_properties(ctx, definition: NEMMosaicDefinition): + properties = _get_mosaic_properties(definition) + first_page = const(0) + paginator = paginate(_show_page, len(properties), first_page, properties) + await ctx.wait(paginator) + + +@ui.layout +async def _show_page(page: int, page_count: int, content): + content = Scrollpage(content[page], page, page_count) + if page + 1 == page_count: + await ConfirmDialog(content) + else: + content.render() + await animate_swipe() + + +def _get_mosaic_properties(definition: NEMMosaicDefinition): + properties = [] + if definition.description: + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Description:', + ui.NORMAL, definition.description) + properties.append(t) + if definition.transferable: + transferable = 'Yes' + else: + transferable = 'No' + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Transferable?', + ui.NORMAL, transferable) + properties.append(t) + if definition.mutable_supply: + imm = 'mutable' + else: + imm = 'immutable' + if definition.supply: + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Initial supply:', + ui.NORMAL, str(definition.supply), + ui.NORMAL, imm) + else: + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Initial supply:', + ui.NORMAL, imm) + properties.append(t) + if definition.levy: + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Levy recipient:', + ui.MONO, *split_address(definition.levy_address)) + properties.append(t) + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Levy namespace:', + ui.NORMAL, definition.levy_namespace, + ui.BOLD, 'Levy mosaic:', + ui.NORMAL, definition.levy_mosaic) + properties.append(t) + if definition.levy == NEMMosaicLevy.MosaicLevy_Absolute: + levy_type = 'absolute' + else: + levy_type = 'percentile' + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Levy type:', + ui.NORMAL, levy_type) + properties.append(t) + + return properties diff --git a/src/apps/nem/multisig/layout.py b/src/apps/nem/multisig/layout.py index 199eca4e7..1615866b6 100644 --- a/src/apps/nem/multisig/layout.py +++ b/src/apps/nem/multisig/layout.py @@ -7,15 +7,15 @@ from trezor.crypto import nem async def ask_multisig(ctx, msg: NEMSignTx): address = nem.compute_address(msg.multisig.signer, msg.transaction.network) if msg.cosigning: - await require_confirm_address(ctx, 'Cosign transaction for', address) + await _require_confirm_address(ctx, 'Cosign transaction for', address) else: - await require_confirm_address(ctx, 'Initiate transaction for', address) + await _require_confirm_address(ctx, 'Initiate transaction for', address) await require_confirm_fee(ctx, 'Confirm multisig fee', msg.multisig.fee) async def ask_aggregate_modification(ctx, msg: NEMSignTx): if not msg.multisig: - await require_confirm_action(ctx, 'Convert account to multisig account?') + await require_confirm_text(ctx, 'Convert account to multisig account?') for m in msg.aggregate_modification.modifications: if m.type == NEMModificationType.CosignatoryModification_Add: @@ -23,14 +23,21 @@ async def ask_aggregate_modification(ctx, msg: NEMSignTx): else: action = 'Remove' address = nem.compute_address(m.public_key, msg.transaction.network) - await require_confirm_address(ctx, action + ' cosignatory?', address) + await _require_confirm_address(ctx, action + ' cosignatory', address) if msg.aggregate_modification.relative_change: if not msg.multisig: action = 'Set minimum cosignatories to ' else: action = 'Modify the number of cosignatories by ' - await require_confirm_action(ctx, action + str(msg.aggregate_modification.relative_change) + '?') + await require_confirm_text(ctx, action + str(msg.aggregate_modification.relative_change) + '?') await require_confirm_final(ctx, msg.transaction.fee) + +async def _require_confirm_address(ctx, action: str, address: str): + content = Text('Confirm address', ui.ICON_SEND, + ui.NORMAL, action, + ui.MONO, *split_address(address), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) diff --git a/src/apps/nem/namespace/layout.py b/src/apps/nem/namespace/layout.py index dcaa3d2fe..06fa78e6c 100644 --- a/src/apps/nem/namespace/layout.py +++ b/src/apps/nem/namespace/layout.py @@ -4,10 +4,16 @@ from trezor.messages import NEMSignTx async def ask_provision_namespace(ctx, msg: NEMSignTx): if msg.provision_namespace.parent: - await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"' + - 'under namespace "' + msg.provision_namespace.parent + '"?') + content = [ui.NORMAL, 'Create namespace', + ui.BOLD, msg.provision_namespace.namespace, + ui.NORMAL, 'under namespace', + ui.BOLD, msg.provision_namespace.parent] + await require_confirm_content(ctx, 'Confirm namespace', content) else: - await require_confirm_action(ctx, 'Create namespace "' + msg.provision_namespace.namespace + '"?') + content = [ui.NORMAL, 'Create namespace', + ui.BOLD, msg.provision_namespace.namespace] + await require_confirm_content(ctx, 'Confirm namespace', content) + await require_confirm_fee(ctx, 'Confirm rental fee', msg.provision_namespace.fee) await require_confirm_final(ctx, msg.transaction.fee) diff --git a/src/apps/nem/transfer/layout.py b/src/apps/nem/transfer/layout.py index bf95c50d2..ed4008662 100644 --- a/src/apps/nem/transfer/layout.py +++ b/src/apps/nem/transfer/layout.py @@ -5,13 +5,12 @@ from trezor.messages import NEMSignTx async def ask_transfer(ctx, msg: NEMSignTx, payload, encrypted): if payload: - await require_confirm_payload(ctx, msg.transfer.payload, encrypted) + await _require_confirm_payload(ctx, msg.transfer.payload, encrypted) for mosaic in msg.transfer.mosaics: - await require_confirm_action(ctx, 'Confirm transfer of ' + str(mosaic.quantity) + - ' raw units of ' + mosaic.namespace + '.' + mosaic.mosaic) + await require_confirm_content(ctx, 'Confirm mosaic', _mosaics_message(mosaic)) - await require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount) + await _require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount) await require_confirm_final(ctx, msg.transaction.fee) @@ -21,5 +20,39 @@ async def ask_importance_transfer(ctx, msg: NEMSignTx): m = 'Activate' else: m = 'Deactivate' - await require_confirm_action(ctx, m + ' remote harvesting?') + await require_confirm_text(ctx, m + ' remote harvesting?') await require_confirm_final(ctx, msg.transaction.fee) + + +async def _require_confirm_transfer(ctx, recipient, value): + content = Text('Confirm transfer', ui.ICON_SEND, + ui.BOLD, 'Send ' + format_amount(value, NEM_MAX_DIVISIBILITY) + ' XEM', + ui.NORMAL, 'to', + ui.MONO, *split_address(recipient), + icon_color=ui.GREEN) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +async def _require_confirm_payload(ctx, payload: bytes, encrypt=False): + payload = str(payload, 'utf-8') + + if len(payload) > 48: + payload = payload[:48] + '..' + if encrypt: + content = Text('Confirm payload', ui.ICON_SEND, + ui.BOLD, 'Encrypted:', + ui.NORMAL, *split_words(payload, 22), + icon_color=ui.GREEN) + else: + content = Text('Confirm payload', ui.ICON_SEND, + ui.BOLD, 'Unencrypted:', + ui.NORMAL, *split_words(payload, 22), + icon_color=ui.RED) + await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) + + +def _mosaics_message(mosaic): + return [ui.NORMAL, 'Confirm transfer of', + ui.BOLD, str(mosaic.quantity) + ' raw units', + ui.NORMAL, 'of', + ui.BOLD, mosaic.namespace + '.' + mosaic.mosaic] From 9a52039b25fe627a8fb5a81f247423d7aacee37a Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 13 Apr 2018 12:12:24 +0200 Subject: [PATCH 42/53] nem/layout: trim and levy fee --- src/apps/nem/layout.py | 6 ++++++ src/apps/nem/mosaic/layout.py | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index 83f525b75..d64a1612f 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -34,3 +34,9 @@ async def require_confirm_final(ctx, fee: int): def split_address(data): return chunks(data, 17) + + +def trim(payload: str, length: int) -> str: + if len(payload) > length: + return payload[:length] + '..' + return payload diff --git a/src/apps/nem/mosaic/layout.py b/src/apps/nem/mosaic/layout.py index 1e3506976..a385c1886 100644 --- a/src/apps/nem/mosaic/layout.py +++ b/src/apps/nem/mosaic/layout.py @@ -63,7 +63,7 @@ def _get_mosaic_properties(definition: NEMMosaicDefinition): if definition.description: t = Text('Confirm properties', ui.ICON_SEND, ui.BOLD, 'Description:', - ui.NORMAL, definition.description) + ui.NORMAL, *split_words(trim(definition.description, 70), 22)) properties.append(t) if definition.transferable: transferable = 'Yes' @@ -92,6 +92,12 @@ def _get_mosaic_properties(definition: NEMMosaicDefinition): ui.BOLD, 'Levy recipient:', ui.MONO, *split_address(definition.levy_address)) properties.append(t) + t = Text('Confirm properties', ui.ICON_SEND, + ui.BOLD, 'Levy fee:', + ui.NORMAL, str(definition.fee), + ui.BOLD, 'Levy divisibility:', + ui.NORMAL, str(definition.divisibility)) + properties.append(t) t = Text('Confirm properties', ui.ICON_SEND, ui.BOLD, 'Levy namespace:', ui.NORMAL, definition.levy_namespace, From 1355b19c77db06fbd15af80dff9483443d92cbd6 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 16 Apr 2018 10:36:02 +0200 Subject: [PATCH 43/53] nem: multisig correct serialization fix --- src/apps/nem/mosaic/serialize.py | 10 ++++++++-- src/apps/nem/multisig/__init__.py | 10 +++++----- src/apps/nem/multisig/layout.py | 2 +- src/apps/nem/multisig/serialize.py | 13 ++++++++----- src/apps/nem/namespace/serialize.py | 5 ++++- src/apps/nem/signing.py | 4 ++-- src/apps/nem/transfer/serialize.py | 12 +++++++++--- src/apps/nem/validators.py | 14 +++++++------- 8 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/apps/nem/mosaic/serialize.py b/src/apps/nem/mosaic/serialize.py index 2300923a0..0eeb34012 100644 --- a/src/apps/nem/mosaic/serialize.py +++ b/src/apps/nem/mosaic/serialize.py @@ -4,7 +4,10 @@ from trezor.messages.NEMSignTx import NEMSignTx def serialize_mosaic_creation(msg: NEMSignTx, public_key: bytes): - w = write_common(msg.transaction, bytearray(public_key), NEM_TRANSACTION_TYPE_MOSAIC_CREATION) + common = msg.transaction + if msg.multisig: + common = msg.multisig + w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_MOSAIC_CREATION) mosaics_w = bytearray() write_bytes_with_length(mosaics_w, bytearray(public_key)) @@ -42,7 +45,10 @@ def serialize_mosaic_creation(msg: NEMSignTx, public_key: bytes): def serialize_mosaic_supply_change(msg: NEMSignTx, public_key: bytes): - w = write_common(msg.transaction, bytearray(public_key), NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE) + common = msg.transaction + if msg.multisig: + common = msg.multisig + w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE) identifier_length = 4 + len(msg.supply_change.namespace) + 4 + len(msg.supply_change.mosaic) write_uint32(w, identifier_length) diff --git a/src/apps/nem/multisig/__init__.py b/src/apps/nem/multisig/__init__.py index d31fd25a5..bbcde9cc0 100644 --- a/src/apps/nem/multisig/__init__.py +++ b/src/apps/nem/multisig/__init__.py @@ -6,15 +6,15 @@ async def ask(ctx, msg: NEMSignTx): await ask_multisig(ctx, msg) -def initiate(public_key, msg: NEMSignTx, inner_tx: bytes) -> bytes: - return serialize_multisig(msg.multisig, public_key, inner_tx) +def initiate(public_key, common: NEMTransactionCommon, inner_tx: bytes) -> bytes: + return serialize_multisig(common, public_key, inner_tx) -def cosign(public_key, msg: NEMSignTx, inner_tx: bytes) -> bytes: - return serialize_multisig_signature(msg.multisig, +def cosign(public_key, common: NEMTransactionCommon, inner_tx: bytes, signer: bytes) -> bytes: + return serialize_multisig_signature(common, public_key, inner_tx, - msg.multisig.signer) + signer) async def aggregate_modification(ctx, public_key: bytes, msg: NEMSignTx): diff --git a/src/apps/nem/multisig/layout.py b/src/apps/nem/multisig/layout.py index 1615866b6..f7251f4e8 100644 --- a/src/apps/nem/multisig/layout.py +++ b/src/apps/nem/multisig/layout.py @@ -10,7 +10,7 @@ async def ask_multisig(ctx, msg: NEMSignTx): await _require_confirm_address(ctx, 'Cosign transaction for', address) else: await _require_confirm_address(ctx, 'Initiate transaction for', address) - await require_confirm_fee(ctx, 'Confirm multisig fee', msg.multisig.fee) + await require_confirm_fee(ctx, 'Confirm multisig fee', msg.transaction.fee) async def ask_aggregate_modification(ctx, msg: NEMSignTx): diff --git a/src/apps/nem/multisig/serialize.py b/src/apps/nem/multisig/serialize.py index 6ba49336d..ca23ae5d5 100644 --- a/src/apps/nem/multisig/serialize.py +++ b/src/apps/nem/multisig/serialize.py @@ -5,8 +5,8 @@ from trezor.crypto import hashlib from trezor.crypto import nem -def serialize_multisig(msg: NEMTransactionCommon, public_key: bytes, inner: bytes): - w = write_common(msg, bytearray(public_key), NEM_TRANSACTION_TYPE_MULTISIG) +def serialize_multisig(common: NEMTransactionCommon, public_key: bytes, inner: bytes): + w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_MULTISIG) write_bytes_with_length(w, bytearray(inner)) return w @@ -24,11 +24,14 @@ def serialize_multisig_signature(common: NEMTransactionCommon, public_key: bytes def serialize_aggregate_modification(msg: NEMSignTx, public_key: bytes): - version = msg.transaction.network << 24 | 1 + common = msg.transaction + if msg.multisig: + common = msg.multisig + version = common.network << 24 | 1 if msg.aggregate_modification.relative_change: - version = msg.transaction.network << 24 | 2 + version = common.network << 24 | 2 - w = write_common(msg.transaction, + w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, version) diff --git a/src/apps/nem/namespace/serialize.py b/src/apps/nem/namespace/serialize.py index 9deeddf33..a60c1e106 100644 --- a/src/apps/nem/namespace/serialize.py +++ b/src/apps/nem/namespace/serialize.py @@ -4,7 +4,10 @@ from trezor.messages.NEMSignTx import NEMSignTx def serialize_provision_namespace(msg: NEMSignTx, public_key: bytes) -> bytearray: - tx = write_common(msg.transaction, + common = msg.transaction + if msg.multisig: + common = msg.multisig + tx = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index d96b49456..9395bc678 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -38,9 +38,9 @@ async def sign_tx(ctx, msg: NEMSignTx): if msg.multisig: # wrap transaction in multisig wrapper if msg.cosigning: - tx = multisig.cosign(_get_public_key(node), msg, tx) + tx = multisig.cosign(_get_public_key(node), msg.transaction, tx, msg.multisig.signer) else: - tx = multisig.initiate(_get_public_key(node), msg, tx) + tx = multisig.initiate(_get_public_key(node), msg.transaction, tx) signature = ed25519.sign(node.private_key(), tx, NEM_HASH_ALG) diff --git a/src/apps/nem/transfer/serialize.py b/src/apps/nem/transfer/serialize.py index 6bc7986cf..9002a9ffd 100644 --- a/src/apps/nem/transfer/serialize.py +++ b/src/apps/nem/transfer/serialize.py @@ -6,10 +6,13 @@ from trezor.crypto import random def serialize_transfer(msg: NEMSignTx, public_key: bytes, payload: bytes=None, encrypted: bool=False) -> bytearray: - tx = write_common(msg.transaction, + common = msg.transaction + if msg.multisig: + common = msg.multisig + tx = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_TRANSFER, - _get_version(msg.transaction.network, msg.transfer.mosaics)) + _get_version(common.network, msg.transfer.mosaics)) write_bytes_with_length(tx, bytearray(msg.transfer.recipient)) write_uint64(tx, msg.transfer.amount) @@ -42,7 +45,10 @@ def serialize_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): def serialize_importance_transfer(msg: NEMSignTx, public_key: bytes) -> bytearray: - w = write_common(msg.transaction, bytearray(public_key), NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER) + common = msg.transaction + if msg.multisig: + common = msg.multisig + w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER) write_uint32(w, msg.importance_transfer.mode) write_bytes_with_length(w, bytearray(msg.importance_transfer.public_key)) diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index 1ac4502e5..e7f8cc439 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -20,6 +20,7 @@ def validate(msg: NEMSignTx): _validate_common(msg.transaction) if msg.multisig: + _validate_common(msg.multisig, True) _validate_multisig(msg.multisig, msg.transaction.network) if not msg.multisig and msg.cosigning: raise ValueError('No multisig transaction to cosign') @@ -76,12 +77,12 @@ def _validate_common(common: NEMTransactionCommon, inner: bool=False): if common.deadline is None: err = 'deadline' - # is_signer = common.signer is not None todo !! - # if inner != is_signer: - # if not inner: - # raise ValueError('Signer not allowed in outer transaction') - # err = 'signer' - # + if not inner and common.signer: + raise ValueError('Signer not allowed in outer transaction') + + if inner and common.signer is None: + err = 'signer' + if err: if inner: raise ValueError('No ' + err + ' provided in inner transaction') @@ -106,7 +107,6 @@ def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer): def _validate_multisig(multisig: NEMTransactionCommon, network: int): - _validate_common(multisig) _validate_public_key(multisig.signer, 'Invalid multisig signer public key provided') if multisig.network != network: raise ValueError('Inner transaction network is different') From a45ac4ad4bf1d9ad9d8f3bc1b93f59571c5ba7ab Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Mon, 23 Apr 2018 12:33:24 +0100 Subject: [PATCH 44/53] nem: refactored to have a common message as an argument --- src/apps/nem/layout.py | 2 +- src/apps/nem/mosaic/__init__.py | 12 ++-- src/apps/nem/mosaic/layout.py | 30 +++++----- src/apps/nem/mosaic/serialize.py | 60 +++++++++---------- src/apps/nem/multisig/__init__.py | 17 ++++-- src/apps/nem/multisig/layout.py | 22 +++---- src/apps/nem/multisig/serialize.py | 11 ++-- src/apps/nem/namespace/__init__.py | 6 +- src/apps/nem/namespace/layout.py | 17 +++--- src/apps/nem/namespace/serialize.py | 18 +++--- src/apps/nem/signing.py | 14 +++-- src/apps/nem/transfer/__init__.py | 19 +++--- src/apps/nem/transfer/layout.py | 20 ++++--- src/apps/nem/transfer/serialize.py | 36 +++++------ tests/test_apps.nem.mosaic_creation.py | 9 +-- tests/test_apps.nem.mosaic_supply_change.py | 9 +-- ...pps.nem.multisig.aggregate_modification.py | 7 ++- tests/test_apps.nem.multisig.py | 4 +- tests/test_apps.nem.namespace.py | 6 +- tests/test_apps.nem.transfer.py | 10 ++-- 20 files changed, 168 insertions(+), 161 deletions(-) diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index d64a1612f..73d8d8332 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -27,7 +27,7 @@ async def require_confirm_final(ctx, fee: int): content = Text('Final confirm', ui.ICON_SEND, ui.NORMAL, 'Sign this transaction', ui.BOLD, 'and pay ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM', - ui.NORMAL, 'for transaction fee?', + ui.NORMAL, 'for network fee?', icon_color=ui.GREEN) await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 diff --git a/src/apps/nem/mosaic/__init__.py b/src/apps/nem/mosaic/__init__.py index 04010fb6c..50a7dc361 100644 --- a/src/apps/nem/mosaic/__init__.py +++ b/src/apps/nem/mosaic/__init__.py @@ -2,11 +2,11 @@ from .layout import * from .serialize import * -async def mosaic_creation(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: - await ask_mosaic_creation(ctx, msg) - return serialize_mosaic_creation(msg, public_key) +async def mosaic_creation(ctx, public_key: bytes, common: NEMTransactionCommon, creation: NEMMosaicCreation) -> bytearray: + await ask_mosaic_creation(ctx, common, creation) + return serialize_mosaic_creation(common, creation, public_key) -async def supply_change(ctx, public_key: bytes, msg: NEMSignTx): - await ask_supply_change(ctx, msg) - return serialize_mosaic_supply_change(msg, public_key) +async def supply_change(ctx, public_key: bytes, common: NEMTransactionCommon, change: NEMMosaicSupplyChange): + await ask_supply_change(ctx, common, change) + return serialize_mosaic_supply_change(common, change, public_key) diff --git a/src/apps/nem/mosaic/layout.py b/src/apps/nem/mosaic/layout.py index a385c1886..842e534ce 100644 --- a/src/apps/nem/mosaic/layout.py +++ b/src/apps/nem/mosaic/layout.py @@ -1,30 +1,32 @@ from apps.nem.layout import * -from trezor.messages import NEMSignTx +from trezor.messages import NEMTransactionCommon from trezor.messages import NEMSupplyChangeType from trezor.messages import NEMMosaicDefinition +from trezor.messages import NEMMosaicCreation +from trezor.messages import NEMMosaicSupplyChange from trezor.messages import NEMMosaicLevy from trezor.ui.scroll import Scrollpage, animate_swipe, paginate -async def ask_mosaic_creation(ctx, msg: NEMSignTx): - await require_confirm_content(ctx, 'Create mosaic', _creation_message(msg.mosaic_creation)) - await _require_confirm_properties(ctx, msg.mosaic_creation.definition) - await require_confirm_fee(ctx, 'Confirm creation fee', msg.mosaic_creation.fee) +async def ask_mosaic_creation(ctx, common: NEMTransactionCommon, creation: NEMMosaicCreation): + await require_confirm_content(ctx, 'Create mosaic', _creation_message(creation)) + await _require_confirm_properties(ctx, creation.definition) + await require_confirm_fee(ctx, 'Confirm creation fee', creation.fee) - await require_confirm_final(ctx, msg.transaction.fee) + await require_confirm_final(ctx, common.fee) -async def ask_supply_change(ctx, msg: NEMSignTx): - await require_confirm_content(ctx, 'Supply change', _supply_message(msg.supply_change)) - if msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Decrease: - ask_msg = 'Decrease supply by ' + str(msg.supply_change.delta) + ' whole units?' - elif msg.supply_change.type == NEMSupplyChangeType.SupplyChange_Increase: - ask_msg = 'Increase supply by ' + str(msg.supply_change.delta) + ' whole units?' +async def ask_supply_change(ctx, common: NEMTransactionCommon, change: NEMMosaicSupplyChange): + await require_confirm_content(ctx, 'Supply change', _supply_message(change)) + if change.type == NEMSupplyChangeType.SupplyChange_Decrease: + msg = 'Decrease supply by ' + str(change.delta) + ' whole units?' + elif change.type == NEMSupplyChangeType.SupplyChange_Increase: + msg = 'Increase supply by ' + str(change.delta) + ' whole units?' else: raise ValueError('Invalid supply change type') - await require_confirm_text(ctx, ask_msg) + await require_confirm_text(ctx, msg) - await require_confirm_final(ctx, msg.transaction.fee) + await require_confirm_final(ctx, common.fee) def _creation_message(mosaic_creation): diff --git a/src/apps/nem/mosaic/serialize.py b/src/apps/nem/mosaic/serialize.py index 0eeb34012..a823d5e87 100644 --- a/src/apps/nem/mosaic/serialize.py +++ b/src/apps/nem/mosaic/serialize.py @@ -1,62 +1,58 @@ from apps.nem.writers import * from apps.nem.helpers import * -from trezor.messages.NEMSignTx import NEMSignTx +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon +from trezor.messages.NEMMosaicSupplyChange import NEMMosaicSupplyChange +from trezor.messages.NEMMosaicCreation import NEMMosaicCreation -def serialize_mosaic_creation(msg: NEMSignTx, public_key: bytes): - common = msg.transaction - if msg.multisig: - common = msg.multisig +def serialize_mosaic_creation(common: NEMTransactionCommon, creation: NEMMosaicCreation, public_key: bytes): w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_MOSAIC_CREATION) mosaics_w = bytearray() write_bytes_with_length(mosaics_w, bytearray(public_key)) - identifier_length = 4 + len(msg.mosaic_creation.definition.namespace) + 4 + len(msg.mosaic_creation.definition.mosaic) + identifier_length = 4 + len(creation.definition.namespace) + 4 + len(creation.definition.mosaic) write_uint32(mosaics_w, identifier_length) - write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.namespace)) - write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.mosaic)) - write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.description)) + write_bytes_with_length(mosaics_w, bytearray(creation.definition.namespace)) + write_bytes_with_length(mosaics_w, bytearray(creation.definition.mosaic)) + write_bytes_with_length(mosaics_w, bytearray(creation.definition.description)) write_uint32(mosaics_w, 4) # number of properties - _write_property(mosaics_w, "divisibility", msg.mosaic_creation.definition.divisibility) - _write_property(mosaics_w, "initialSupply", msg.mosaic_creation.definition.supply) - _write_property(mosaics_w, "supplyMutable", msg.mosaic_creation.definition.mutable_supply) - _write_property(mosaics_w, "transferable", msg.mosaic_creation.definition.transferable) + _write_property(mosaics_w, "divisibility", creation.definition.divisibility) + _write_property(mosaics_w, "initialSupply", creation.definition.supply) + _write_property(mosaics_w, "supplyMutable", creation.definition.mutable_supply) + _write_property(mosaics_w, "transferable", creation.definition.transferable) - if msg.mosaic_creation.definition.levy: - levy_identifier_length = 4 + len(msg.mosaic_creation.definition.levy_namespace) + 4 + len(msg.mosaic_creation.definition.levy_mosaic) - write_uint32(mosaics_w, 4 + 4 + len(msg.mosaic_creation.definition.levy_address) + 4 + levy_identifier_length + 8) - write_uint32(mosaics_w, msg.mosaic_creation.definition.levy) - write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.levy_address)) + if creation.definition.levy: + levy_identifier_length = 4 + len(creation.definition.levy_namespace) + 4 + len(creation.definition.levy_mosaic) + write_uint32(mosaics_w, 4 + 4 + len(creation.definition.levy_address) + 4 + levy_identifier_length + 8) + write_uint32(mosaics_w, creation.definition.levy) + write_bytes_with_length(mosaics_w, bytearray(creation.definition.levy_address)) write_uint32(mosaics_w, levy_identifier_length) - write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.levy_namespace)) - write_bytes_with_length(mosaics_w, bytearray(msg.mosaic_creation.definition.levy_mosaic)) - write_uint64(mosaics_w, msg.mosaic_creation.definition.fee) + write_bytes_with_length(mosaics_w, bytearray(creation.definition.levy_namespace)) + write_bytes_with_length(mosaics_w, bytearray(creation.definition.levy_mosaic)) + write_uint64(mosaics_w, creation.definition.fee) else: write_uint32(mosaics_w, 0) # write mosaic bytes with length write_bytes_with_length(w, mosaics_w) - write_bytes_with_length(w, bytearray(msg.mosaic_creation.sink)) - write_uint64(w, msg.mosaic_creation.fee) + write_bytes_with_length(w, bytearray(creation.sink)) + write_uint64(w, creation.fee) return w -def serialize_mosaic_supply_change(msg: NEMSignTx, public_key: bytes): - common = msg.transaction - if msg.multisig: - common = msg.multisig +def serialize_mosaic_supply_change(common: NEMTransactionCommon, change: NEMMosaicSupplyChange, public_key: bytes): w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE) - identifier_length = 4 + len(msg.supply_change.namespace) + 4 + len(msg.supply_change.mosaic) + identifier_length = 4 + len(change.namespace) + 4 + len(change.mosaic) write_uint32(w, identifier_length) - write_bytes_with_length(w, bytearray(msg.supply_change.namespace)) - write_bytes_with_length(w, bytearray(msg.supply_change.mosaic)) + write_bytes_with_length(w, bytearray(change.namespace)) + write_bytes_with_length(w, bytearray(change.mosaic)) - write_uint32(w, msg.supply_change.type) - write_uint64(w, msg.supply_change.delta) + write_uint32(w, change.type) + write_uint64(w, change.delta) return w diff --git a/src/apps/nem/multisig/__init__.py b/src/apps/nem/multisig/__init__.py index bbcde9cc0..08dfe91d5 100644 --- a/src/apps/nem/multisig/__init__.py +++ b/src/apps/nem/multisig/__init__.py @@ -1,5 +1,6 @@ from .serialize import * from .layout import * +from trezor.messages.NEMAggregateModification import NEMAggregateModification async def ask(ctx, msg: NEMSignTx): @@ -17,13 +18,17 @@ def cosign(public_key, common: NEMTransactionCommon, inner_tx: bytes, signer: by signer) -async def aggregate_modification(ctx, public_key: bytes, msg: NEMSignTx): - await ask_aggregate_modification(ctx, msg) - w = serialize_aggregate_modification(msg, public_key) +async def aggregate_modification(ctx, + public_key: bytes, + common: NEMTransactionCommon, + aggr: NEMAggregateModification, + multisig: bool): + await ask_aggregate_modification(ctx, common, aggr, multisig) + w = serialize_aggregate_modification(common, aggr, public_key) - for m in msg.aggregate_modification.modifications: + for m in aggr.modifications: serialize_cosignatory_modification(w, m.type, m.public_key) - if msg.aggregate_modification.relative_change: - serialize_minimum_cosignatories(w, msg.aggregate_modification.relative_change) + if aggr.relative_change: + serialize_minimum_cosignatories(w, aggr.relative_change) return w diff --git a/src/apps/nem/multisig/layout.py b/src/apps/nem/multisig/layout.py index f7251f4e8..d89dc1bc2 100644 --- a/src/apps/nem/multisig/layout.py +++ b/src/apps/nem/multisig/layout.py @@ -1,6 +1,8 @@ from apps.nem.layout import * +from trezor.messages import NEMAggregateModification from trezor.messages import NEMModificationType from trezor.messages import NEMSignTx +from trezor.messages import NEMTransactionCommon from trezor.crypto import nem @@ -13,26 +15,26 @@ async def ask_multisig(ctx, msg: NEMSignTx): await require_confirm_fee(ctx, 'Confirm multisig fee', msg.transaction.fee) -async def ask_aggregate_modification(ctx, msg: NEMSignTx): - if not msg.multisig: +async def ask_aggregate_modification(ctx, common: NEMTransactionCommon, mod: NEMAggregateModification, multisig: bool): + if not multisig: await require_confirm_text(ctx, 'Convert account to multisig account?') - for m in msg.aggregate_modification.modifications: + for m in mod.modifications: if m.type == NEMModificationType.CosignatoryModification_Add: action = 'Add' else: action = 'Remove' - address = nem.compute_address(m.public_key, msg.transaction.network) + address = nem.compute_address(m.public_key, common.network) await _require_confirm_address(ctx, action + ' cosignatory', address) - if msg.aggregate_modification.relative_change: - if not msg.multisig: - action = 'Set minimum cosignatories to ' - else: + if mod.relative_change: + if multisig: action = 'Modify the number of cosignatories by ' - await require_confirm_text(ctx, action + str(msg.aggregate_modification.relative_change) + '?') + else: + action = 'Set minimum cosignatories to ' + await require_confirm_text(ctx, action + str(mod.relative_change) + '?') - await require_confirm_final(ctx, msg.transaction.fee) + await require_confirm_final(ctx, common.fee) async def _require_confirm_address(ctx, action: str, address: str): diff --git a/src/apps/nem/multisig/serialize.py b/src/apps/nem/multisig/serialize.py index ca23ae5d5..a7e6eee83 100644 --- a/src/apps/nem/multisig/serialize.py +++ b/src/apps/nem/multisig/serialize.py @@ -3,6 +3,8 @@ from apps.nem.helpers import * from trezor.messages.NEMSignTx import NEMSignTx from trezor.crypto import hashlib from trezor.crypto import nem +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon +from trezor.messages.NEMAggregateModification import NEMAggregateModification def serialize_multisig(common: NEMTransactionCommon, public_key: bytes, inner: bytes): @@ -23,19 +25,16 @@ def serialize_multisig_signature(common: NEMTransactionCommon, public_key: bytes return w -def serialize_aggregate_modification(msg: NEMSignTx, public_key: bytes): - common = msg.transaction - if msg.multisig: - common = msg.multisig +def serialize_aggregate_modification(common: NEMTransactionCommon, mod: NEMAggregateModification, public_key: bytes): version = common.network << 24 | 1 - if msg.aggregate_modification.relative_change: + if mod.relative_change: version = common.network << 24 | 2 w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, version) - write_uint32(w, len(msg.aggregate_modification.modifications)) + write_uint32(w, len(mod.modifications)) return w diff --git a/src/apps/nem/namespace/__init__.py b/src/apps/nem/namespace/__init__.py index f6ae3cc70..bdf8975b4 100644 --- a/src/apps/nem/namespace/__init__.py +++ b/src/apps/nem/namespace/__init__.py @@ -2,6 +2,6 @@ from .layout import * from .serialize import * -async def namespace(ctx, public_key: bytes, msg: NEMSignTx) -> bytearray: - await ask_provision_namespace(ctx, msg) - return serialize_provision_namespace(msg, public_key) +async def namespace(ctx, public_key: bytes, common: NEMTransactionCommon, namespace: NEMProvisionNamespace) -> bytearray: + await ask_provision_namespace(ctx, common, namespace) + return serialize_provision_namespace(common, namespace, public_key) diff --git a/src/apps/nem/namespace/layout.py b/src/apps/nem/namespace/layout.py index 06fa78e6c..ae7a57b85 100644 --- a/src/apps/nem/namespace/layout.py +++ b/src/apps/nem/namespace/layout.py @@ -1,19 +1,20 @@ from apps.nem.layout import * -from trezor.messages import NEMSignTx +from trezor.messages import NEMTransactionCommon +from trezor.messages import NEMProvisionNamespace -async def ask_provision_namespace(ctx, msg: NEMSignTx): - if msg.provision_namespace.parent: +async def ask_provision_namespace(ctx, common: NEMTransactionCommon, namespace: NEMProvisionNamespace): + if namespace.parent: content = [ui.NORMAL, 'Create namespace', - ui.BOLD, msg.provision_namespace.namespace, + ui.BOLD, namespace.namespace, ui.NORMAL, 'under namespace', - ui.BOLD, msg.provision_namespace.parent] + ui.BOLD, namespace.parent] await require_confirm_content(ctx, 'Confirm namespace', content) else: content = [ui.NORMAL, 'Create namespace', - ui.BOLD, msg.provision_namespace.namespace] + ui.BOLD, namespace.namespace] await require_confirm_content(ctx, 'Confirm namespace', content) - await require_confirm_fee(ctx, 'Confirm rental fee', msg.provision_namespace.fee) + await require_confirm_fee(ctx, 'Confirm rental fee', namespace.fee) - await require_confirm_final(ctx, msg.transaction.fee) + await require_confirm_final(ctx, common.fee) diff --git a/src/apps/nem/namespace/serialize.py b/src/apps/nem/namespace/serialize.py index a60c1e106..3df49d121 100644 --- a/src/apps/nem/namespace/serialize.py +++ b/src/apps/nem/namespace/serialize.py @@ -1,21 +1,19 @@ from apps.nem.helpers import * from apps.nem.writers import * -from trezor.messages.NEMSignTx import NEMSignTx +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon +from trezor.messages.NEMProvisionNamespace import NEMProvisionNamespace -def serialize_provision_namespace(msg: NEMSignTx, public_key: bytes) -> bytearray: - common = msg.transaction - if msg.multisig: - common = msg.multisig +def serialize_provision_namespace(common: NEMTransactionCommon, namespace: NEMProvisionNamespace, public_key: bytes) -> bytearray: tx = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE) - write_bytes_with_length(tx, bytearray(msg.provision_namespace.sink)) - write_uint64(tx, msg.provision_namespace.fee) - write_bytes_with_length(tx, bytearray(msg.provision_namespace.namespace)) - if msg.provision_namespace.parent: - write_bytes_with_length(tx, bytearray(msg.provision_namespace.parent)) + write_bytes_with_length(tx, bytearray(namespace.sink)) + write_uint64(tx, namespace.fee) + write_bytes_with_length(tx, bytearray(namespace.namespace)) + if namespace.parent: + write_bytes_with_length(tx, bytearray(namespace.parent)) else: write_uint32(tx, 0xffffffff) diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 9395bc678..360423aaf 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -17,21 +17,23 @@ async def sign_tx(ctx, msg: NEMSignTx): if msg.multisig: public_key = msg.multisig.signer await multisig.ask(ctx, msg) + common = msg.multisig else: public_key = _get_public_key(node) + common = msg.transaction if msg.transfer: - tx = await transfer.transfer(ctx, public_key, msg, node) + tx = await transfer.transfer(ctx, public_key, common, msg.transfer, node) elif msg.provision_namespace: - tx = await namespace.namespace(ctx, public_key, msg) + tx = await namespace.namespace(ctx, public_key, common, msg.provision_namespace) elif msg.mosaic_creation: - tx = await mosaic.mosaic_creation(ctx, public_key, msg) + tx = await mosaic.mosaic_creation(ctx, public_key, common, msg.mosaic_creation) elif msg.supply_change: - tx = await mosaic.supply_change(ctx, public_key, msg) + tx = await mosaic.supply_change(ctx, public_key, common, msg.supply_change) elif msg.aggregate_modification: - tx = await multisig.aggregate_modification(ctx, public_key, msg) + tx = await multisig.aggregate_modification(ctx, public_key, common, msg.aggregate_modification, msg.multisig is not None) elif msg.importance_transfer: - tx = await transfer.importance_transfer(ctx, public_key, msg) + tx = await transfer.importance_transfer(ctx, public_key, common, msg.importance_transfer) else: raise ValueError('No transaction provided') diff --git a/src/apps/nem/transfer/__init__.py b/src/apps/nem/transfer/__init__.py index 7c9cb0d91..eceaed2a3 100644 --- a/src/apps/nem/transfer/__init__.py +++ b/src/apps/nem/transfer/__init__.py @@ -1,19 +1,20 @@ from .layout import * from .serialize import * +from trezor.messages.NEMSignTx import NEMTransfer -async def transfer(ctx, public_key: bytes, msg: NEMSignTx, node): - msg.transfer.mosaics = canonicalize_mosaics(msg.transfer.mosaics) +async def transfer(ctx, public_key: bytes, common: NEMTransactionCommon, transfer: NEMTransfer, node): + transfer.mosaics = canonicalize_mosaics(transfer.mosaics) - payload, encrypted = get_transfer_payload(msg, node) - await ask_transfer(ctx, msg, payload, encrypted) + payload, encrypted = get_transfer_payload(transfer, node) + await ask_transfer(ctx, common, transfer, payload, encrypted) - w = serialize_transfer(msg, public_key, payload, encrypted) - for mosaic in msg.transfer.mosaics: + w = serialize_transfer(common, transfer, public_key, payload, encrypted) + for mosaic in transfer.mosaics: serialize_mosaic(w, mosaic.namespace, mosaic.mosaic, mosaic.quantity) return w -async def importance_transfer(ctx, public_key: bytes, msg: NEMSignTx): - await ask_importance_transfer(ctx, msg) - return serialize_importance_transfer(msg, public_key) +async def importance_transfer(ctx, public_key: bytes, common: NEMTransactionCommon, imp: NEMImportanceTransfer): + await ask_importance_transfer(ctx, common, imp) + return serialize_importance_transfer(common, imp, public_key) diff --git a/src/apps/nem/transfer/layout.py b/src/apps/nem/transfer/layout.py index ed4008662..e20007d35 100644 --- a/src/apps/nem/transfer/layout.py +++ b/src/apps/nem/transfer/layout.py @@ -1,27 +1,29 @@ from apps.nem.layout import * from trezor.messages import NEMImportanceTransferMode -from trezor.messages import NEMSignTx +from trezor.messages import NEMTransfer +from trezor.messages import NEMImportanceTransfer +from trezor.messages import NEMTransactionCommon -async def ask_transfer(ctx, msg: NEMSignTx, payload, encrypted): +async def ask_transfer(ctx, common: NEMTransactionCommon, transfer: NEMTransfer, payload, encrypted): if payload: - await _require_confirm_payload(ctx, msg.transfer.payload, encrypted) + await _require_confirm_payload(ctx, transfer.payload, encrypted) - for mosaic in msg.transfer.mosaics: + for mosaic in transfer.mosaics: await require_confirm_content(ctx, 'Confirm mosaic', _mosaics_message(mosaic)) - await _require_confirm_transfer(ctx, msg.transfer.recipient, msg.transfer.amount) + await _require_confirm_transfer(ctx, transfer.recipient, transfer.amount) - await require_confirm_final(ctx, msg.transaction.fee) + await require_confirm_final(ctx, common.fee) -async def ask_importance_transfer(ctx, msg: NEMSignTx): - if msg.importance_transfer.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: +async def ask_importance_transfer(ctx, common: NEMTransactionCommon, imp: NEMImportanceTransfer): + if imp.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: m = 'Activate' else: m = 'Deactivate' await require_confirm_text(ctx, m + ' remote harvesting?') - await require_confirm_final(ctx, msg.transaction.fee) + await require_confirm_final(ctx, common.fee) async def _require_confirm_transfer(ctx, recipient, value): diff --git a/src/apps/nem/transfer/serialize.py b/src/apps/nem/transfer/serialize.py index 9002a9ffd..ec869c2e3 100644 --- a/src/apps/nem/transfer/serialize.py +++ b/src/apps/nem/transfer/serialize.py @@ -1,21 +1,20 @@ from apps.nem.writers import * from apps.nem.helpers import * from trezor.messages.NEMMosaic import NEMMosaic -from trezor.messages.NEMSignTx import NEMSignTx +from trezor.messages.NEMImportanceTransfer import NEMImportanceTransfer +from trezor.messages.NEMTransfer import NEMTransfer from trezor.crypto import random -def serialize_transfer(msg: NEMSignTx, public_key: bytes, payload: bytes=None, encrypted: bool=False) -> bytearray: - common = msg.transaction - if msg.multisig: - common = msg.multisig +def serialize_transfer(common: NEMTransactionCommon, transfer: NEMTransfer, + public_key: bytes, payload: bytes=None, encrypted: bool=False) -> bytearray: tx = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_TRANSFER, - _get_version(common.network, msg.transfer.mosaics)) + _get_version(common.network, transfer.mosaics)) - write_bytes_with_length(tx, bytearray(msg.transfer.recipient)) - write_uint64(tx, msg.transfer.amount) + write_bytes_with_length(tx, bytearray(transfer.recipient)) + write_uint64(tx, transfer.amount) if payload: # payload + payload size (u32) + encryption flag (u32) @@ -28,8 +27,8 @@ def serialize_transfer(msg: NEMSignTx, public_key: bytes, payload: bytes=None, e else: write_uint32(tx, 0) - if msg.transfer.mosaics: - write_uint32(tx, len(msg.transfer.mosaics)) + if transfer.mosaics: + write_uint32(tx, len(transfer.mosaics)) return tx @@ -44,24 +43,21 @@ def serialize_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): write_uint64(w, quantity) -def serialize_importance_transfer(msg: NEMSignTx, public_key: bytes) -> bytearray: - common = msg.transaction - if msg.multisig: - common = msg.multisig +def serialize_importance_transfer(common: NEMTransactionCommon, imp: NEMImportanceTransfer, public_key: bytes)-> bytearray: w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER) - write_uint32(w, msg.importance_transfer.mode) - write_bytes_with_length(w, bytearray(msg.importance_transfer.public_key)) + write_uint32(w, imp.mode) + write_bytes_with_length(w, bytearray(imp.public_key)) return w -def get_transfer_payload(msg: NEMSignTx, node) -> [bytes, bool]: - payload = msg.transfer.payload +def get_transfer_payload(transfer: NEMTransfer, node) -> [bytes, bool]: + payload = transfer.payload encrypted = False - if msg.transfer.public_key is not None: + if transfer.public_key is not None: if payload is None: raise ValueError("Public key provided but no payload to encrypt") - payload = _encrypt(node, msg.transfer.public_key, msg.transfer.payload) + payload = _encrypt(node, transfer.public_key, transfer.payload) encrypted = True return payload, encrypted diff --git a/tests/test_apps.nem.mosaic_creation.py b/tests/test_apps.nem.mosaic_creation.py index 7085dad48..2c445673b 100644 --- a/tests/test_apps.nem.mosaic_creation.py +++ b/tests/test_apps.nem.mosaic_creation.py @@ -2,6 +2,7 @@ from common import * from apps.nem.mosaic import * from trezor.crypto import hashlib +from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMMosaicCreation import NEMMosaicCreation from trezor.messages.NEMMosaicDefinition import NEMMosaicDefinition @@ -30,7 +31,7 @@ class TestNemMosaicCreation(unittest.TestCase): 'TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC', 50000000000) - t = serialize_mosaic_creation(m, unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324')) + t = serialize_mosaic_creation(m.transaction, m.mosaic_creation, unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324')) self.assertEqual(t, unhexlify('014000000100009870b4d60020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd32400f36f060000000080c2d600de00000020000000994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd3241f0000001000000067696d72652e67616d65732e706f6e6707000000706164646c65731b000000506164646c657320666f722074686520626f6e672067616d652e0a04000000150000000c00000064697669736962696c69747901000000301a0000000d000000696e697469616c537570706c79050000003130303030190000000d000000737570706c794d757461626c650400000074727565180000000c0000007472616e7366657261626c650400000074727565000000002800000054424d4f534149434f443446353445453543444d523233434342474f414d3258534a4252354f4c4300743ba40b000000')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa')) @@ -56,7 +57,7 @@ class TestNemMosaicCreation(unittest.TestCase): "TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC", 50000000000) - t = serialize_mosaic_creation(m, unhexlify("244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc"),) + t = serialize_mosaic_creation(m.transaction, m.mosaic_creation, unhexlify("244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc"),) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55')) @@ -80,7 +81,7 @@ class TestNemMosaicCreation(unittest.TestCase): "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", 500000000) - t = serialize_mosaic_creation(m, unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a")) + t = serialize_mosaic_creation(m.transaction, m.mosaic_creation, unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a")) self.assertEqual(t, unhexlify('0140000001000068ccaf200420000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a002d3101000000004c0122040c01000020000000a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a0f0000000300000064696d04000000636f696e0800000044494d20434f494e04000000150000000c00000064697669736962696c69747901000000361f0000000d000000696e697469616c537570706c790a000000393030303030303030301a0000000d000000737570706c794d757461626c650500000066616c7365180000000c0000007472616e7366657261626c6504000000747275654b00000002000000280000004e4347474c564f32473343554143564935474e58324b52424a5351434e3452444c325a574a3444500f0000000300000064696d04000000636f696e0a00000000000000280000004e424d4f534149434f443446353445453543444d523233434342474f414d325853495558365452530065cd1d00000000')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6')) @@ -108,7 +109,7 @@ class TestNemMosaicCreation(unittest.TestCase): "", "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", 50000000000) - t = serialize_mosaic_creation(m, unhexlify("58956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f23")) + t = serialize_mosaic_creation(m.transaction, m.mosaic_creation, unhexlify("58956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f23")) self.assertEqual(t, unhexlify('0140000001000068d2dd97012000000058956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f2300f36f0600000000e2eb9701c80100002000000058956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f2317000000060000006a61626f3338090000007265645f746f6b656e0c0100005468697320746f6b656e20697320746f2063656c656272617465207468652072656c65617365206f66204e616d6573706163657320616e64204d6f7361696373206f6e20746865204e454d2073797374656d2e205468697320746f6b656e207761732074686520666973742065766572206d6f736169632063726561746564206f74686572207468616e206e656d2e78656d2e20546865726520617265206f6e6c792031302c3030302052656420546f6b656e7320746861742077696c6c206576657220626520637265617465642e20497420686173206e6f206c65767920616e642063616e2062652074726164656420667265656c7920616d6f6e6720746869726420706172746965732e04000000150000000c00000064697669736962696c69747901000000321a0000000d000000696e697469616c537570706c790500000031303030301a0000000d000000737570706c794d757461626c650500000066616c7365180000000c0000007472616e7366657261626c65040000007472756500000000280000004e424d4f534149434f443446353445453543444d523233434342474f414d3258534955583654525300743ba40b000000')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b')) diff --git a/tests/test_apps.nem.mosaic_supply_change.py b/tests/test_apps.nem.mosaic_supply_change.py index 48a77c142..834ed941e 100644 --- a/tests/test_apps.nem.mosaic_supply_change.py +++ b/tests/test_apps.nem.mosaic_supply_change.py @@ -2,6 +2,7 @@ from common import * from apps.nem.mosaic import * from trezor.crypto import hashlib +from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMMosaicSupplyChange import NEMMosaicSupplyChange @@ -18,7 +19,7 @@ class TestNemMosaicSupplyChange(unittest.TestCase): "paddles", 1, 1234) - t = serialize_mosaic_supply_change(m, unhexlify("994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324")) + t = serialize_mosaic_supply_change(m.transaction, m.supply_change, unhexlify("994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324")) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50')) @@ -32,7 +33,7 @@ class TestNemMosaicSupplyChange(unittest.TestCase): "coupons", 2, 1) - t = serialize_mosaic_supply_change(m, unhexlify("84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5")) + t = serialize_mosaic_supply_change(m.transaction, m.supply_change, unhexlify("84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5")) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2')) @@ -46,7 +47,7 @@ class TestNemMosaicSupplyChange(unittest.TestCase): "abv", 1, 9000000) - t = serialize_mosaic_supply_change(m, unhexlify("b7ccc27b21ba6cf5c699a8dc86ba6ba98950442597ff9fa30e0abe0f5f4dd05d")) + t = serialize_mosaic_supply_change(m.transaction, m.supply_change, unhexlify("b7ccc27b21ba6cf5c699a8dc86ba6ba98950442597ff9fa30e0abe0f5f4dd05d")) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff')) @@ -60,7 +61,7 @@ class TestNemMosaicSupplyChange(unittest.TestCase): "wasabi", 2, 20) - t = serialize_mosaic_supply_change(m, unhexlify("75f001a8641e2ce5c4386883dda561399ed346177411b492a677b73899502f13")) + t = serialize_mosaic_supply_change(m.transaction, m.supply_change, unhexlify("75f001a8641e2ce5c4386883dda561399ed346177411b492a677b73899502f13")) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185')) diff --git a/tests/test_apps.nem.multisig.aggregate_modification.py b/tests/test_apps.nem.multisig.aggregate_modification.py index 01daf6519..81258f9b2 100644 --- a/tests/test_apps.nem.multisig.aggregate_modification.py +++ b/tests/test_apps.nem.multisig.aggregate_modification.py @@ -5,6 +5,7 @@ from trezor.crypto import hashlib from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMAggregateModification import NEMAggregateModification from trezor.messages.NEMCosignatoryModification import NEMCosignatoryModification +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon class TestNemMultisigAggregateModification(unittest.TestCase): @@ -17,7 +18,7 @@ class TestNemMultisigAggregateModification(unittest.TestCase): 0, 2, 0) - t = serialize_aggregate_modification(m, unhexlify("462ee976890916e54fa825d26bdd0235f5eb5b6a143c199ab0ae5ee9328e08ce")) + t = serialize_aggregate_modification(m.transaction, m.aggregate_modification, unhexlify("462ee976890916e54fa825d26bdd0235f5eb5b6a143c199ab0ae5ee9328e08ce")) serialize_cosignatory_modification(t, 1, unhexlify( "994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324")) @@ -34,7 +35,7 @@ class TestNemMultisigAggregateModification(unittest.TestCase): 0, 5, 0) - t = serialize_aggregate_modification(m, unhexlify("f41b99320549741c5cce42d9e4bb836d98c50ed5415d0c3c2912d1bb50e6a0e5")) + t = serialize_aggregate_modification(m.transaction, m.aggregate_modification, unhexlify("f41b99320549741c5cce42d9e4bb836d98c50ed5415d0c3c2912d1bb50e6a0e5")) serialize_cosignatory_modification(t, 1, unhexlify( "1fbdbdde28daf828245e4533765726f0b7790e0b7146e2ce205df3e86366980b")) @@ -58,7 +59,7 @@ class TestNemMultisigAggregateModification(unittest.TestCase): 6545854, 4, 2) - t = serialize_aggregate_modification(m, unhexlify("6bf7849c1eec6a2002995cc457dc00c4e29bad5c88de63f51e42dfdcd7b2131d")) + t = serialize_aggregate_modification(m.transaction, m.aggregate_modification, unhexlify("6bf7849c1eec6a2002995cc457dc00c4e29bad5c88de63f51e42dfdcd7b2131d")) serialize_cosignatory_modification(t, 1, unhexlify( "5f53d076c8c3ec3110b98364bc423092c3ec2be2b1b3c40fd8ab68d54fa39295")) diff --git a/tests/test_apps.nem.multisig.py b/tests/test_apps.nem.multisig.py index bd37ef65b..598034438 100644 --- a/tests/test_apps.nem.multisig.py +++ b/tests/test_apps.nem.multisig.py @@ -18,7 +18,7 @@ class TestNemMultisig(unittest.TestCase): 3960639, 1, 0) - base_tx = serialize_aggregate_modification(m, unhexlify("abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac")) + base_tx = serialize_aggregate_modification(m.transaction, m.aggregate_modification, unhexlify("abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac")) base_tx = serialize_cosignatory_modification(base_tx, 2, unhexlify("e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3")) m = _create_common_msg(NEM_NETWORK_TESTNET, @@ -48,7 +48,7 @@ class TestNemMultisig(unittest.TestCase): "", "NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA", 5000000000) - base_tx = serialize_provision_namespace(m, unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a")) + base_tx = serialize_provision_namespace(m.transaction, m.provision_namespace, unhexlify("a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a")) m = _create_common_msg(NEM_NETWORK_MAINNET, 59414272, diff --git a/tests/test_apps.nem.namespace.py b/tests/test_apps.nem.namespace.py index b84bf6729..7165158b4 100644 --- a/tests/test_apps.nem.namespace.py +++ b/tests/test_apps.nem.namespace.py @@ -19,7 +19,7 @@ class TestNemNamespace(unittest.TestCase): '', 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35', 5000000000) - t = serialize_provision_namespace(m, unhexlify('84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5')) + t = serialize_provision_namespace(m.transaction, m.provision_namespace, unhexlify('84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('f7cab28da57204d01a907c697836577a4ae755e6c9bac60dcc318494a22debb3')) # http://bob.nem.ninja:8765/#/namespace/7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d @@ -31,7 +31,7 @@ class TestNemNamespace(unittest.TestCase): 'alice', 'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35', 5000000000) - t = serialize_provision_namespace(m, unhexlify('244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc')) + t = serialize_provision_namespace(m.transaction, m.provision_namespace, unhexlify('244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d')) @@ -44,7 +44,7 @@ class TestNemNamespace(unittest.TestCase): '', 'NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA', 50000000000) - t = serialize_provision_namespace(m, unhexlify('9f3c14f304309c8b72b2821339c4428793b1518bea72d58dd01f19d523518614')) + t = serialize_provision_namespace(m.transaction, m.provision_namespace, unhexlify('9f3c14f304309c8b72b2821339c4428793b1518bea72d58dd01f19d523518614')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569')) diff --git a/tests/test_apps.nem.transfer.py b/tests/test_apps.nem.transfer.py index bf37ca8dd..0ffa04467 100644 --- a/tests/test_apps.nem.transfer.py +++ b/tests/test_apps.nem.transfer.py @@ -19,7 +19,7 @@ class TestNemTransfer(unittest.TestCase): 'TBGIMRE4SBFRUJXMH7DVF2IBY36L2EDWZ37GVSC4', 50000000000000) - t = serialize_transfer(m, unhexlify('e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f')) + t = serialize_transfer(m.transaction, m.transfer, unhexlify('e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f')) self.assertEqual(t, unhexlify('01010000010000980000000020000000e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f00000000000000000000000028000000544247494d52453453424652554a584d48374456463249425933364c324544575a3337475653433400203d88792d000000000000')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f')) @@ -33,7 +33,7 @@ class TestNemTransfer(unittest.TestCase): 'NBT3WHA2YXG2IR4PWKFFMO772JWOITTD2V4PECSB', 5175000000000) - t = serialize_transfer(m, + t = serialize_transfer(m.transaction, m.transfer, unhexlify('8d07f90fb4bbe7715fa327c926770166a11be2e494a970605f2e12557f66c9b9'), bytearray('Good luck!')) self.assertEqual(hashlib.sha3_256(t).digest(True), unhexlify('e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb')) @@ -48,7 +48,7 @@ class TestNemTransfer(unittest.TestCase): 'NALICEPFLZQRZGPRIJTMJOCPWDNECXTNNG7QLSG3', 30000000) - t = serialize_transfer(m, + t = serialize_transfer(m.transaction, m.transfer, unhexlify('f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01'), unhexlify('4d9dcf9186967d30be93d6d5404ded22812dbbae7c3f0de501bcd7228cba45bded13000eec7b4c6215fc4d3588168c9218167cec98e6977359153a4132e050f594548e61e0dc61c153f0f53c5e65c595239c9eb7c4e7d48e0f4bb8b1dd2f5ddc'), True) @@ -65,7 +65,7 @@ class TestNemTransfer(unittest.TestCase): 3000000, 2) - t = serialize_transfer(m, + t = serialize_transfer(m.transaction, m.transfer, unhexlify('994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324'), bytearray('sending you 3 pairs of paddles\n'), False) @@ -86,7 +86,7 @@ class TestNemTransfer(unittest.TestCase): 1000000, 1) - t = serialize_transfer(m, + t = serialize_transfer(m.transaction, m.transfer, unhexlify('f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01'), bytearray('enjoy! :)'), False) From 616d117648c9da165790f4ccdb61ef6844f3d579 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 24 Apr 2018 17:17:40 +0100 Subject: [PATCH 45/53] nem: mosaics are checked against definition and user is asked appropriately --- src/apps/nem/helpers.py | 1 + src/apps/nem/mosaic/definitions.py | 67 +++++++++++++++++++ src/apps/nem/transfer/layout.py | 58 +++++++++++++--- ...icalization.py => test_apps.nem.mosaic.py} | 19 +++++- 4 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 src/apps/nem/mosaic/definitions.py rename tests/{test_apps.nem.mosaic.canonicalization.py => test_apps.nem.mosaic.py} (85%) diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index c7f9f65a1..46f7402df 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -21,6 +21,7 @@ NEM_SALT_SIZE = const(32) AES_BLOCK_SIZE = const(16) NEM_HASH_ALG = 'keccak' NEM_PUBLIC_KEY_SIZE = const(32) # ed25519 public key +NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE = const(10000) NEM_MAX_PLAIN_PAYLOAD_SIZE = const(1024) NEM_MAX_ENCRYPTED_PAYLOAD_SIZE = const(960) diff --git a/src/apps/nem/mosaic/definitions.py b/src/apps/nem/mosaic/definitions.py new file mode 100644 index 000000000..f283f074c --- /dev/null +++ b/src/apps/nem/mosaic/definitions.py @@ -0,0 +1,67 @@ +# todo move to common and generate via script + +def get_mosaic_definition(namespace_name: str, mosaic_name: str, network: int): + for m in mosaics: + if namespace_name == m["namespace"] and mosaic_name == m["mosaic"]: + if ("networks" not in m) or (network in m["networks"]): + return m + return None + + +mosaics = [ + { + "name": "XEM", + "ticker": " XEM", + "namespace": "nem", + "mosaic": "xem", + "divisibility": 6 + }, + { + "name": "DIMCOIN", + "ticker": " DIM", + "namespace": "dim", + "mosaic": "coin", + "divisibility": 6, + "levy": "MosaicLevy_Percentile", + "fee": 10, + "levy_namespace": "dim", + "levy_mosaic": "coin", + "networks": [ 104 ] + }, + { + "name": "DIM TOKEN", + "ticker": " DIMTOK", + "namespace": "dim", + "mosaic": "token", + "divisibility": 6, + "networks": [ 104 ] + }, + { + "name": "Breeze Token", + "ticker": " BREEZE", + "namespace": "breeze", + "mosaic": "breeze-token", + "divisibility": 0, + "networks": [ 104 ] + }, + { + "name": "PacNEM Game Credits", + "ticker": " PAC:HRT", + "namespace": "pacnem", + "mosaic": "heart", + "divisibility": 0, + "networks": [ 104 ] + }, + { + "name": "PacNEM Score Tokens", + "ticker": " PAC:CHS", + "namespace": "pacnem", + "mosaic": "cheese", + "divisibility": 6, + "levy": "MosaicLevy_Percentile", + "fee": 100, + "levy_namespace": "nem", + "levy_mosaic": "xem", + "networks": [ 104 ] + } +] diff --git a/src/apps/nem/transfer/layout.py b/src/apps/nem/transfer/layout.py index e20007d35..f3121645d 100644 --- a/src/apps/nem/transfer/layout.py +++ b/src/apps/nem/transfer/layout.py @@ -3,6 +3,9 @@ from trezor.messages import NEMImportanceTransferMode from trezor.messages import NEMTransfer from trezor.messages import NEMImportanceTransfer from trezor.messages import NEMTransactionCommon +from trezor.messages import NEMMosaic +from trezor.messages import NEMMosaicLevy +from apps.nem.mosaic.definitions import get_mosaic_definition async def ask_transfer(ctx, common: NEMTransactionCommon, transfer: NEMTransfer, payload, encrypted): @@ -10,13 +13,59 @@ async def ask_transfer(ctx, common: NEMTransactionCommon, transfer: NEMTransfer, await _require_confirm_payload(ctx, transfer.payload, encrypted) for mosaic in transfer.mosaics: - await require_confirm_content(ctx, 'Confirm mosaic', _mosaics_message(mosaic)) + await ask_transfer_mosaic(ctx, mosaic, common.network) await _require_confirm_transfer(ctx, transfer.recipient, transfer.amount) await require_confirm_final(ctx, common.fee) +async def ask_transfer_mosaic(ctx, mosaic: NEMMosaic, network: int): + definition = get_mosaic_definition(mosaic.namespace, mosaic.mosaic, network) + + if definition: + msg = Text('Confirm mosaic', ui.ICON_SEND, + ui.NORMAL, 'Confirm transfer of', + ui.BOLD, format_amount(mosaic.quantity, definition["divisibility"]) + definition["ticker"], + ui.NORMAL, 'of', + ui.BOLD, definition["name"], + icon_color=ui.GREEN) + await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) + + if "levy" in definition and "fee" in definition: + levy_msg = _get_levy_msg(definition, mosaic.quantity, network) + msg = Text('Confirm mosaic', ui.ICON_SEND, + ui.NORMAL, 'Confirm mosaic', + ui.NORMAL, 'levy fee of', + ui.BOLD, levy_msg, + icon_color=ui.GREEN) + await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) + + else: + msg = Text('Confirm mosaic', ui.ICON_SEND, + ui.BOLD, 'Unknown mosaic!', + ui.NORMAL, *split_words('Divisibility and levy cannot be shown for unknown mosaics', 22), + icon_color=ui.RED) + await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) + + msg = Text('Confirm mosaic', ui.ICON_SEND, + ui.NORMAL, 'Confirm transfer of', + ui.BOLD, str(mosaic.quantity) + ' raw units', + ui.NORMAL, 'of', + ui.BOLD, mosaic.namespace + '.' + mosaic.mosaic, + icon_color=ui.GREEN) + await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) + + +def _get_levy_msg(mosaic_definition, quantity: int, network: int) -> str: + levy_definition = get_mosaic_definition(mosaic_definition["levy_namespace"], mosaic_definition["levy_mosaic"], network) + if mosaic_definition["levy"] == NEMMosaicLevy.MosaicLevy_Absolute: + levy_fee = mosaic_definition["fee"] + else: + levy_fee = quantity * mosaic_definition["fee"] / NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE + return format_amount(levy_fee, levy_definition["divisibility"]) + levy_definition["ticker"] + + async def ask_importance_transfer(ctx, common: NEMTransactionCommon, imp: NEMImportanceTransfer): if imp.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: m = 'Activate' @@ -51,10 +100,3 @@ async def _require_confirm_payload(ctx, payload: bytes, encrypt=False): ui.NORMAL, *split_words(payload, 22), icon_color=ui.RED) await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput) - - -def _mosaics_message(mosaic): - return [ui.NORMAL, 'Confirm transfer of', - ui.BOLD, str(mosaic.quantity) + ' raw units', - ui.NORMAL, 'of', - ui.BOLD, mosaic.namespace + '.' + mosaic.mosaic] diff --git a/tests/test_apps.nem.mosaic.canonicalization.py b/tests/test_apps.nem.mosaic.py similarity index 85% rename from tests/test_apps.nem.mosaic.canonicalization.py rename to tests/test_apps.nem.mosaic.py index 01c95f1c4..dde6c6ec7 100644 --- a/tests/test_apps.nem.mosaic.canonicalization.py +++ b/tests/test_apps.nem.mosaic.py @@ -1,8 +1,25 @@ from common import * from apps.nem.transfer import * +from apps.nem.mosaic.definitions import get_mosaic_definition -class TestNemMosaicCanonicalization(unittest.TestCase): +class TestNemMosaic(unittest.TestCase): + + def test_get_mosaic_definition(self): + m = get_mosaic_definition("nem", "xem", 104) + self.assertEqual(m["name"], "XEM") + self.assertEqual(m["ticker"], " XEM") + + m = get_mosaic_definition("nem", "xxx", 104) + self.assertEqual(m, None) + + m = get_mosaic_definition("aaaa", "xxx", 104) + self.assertEqual(m, None) + + m = get_mosaic_definition("pacnem", "cheese", 104) + self.assertEqual(m["name"], "PacNEM Score Tokens") + self.assertEqual(m["ticker"], " PAC:CHS") + self.assertEqual(m["fee"], 100) def test_mosaic_canonicalization(self): a = NEMMosaic() From 0093d21bb68150f6300645f2a1b562447d92fb42 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 27 Apr 2018 16:28:56 +0100 Subject: [PATCH 46/53] nem: correct mosaic quantities --- src/apps/nem/helpers.py | 1 + src/apps/nem/mosaic/definitions.py | 7 +++++++ src/apps/nem/transfer/layout.py | 32 ++++++++++++++++++++++-------- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index 46f7402df..dbb549058 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -22,6 +22,7 @@ AES_BLOCK_SIZE = const(16) NEM_HASH_ALG = 'keccak' NEM_PUBLIC_KEY_SIZE = const(32) # ed25519 public key NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE = const(10000) +NEM_MOSAIC_AMOUNT_DIVISOR = const(1000000) NEM_MAX_PLAIN_PAYLOAD_SIZE = const(1024) NEM_MAX_ENCRYPTED_PAYLOAD_SIZE = const(960) diff --git a/src/apps/nem/mosaic/definitions.py b/src/apps/nem/mosaic/definitions.py index f283f074c..c1daf9e3c 100644 --- a/src/apps/nem/mosaic/definitions.py +++ b/src/apps/nem/mosaic/definitions.py @@ -1,5 +1,6 @@ # todo move to common and generate via script + def get_mosaic_definition(namespace_name: str, mosaic_name: str, network: int): for m in mosaics: if namespace_name == m["namespace"] and mosaic_name == m["mosaic"]: @@ -8,6 +9,12 @@ def get_mosaic_definition(namespace_name: str, mosaic_name: str, network: int): return None +def is_nem_xem_mosaic(namespace_name: str, mosaic_name: str): + if namespace_name == "nem" and mosaic_name == "xem": + return True + return False + + mosaics = [ { "name": "XEM", diff --git a/src/apps/nem/transfer/layout.py b/src/apps/nem/transfer/layout.py index f3121645d..7bf4f948d 100644 --- a/src/apps/nem/transfer/layout.py +++ b/src/apps/nem/transfer/layout.py @@ -1,11 +1,11 @@ from apps.nem.layout import * +from apps.nem.mosaic.definitions import * from trezor.messages import NEMImportanceTransferMode from trezor.messages import NEMTransfer from trezor.messages import NEMImportanceTransfer from trezor.messages import NEMTransactionCommon from trezor.messages import NEMMosaic from trezor.messages import NEMMosaicLevy -from apps.nem.mosaic.definitions import get_mosaic_definition async def ask_transfer(ctx, common: NEMTransactionCommon, transfer: NEMTransfer, payload, encrypted): @@ -13,27 +13,31 @@ async def ask_transfer(ctx, common: NEMTransactionCommon, transfer: NEMTransfer, await _require_confirm_payload(ctx, transfer.payload, encrypted) for mosaic in transfer.mosaics: - await ask_transfer_mosaic(ctx, mosaic, common.network) + await ask_transfer_mosaic(ctx, common, transfer, mosaic) - await _require_confirm_transfer(ctx, transfer.recipient, transfer.amount) + await _require_confirm_transfer(ctx, transfer.recipient, _get_xem_amount(transfer)) await require_confirm_final(ctx, common.fee) -async def ask_transfer_mosaic(ctx, mosaic: NEMMosaic, network: int): - definition = get_mosaic_definition(mosaic.namespace, mosaic.mosaic, network) +async def ask_transfer_mosaic(ctx, common: NEMTransactionCommon, transfer: NEMTransfer, mosaic: NEMMosaic): + if is_nem_xem_mosaic(mosaic.namespace, mosaic.mosaic): + return + + definition = get_mosaic_definition(mosaic.namespace, mosaic.mosaic, common.network) + mosaic_quantity = mosaic.quantity * transfer.amount / NEM_MOSAIC_AMOUNT_DIVISOR if definition: msg = Text('Confirm mosaic', ui.ICON_SEND, ui.NORMAL, 'Confirm transfer of', - ui.BOLD, format_amount(mosaic.quantity, definition["divisibility"]) + definition["ticker"], + ui.BOLD, format_amount(mosaic_quantity, definition["divisibility"]) + definition["ticker"], ui.NORMAL, 'of', ui.BOLD, definition["name"], icon_color=ui.GREEN) await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) if "levy" in definition and "fee" in definition: - levy_msg = _get_levy_msg(definition, mosaic.quantity, network) + levy_msg = _get_levy_msg(definition, mosaic.quantity, common.network) msg = Text('Confirm mosaic', ui.ICON_SEND, ui.NORMAL, 'Confirm mosaic', ui.NORMAL, 'levy fee of', @@ -50,13 +54,25 @@ async def ask_transfer_mosaic(ctx, mosaic: NEMMosaic, network: int): msg = Text('Confirm mosaic', ui.ICON_SEND, ui.NORMAL, 'Confirm transfer of', - ui.BOLD, str(mosaic.quantity) + ' raw units', + ui.BOLD, str(mosaic_quantity) + ' raw units', ui.NORMAL, 'of', ui.BOLD, mosaic.namespace + '.' + mosaic.mosaic, icon_color=ui.GREEN) await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) +def _get_xem_amount(transfer: NEMTransfer): + # mosaics are empty the transfer.amount denotes the xem amount + if not len(mosaics): + return transfer.amount + # otherwise xem amount is taken from the nem xem mosaic if present + for mosaic in transfer.mosaics: + if is_nem_xem_mosaic(mosaic.namespace, mosaic.mosaic): + return mosaic.quantity * transfer.amount / NEM_MOSAIC_AMOUNT_DIVISOR + # if there are mosaics but do not include xem, 0 xem is sent + return 0 + + def _get_levy_msg(mosaic_definition, quantity: int, network: int) -> str: levy_definition = get_mosaic_definition(mosaic_definition["levy_namespace"], mosaic_definition["levy_mosaic"], network) if mosaic_definition["levy"] == NEMMosaicLevy.MosaicLevy_Absolute: From 34224748115fc221a0915e5ec3a4977496f0bf57 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 2 May 2018 16:47:24 +0200 Subject: [PATCH 47/53] nem: mosaics definitions are generated from trezor-common --- src/apps/nem/mosaic/helpers.py | 15 ++++++ .../mosaic/{definitions.py => nem_mosaics.py} | 31 ++++--------- src/apps/nem/transfer/layout.py | 2 +- tests/test_apps.nem.mosaic.py | 2 +- tools/codegen/gen_nem_mosaics.py | 46 +++++++++++++++++++ 5 files changed, 71 insertions(+), 25 deletions(-) create mode 100644 src/apps/nem/mosaic/helpers.py rename src/apps/nem/mosaic/{definitions.py => nem_mosaics.py} (63%) create mode 100755 tools/codegen/gen_nem_mosaics.py diff --git a/src/apps/nem/mosaic/helpers.py b/src/apps/nem/mosaic/helpers.py new file mode 100644 index 000000000..9f112b39e --- /dev/null +++ b/src/apps/nem/mosaic/helpers.py @@ -0,0 +1,15 @@ +from .nem_mosaics import mosaics + + +def get_mosaic_definition(namespace_name: str, mosaic_name: str, network: int): + for m in mosaics: + if namespace_name == m["namespace"] and mosaic_name == m["mosaic"]: + if ("networks" not in m) or (network in m["networks"]): + return m + return None + + +def is_nem_xem_mosaic(namespace_name: str, mosaic_name: str): + if namespace_name == "nem" and mosaic_name == "xem": + return True + return False diff --git a/src/apps/nem/mosaic/definitions.py b/src/apps/nem/mosaic/nem_mosaics.py similarity index 63% rename from src/apps/nem/mosaic/definitions.py rename to src/apps/nem/mosaic/nem_mosaics.py index c1daf9e3c..94d70caaa 100644 --- a/src/apps/nem/mosaic/definitions.py +++ b/src/apps/nem/mosaic/nem_mosaics.py @@ -1,19 +1,4 @@ -# todo move to common and generate via script - - -def get_mosaic_definition(namespace_name: str, mosaic_name: str, network: int): - for m in mosaics: - if namespace_name == m["namespace"] and mosaic_name == m["mosaic"]: - if ("networks" not in m) or (network in m["networks"]): - return m - return None - - -def is_nem_xem_mosaic(namespace_name: str, mosaic_name: str): - if namespace_name == "nem" and mosaic_name == "xem": - return True - return False - +# generated using gen_nem_mosaics.py from trezor-common nem_mosaics.json - do not edit directly! mosaics = [ { @@ -21,7 +6,7 @@ mosaics = [ "ticker": " XEM", "namespace": "nem", "mosaic": "xem", - "divisibility": 6 + "divisibility": 6, }, { "name": "DIMCOIN", @@ -33,7 +18,7 @@ mosaics = [ "fee": 10, "levy_namespace": "dim", "levy_mosaic": "coin", - "networks": [ 104 ] + "networks": [104], }, { "name": "DIM TOKEN", @@ -41,7 +26,7 @@ mosaics = [ "namespace": "dim", "mosaic": "token", "divisibility": 6, - "networks": [ 104 ] + "networks": [104], }, { "name": "Breeze Token", @@ -49,7 +34,7 @@ mosaics = [ "namespace": "breeze", "mosaic": "breeze-token", "divisibility": 0, - "networks": [ 104 ] + "networks": [104], }, { "name": "PacNEM Game Credits", @@ -57,7 +42,7 @@ mosaics = [ "namespace": "pacnem", "mosaic": "heart", "divisibility": 0, - "networks": [ 104 ] + "networks": [104], }, { "name": "PacNEM Score Tokens", @@ -69,6 +54,6 @@ mosaics = [ "fee": 100, "levy_namespace": "nem", "levy_mosaic": "xem", - "networks": [ 104 ] - } + "networks": [104], + }, ] diff --git a/src/apps/nem/transfer/layout.py b/src/apps/nem/transfer/layout.py index 7bf4f948d..6b339989d 100644 --- a/src/apps/nem/transfer/layout.py +++ b/src/apps/nem/transfer/layout.py @@ -1,5 +1,5 @@ from apps.nem.layout import * -from apps.nem.mosaic.definitions import * +from apps.nem.mosaic.helpers import * from trezor.messages import NEMImportanceTransferMode from trezor.messages import NEMTransfer from trezor.messages import NEMImportanceTransfer diff --git a/tests/test_apps.nem.mosaic.py b/tests/test_apps.nem.mosaic.py index dde6c6ec7..f17d9a046 100644 --- a/tests/test_apps.nem.mosaic.py +++ b/tests/test_apps.nem.mosaic.py @@ -1,6 +1,6 @@ from common import * from apps.nem.transfer import * -from apps.nem.mosaic.definitions import get_mosaic_definition +from apps.nem.mosaic.helpers import get_mosaic_definition class TestNemMosaic(unittest.TestCase): diff --git a/tools/codegen/gen_nem_mosaics.py b/tools/codegen/gen_nem_mosaics.py new file mode 100755 index 000000000..b24dfcd77 --- /dev/null +++ b/tools/codegen/gen_nem_mosaics.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import json + + +def format_str(value): + return '"' + value + '"' + + +def format_primitive(value): + if isinstance(value, int): + return value + elif isinstance(value, str): + return format_str(value) + elif isinstance(value, list): + return value + else: + raise TypeError + + +fields = [ + 'name', + 'ticker', + 'namespace', + 'mosaic', + 'divisibility', + 'levy', + 'fee', + 'levy_namespace', + 'levy_mosaic', + 'networks', +] + +mosaics = json.load(open('../../vendor/trezor-common/defs/nem/nem_mosaics.json', 'r')) + +print('# generated using gen_nem_mosaics.py from trezor-common nem_mosaics.json - do not edit directly!') +print('') +print('mosaics = [') +for m in mosaics: + print(' {') + for name in fields: + if name in m: + print(' %s: %s,' % (format_str(name), format_primitive(m[name]))) + # else: + # print(' %s: None,' % format_str(name)) + print(' },') +print(']') From e0014a76d56479a89ee82a748507dc1460783d49 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 3 May 2018 14:04:19 +0200 Subject: [PATCH 48/53] nem: xem amount and mosaic levy fix --- src/apps/nem/transfer/layout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apps/nem/transfer/layout.py b/src/apps/nem/transfer/layout.py index 6b339989d..af399aeac 100644 --- a/src/apps/nem/transfer/layout.py +++ b/src/apps/nem/transfer/layout.py @@ -37,7 +37,7 @@ async def ask_transfer_mosaic(ctx, common: NEMTransactionCommon, transfer: NEMTr await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) if "levy" in definition and "fee" in definition: - levy_msg = _get_levy_msg(definition, mosaic.quantity, common.network) + levy_msg = _get_levy_msg(definition, mosaic_quantity, common.network) msg = Text('Confirm mosaic', ui.ICON_SEND, ui.NORMAL, 'Confirm mosaic', ui.NORMAL, 'levy fee of', @@ -63,7 +63,7 @@ async def ask_transfer_mosaic(ctx, common: NEMTransactionCommon, transfer: NEMTr def _get_xem_amount(transfer: NEMTransfer): # mosaics are empty the transfer.amount denotes the xem amount - if not len(mosaics): + if not len(transfer.mosaics): return transfer.amount # otherwise xem amount is taken from the nem xem mosaic if present for mosaic in transfer.mosaics: From 9855a50526ddc66a440f272962d1218ec4dfde05 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 4 May 2018 12:14:23 +0200 Subject: [PATCH 49/53] nem: readme --- src/apps/nem/README.md | 64 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/apps/nem/README.md diff --git a/src/apps/nem/README.md b/src/apps/nem/README.md new file mode 100644 index 000000000..0dace162f --- /dev/null +++ b/src/apps/nem/README.md @@ -0,0 +1,64 @@ +# NEM + +MAINTAINER = Tomas Susanka + +AUTHOR = Tomas Susanka + +REVIEWER = Jan Pochyla + +ADVISORS = Grégory Saive, Saleem Rashid + +----- + +This implementation of NEM for Trezor Core is mostly based on the trezor-mcu C implementation by Saleem Rashid. The protobuf messages are heavily inspired by the [NEM NSI API](https://nemproject.github.io/). + +You can read a lot about NEM in the [Technical Reference paper](https://nem.io/wp-content/themes/nem/files/NEM_techRef.pdf). + +This app supports number of NEM services (transfers, mosaics, namespaces and multisig), also called _transactions_ as the general term. Each of those services is divided into corresponding folders. In those folders we use `serialize.py` for the transaction serialization and `layout.py` to display data and to require user interactions. + +In this app we support the following: + +### Mosaics + +You can read more on mosaics [here](https://blog.nem.io/mosaics-and-namespaces-2/). Each mosaic has a name and lives under certain namespace, this identification _namespace.mosaic_ is unique in the network. + +Trezor Core supports mosaic creation and changing the mosaic's supply. + +### Namespaces + +You can read more on namespaces at the [same link](https://blog.nem.io/mosaics-and-namespaces-2/). Namespace creation is supported. + +### Transfers + +There is a number of things the term _transfer_ may refer to: + +##### Regular transfers + +The traditional transfer of the native NEM coins – XEM. + +##### Mosaic transfers + +Except XEM you can also transfer mosaics. Mosaics are _attached_ to a regular transfer. + +Each such attached mosaic has a `quantity` denoting the amount of such mosaic to be transferred. There is a catch though: the actual amount of the mosaic to be transferred isn't `quantity` but `mosaic.quantity * transfer.amount`. In other words, the quantity is multiplied by the `amount` field in transfer. This is most likely due to backwards compatibility where transfers with amount 0 where discarded. + +You can also transfer XEM and mosaics at the same time. In that case you need to attach the nem.xem mosaic. From the user point of view Trezor shows this as a regular XEM transfer. + +##### Importance transfer + +Importance transfer is a special kind of transaction, which enables (or disables) delegated harvesting. + +### Multisig + +NEM supports multisig accounts. First you convert an account into a multisig and add cosignatories. After that any cosignatory can initiate a multisig transaction. + +Multisig is a wrapper, so you can use any of the services mentioned above wrapped into a multisig transaction requiring n out of m signatures. + +A common scenario to test out multisig with NanoWallet might be: + +- Create a simple account without Trezor (account A) +- Create another account with Trezor One (account B) +- Convert A to a multisig with B as a cosigner +- Create another account with Trezor T (account C) +- Add C as a cosigner +- Try to send a transaction where B and C cosigns From 3dc1c79b1adadc84bad659e559f85e425ac58b06 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Tue, 5 Jun 2018 16:23:23 +0200 Subject: [PATCH 50/53] nem: cleanup modtrezorcrypto bindings --- .../modtrezorcrypto/modtrezorcrypto-bip32.h | 24 +++++++++---------- .../modtrezorcrypto/modtrezorcrypto-nem.h | 15 +++++++----- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h index da3fae52f..1542b2173 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h @@ -40,7 +40,6 @@ STATIC const mp_obj_type_t mod_trezorcrypto_HDNode_type; #define XPUB_MAXLEN 128 #define ADDRESS_MAXLEN 36 -#define NEM_ADDRESS_SIZE 40 /// def __init__(self, /// depth: int, @@ -309,6 +308,7 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_address(mp_obj_t self, mp_obj_t version) mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(self); uint32_t v = trezor_obj_get_uint(version); + char address[ADDRESS_MAXLEN]; hdnode_get_address(&o->hdnode, v, address, ADDRESS_MAXLEN); return mp_obj_new_str(address, strlen(address), false); @@ -322,12 +322,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_address_obj, mod_trezor STATIC mp_obj_t mod_trezorcrypto_HDNode_nem_address(mp_obj_t self, mp_obj_t network) { mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(self); - uint8_t n = mp_obj_get_int_truncated(network); - char address[NEM_ADDRESS_SIZE + 1]; + uint8_t n = trezor_obj_get_uint8(network); + + char address[NEM_ADDRESS_SIZE + 1]; // + 1 for the 0 byte if (!hdnode_get_nem_address(&o->hdnode, n, address)) { mp_raise_ValueError("Failed to compute a NEM address"); } - return mp_obj_new_str(address, strlen(address), false); + return mp_obj_new_str_of_type(&mp_type_str, (const uint8_t *)address, strlen(address)); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_nem_address_obj, mod_trezorcrypto_HDNode_nem_address); @@ -347,29 +348,28 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_nem_encrypt(size_t n_args, const mp_obj_ mp_buffer_info_t iv; mp_get_buffer_raise(args[2], &iv, MP_BUFFER_READ); if (iv.len != 16) { - mp_raise_ValueError("IV has invalid length"); + mp_raise_ValueError("iv has invalid length"); } mp_buffer_info_t salt; mp_get_buffer_raise(args[3], &salt, MP_BUFFER_READ); if (salt.len != NEM_SALT_SIZE) { - mp_raise_ValueError("Salt has invalid length"); + mp_raise_ValueError("salt has invalid length"); } mp_buffer_info_t payload; mp_get_buffer_raise(args[4], &payload, MP_BUFFER_READ); if (payload.len == 0) { - mp_raise_ValueError("Payload is empty"); + mp_raise_ValueError("payload is empty"); } - uint8_t buffer[NEM_ENCRYPTED_SIZE(payload.len)]; - - if (!hdnode_nem_encrypt(&o->hdnode, *(const ed25519_public_key *)transfer_pk.buf, iv.buf, salt.buf, payload.buf, payload.len, buffer)) { + vstr_t vstr; + vstr_init_len(&vstr, NEM_ENCRYPTED_SIZE(payload.len)); + if (!hdnode_nem_encrypt(&o->hdnode, *(const ed25519_public_key *)transfer_pk.buf, iv.buf, salt.buf, payload.buf, payload.len, (uint8_t *)vstr.buf)) { mp_raise_ValueError("HDNode nem encrypt failed"); } - return mp_obj_new_bytes(buffer, sizeof(buffer)); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorcrypto_HDNode_nem_encrypt_obj, 5, 5, mod_trezorcrypto_HDNode_nem_encrypt); - /// def ethereum_pubkeyhash(self) -> bytes: /// ''' /// Compute an Ethereum pubkeyhash (aka address) from the HD node. diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h index d3dfb7dd2..57d199029 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-nem.h @@ -18,6 +18,9 @@ */ #include "py/objstr.h" + +#include "embed/extmod/trezorobj.h" + #include "nem.h" /// def validate_address(address: str, network: int) -> bool: @@ -29,7 +32,7 @@ STATIC mp_obj_t mod_trezorcrypto_nem_validate_address(mp_obj_t address, mp_obj_t mp_buffer_info_t addr; mp_get_buffer_raise(address, &addr, MP_BUFFER_READ); - uint32_t n = mp_obj_get_int_truncated(network); + uint32_t n = trezor_obj_get_uint(network); return mp_obj_new_bool(nem_validate_address(addr.buf, n)); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_nem_validate_address_obj, mod_trezorcrypto_nem_validate_address); @@ -39,16 +42,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_nem_validate_address_obj, mod_ /// Compute a NEM address from a public key /// ''' STATIC mp_obj_t mod_trezorcrypto_nem_compute_address(mp_obj_t public_key, mp_obj_t network) { - mp_buffer_info_t p; mp_get_buffer_raise(public_key, &p, MP_BUFFER_READ); - uint32_t n = mp_obj_get_int_truncated(network); - char address[NEM_ADDRESS_SIZE + 1]; + uint32_t n = trezor_obj_get_uint(network); + + char address[NEM_ADDRESS_SIZE + 1]; // + 1 for the 0 byte if (!nem_get_address(p.buf, n, address)) { - mp_raise_ValueError("Failed to compute a NEM address from a provided public key"); + mp_raise_ValueError("Failed to compute a NEM address from provided public key"); } - return mp_obj_new_str(address, strlen(address), false); + return mp_obj_new_str_of_type(&mp_type_str, (const uint8_t *)address, strlen(address)); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_nem_compute_address_obj, mod_trezorcrypto_nem_compute_address); From 1b495324e7fdb5e76d193ad4686062a9306a9506 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 6 Jun 2018 14:04:56 +0200 Subject: [PATCH 51/53] nem: remove star-imports, fix some style --- src/apps/nem/get_address.py | 50 ++++++++-------------- src/apps/nem/helpers.py | 9 ++++ src/apps/nem/layout.py | 43 ++++++++++--------- src/apps/nem/mosaic/__init__.py | 17 +++++--- src/apps/nem/mosaic/helpers.py | 4 +- src/apps/nem/mosaic/layout.py | 19 ++++++--- src/apps/nem/mosaic/serialize.py | 27 ++++++------ src/apps/nem/multisig/__init__.py | 23 +++++----- src/apps/nem/multisig/layout.py | 14 +++--- src/apps/nem/multisig/serialize.py | 13 +++--- src/apps/nem/namespace/__init__.py | 10 +++-- src/apps/nem/namespace/layout.py | 16 ++++--- src/apps/nem/namespace/serialize.py | 7 +-- src/apps/nem/signing.py | 16 +++---- src/apps/nem/transfer/__init__.py | 22 +++++----- src/apps/nem/transfer/layout.py | 66 +++++++++++++++++------------ src/apps/nem/transfer/serialize.py | 33 +++++++++------ src/apps/nem/validators.py | 64 ++++++++++++++-------------- src/apps/nem/writers.py | 7 ++- 19 files changed, 249 insertions(+), 211 deletions(-) diff --git a/src/apps/nem/get_address.py b/src/apps/nem/get_address.py index 6dce87eaf..b1d2f8427 100644 --- a/src/apps/nem/get_address.py +++ b/src/apps/nem/get_address.py @@ -1,11 +1,14 @@ -from .validators import * -from apps.common import seed -from apps.common.confirm import confirm from trezor import ui -from trezor.messages.NEMAddress import NEMAddress from trezor.messages import ButtonRequestType +from trezor.messages.NEMAddress import NEMAddress from trezor.ui.text import Text -from trezor.utils import chunks + +from apps.common import seed +from apps.common.confirm import require_confirm + +from .layout import split_address +from .helpers import get_network_str, NEM_CURVE +from .validators import validate_network async def get_address(ctx, msg): @@ -14,35 +17,16 @@ async def get_address(ctx, msg): address = node.nem_address(network) if msg.show_display: - while True: - if await _show_address(ctx, address, network): - break + await _require_confirm_address(ctx, address, network) return NEMAddress(address=address) -async def _show_address(ctx, address: str, network: int): - lines = _split_address(address) - content = Text('Export NEM address', ui.ICON_RECEIVE, - ui.MONO, _get_network_str(network) + ' network', - ui.MONO, *lines, - icon_color=ui.GREEN) - - return await confirm( - ctx, - content, - code=ButtonRequestType.Address, - cancel_style=ui.BTN_KEY) - - -def _split_address(address: str): - return chunks(address, 17) - - -def _get_network_str(network: int) -> str: - if network == NEM_NETWORK_MAINNET: - return 'Mainnet' - elif network == NEM_NETWORK_TESTNET: - return 'Testnet' - elif network == NEM_NETWORK_MIJIN: - return 'Mijin' +async def _require_confirm_address(ctx, address: str, network: int): + lines = split_address(address) + content = Text( + 'Export NEM address', ui.ICON_RECEIVE, + ui.NORMAL, '%s network' % get_network_str(network), + ui.MONO, *lines, + icon_color=ui.GREEN) + await require_confirm(ctx, content, code=ButtonRequestType.Address) diff --git a/src/apps/nem/helpers.py b/src/apps/nem/helpers.py index dbb549058..cc2b4ad68 100644 --- a/src/apps/nem/helpers.py +++ b/src/apps/nem/helpers.py @@ -26,3 +26,12 @@ NEM_MOSAIC_AMOUNT_DIVISOR = const(1000000) NEM_MAX_PLAIN_PAYLOAD_SIZE = const(1024) NEM_MAX_ENCRYPTED_PAYLOAD_SIZE = const(960) + + +def get_network_str(network: int) -> str: + if network == NEM_NETWORK_MAINNET: + return 'Mainnet' + elif network == NEM_NETWORK_TESTNET: + return 'Testnet' + elif network == NEM_NETWORK_MIJIN: + return 'Mijin' diff --git a/src/apps/nem/layout.py b/src/apps/nem/layout.py index 73d8d8332..ce9a64a86 100644 --- a/src/apps/nem/layout.py +++ b/src/apps/nem/layout.py @@ -1,39 +1,44 @@ -from apps.common.confirm import * from trezor import ui from trezor.messages import ButtonRequestType from trezor.ui.text import Text from trezor.utils import chunks, format_amount, split_words -from .helpers import * + +from apps.common.confirm import require_confirm, require_hold_to_confirm + +from .helpers import NEM_MAX_DIVISIBILITY async def require_confirm_text(ctx, action: str): - await require_confirm_content(ctx, 'Confirm action', split_words(action, 18)) + words = split_words(action, 18) + await require_confirm_content(ctx, 'Confirm action', words) async def require_confirm_fee(ctx, action: str, fee: int): - content = [ui.NORMAL, action, - ui.BOLD, format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM'] + content = ( + ui.NORMAL, action, + ui.BOLD, '%s XEM' % format_amount(fee, NEM_MAX_DIVISIBILITY), + ) await require_confirm_content(ctx, 'Confirm fee', content) -async def require_confirm_content(ctx, headline: str, content: []): - text = Text(headline, ui.ICON_SEND, - *content, - icon_color=ui.GREEN) +async def require_confirm_content(ctx, headline: str, content: list): + text = Text(headline, ui.ICON_SEND, *content, icon_color=ui.GREEN) await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput) async def require_confirm_final(ctx, fee: int): - content = Text('Final confirm', ui.ICON_SEND, - ui.NORMAL, 'Sign this transaction', - ui.BOLD, 'and pay ' + format_amount(fee, NEM_MAX_DIVISIBILITY) + ' XEM', - ui.NORMAL, 'for network fee?', - icon_color=ui.GREEN) - await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) # we use SignTx, not ConfirmOutput, for compatibility with T1 - - -def split_address(data): - return chunks(data, 17) + content = Text( + 'Final confirm', ui.ICON_SEND, + ui.NORMAL, 'Sign this transaction', + ui.BOLD, 'and pay %s XEM' % format_amount(fee, NEM_MAX_DIVISIBILITY), + ui.NORMAL, 'for network fee?', + icon_color=ui.GREEN) + # we use SignTx, not ConfirmOutput, for compatibility with T1 + await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx) + + +def split_address(address: str): + return chunks(address, 17) def trim(payload: str, length: int) -> str: diff --git a/src/apps/nem/mosaic/__init__.py b/src/apps/nem/mosaic/__init__.py index 50a7dc361..da6ba45d5 100644 --- a/src/apps/nem/mosaic/__init__.py +++ b/src/apps/nem/mosaic/__init__.py @@ -1,12 +1,15 @@ -from .layout import * -from .serialize import * +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon +from trezor.messages.NEMMosaicCreation import NEMMosaicCreation +from trezor.messages.NEMMosaicSupplyChange import NEMMosaicSupplyChange + +from . import layout, serialize async def mosaic_creation(ctx, public_key: bytes, common: NEMTransactionCommon, creation: NEMMosaicCreation) -> bytearray: - await ask_mosaic_creation(ctx, common, creation) - return serialize_mosaic_creation(common, creation, public_key) + await layout.ask_mosaic_creation(ctx, common, creation) + return serialize.serialize_mosaic_creation(common, creation, public_key) -async def supply_change(ctx, public_key: bytes, common: NEMTransactionCommon, change: NEMMosaicSupplyChange): - await ask_supply_change(ctx, common, change) - return serialize_mosaic_supply_change(common, change, public_key) +async def supply_change(ctx, public_key: bytes, common: NEMTransactionCommon, change: NEMMosaicSupplyChange) -> bytearray: + await layout.ask_supply_change(ctx, common, change) + return serialize.serialize_mosaic_supply_change(common, change, public_key) diff --git a/src/apps/nem/mosaic/helpers.py b/src/apps/nem/mosaic/helpers.py index 9f112b39e..af56432a7 100644 --- a/src/apps/nem/mosaic/helpers.py +++ b/src/apps/nem/mosaic/helpers.py @@ -1,7 +1,7 @@ from .nem_mosaics import mosaics -def get_mosaic_definition(namespace_name: str, mosaic_name: str, network: int): +def get_mosaic_definition(namespace_name: str, mosaic_name: str, network: int) -> dict: for m in mosaics: if namespace_name == m["namespace"] and mosaic_name == m["mosaic"]: if ("networks" not in m) or (network in m["networks"]): @@ -9,7 +9,7 @@ def get_mosaic_definition(namespace_name: str, mosaic_name: str, network: int): return None -def is_nem_xem_mosaic(namespace_name: str, mosaic_name: str): +def is_nem_xem_mosaic(namespace_name: str, mosaic_name: str) -> bool: if namespace_name == "nem" and mosaic_name == "xem": return True return False diff --git a/src/apps/nem/mosaic/layout.py b/src/apps/nem/mosaic/layout.py index 842e534ce..2670dcdcb 100644 --- a/src/apps/nem/mosaic/layout.py +++ b/src/apps/nem/mosaic/layout.py @@ -1,11 +1,16 @@ -from apps.nem.layout import * -from trezor.messages import NEMTransactionCommon -from trezor.messages import NEMSupplyChangeType -from trezor.messages import NEMMosaicDefinition -from trezor.messages import NEMMosaicCreation -from trezor.messages import NEMMosaicSupplyChange -from trezor.messages import NEMMosaicLevy +from micropython import const +from trezor import ui +from trezor.messages import (NEMMosaicCreation, NEMMosaicDefinition, + NEMMosaicLevy, NEMMosaicSupplyChange, + NEMSupplyChangeType, NEMTransactionCommon) +from trezor.ui.confirm import ConfirmDialog from trezor.ui.scroll import Scrollpage, animate_swipe, paginate +from trezor.ui.text import Text +from trezor.utils import split_words + +from ..layout import (require_confirm_content, require_confirm_fee, + require_confirm_final, require_confirm_text, + split_address, trim) async def ask_mosaic_creation(ctx, common: NEMTransactionCommon, creation: NEMMosaicCreation): diff --git a/src/apps/nem/mosaic/serialize.py b/src/apps/nem/mosaic/serialize.py index a823d5e87..aee2a97d4 100644 --- a/src/apps/nem/mosaic/serialize.py +++ b/src/apps/nem/mosaic/serialize.py @@ -1,8 +1,11 @@ -from apps.nem.writers import * -from apps.nem.helpers import * -from trezor.messages.NEMTransactionCommon import NEMTransactionCommon -from trezor.messages.NEMMosaicSupplyChange import NEMMosaicSupplyChange from trezor.messages.NEMMosaicCreation import NEMMosaicCreation +from trezor.messages.NEMMosaicSupplyChange import NEMMosaicSupplyChange +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon + +from ..helpers import (NEM_TRANSACTION_TYPE_MOSAIC_CREATION, + NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE) +from ..writers import (write_bytes_with_length, write_common, write_uint32, + write_uint64) def serialize_mosaic_creation(common: NEMTransactionCommon, creation: NEMMosaicCreation, public_key: bytes): @@ -17,10 +20,10 @@ def serialize_mosaic_creation(common: NEMTransactionCommon, creation: NEMMosaicC write_bytes_with_length(mosaics_w, bytearray(creation.definition.description)) write_uint32(mosaics_w, 4) # number of properties - _write_property(mosaics_w, "divisibility", creation.definition.divisibility) - _write_property(mosaics_w, "initialSupply", creation.definition.supply) - _write_property(mosaics_w, "supplyMutable", creation.definition.mutable_supply) - _write_property(mosaics_w, "transferable", creation.definition.transferable) + _write_property(mosaics_w, 'divisibility', creation.definition.divisibility) + _write_property(mosaics_w, 'initialSupply', creation.definition.supply) + _write_property(mosaics_w, 'supplyMutable', creation.definition.mutable_supply) + _write_property(mosaics_w, 'transferable', creation.definition.transferable) if creation.definition.levy: levy_identifier_length = 4 + len(creation.definition.levy_namespace) + 4 + len(creation.definition.levy_mosaic) @@ -58,15 +61,15 @@ def serialize_mosaic_supply_change(common: NEMTransactionCommon, change: NEMMosa def _write_property(w: bytearray, name: str, value): if value is None: - if name in ['divisibility', 'initialSupply']: + if name in ('divisibility', 'initialSupply'): value = 0 - elif name in ['supplyMutable', 'transferable']: + elif name in ('supplyMutable', 'transferable'): value = False if type(value) == bool: if value: - value = "true" + value = 'true' else: - value = "false" + value = 'false' elif type(value) == int: value = str(value) elif type(value) != str: diff --git a/src/apps/nem/multisig/__init__.py b/src/apps/nem/multisig/__init__.py index 08dfe91d5..2f98386f3 100644 --- a/src/apps/nem/multisig/__init__.py +++ b/src/apps/nem/multisig/__init__.py @@ -1,21 +1,20 @@ -from .serialize import * -from .layout import * from trezor.messages.NEMAggregateModification import NEMAggregateModification +from trezor.messages.NEMSignTx import NEMSignTx +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon + +from . import layout, serialize async def ask(ctx, msg: NEMSignTx): - await ask_multisig(ctx, msg) + await layout.ask_multisig(ctx, msg) def initiate(public_key, common: NEMTransactionCommon, inner_tx: bytes) -> bytes: - return serialize_multisig(common, public_key, inner_tx) + return serialize.serialize_multisig(common, public_key, inner_tx) def cosign(public_key, common: NEMTransactionCommon, inner_tx: bytes, signer: bytes) -> bytes: - return serialize_multisig_signature(common, - public_key, - inner_tx, - signer) + return serialize.serialize_multisig_signature(common, public_key, inner_tx, signer) async def aggregate_modification(ctx, @@ -23,12 +22,12 @@ async def aggregate_modification(ctx, common: NEMTransactionCommon, aggr: NEMAggregateModification, multisig: bool): - await ask_aggregate_modification(ctx, common, aggr, multisig) - w = serialize_aggregate_modification(common, aggr, public_key) + await layout.ask_aggregate_modification(ctx, common, aggr, multisig) + w = serialize.serialize_aggregate_modification(common, aggr, public_key) for m in aggr.modifications: - serialize_cosignatory_modification(w, m.type, m.public_key) + serialize.serialize_cosignatory_modification(w, m.type, m.public_key) if aggr.relative_change: - serialize_minimum_cosignatories(w, aggr.relative_change) + serialize.serialize_minimum_cosignatories(w, aggr.relative_change) return w diff --git a/src/apps/nem/multisig/layout.py b/src/apps/nem/multisig/layout.py index d89dc1bc2..ff800dde0 100644 --- a/src/apps/nem/multisig/layout.py +++ b/src/apps/nem/multisig/layout.py @@ -1,9 +1,13 @@ -from apps.nem.layout import * -from trezor.messages import NEMAggregateModification -from trezor.messages import NEMModificationType -from trezor.messages import NEMSignTx -from trezor.messages import NEMTransactionCommon +from trezor import ui from trezor.crypto import nem +from trezor.messages import (ButtonRequestType, NEMAggregateModification, + NEMModificationType, NEMSignTx, + NEMTransactionCommon) +from trezor.ui.text import Text + +from ..layout import (require_confirm, require_confirm_fee, + require_confirm_final, require_confirm_text, + split_address) async def ask_multisig(ctx, msg: NEMSignTx): diff --git a/src/apps/nem/multisig/serialize.py b/src/apps/nem/multisig/serialize.py index a7e6eee83..98358fa9c 100644 --- a/src/apps/nem/multisig/serialize.py +++ b/src/apps/nem/multisig/serialize.py @@ -1,10 +1,11 @@ -from apps.nem.writers import * -from apps.nem.helpers import * -from trezor.messages.NEMSignTx import NEMSignTx -from trezor.crypto import hashlib -from trezor.crypto import nem -from trezor.messages.NEMTransactionCommon import NEMTransactionCommon +from trezor.crypto import hashlib, nem from trezor.messages.NEMAggregateModification import NEMAggregateModification +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon + +from ..helpers import (NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, + NEM_TRANSACTION_TYPE_MULTISIG, + NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE) +from ..writers import write_bytes_with_length, write_common, write_uint32 def serialize_multisig(common: NEMTransactionCommon, public_key: bytes, inner: bytes): diff --git a/src/apps/nem/namespace/__init__.py b/src/apps/nem/namespace/__init__.py index bdf8975b4..662c30b5c 100644 --- a/src/apps/nem/namespace/__init__.py +++ b/src/apps/nem/namespace/__init__.py @@ -1,7 +1,9 @@ -from .layout import * -from .serialize import * +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon +from trezor.messages.NEMProvisionNamespace import NEMProvisionNamespace + +from . import layout, serialize async def namespace(ctx, public_key: bytes, common: NEMTransactionCommon, namespace: NEMProvisionNamespace) -> bytearray: - await ask_provision_namespace(ctx, common, namespace) - return serialize_provision_namespace(common, namespace, public_key) + await layout.ask_provision_namespace(ctx, common, namespace) + return serialize.serialize_provision_namespace(common, namespace, public_key) diff --git a/src/apps/nem/namespace/layout.py b/src/apps/nem/namespace/layout.py index ae7a57b85..012cdec93 100644 --- a/src/apps/nem/namespace/layout.py +++ b/src/apps/nem/namespace/layout.py @@ -1,18 +1,20 @@ -from apps.nem.layout import * -from trezor.messages import NEMTransactionCommon -from trezor.messages import NEMProvisionNamespace +from trezor import ui +from trezor.messages import NEMProvisionNamespace, NEMTransactionCommon + +from ..layout import (require_confirm_content, require_confirm_fee, + require_confirm_final) async def ask_provision_namespace(ctx, common: NEMTransactionCommon, namespace: NEMProvisionNamespace): if namespace.parent: - content = [ui.NORMAL, 'Create namespace', + content = (ui.NORMAL, 'Create namespace', ui.BOLD, namespace.namespace, ui.NORMAL, 'under namespace', - ui.BOLD, namespace.parent] + ui.BOLD, namespace.parent) await require_confirm_content(ctx, 'Confirm namespace', content) else: - content = [ui.NORMAL, 'Create namespace', - ui.BOLD, namespace.namespace] + content = (ui.NORMAL, 'Create namespace', + ui.BOLD, namespace.namespace) await require_confirm_content(ctx, 'Confirm namespace', content) await require_confirm_fee(ctx, 'Confirm rental fee', namespace.fee) diff --git a/src/apps/nem/namespace/serialize.py b/src/apps/nem/namespace/serialize.py index 3df49d121..0cf6fd31b 100644 --- a/src/apps/nem/namespace/serialize.py +++ b/src/apps/nem/namespace/serialize.py @@ -1,7 +1,8 @@ -from apps.nem.helpers import * -from apps.nem.writers import * -from trezor.messages.NEMTransactionCommon import NEMTransactionCommon from trezor.messages.NEMProvisionNamespace import NEMProvisionNamespace +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon + +from ..helpers import NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE +from ..writers import write_bytes_with_length, write_common, write_uint32, write_uint64 def serialize_provision_namespace(common: NEMTransactionCommon, namespace: NEMProvisionNamespace, public_key: bytes) -> bytearray: diff --git a/src/apps/nem/signing.py b/src/apps/nem/signing.py index 360423aaf..d6739a487 100644 --- a/src/apps/nem/signing.py +++ b/src/apps/nem/signing.py @@ -1,13 +1,11 @@ -from apps.nem import namespace -from apps.nem import transfer -from apps.nem import mosaic -from apps.nem import multisig -from apps.nem.validators import validate -from apps.nem.helpers import * -from apps.common import seed -from trezor.messages.NEMSignTx import NEMSignTx -from trezor.messages.NEMSignedTx import NEMSignedTx from trezor.crypto.curve import ed25519 +from trezor.messages.NEMSignedTx import NEMSignedTx +from trezor.messages.NEMSignTx import NEMSignTx +from apps.common import seed + +from . import mosaic, multisig, namespace, transfer +from .helpers import NEM_CURVE, NEM_HASH_ALG +from .validators import validate async def sign_tx(ctx, msg: NEMSignTx): diff --git a/src/apps/nem/transfer/__init__.py b/src/apps/nem/transfer/__init__.py index eceaed2a3..f9fcc4173 100644 --- a/src/apps/nem/transfer/__init__.py +++ b/src/apps/nem/transfer/__init__.py @@ -1,20 +1,22 @@ -from .layout import * -from .serialize import * -from trezor.messages.NEMSignTx import NEMTransfer +from trezor.messages.NEMTransfer import NEMTransfer +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon +from trezor.messages.NEMImportanceTransfer import NEMImportanceTransfer + +from . import layout, serialize async def transfer(ctx, public_key: bytes, common: NEMTransactionCommon, transfer: NEMTransfer, node): - transfer.mosaics = canonicalize_mosaics(transfer.mosaics) + transfer.mosaics = serialize.canonicalize_mosaics(transfer.mosaics) + payload, encrypted = serialize.get_transfer_payload(transfer, node) - payload, encrypted = get_transfer_payload(transfer, node) - await ask_transfer(ctx, common, transfer, payload, encrypted) + await layout.ask_transfer(ctx, common, transfer, payload, encrypted) - w = serialize_transfer(common, transfer, public_key, payload, encrypted) + w = serialize.serialize_transfer(common, transfer, public_key, payload, encrypted) for mosaic in transfer.mosaics: - serialize_mosaic(w, mosaic.namespace, mosaic.mosaic, mosaic.quantity) + serialize.serialize_mosaic(w, mosaic.namespace, mosaic.mosaic, mosaic.quantity) return w async def importance_transfer(ctx, public_key: bytes, common: NEMTransactionCommon, imp: NEMImportanceTransfer): - await ask_importance_transfer(ctx, common, imp) - return serialize_importance_transfer(common, imp, public_key) + await layout.ask_importance_transfer(ctx, common, imp) + return serialize.serialize_importance_transfer(common, imp, public_key) diff --git a/src/apps/nem/transfer/layout.py b/src/apps/nem/transfer/layout.py index af399aeac..5d459c1db 100644 --- a/src/apps/nem/transfer/layout.py +++ b/src/apps/nem/transfer/layout.py @@ -1,22 +1,24 @@ -from apps.nem.layout import * -from apps.nem.mosaic.helpers import * -from trezor.messages import NEMImportanceTransferMode -from trezor.messages import NEMTransfer -from trezor.messages import NEMImportanceTransfer -from trezor.messages import NEMTransactionCommon -from trezor.messages import NEMMosaic -from trezor.messages import NEMMosaicLevy +from trezor import ui +from trezor.messages import (ButtonRequestType, NEMImportanceTransfer, + NEMImportanceTransferMode, NEMMosaic, + NEMMosaicLevy, NEMTransactionCommon, NEMTransfer) +from trezor.ui.text import Text +from trezor.utils import format_amount, split_words +from apps.common.confirm import require_confirm -async def ask_transfer(ctx, common: NEMTransactionCommon, transfer: NEMTransfer, payload, encrypted): +from ..helpers import (NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE, + NEM_MAX_DIVISIBILITY, NEM_MOSAIC_AMOUNT_DIVISOR) +from ..layout import require_confirm_final, require_confirm_text, split_address +from ..mosaic.helpers import get_mosaic_definition, is_nem_xem_mosaic + + +async def ask_transfer(ctx, common: NEMTransactionCommon, transfer: NEMTransfer, payload: bytes, encrypted: bool): if payload: await _require_confirm_payload(ctx, transfer.payload, encrypted) - for mosaic in transfer.mosaics: await ask_transfer_mosaic(ctx, common, transfer, mosaic) - await _require_confirm_transfer(ctx, transfer.recipient, _get_xem_amount(transfer)) - await require_confirm_final(ctx, common.fee) @@ -29,20 +31,22 @@ async def ask_transfer_mosaic(ctx, common: NEMTransactionCommon, transfer: NEMTr if definition: msg = Text('Confirm mosaic', ui.ICON_SEND, - ui.NORMAL, 'Confirm transfer of', - ui.BOLD, format_amount(mosaic_quantity, definition["divisibility"]) + definition["ticker"], + 'Confirm transfer of', + ui.BOLD, format_amount(mosaic_quantity, definition['divisibility']) + definition['ticker'], ui.NORMAL, 'of', - ui.BOLD, definition["name"], + ui.BOLD, definition['name'], icon_color=ui.GREEN) + await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) - if "levy" in definition and "fee" in definition: + if 'levy' in definition and 'fee' in definition: levy_msg = _get_levy_msg(definition, mosaic_quantity, common.network) msg = Text('Confirm mosaic', ui.ICON_SEND, - ui.NORMAL, 'Confirm mosaic', - ui.NORMAL, 'levy fee of', + 'Confirm mosaic', + 'levy fee of', ui.BOLD, levy_msg, icon_color=ui.GREEN) + await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) else: @@ -54,16 +58,16 @@ async def ask_transfer_mosaic(ctx, common: NEMTransactionCommon, transfer: NEMTr msg = Text('Confirm mosaic', ui.ICON_SEND, ui.NORMAL, 'Confirm transfer of', - ui.BOLD, str(mosaic_quantity) + ' raw units', + ui.BOLD, '%s raw units' % mosaic_quantity, ui.NORMAL, 'of', - ui.BOLD, mosaic.namespace + '.' + mosaic.mosaic, + ui.BOLD, '%s.%s' % (mosaic.namespace, mosaic.mosaic), icon_color=ui.GREEN) await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) def _get_xem_amount(transfer: NEMTransfer): - # mosaics are empty the transfer.amount denotes the xem amount - if not len(transfer.mosaics): + # if mosaics are empty the transfer.amount denotes the xem amount + if not transfer.mosaics: return transfer.amount # otherwise xem amount is taken from the nem xem mosaic if present for mosaic in transfer.mosaics: @@ -74,12 +78,18 @@ def _get_xem_amount(transfer: NEMTransfer): def _get_levy_msg(mosaic_definition, quantity: int, network: int) -> str: - levy_definition = get_mosaic_definition(mosaic_definition["levy_namespace"], mosaic_definition["levy_mosaic"], network) - if mosaic_definition["levy"] == NEMMosaicLevy.MosaicLevy_Absolute: - levy_fee = mosaic_definition["fee"] + levy_definition = get_mosaic_definition( + mosaic_definition['levy_namespace'], + mosaic_definition['levy_mosaic'], + network) + if mosaic_definition['levy'] == NEMMosaicLevy.MosaicLevy_Absolute: + levy_fee = mosaic_definition['fee'] else: - levy_fee = quantity * mosaic_definition["fee"] / NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE - return format_amount(levy_fee, levy_definition["divisibility"]) + levy_definition["ticker"] + levy_fee = quantity * mosaic_definition['fee'] / NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE + return format_amount( + levy_fee, + levy_definition['divisibility'] + ) + levy_definition['ticker'] async def ask_importance_transfer(ctx, common: NEMTransactionCommon, imp: NEMImportanceTransfer): @@ -93,7 +103,7 @@ async def ask_importance_transfer(ctx, common: NEMTransactionCommon, imp: NEMImp async def _require_confirm_transfer(ctx, recipient, value): content = Text('Confirm transfer', ui.ICON_SEND, - ui.BOLD, 'Send ' + format_amount(value, NEM_MAX_DIVISIBILITY) + ' XEM', + ui.BOLD, 'Send %s XEM' % format_amount(value, NEM_MAX_DIVISIBILITY), ui.NORMAL, 'to', ui.MONO, *split_address(recipient), icon_color=ui.GREEN) diff --git a/src/apps/nem/transfer/serialize.py b/src/apps/nem/transfer/serialize.py index ec869c2e3..9086c32bb 100644 --- a/src/apps/nem/transfer/serialize.py +++ b/src/apps/nem/transfer/serialize.py @@ -1,15 +1,21 @@ -from apps.nem.writers import * -from apps.nem.helpers import * -from trezor.messages.NEMMosaic import NEMMosaic +from trezor.crypto import random from trezor.messages.NEMImportanceTransfer import NEMImportanceTransfer +from trezor.messages.NEMMosaic import NEMMosaic +from trezor.messages.NEMTransactionCommon import NEMTransactionCommon from trezor.messages.NEMTransfer import NEMTransfer -from trezor.crypto import random + +from ..helpers import (AES_BLOCK_SIZE, NEM_SALT_SIZE, + NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER, + NEM_TRANSACTION_TYPE_TRANSFER) +from ..writers import write_bytes_with_length, write_common, write_uint32, write_uint64 -def serialize_transfer(common: NEMTransactionCommon, transfer: NEMTransfer, - public_key: bytes, payload: bytes=None, encrypted: bool=False) -> bytearray: - tx = write_common(common, - bytearray(public_key), +def serialize_transfer(common: NEMTransactionCommon, + transfer: NEMTransfer, + public_key: bytes, + payload: bytes = None, + encrypted: bool = False) -> bytearray: + tx = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_TRANSFER, _get_version(common.network, transfer.mosaics)) @@ -43,8 +49,11 @@ def serialize_mosaic(w: bytearray, namespace: str, mosaic: str, quantity: int): write_uint64(w, quantity) -def serialize_importance_transfer(common: NEMTransactionCommon, imp: NEMImportanceTransfer, public_key: bytes)-> bytearray: - w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER) +def serialize_importance_transfer(common: NEMTransactionCommon, + imp: NEMImportanceTransfer, + public_key: bytes) -> bytearray: + w = write_common(common, bytearray(public_key), + NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER) write_uint32(w, imp.mode) write_bytes_with_length(w, bytearray(imp.public_key)) @@ -56,7 +65,7 @@ def get_transfer_payload(transfer: NEMTransfer, node) -> [bytes, bool]: encrypted = False if transfer.public_key is not None: if payload is None: - raise ValueError("Public key provided but no payload to encrypt") + raise ValueError('Public key provided but no payload to encrypt') payload = _encrypt(node, transfer.public_key, transfer.payload) encrypted = True @@ -90,7 +99,7 @@ def are_mosaics_equal(a: NEMMosaic, b: NEMMosaic) -> bool: def merge_mosaics(mosaics: list) -> list: - if not len(mosaics): + if not mosaics: return list() ret = list() for i in mosaics: diff --git a/src/apps/nem/validators.py b/src/apps/nem/validators.py index e7f8cc439..c5d11c0e4 100644 --- a/src/apps/nem/validators.py +++ b/src/apps/nem/validators.py @@ -1,18 +1,19 @@ -from apps.nem.helpers import * -from trezor.messages import NEMModificationType -from trezor.messages.NEMSignTx import NEMAggregateModification -from trezor.messages.NEMSignTx import NEMImportanceTransfer -from trezor.messages.NEMSignTx import NEMMosaicCreation -from trezor.messages.NEMSignTx import NEMMosaicSupplyChange -from trezor.messages.NEMSignTx import NEMProvisionNamespace -from trezor.messages.NEMSignTx import NEMSignTx -from trezor.messages.NEMSignTx import NEMTransactionCommon -from trezor.messages.NEMSignTx import NEMTransfer from trezor.crypto import nem +from trezor.messages import NEMModificationType +from trezor.messages.NEMSignTx import (NEMAggregateModification, + NEMImportanceTransfer, + NEMMosaicCreation, + NEMMosaicSupplyChange, + NEMProvisionNamespace, NEMSignTx, + NEMTransactionCommon, NEMTransfer) +from .helpers import (NEM_MAX_DIVISIBILITY, NEM_MAX_ENCRYPTED_PAYLOAD_SIZE, + NEM_MAX_PLAIN_PAYLOAD_SIZE, NEM_MAX_SUPPLY, + NEM_NETWORK_MAINNET, NEM_NETWORK_MIJIN, + NEM_NETWORK_TESTNET, NEM_PUBLIC_KEY_SIZE) -def validate(msg: NEMSignTx): +def validate(msg: NEMSignTx): if msg.transaction is None: raise ValueError('No common provided') @@ -42,13 +43,9 @@ def validate(msg: NEMSignTx): def validate_network(network: int) -> int: if network is None: return NEM_NETWORK_MAINNET - _validate_network(network) - return network - - -def _validate_network(network: int): - if network not in [NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN]: + if network not in (NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET, NEM_NETWORK_MIJIN): raise ValueError('Invalid NEM network') + return network def _validate_single_tx(msg: NEMSignTx): @@ -65,8 +62,7 @@ def _validate_single_tx(msg: NEMSignTx): raise ValueError('More than one transaction provided') -def _validate_common(common: NEMTransactionCommon, inner: bool=False): - +def _validate_common(common: NEMTransactionCommon, inner: bool = False): common.network = validate_network(common.network) err = None @@ -85,19 +81,19 @@ def _validate_common(common: NEMTransactionCommon, inner: bool=False): if err: if inner: - raise ValueError('No ' + err + ' provided in inner transaction') + raise ValueError('No %s provided in inner transaction' % err) else: - raise ValueError('No ' + err + ' provided') + raise ValueError('No %s provided' % err) if common.signer is not None: _validate_public_key(common.signer, 'Invalid signer public key in inner transaction') -def _validate_public_key(public_key: bytes, err_msg): +def _validate_public_key(public_key: bytes, err_msg: str): if not public_key: - raise ValueError(err_msg + ' (none provided)') + raise ValueError('%s (none provided)' % err_msg) if len(public_key) != NEM_PUBLIC_KEY_SIZE: - raise ValueError(err_msg + ' (invalid length)') + raise ValueError('%s (invalid length)' % err_msg) def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer): @@ -107,24 +103,26 @@ def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer): def _validate_multisig(multisig: NEMTransactionCommon, network: int): - _validate_public_key(multisig.signer, 'Invalid multisig signer public key provided') if multisig.network != network: raise ValueError('Inner transaction network is different') + _validate_public_key(multisig.signer, 'Invalid multisig signer public key provided') -def _validate_aggregate_modification(aggregate_modification: NEMAggregateModification, creation: bool=False): +def _validate_aggregate_modification( + aggregate_modification: NEMAggregateModification, + creation: bool = False): - if creation and len(aggregate_modification.modifications) == 0: + if creation and not aggregate_modification.modifications: raise ValueError('No modifications provided') for m in aggregate_modification.modifications: if not m.type: raise ValueError('No modification type provided') - if m.type not in [ + if m.type not in ( NEMModificationType.CosignatoryModification_Add, NEMModificationType.CosignatoryModification_Delete - ]: - raise ValueError('Unknown aggregate modification ') + ): + raise ValueError('Unknown aggregate modification') if creation and m.type == NEMModificationType.CosignatoryModification_Delete: raise ValueError('Cannot remove cosignatory when converting account') _validate_public_key(m.public_key, 'Invalid cosignatory public key provided') @@ -156,7 +154,7 @@ def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int): raise ValueError('Name not allowed in mosaic creation transactions') if mosaic_creation.definition.ticker is not None: raise ValueError('Ticker not allowed in mosaic creation transactions') - if len(mosaic_creation.definition.networks): + if mosaic_creation.definition.networks: raise ValueError('Networks not allowed in mosaic creation transactions') if mosaic_creation.definition.namespace is None: @@ -165,9 +163,9 @@ def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int): raise ValueError('No mosaic name provided') if mosaic_creation.definition.supply is not None and mosaic_creation.definition.divisibility is None: - raise ValueError('Definition divisibility needs to be provided when supply is') + raise ValueError('Definition divisibility needs to be provided when supply is') if mosaic_creation.definition.supply is None and mosaic_creation.definition.divisibility is not None: - raise ValueError('Definition supply needs to be provided when divisibility is') + raise ValueError('Definition supply needs to be provided when divisibility is') if mosaic_creation.definition.levy is not None: if mosaic_creation.definition.fee is None: diff --git a/src/apps/nem/writers.py b/src/apps/nem/writers.py index dfd601551..5bcb6c0c5 100644 --- a/src/apps/nem/writers.py +++ b/src/apps/nem/writers.py @@ -1,4 +1,3 @@ - from trezor.messages.NEMTransactionCommon import NEMTransactionCommon @@ -29,8 +28,12 @@ def write_bytes_with_length(w, buf: bytearray): write_bytes(w, buf) -def write_common(common: NEMTransactionCommon, public_key: bytearray, transaction_type: int, version: int=None) -> bytearray: +def write_common(common: NEMTransactionCommon, + public_key: bytearray, + transaction_type: int, + version: int = None) -> bytearray: ret = bytearray() + write_uint32(ret, transaction_type) if version is None: version = common.network << 24 | 1 From df7a8977ea76afefc555aee94c54d5ce78178dda Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Wed, 6 Jun 2018 14:05:02 +0200 Subject: [PATCH 52/53] nem: allow tests --- pytest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 0f83d26ca..8f2bb1509 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,4 @@ [pytest] addopts = --pyargs trezorlib.tests.device_tests xfail_strict = true -run_xfail = lisk +run_xfail = lisk nem From b13dba115a05d0c671edd84e3f155d78021da40f Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Thu, 7 Jun 2018 13:18:45 +0200 Subject: [PATCH 53/53] nem: fix unit tests --- tests/test_apps.nem.mosaic.py | 4 +++- tests/test_apps.nem.mosaic_creation.py | 3 +++ tests/test_apps.nem.mosaic_supply_change.py | 3 +++ tests/test_apps.nem.multisig.aggregate_modification.py | 3 +++ tests/test_apps.nem.multisig.py | 4 ++++ tests/test_apps.nem.namespace.py | 3 +++ tests/test_apps.nem.transfer.py | 5 ++++- 7 files changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/test_apps.nem.mosaic.py b/tests/test_apps.nem.mosaic.py index f17d9a046..7cee25d3e 100644 --- a/tests/test_apps.nem.mosaic.py +++ b/tests/test_apps.nem.mosaic.py @@ -1,6 +1,8 @@ from common import * -from apps.nem.transfer import * +from trezor.messages.NEMMosaic import NEMMosaic from apps.nem.mosaic.helpers import get_mosaic_definition +from apps.nem.transfer import * +from apps.nem.transfer.serialize import * class TestNemMosaic(unittest.TestCase): diff --git a/tests/test_apps.nem.mosaic_creation.py b/tests/test_apps.nem.mosaic_creation.py index 2c445673b..6c387bfab 100644 --- a/tests/test_apps.nem.mosaic_creation.py +++ b/tests/test_apps.nem.mosaic_creation.py @@ -1,6 +1,9 @@ from common import * +from apps.nem.helpers import * from apps.nem.mosaic import * +from apps.nem.mosaic.serialize import * + from trezor.crypto import hashlib from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMMosaicCreation import NEMMosaicCreation diff --git a/tests/test_apps.nem.mosaic_supply_change.py b/tests/test_apps.nem.mosaic_supply_change.py index 834ed941e..424b0a67e 100644 --- a/tests/test_apps.nem.mosaic_supply_change.py +++ b/tests/test_apps.nem.mosaic_supply_change.py @@ -1,6 +1,9 @@ from common import * +from apps.nem.helpers import * from apps.nem.mosaic import * +from apps.nem.mosaic.serialize import * + from trezor.crypto import hashlib from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMMosaicSupplyChange import NEMMosaicSupplyChange diff --git a/tests/test_apps.nem.multisig.aggregate_modification.py b/tests/test_apps.nem.multisig.aggregate_modification.py index 81258f9b2..3b145d3a9 100644 --- a/tests/test_apps.nem.multisig.aggregate_modification.py +++ b/tests/test_apps.nem.multisig.aggregate_modification.py @@ -1,6 +1,9 @@ from common import * +from apps.nem.helpers import * from apps.nem.multisig import * +from apps.nem.multisig.serialize import * + from trezor.crypto import hashlib from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMAggregateModification import NEMAggregateModification diff --git a/tests/test_apps.nem.multisig.py b/tests/test_apps.nem.multisig.py index 598034438..df85f3b7a 100644 --- a/tests/test_apps.nem.multisig.py +++ b/tests/test_apps.nem.multisig.py @@ -1,7 +1,11 @@ from common import * +from apps.nem.helpers import * from apps.nem.multisig import * +from apps.nem.multisig.serialize import * from apps.nem.namespace import * +from apps.nem.namespace.serialize import * + from trezor.messages.NEMSignTx import NEMSignTx from trezor.messages.NEMAggregateModification import NEMAggregateModification from trezor.messages.NEMProvisionNamespace import NEMProvisionNamespace diff --git a/tests/test_apps.nem.namespace.py b/tests/test_apps.nem.namespace.py index 7165158b4..c9375800d 100644 --- a/tests/test_apps.nem.namespace.py +++ b/tests/test_apps.nem.namespace.py @@ -1,6 +1,9 @@ from common import * +from apps.nem.helpers import * from apps.nem.namespace import * +from apps.nem.namespace.serialize import * + from trezor.crypto import hashlib from trezor.messages.NEMProvisionNamespace import NEMProvisionNamespace from trezor.messages.NEMSignTx import NEMSignTx diff --git a/tests/test_apps.nem.transfer.py b/tests/test_apps.nem.transfer.py index 0ffa04467..356dc27ba 100644 --- a/tests/test_apps.nem.transfer.py +++ b/tests/test_apps.nem.transfer.py @@ -1,7 +1,10 @@ from common import * -from apps.nem.transfer import * +from apps.nem.helpers import * from apps.nem.mosaic import * +from apps.nem.transfer import * +from apps.nem.transfer.serialize import * + from trezor.crypto import hashlib from trezor.messages.NEMTransfer import NEMTransfer from trezor.messages.NEMSignTx import NEMSignTx