commit
b1a870177c
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "py/objstr.h"
|
||||||
|
|
||||||
|
#include "embed/extmod/trezorobj.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 = 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);
|
||||||
|
|
||||||
|
/// 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 = 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 provided public key");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
};
|
@ -1,4 +1,4 @@
|
|||||||
[pytest]
|
[pytest]
|
||||||
addopts = --pyargs trezorlib.tests.device_tests
|
addopts = --pyargs trezorlib.tests.device_tests
|
||||||
xfail_strict = true
|
xfail_strict = true
|
||||||
run_xfail = lisk
|
run_xfail = lisk nem
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
from trezor.wire import register, protobuf_workflow
|
||||||
|
from trezor.messages.wire_types import NEMGetAddress, NEMSignTx
|
||||||
|
|
||||||
|
|
||||||
|
def dispatch_NemGetAddress(*args, **kwargs):
|
||||||
|
from .get_address import get_address
|
||||||
|
return get_address(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def dispatch_NemSignTx(*args, **kwargs):
|
||||||
|
from .signing import sign_tx
|
||||||
|
return sign_tx(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def boot():
|
||||||
|
register(NEMGetAddress, protobuf_workflow, dispatch_NemGetAddress)
|
||||||
|
register(NEMSignTx, protobuf_workflow, dispatch_NemSignTx)
|
@ -0,0 +1,32 @@
|
|||||||
|
from trezor import ui
|
||||||
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.messages.NEMAddress import NEMAddress
|
||||||
|
from trezor.ui.text import Text
|
||||||
|
|
||||||
|
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):
|
||||||
|
network = validate_network(msg.network)
|
||||||
|
node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE)
|
||||||
|
address = node.nem_address(network)
|
||||||
|
|
||||||
|
if msg.show_display:
|
||||||
|
await _require_confirm_address(ctx, address, network)
|
||||||
|
|
||||||
|
return NEMAddress(address=address)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
@ -0,0 +1,37 @@
|
|||||||
|
from micropython import const
|
||||||
|
|
||||||
|
NEM_NETWORK_MAINNET = const(0x68)
|
||||||
|
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)
|
||||||
|
|
||||||
|
NEM_MAX_DIVISIBILITY = const(6)
|
||||||
|
NEM_MAX_SUPPLY = const(9000000000)
|
||||||
|
|
||||||
|
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_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'
|
@ -0,0 +1,47 @@
|
|||||||
|
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 apps.common.confirm import require_confirm, require_hold_to_confirm
|
||||||
|
|
||||||
|
from .helpers import NEM_MAX_DIVISIBILITY
|
||||||
|
|
||||||
|
|
||||||
|
async def require_confirm_text(ctx, action: str):
|
||||||
|
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, '%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: 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 %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:
|
||||||
|
if len(payload) > length:
|
||||||
|
return payload[:length] + '..'
|
||||||
|
return payload
|
@ -0,0 +1,15 @@
|
|||||||
|
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 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) -> bytearray:
|
||||||
|
await layout.ask_supply_change(ctx, common, change)
|
||||||
|
return serialize.serialize_mosaic_supply_change(common, change, public_key)
|
@ -0,0 +1,15 @@
|
|||||||
|
from .nem_mosaics import mosaics
|
||||||
|
|
||||||
|
|
||||||
|
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"]):
|
||||||
|
return m
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_nem_xem_mosaic(namespace_name: str, mosaic_name: str) -> bool:
|
||||||
|
if namespace_name == "nem" and mosaic_name == "xem":
|
||||||
|
return True
|
||||||
|
return False
|
@ -0,0 +1,123 @@
|
|||||||
|
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):
|
||||||
|
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, common.fee)
|
||||||
|
|
||||||
|
|
||||||
|
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, msg)
|
||||||
|
|
||||||
|
await require_confirm_final(ctx, common.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, *split_words(trim(definition.description, 70), 22))
|
||||||
|
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 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,
|
||||||
|
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
|
@ -0,0 +1,59 @@
|
|||||||
|
# generated using gen_nem_mosaics.py from trezor-common nem_mosaics.json - do not edit directly!
|
||||||
|
|
||||||
|
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],
|
||||||
|
},
|
||||||
|
]
|
@ -0,0 +1,79 @@
|
|||||||
|
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):
|
||||||
|
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(creation.definition.namespace) + 4 + len(creation.definition.mosaic)
|
||||||
|
write_uint32(mosaics_w, identifier_length)
|
||||||
|
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', 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)
|
||||||
|
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(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(creation.sink))
|
||||||
|
write_uint64(w, creation.fee)
|
||||||
|
|
||||||
|
return w
|
||||||
|
|
||||||
|
|
||||||
|
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(change.namespace) + 4 + len(change.mosaic)
|
||||||
|
write_uint32(w, identifier_length)
|
||||||
|
write_bytes_with_length(w, bytearray(change.namespace))
|
||||||
|
write_bytes_with_length(w, bytearray(change.mosaic))
|
||||||
|
|
||||||
|
write_uint32(w, change.type)
|
||||||
|
write_uint64(w, change.delta)
|
||||||
|
return w
|
||||||
|
|
||||||
|
|
||||||
|
def _write_property(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))
|
@ -0,0 +1,33 @@
|
|||||||
|
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 layout.ask_multisig(ctx, msg)
|
||||||
|
|
||||||
|
|
||||||
|
def initiate(public_key, common: NEMTransactionCommon, inner_tx: bytes) -> bytes:
|
||||||
|
return serialize.serialize_multisig(common, public_key, inner_tx)
|
||||||
|
|
||||||
|
|
||||||
|
def cosign(public_key, common: NEMTransactionCommon, inner_tx: bytes, signer: bytes) -> bytes:
|
||||||
|
return serialize.serialize_multisig_signature(common, public_key, inner_tx, signer)
|
||||||
|
|
||||||
|
|
||||||
|
async def aggregate_modification(ctx,
|
||||||
|
public_key: bytes,
|
||||||
|
common: NEMTransactionCommon,
|
||||||
|
aggr: NEMAggregateModification,
|
||||||
|
multisig: bool):
|
||||||
|
await layout.ask_aggregate_modification(ctx, common, aggr, multisig)
|
||||||
|
w = serialize.serialize_aggregate_modification(common, aggr, public_key)
|
||||||
|
|
||||||
|
for m in aggr.modifications:
|
||||||
|
serialize.serialize_cosignatory_modification(w, m.type, m.public_key)
|
||||||
|
|
||||||
|
if aggr.relative_change:
|
||||||
|
serialize.serialize_minimum_cosignatories(w, aggr.relative_change)
|
||||||
|
return w
|
@ -0,0 +1,49 @@
|
|||||||
|
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):
|
||||||
|
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.transaction.fee)
|
||||||
|
|
||||||
|
|
||||||
|
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 mod.modifications:
|
||||||
|
if m.type == NEMModificationType.CosignatoryModification_Add:
|
||||||
|
action = 'Add'
|
||||||
|
else:
|
||||||
|
action = 'Remove'
|
||||||
|
address = nem.compute_address(m.public_key, common.network)
|
||||||
|
await _require_confirm_address(ctx, action + ' cosignatory', address)
|
||||||
|
|
||||||
|
if mod.relative_change:
|
||||||
|
if multisig:
|
||||||
|
action = 'Modify the number of cosignatories by '
|
||||||
|
else:
|
||||||
|
action = 'Set minimum cosignatories to '
|
||||||
|
await require_confirm_text(ctx, action + str(mod.relative_change) + '?')
|
||||||
|
|
||||||
|
await require_confirm_final(ctx, common.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)
|
@ -0,0 +1,51 @@
|
|||||||
|
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):
|
||||||
|
w = write_common(common, bytearray(public_key), NEM_TRANSACTION_TYPE_MULTISIG)
|
||||||
|
write_bytes_with_length(w, bytearray(inner))
|
||||||
|
return w
|
||||||
|
|
||||||
|
|
||||||
|
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))
|
||||||
|
write_bytes_with_length(w, digest)
|
||||||
|
write_bytes_with_length(w, address)
|
||||||
|
return w
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_aggregate_modification(common: NEMTransactionCommon, mod: NEMAggregateModification, public_key: bytes):
|
||||||
|
version = common.network << 24 | 1
|
||||||
|
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(mod.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)
|
@ -0,0 +1,9 @@
|
|||||||
|
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 layout.ask_provision_namespace(ctx, common, namespace)
|
||||||
|
return serialize.serialize_provision_namespace(common, namespace, public_key)
|
@ -0,0 +1,22 @@
|
|||||||
|
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',
|
||||||
|
ui.BOLD, namespace.namespace,
|
||||||
|
ui.NORMAL, 'under namespace',
|
||||||
|
ui.BOLD, namespace.parent)
|
||||||
|
await require_confirm_content(ctx, 'Confirm namespace', content)
|
||||||
|
else:
|
||||||
|
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)
|
||||||
|
|
||||||
|
await require_confirm_final(ctx, common.fee)
|
@ -0,0 +1,21 @@
|
|||||||
|
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:
|
||||||
|
tx = write_common(common,
|
||||||
|
bytearray(public_key),
|
||||||
|
NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
return tx
|
@ -0,0 +1,55 @@
|
|||||||
|
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):
|
||||||
|
validate(msg)
|
||||||
|
node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE)
|
||||||
|
|
||||||
|
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, common, msg.transfer, node)
|
||||||
|
elif msg.provision_namespace:
|
||||||
|
tx = await namespace.namespace(ctx, public_key, common, msg.provision_namespace)
|
||||||
|
elif msg.mosaic_creation:
|
||||||
|
tx = await mosaic.mosaic_creation(ctx, public_key, common, msg.mosaic_creation)
|
||||||
|
elif msg.supply_change:
|
||||||
|
tx = await mosaic.supply_change(ctx, public_key, common, msg.supply_change)
|
||||||
|
elif msg.aggregate_modification:
|
||||||
|
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, common, msg.importance_transfer)
|
||||||
|
else:
|
||||||
|
raise ValueError('No transaction provided')
|
||||||
|
|
||||||
|
if msg.multisig:
|
||||||
|
# wrap transaction in multisig wrapper
|
||||||
|
if msg.cosigning:
|
||||||
|
tx = multisig.cosign(_get_public_key(node), msg.transaction, tx, msg.multisig.signer)
|
||||||
|
else:
|
||||||
|
tx = multisig.initiate(_get_public_key(node), msg.transaction, tx)
|
||||||
|
|
||||||
|
signature = ed25519.sign(node.private_key(), tx, NEM_HASH_ALG)
|
||||||
|
|
||||||
|
resp = NEMSignedTx()
|
||||||
|
resp.data = tx
|
||||||
|
resp.signature = signature
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
def _get_public_key(node) -> bytes:
|
||||||
|
# 0x01 prefix is not part of the actual public key, hence removed
|
||||||
|
return node.public_key()[1:]
|
@ -0,0 +1,22 @@
|
|||||||
|
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 = serialize.canonicalize_mosaics(transfer.mosaics)
|
||||||
|
payload, encrypted = serialize.get_transfer_payload(transfer, node)
|
||||||
|
|
||||||
|
await layout.ask_transfer(ctx, common, transfer, payload, encrypted)
|
||||||
|
|
||||||
|
w = serialize.serialize_transfer(common, transfer, public_key, payload, encrypted)
|
||||||
|
for mosaic in transfer.mosaics:
|
||||||
|
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 layout.ask_importance_transfer(ctx, common, imp)
|
||||||
|
return serialize.serialize_importance_transfer(common, imp, public_key)
|
@ -0,0 +1,128 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
'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, common.network)
|
||||||
|
msg = Text('Confirm mosaic', ui.ICON_SEND,
|
||||||
|
'Confirm mosaic',
|
||||||
|
'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, '%s raw units' % mosaic_quantity,
|
||||||
|
ui.NORMAL, 'of',
|
||||||
|
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):
|
||||||
|
# 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:
|
||||||
|
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:
|
||||||
|
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'
|
||||||
|
else:
|
||||||
|
m = 'Deactivate'
|
||||||
|
await require_confirm_text(ctx, m + ' remote harvesting?')
|
||||||
|
await require_confirm_final(ctx, common.fee)
|
||||||
|
|
||||||
|
|
||||||
|
async def _require_confirm_transfer(ctx, recipient, value):
|
||||||
|
content = Text('Confirm transfer', ui.ICON_SEND,
|
||||||
|
ui.BOLD, 'Send %s XEM' % format_amount(value, NEM_MAX_DIVISIBILITY),
|
||||||
|
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)
|
@ -0,0 +1,117 @@
|
|||||||
|
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 ..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),
|
||||||
|
NEM_TRANSACTION_TYPE_TRANSFER,
|
||||||
|
_get_version(common.network, transfer.mosaics))
|
||||||
|
|
||||||
|
write_bytes_with_length(tx, bytearray(transfer.recipient))
|
||||||
|
write_uint64(tx, 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 transfer.mosaics:
|
||||||
|
write_uint32(tx, len(transfer.mosaics))
|
||||||
|
|
||||||
|
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(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))
|
||||||
|
return w
|
||||||
|
|
||||||
|
|
||||||
|
def get_transfer_payload(transfer: NEMTransfer, node) -> [bytes, bool]:
|
||||||
|
payload = transfer.payload
|
||||||
|
encrypted = False
|
||||||
|
if transfer.public_key is not None:
|
||||||
|
if payload is None:
|
||||||
|
raise ValueError('Public key provided but no payload to encrypt')
|
||||||
|
payload = _encrypt(node, transfer.public_key, 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
|
||||||
|
|
||||||
|
|
||||||
|
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 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))
|
@ -0,0 +1,236 @@
|
|||||||
|
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):
|
||||||
|
if msg.transaction is None:
|
||||||
|
raise ValueError('No common provided')
|
||||||
|
|
||||||
|
_validate_single_tx(msg)
|
||||||
|
_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')
|
||||||
|
|
||||||
|
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, msg.multisig is None)
|
||||||
|
if msg.importance_transfer:
|
||||||
|
_validate_importance_transfer(msg.importance_transfer)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_network(network: int) -> int:
|
||||||
|
if network is None:
|
||||||
|
return NEM_NETWORK_MAINNET
|
||||||
|
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):
|
||||||
|
# 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 common.timestamp is None:
|
||||||
|
err = 'timestamp'
|
||||||
|
if common.fee is None:
|
||||||
|
err = 'fee'
|
||||||
|
if common.deadline is None:
|
||||||
|
err = 'deadline'
|
||||||
|
|
||||||
|
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 %s provided in inner transaction' % err)
|
||||||
|
else:
|
||||||
|
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: str):
|
||||||
|
if not public_key:
|
||||||
|
raise ValueError('%s (none provided)' % err_msg)
|
||||||
|
if len(public_key) != NEM_PUBLIC_KEY_SIZE:
|
||||||
|
raise ValueError('%s (invalid length)' % err_msg)
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_importance_transfer(importance_transfer: NEMImportanceTransfer):
|
||||||
|
if importance_transfer.mode is None:
|
||||||
|
raise ValueError('No mode provided')
|
||||||
|
_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')
|
||||||
|
_validate_public_key(multisig.signer, 'Invalid multisig signer public key provided')
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_aggregate_modification(
|
||||||
|
aggregate_modification: NEMAggregateModification,
|
||||||
|
creation: bool = False):
|
||||||
|
|
||||||
|
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 (
|
||||||
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_supply_change(supply_change: NEMMosaicSupplyChange):
|
||||||
|
if supply_change.namespace is None:
|
||||||
|
raise ValueError('No namespace provided')
|
||||||
|
if supply_change.mosaic is None:
|
||||||
|
raise ValueError('No mosaic provided')
|
||||||
|
if supply_change.type is None:
|
||||||
|
raise ValueError('No type provided')
|
||||||
|
if supply_change.delta is None:
|
||||||
|
raise ValueError('No delta provided')
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_mosaic_creation(mosaic_creation: NEMMosaicCreation, network: int):
|
||||||
|
if mosaic_creation.definition is None:
|
||||||
|
raise ValueError('No mosaic definition provided')
|
||||||
|
if mosaic_creation.sink is None:
|
||||||
|
raise ValueError('No creation sink provided')
|
||||||
|
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 is not None:
|
||||||
|
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 mosaic_creation.definition.networks:
|
||||||
|
raise ValueError('Networks not allowed in mosaic creation transactions')
|
||||||
|
|
||||||
|
if mosaic_creation.definition.namespace is None:
|
||||||
|
raise ValueError('No mosaic namespace provided')
|
||||||
|
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')
|
||||||
|
if mosaic_creation.definition.levy_address is None:
|
||||||
|
raise ValueError('No levy address provided')
|
||||||
|
if mosaic_creation.definition.levy_namespace is None:
|
||||||
|
raise ValueError('No levy namespace provided')
|
||||||
|
if mosaic_creation.definition.levy_mosaic is None:
|
||||||
|
raise ValueError('No levy mosaic name provided')
|
||||||
|
|
||||||
|
if mosaic_creation.definition.divisibility is None:
|
||||||
|
raise ValueError('No divisibility provided')
|
||||||
|
if mosaic_creation.definition.supply is None:
|
||||||
|
raise ValueError('No supply provided')
|
||||||
|
if mosaic_creation.definition.mutable_supply is None:
|
||||||
|
raise ValueError('No supply mutability provided')
|
||||||
|
if mosaic_creation.definition.transferable is None:
|
||||||
|
raise ValueError('No mosaic transferability provided')
|
||||||
|
if mosaic_creation.definition.description is None:
|
||||||
|
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 provision_namespace.namespace is None:
|
||||||
|
raise ValueError('No namespace provided')
|
||||||
|
if provision_namespace.sink is None:
|
||||||
|
raise ValueError('No rental sink provided')
|
||||||
|
if provision_namespace.fee is None:
|
||||||
|
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 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')
|
||||||
|
|
||||||
|
for m in transfer.mosaics:
|
||||||
|
if m.namespace is None:
|
||||||
|
raise ValueError('No mosaic namespace provided')
|
||||||
|
if m.mosaic is None:
|
||||||
|
raise ValueError('No mosaic name provided')
|
||||||
|
if m.quantity is None:
|
||||||
|
raise ValueError('No mosaic quantity provided')
|
@ -0,0 +1,47 @@
|
|||||||
|
from trezor.messages.NEMTransactionCommon import NEMTransactionCommon
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
write_uint32(ret, version)
|
||||||
|
write_uint32(ret, common.timestamp)
|
||||||
|
|
||||||
|
write_bytes_with_length(ret, public_key)
|
||||||
|
write_uint64(ret, common.fee)
|
||||||
|
write_uint32(ret, common.deadline)
|
||||||
|
|
||||||
|
return ret
|
@ -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
|
||||||
|
@ -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()
|
@ -0,0 +1,237 @@
|
|||||||
|
from common import *
|
||||||
|
from ubinascii import unhexlify
|
||||||
|
from trezor.crypto import bip32
|
||||||
|
from apps.nem.helpers import NEM_NETWORK_MAINNET, NEM_CURVE
|
||||||
|
|
||||||
|
|
||||||
|
class TestNemHDNode(unittest.TestCase):
|
||||||
|
|
||||||
|
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',
|
||||||
|
'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]))
|
||||||
|
|
||||||
|
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()
|
@ -0,0 +1,165 @@
|
|||||||
|
from common 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):
|
||||||
|
|
||||||
|
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()
|
||||||
|
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 = 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_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 = merge_mosaics([a, b])
|
||||||
|
self.assertEqual(merged[0].quantity, 2)
|
||||||
|
self.assertEqual(len(merged), 1)
|
||||||
|
|
||||||
|
a.quantity = 1
|
||||||
|
b.quantity = 10
|
||||||
|
merged = merge_mosaics([a, b])
|
||||||
|
self.assertEqual(merged[0].quantity, 11)
|
||||||
|
|
||||||
|
a.namespace = 'abcdef'
|
||||||
|
merged = merge_mosaics([a, b])
|
||||||
|
self.assertEqual(len(merged), 2)
|
||||||
|
|
||||||
|
c = NEMMosaic()
|
||||||
|
c.namespace = 'abc'
|
||||||
|
c.mosaic = 'xxx'
|
||||||
|
c.quantity = 2
|
||||||
|
merged = 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 = 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 = merge_mosaics([a, b, c])
|
||||||
|
self.assertEqual(merged[0].quantity, 6)
|
||||||
|
self.assertEqual(len(merged), 1)
|
||||||
|
|
||||||
|
def test_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 = sort_mosaics([a, b])
|
||||||
|
self.assertEqual(res, [b, a])
|
||||||
|
|
||||||
|
a.namespace = ''
|
||||||
|
b.namespace = 'a.b.c'
|
||||||
|
res = sort_mosaics([a, b])
|
||||||
|
self.assertEqual(res, [a, b])
|
||||||
|
|
||||||
|
a.namespace = 'z.z.z'
|
||||||
|
b.namespace = 'a.b.c'
|
||||||
|
res = sort_mosaics([a, b])
|
||||||
|
self.assertEqual(res, [b, a])
|
||||||
|
|
||||||
|
a.namespace = 'a'
|
||||||
|
b.namespace = 'a'
|
||||||
|
a.mosaic = 'mosaic'
|
||||||
|
b.mosaic = 'mosaic'
|
||||||
|
res = sort_mosaics([a, b])
|
||||||
|
self.assertEqual(res, [a, b])
|
||||||
|
|
||||||
|
a.mosaic = 'www'
|
||||||
|
b.mosaic = 'aaa'
|
||||||
|
res = sort_mosaics([a, b])
|
||||||
|
self.assertEqual(res, [b, a])
|
||||||
|
|
||||||
|
c = NEMMosaic()
|
||||||
|
c.namespace = 'a'
|
||||||
|
c.mosaic = 'zzz'
|
||||||
|
res = sort_mosaics([a, b, c])
|
||||||
|
self.assertEqual(res, [b, a, c])
|
||||||
|
|
||||||
|
c.mosaic = 'bbb'
|
||||||
|
res = sort_mosaics([a, b, c])
|
||||||
|
self.assertEqual(res, [b, c, a])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -0,0 +1,154 @@
|
|||||||
|
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
|
||||||
|
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.transaction, m.mosaic_creation, 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.transaction, m.mosaic_creation, 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.transaction, m.mosaic_creation, 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.transaction, m.mosaic_creation, 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()
|
@ -0,0 +1,91 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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.transaction, m.supply_change, 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.transaction, m.supply_change, 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.transaction, m.supply_change, 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.transaction, m.supply_change, 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()
|
@ -0,0 +1,98 @@
|
|||||||
|
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
|
||||||
|
from trezor.messages.NEMCosignatoryModification import NEMCosignatoryModification
|
||||||
|
from trezor.messages.NEMTransactionCommon import NEMTransactionCommon
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
0)
|
||||||
|
t = serialize_aggregate_modification(m.transaction, m.aggregate_modification, 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,
|
||||||
|
0)
|
||||||
|
t = serialize_aggregate_modification(m.transaction, m.aggregate_modification, 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,
|
||||||
|
2)
|
||||||
|
t = serialize_aggregate_modification(m.transaction, m.aggregate_modification, 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: int):
|
||||||
|
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()
|
@ -0,0 +1,116 @@
|
|||||||
|
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
|
||||||
|
from trezor.messages.NEMCosignatoryModification import NEMCosignatoryModification
|
||||||
|
|
||||||
|
|
||||||
|
class TestNemMultisig(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_nem_multisig(self):
|
||||||
|
# http://bob.nem.ninja:8765/#/multisig/7d3a7087023ee29005262016706818579a2b5499eb9ca76bad98c1e6f4c46642
|
||||||
|
m = _create_msg(NEM_NETWORK_TESTNET,
|
||||||
|
3939039,
|
||||||
|
16000000,
|
||||||
|
3960639,
|
||||||
|
1,
|
||||||
|
0)
|
||||||
|
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,
|
||||||
|
3939039,
|
||||||
|
6000000,
|
||||||
|
3960639)
|
||||||
|
multisig = serialize_multisig(m, unhexlify("59d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e"), base_tx)
|
||||||
|
|
||||||
|
self.assertEqual(multisig, unhexlify("0410000001000098df1a3c002000000059d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e808d5b00000000003f6f3c006c0000000110000001000098df1a3c0020000000abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac0024f400000000003f6f3c0001000000280000000200000020000000e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3"))
|
||||||
|
|
||||||
|
address_pubkey = unhexlify("abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac")
|
||||||
|
m = _create_common_msg(NEM_NETWORK_TESTNET,
|
||||||
|
3939891,
|
||||||
|
6000000,
|
||||||
|
3961491)
|
||||||
|
multisig = serialize_multisig_signature(m, unhexlify("71cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4"), base_tx, address_pubkey)
|
||||||
|
|
||||||
|
self.assertEqual(multisig, unhexlify("0210000001000098331e3c002000000071cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4808d5b000000000093723c0024000000200000008ec165580bdabfd31ce6007a1748ce5bdf30eab7a214743097de3bc822ac7e002800000054435258595551494d464137414f474c354c463359574c43375641424c59554d4a35414342554e4c"))
|
||||||
|
|
||||||
|
def test_nem_multisig_2(self):
|
||||||
|
# http://chain.nem.ninja/#/multisig/1016cf3bdd61bd57b9b2b07b6ff2dee390279d8d899265bdc23d42360abe2e6c
|
||||||
|
m = _create_provision_msg(NEM_NETWORK_MAINNET,
|
||||||
|
59414272,
|
||||||
|
20000000,
|
||||||
|
59500672,
|
||||||
|
"dim",
|
||||||
|
"",
|
||||||
|
"NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA",
|
||||||
|
5000000000)
|
||||||
|
base_tx = serialize_provision_namespace(m.transaction, m.provision_namespace, 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_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)
|
||||||
|
multisig = serialize_multisig_signature(m, unhexlify("7ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87"), base_tx, address_pubkey)
|
||||||
|
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: int):
|
||||||
|
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()
|
@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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.transaction, m.provision_namespace, 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.transaction, m.provision_namespace, 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.transaction, m.provision_namespace, 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()
|
@ -0,0 +1,119 @@
|
|||||||
|
from common 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
|
||||||
|
|
||||||
|
|
||||||
|
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.transaction, m.transfer, 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.transaction, m.transfer,
|
||||||
|
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.transaction, m.transfer,
|
||||||
|
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.transaction, m.transfer,
|
||||||
|
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.transaction, m.transfer,
|
||||||
|
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()
|
@ -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(']')
|
Loading…
Reference in new issue