From 23df799e776af17ac5cf396b84f6f089f82cefd7 Mon Sep 17 00:00:00 2001 From: Matus Zamborsky Date: Sat, 25 May 2019 12:00:43 +0200 Subject: [PATCH] common+python: add Ontology support python cli: change option short names core: fix ontology capability generation core+python: post rebase 'make gen' and fixes python/ontology - cleanup writer test/ontology: added proper marks and removed class test/ontology: added missing mark --- common/protob/Makefile | 2 +- common/protob/messages-management.proto | 1 + common/protob/messages-ontology.proto | 134 ++++++++++++ common/protob/messages.proto | 8 + core/src/apps/homescreen/__init__.py | 1 + core/src/apps/ontology/__init__.py | 16 ++ core/src/apps/ontology/const.py | 9 + core/src/apps/ontology/get_address.py | 26 +++ core/src/apps/ontology/get_public_key.py | 20 ++ core/src/apps/ontology/helpers.py | 44 ++++ core/src/apps/ontology/layout.py | 61 ++++++ core/src/apps/ontology/sc/builder.py | 51 +++++ core/src/apps/ontology/sc/native_builder.py | 120 +++++++++++ core/src/apps/ontology/sc/opcode.py | 31 +++ core/src/apps/ontology/serialize.py | 104 ++++++++++ core/src/apps/ontology/sign_tx.py | 125 ++++++++++++ core/src/apps/ontology/writer.py | 22 ++ core/src/main.py | 2 + core/src/trezor/messages/Capability.py | 2 + core/src/trezor/messages/Features.py | 4 +- core/src/trezor/messages/MessageType.py | 6 + core/src/trezor/messages/OntologyAddress.py | 26 +++ core/src/trezor/messages/OntologyAsset.py | 7 + .../src/trezor/messages/OntologyGetAddress.py | 29 +++ .../trezor/messages/OntologyGetPublicKey.py | 29 +++ .../messages/OntologyOntIdAddAttributes.py | 33 +++ .../trezor/messages/OntologyOntIdAttribute.py | 31 +++ .../trezor/messages/OntologyOntIdRegister.py | 28 +++ core/src/trezor/messages/OntologyPublicKey.py | 26 +++ core/src/trezor/messages/OntologySignTx.py | 47 +++++ core/src/trezor/messages/OntologySignedTx.py | 29 +++ .../trezor/messages/OntologyTransaction.py | 45 ++++ core/src/trezor/messages/OntologyTransfer.py | 35 ++++ .../trezor/messages/OntologyTxAttribute.py | 28 +++ .../trezor/messages/OntologyWithdrawOng.py | 31 +++ python/src/trezorlib/cli/ontology.py | 193 ++++++++++++++++++ python/src/trezorlib/cli/trezorctl.py | 2 + python/src/trezorlib/messages/Capability.py | 1 + python/src/trezorlib/messages/Features.py | 4 +- python/src/trezorlib/messages/MessageType.py | 6 + .../src/trezorlib/messages/OntologyAddress.py | 26 +++ .../src/trezorlib/messages/OntologyAsset.py | 7 + .../trezorlib/messages/OntologyGetAddress.py | 29 +++ .../messages/OntologyGetPublicKey.py | 29 +++ .../messages/OntologyOntIdAddAttributes.py | 33 +++ .../messages/OntologyOntIdAttribute.py | 31 +++ .../messages/OntologyOntIdRegister.py | 28 +++ .../trezorlib/messages/OntologyPublicKey.py | 26 +++ .../src/trezorlib/messages/OntologySignTx.py | 47 +++++ .../trezorlib/messages/OntologySignedTx.py | 29 +++ .../trezorlib/messages/OntologyTransaction.py | 45 ++++ .../trezorlib/messages/OntologyTransfer.py | 35 ++++ .../trezorlib/messages/OntologyTxAttribute.py | 28 +++ .../trezorlib/messages/OntologyWithdrawOng.py | 31 +++ python/src/trezorlib/messages/__init__.py | 14 ++ python/src/trezorlib/ontology.py | 66 ++++++ python/trezorlib/coins.json | 1 + .../test_msg_ontology_getaddress.py | 41 ++++ ...msg_ontology_sign_ont_id_add_attributes.py | 77 +++++++ .../test_msg_ontology_sign_ont_id_register.py | 70 +++++++ .../device_tests/test_msg_ontology_sign_tx.py | 92 +++++++++ .../test_msg_ontology_sign_withdraw_ong.py | 56 +++++ tools/build_protobuf | 4 +- 63 files changed, 2258 insertions(+), 6 deletions(-) create mode 100644 common/protob/messages-ontology.proto create mode 100644 core/src/apps/ontology/__init__.py create mode 100644 core/src/apps/ontology/const.py create mode 100644 core/src/apps/ontology/get_address.py create mode 100644 core/src/apps/ontology/get_public_key.py create mode 100644 core/src/apps/ontology/helpers.py create mode 100644 core/src/apps/ontology/layout.py create mode 100644 core/src/apps/ontology/sc/builder.py create mode 100644 core/src/apps/ontology/sc/native_builder.py create mode 100644 core/src/apps/ontology/sc/opcode.py create mode 100644 core/src/apps/ontology/serialize.py create mode 100644 core/src/apps/ontology/sign_tx.py create mode 100644 core/src/apps/ontology/writer.py create mode 100644 core/src/trezor/messages/OntologyAddress.py create mode 100644 core/src/trezor/messages/OntologyAsset.py create mode 100644 core/src/trezor/messages/OntologyGetAddress.py create mode 100644 core/src/trezor/messages/OntologyGetPublicKey.py create mode 100644 core/src/trezor/messages/OntologyOntIdAddAttributes.py create mode 100644 core/src/trezor/messages/OntologyOntIdAttribute.py create mode 100644 core/src/trezor/messages/OntologyOntIdRegister.py create mode 100644 core/src/trezor/messages/OntologyPublicKey.py create mode 100644 core/src/trezor/messages/OntologySignTx.py create mode 100644 core/src/trezor/messages/OntologySignedTx.py create mode 100644 core/src/trezor/messages/OntologyTransaction.py create mode 100644 core/src/trezor/messages/OntologyTransfer.py create mode 100644 core/src/trezor/messages/OntologyTxAttribute.py create mode 100644 core/src/trezor/messages/OntologyWithdrawOng.py create mode 100644 python/src/trezorlib/cli/ontology.py create mode 100644 python/src/trezorlib/messages/OntologyAddress.py create mode 100644 python/src/trezorlib/messages/OntologyAsset.py create mode 100644 python/src/trezorlib/messages/OntologyGetAddress.py create mode 100644 python/src/trezorlib/messages/OntologyGetPublicKey.py create mode 100644 python/src/trezorlib/messages/OntologyOntIdAddAttributes.py create mode 100644 python/src/trezorlib/messages/OntologyOntIdAttribute.py create mode 100644 python/src/trezorlib/messages/OntologyOntIdRegister.py create mode 100644 python/src/trezorlib/messages/OntologyPublicKey.py create mode 100644 python/src/trezorlib/messages/OntologySignTx.py create mode 100644 python/src/trezorlib/messages/OntologySignedTx.py create mode 100644 python/src/trezorlib/messages/OntologyTransaction.py create mode 100644 python/src/trezorlib/messages/OntologyTransfer.py create mode 100644 python/src/trezorlib/messages/OntologyTxAttribute.py create mode 100644 python/src/trezorlib/messages/OntologyWithdrawOng.py create mode 100644 python/src/trezorlib/ontology.py create mode 100644 python/trezorlib/coins.json create mode 100644 tests/device_tests/test_msg_ontology_getaddress.py create mode 100644 tests/device_tests/test_msg_ontology_sign_ont_id_add_attributes.py create mode 100644 tests/device_tests/test_msg_ontology_sign_ont_id_register.py create mode 100644 tests/device_tests/test_msg_ontology_sign_tx.py create mode 100644 tests/device_tests/test_msg_ontology_sign_withdraw_ong.py diff --git a/common/protob/Makefile b/common/protob/Makefile index e3fe8b857..f1aa9774b 100644 --- a/common/protob/Makefile +++ b/common/protob/Makefile @@ -1,4 +1,4 @@ -check: messages.pb messages-binance.pb messages-bitcoin.pb messages-bootloader.pb messages-cardano.pb messages-common.pb messages-crypto.pb messages-debug.pb messages-ethereum.pb messages-lisk.pb messages-management.pb messages-monero.pb messages-nem.pb messages-ripple.pb messages-stellar.pb messages-tezos.pb messages-eos.pb +check: messages.pb messages-binance.pb messages-bitcoin.pb messages-bootloader.pb messages-cardano.pb messages-common.pb messages-crypto.pb messages-debug.pb messages-ethereum.pb messages-lisk.pb messages-management.pb messages-monero.pb messages-nem.pb messages-ripple.pb messages-stellar.pb messages-tezos.pb messages-ontology.pb messages-eos.pb %.pb: %.proto protoc -I/usr/include -I. $< -o $@ diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index 8b7f0b215..0ffdb143a 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -83,6 +83,7 @@ message Features { Capability_U2F = 14; Capability_Shamir = 15; Capability_ShamirGroups = 16; + Capability_Ontology = 17; } optional BackupType backup_type = 31; // type of device backup (BIP-39 / SLIP-39 basic / SLIP-39 advanced) optional bool sd_card_present = 32; // is SD card present diff --git a/common/protob/messages-ontology.proto b/common/protob/messages-ontology.proto new file mode 100644 index 000000000..6a3c60e30 --- /dev/null +++ b/common/protob/messages-ontology.proto @@ -0,0 +1,134 @@ +syntax = "proto2"; +package hw.trezor.messages.ontology; + +// Sugar for easier handling in Java +option java_package = "com.satoshilabs.trezor.lib.protobuf"; +option java_outer_classname = "TrezorMessageOntology"; + +/** + * Ontology Transaction + * @embed + */ +message OntologyTransaction { + optional uint32 version = 1; + optional uint32 type = 2; + optional uint32 nonce = 3; + optional uint64 gas_price = 4; + optional uint64 gas_limit = 5; + optional string payer = 6; + repeated OntologyTxAttribute tx_attributes = 7; + /** + * Attribute of Ontology transaction + */ + message OntologyTxAttribute { + optional uint32 usage = 1; + optional bytes data = 2; + } +} + +/** + * Request: Ask device for Ontology public key corresponding to address_n path + * @start + * @next OntologyPublicKey + */ +message OntologyGetPublicKey { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional bool show_display = 2; // Optionally show on display before sending the result +} + +/** + * Response: Contains Ontology public key derived from device private seed + * @end + */ +message OntologyPublicKey { + optional bytes public_key = 1; // Ontology public key +} + +/** + * Request: Ask device for Ontology address corresponding to address_n path + * @start + * @next OntologyAddress + */ +message OntologyGetAddress { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional bool show_display = 2; // Optionally show on display before sending the result +} + +/** + * Response: Contains Ontology address derived from device private seed + * @end + */ +message OntologyAddress { + optional string address = 1; // Ontology address +} + +/** + * Request: Ask device to sign Ontology transaction + * @start + * @next OntologySignedTx + */ +message OntologySignTx{ + repeated uint32 address_n = 1; + optional OntologyTransaction transaction = 2; + optional OntologyTransfer transfer = 3; + optional OntologyWithdrawOng withdraw_ong = 4; + optional OntologyOntIdRegister ont_id_register = 5; + optional OntologyOntIdAddAttributes ont_id_add_attributes = 6; + + message OntologyTransfer { + optional OntologyAsset asset = 1; + optional uint64 amount = 2; + optional string from_address = 3; + optional string to_address = 4; + /** + * Ontology Asset + */ + enum OntologyAsset { + ONT = 1; + ONG = 2; + } + } + + /** + * Ontology ONG Withdrawal + */ + message OntologyWithdrawOng { + optional uint64 amount = 1; + optional string from_address = 2; + optional string to_address = 3; + } + + /** + * Ontology ONT ID registration + */ + message OntologyOntIdRegister { + optional string ont_id = 1; + optional bytes public_key = 2; + } + + /** + * Ontology ONT ID attributes adding + */ + message OntologyOntIdAddAttributes { + optional string ont_id = 1; + optional bytes public_key = 2; + repeated OntologyOntIdAttribute ont_id_attributes = 3; + /** + * Attribute of Ontology ONT ID + */ + message OntologyOntIdAttribute { + optional string key = 1; + optional string type = 2; + optional string value = 3; + } + } +} + +/** + * Response: Contains Ontology tx signature and payload + * @end + */ +message OntologySignedTx { + optional bytes signature = 1; + optional bytes payload = 2; +} diff --git a/common/protob/messages.proto b/common/protob/messages.proto index 9da9a70c6..0dac936de 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -177,6 +177,14 @@ enum MessageType { MessageType_CardanoTxAck = 309 [(wire_in) = true]; MessageType_CardanoSignedTx = 310 [(wire_out) = true]; + // Ontology + MessageType_OntologyGetAddress = 350 [(wire_in) = true]; + MessageType_OntologyAddress = 351 [(wire_out) = true]; + MessageType_OntologyGetPublicKey = 352 [(wire_in) = true]; + MessageType_OntologyPublicKey = 353 [(wire_out) = true]; + MessageType_OntologySignTx = 354 [(wire_in) = true]; + MessageType_OntologySignedTx = 355 [(wire_out) = true]; + // Ripple MessageType_RippleGetAddress = 400 [(wire_in) = true]; MessageType_RippleAddress = 401 [(wire_out) = true]; diff --git a/core/src/apps/homescreen/__init__.py b/core/src/apps/homescreen/__init__.py index db711fd11..236bcc9ac 100644 --- a/core/src/apps/homescreen/__init__.py +++ b/core/src/apps/homescreen/__init__.py @@ -61,6 +61,7 @@ def get_features() -> Features: Capability.Lisk, Capability.Monero, Capability.NEM, + Capability.Ontology, Capability.Ripple, Capability.Stellar, Capability.Tezos, diff --git a/core/src/apps/ontology/__init__.py b/core/src/apps/ontology/__init__.py new file mode 100644 index 000000000..5d937bc65 --- /dev/null +++ b/core/src/apps/ontology/__init__.py @@ -0,0 +1,16 @@ +from trezor import wire +from trezor.messages import MessageType + +from apps.common import HARDENED + +CURVE = "nist256p1" + + +def boot() -> None: + ns = [ + [CURVE, HARDENED | 44, HARDENED | 1024], + [CURVE, HARDENED | 44, HARDENED | 888], + ] + wire.add(MessageType.OntologyGetPublicKey, __name__, "get_public_key", ns) + wire.add(MessageType.OntologyGetAddress, __name__, "get_address", ns) + wire.add(MessageType.OntologySignTx, __name__, "sign_tx", ns) diff --git a/core/src/apps/ontology/const.py b/core/src/apps/ontology/const.py new file mode 100644 index 000000000..239d65e7c --- /dev/null +++ b/core/src/apps/ontology/const.py @@ -0,0 +1,9 @@ +ONT_CONTRACT = ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" +) +ONG_CONTRACT = ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02" +) +ONTID_CONTRACT = ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03" +) diff --git a/core/src/apps/ontology/get_address.py b/core/src/apps/ontology/get_address.py new file mode 100644 index 000000000..e823c11d5 --- /dev/null +++ b/core/src/apps/ontology/get_address.py @@ -0,0 +1,26 @@ +from trezor.crypto.curve import nist256p1 +from trezor.messages.OntologyAddress import OntologyAddress +from trezor.messages.OntologyGetAddress import OntologyGetAddress + +from .helpers import CURVE, get_address_from_public_key, validate_full_path + +from apps.common import paths +from apps.common.layout import show_address, show_qr + + +async def get_address(ctx, msg: OntologyGetAddress, keychain) -> OntologyAddress: + await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + + node = keychain.derive(msg.address_n, CURVE) + seckey = node.private_key() + public_key = nist256p1.publickey(seckey, True) + address = get_address_from_public_key(public_key) + + if msg.show_display: + while True: + if await show_address(ctx, address): + break + if await show_qr(ctx, address): + break + + return OntologyAddress(address=address) diff --git a/core/src/apps/ontology/get_public_key.py b/core/src/apps/ontology/get_public_key.py new file mode 100644 index 000000000..16eef44d5 --- /dev/null +++ b/core/src/apps/ontology/get_public_key.py @@ -0,0 +1,20 @@ +from trezor.crypto.curve import nist256p1 +from trezor.messages.OntologyGetPublicKey import OntologyGetPublicKey +from trezor.messages.OntologyPublicKey import OntologyPublicKey + +from .helpers import CURVE, validate_full_path + +from apps.common import layout, paths + + +async def get_public_key(ctx, msg: OntologyGetPublicKey, keychain) -> OntologyPublicKey: + await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + + node = keychain.derive(msg.address_n, CURVE) + seckey = node.private_key() + public_key = nist256p1.publickey(seckey, True) + + if msg.show_display: + await layout.show_pubkey(ctx, public_key) + + return OntologyPublicKey(public_key=public_key) diff --git a/core/src/apps/ontology/helpers.py b/core/src/apps/ontology/helpers.py new file mode 100644 index 000000000..8337ef144 --- /dev/null +++ b/core/src/apps/ontology/helpers.py @@ -0,0 +1,44 @@ +from trezor.crypto import base58 +from trezor.crypto.scripts import sha256_ripemd160_digest + +from apps.common import HARDENED + +CURVE = "nist256p1" + + +def get_address_from_public_key(pubkey: bytes) -> str: + """ + Computes address from public key + """ + address_bytes = b"\x17" + sha256_ripemd160_digest(b"\x21" + pubkey + b"\xac") + return base58.encode_check(address_bytes) + + +def get_bytes_from_address(address: str) -> bytes: + """ + Converts base58check address to hex representation + """ + return base58.decode_check(address)[1:] + + +def validate_full_path(path: list) -> bool: + """ + Validates derivation path to fit 44'/1024'/a'/{0,1}/i, + where `a` is an account number and i an address index. + The max value for `a` is 20, 1 000 000 for `i`. + The derivation scheme v1 allowed a'/0/i only, + but in v2 it can be a'/1/i as well. + """ + if len(path) != 5: + return False + if path[0] != 44 | HARDENED: + return False + if path[1] != 1024 | HARDENED and path[1] != 888 | HARDENED: + return False + if path[2] < HARDENED or path[2] > 20 | HARDENED: + return False + if path[3] != 0 and path[3] != 1: + return False + if path[4] > 1000000: + return False + return True diff --git a/core/src/apps/ontology/layout.py b/core/src/apps/ontology/layout.py new file mode 100644 index 000000000..fc6c14593 --- /dev/null +++ b/core/src/apps/ontology/layout.py @@ -0,0 +1,61 @@ +from ubinascii import hexlify + +from trezor import ui +from trezor.messages import ButtonRequestType +from trezor.ui.scroll import Paginated +from trezor.ui.text import Text +from trezor.utils import chunks, format_amount + +from apps.common.confirm import require_confirm + + +async def require_confirm_transfer_ont(ctx, dest, value): + text = Text("Confirm sending", ui.ICON_SEND, icon_color=ui.GREEN) + text.bold(format_amount(value, 0) + "ONT") + text.mono(*split_address("To: " + dest)) + return await require_confirm(ctx, text, ButtonRequestType.SignTx) + + +async def require_confirm_transfer_ong(ctx, dest, value): + text = Text("Confirm sending", ui.ICON_SEND, icon_color=ui.GREEN) + text.bold(format_amount(value, 9) + "ONG") + text.mono(*split_address("To: " + dest)) + return await require_confirm(ctx, text, ButtonRequestType.SignTx) + + +async def require_confirm_withdraw_ong(ctx, value): + text = Text("Confirm withdraw of ", ui.ICON_SEND, icon_color=ui.GREEN) + text.bold(format_amount(value, 9) + "ONG") + return await require_confirm(ctx, text, ButtonRequestType.SignTx) + + +async def require_confirm_ont_id_register(ctx, ont_id, public_key): + t = Text("Confirm registering", ui.ICON_SEND, ui.GREEN) + key = hexlify(public_key).decode() + t.normal("for " + ont_id + " with public key " + key) + pages = [t] + + return await require_confirm(ctx, Paginated(pages), code=ButtonRequestType.SignTx) + + +async def require_confirm_ont_id_add_attributes(ctx, ont_id, public_key, attributes): + key = hexlify(public_key).decode() + t = Text("Confirm attributes", ui.ICON_SEND, ui.GREEN) + t.normal("for " + ont_id + " with public key " + key) + pages = [t] + for attribute in attributes: + t1 = Text("Attribute:") + t1.normal("Name " + attribute.key) + t1.normal("Type: " + attribute.type) + t1.normal("Value: " + attribute.value) + pages.append(t1) + + return await require_confirm(ctx, Paginated(pages), ButtonRequestType.SignTx) + + +def split_address(address): + return chunks(address, 16) + + +def split_str(text: str): + return list(chunks(text, 16)) diff --git a/core/src/apps/ontology/sc/builder.py b/core/src/apps/ontology/sc/builder.py new file mode 100644 index 000000000..d71f9019c --- /dev/null +++ b/core/src/apps/ontology/sc/builder.py @@ -0,0 +1,51 @@ +from .. import writer +from . import opcode + + +def write_push_bytes(ret: bytearray, param: bytes) -> None: + """ + Writes PUSH BYTES instruction + """ + length = len(param) + + if length < opcode.PUSHBYTES75: + writer.write_byte(ret, length) + elif length < 0x100: + writer.write_byte(ret, opcode.PUSHDATA1) + writer.write_byte(ret, length) + elif length < 0x10000: + writer.write_byte(ret, opcode.PUSHDATA2) + writer.write_uint16(ret, length) + else: + writer.write_byte(ret, opcode.PUSHDATA4) + writer.write_uint32(ret, length) + + writer.write_bytes(ret, param) + + +def write_push_int(ret: bytearray, param: int) -> None: + """ + Writes PUSH INT instruction + """ + if param == -1: + writer.write_byte(ret, opcode.PUSHM1) + elif param == 0: + writer.write_byte(ret, opcode.PUSH0) + elif 0 < param < 16: + num = opcode.PUSH1 - 1 + param + writer.write_byte(ret, num) + else: + # number encoded as 8 bytes + num = bytearray() + writer.write_uint64(num, param) + write_push_bytes(ret, bytes(num)) + + +def write_push_bool(ret: bytearray, param: bool) -> None: + """ + Writes PUSH BOOL instruction + """ + if param: + writer.write_byte(ret, opcode.PUSH1) + else: + writer.write_byte(ret, opcode.PUSH0) diff --git a/core/src/apps/ontology/sc/native_builder.py b/core/src/apps/ontology/sc/native_builder.py new file mode 100644 index 000000000..1531e14a6 --- /dev/null +++ b/core/src/apps/ontology/sc/native_builder.py @@ -0,0 +1,120 @@ +from ubinascii import unhexlify + +from trezor import wire + +from .. import writer +from . import builder, opcode + + +class ParamStruct: + """ + Special struct for smart contract argument passing + """ + + def __init__(self, arr: list): + self.arr = arr + + +def build_native_call(func_name: str, params: list, contract: bytes) -> bytes: + """ + Builds native contract call + """ + ret = bytearray() + + _write_native_code_script(ret, params) + builder.write_push_bytes(ret, func_name.encode()) + builder.write_push_bytes(ret, contract) + builder.write_push_int(ret, 0) + writer.write_byte(ret, opcode.SYSCALL) + builder.write_push_bytes(ret, b"Ontology.Native.Invoke") + + return ret + + +def _write_native_code_script(ret: bytearray, arr: list) -> None: + """ + Writes native code script from supplied data + """ + for val in reversed(arr): + if isinstance(val, str): + builder.write_push_bytes(ret, unhexlify(val)) + + elif isinstance(val, (bytes, bytearray)): + builder.write_push_bytes(ret, val) + + elif isinstance(val, bool): + builder.write_push_bool(ret, val) + + elif isinstance(val, int): + builder.write_push_int(ret, val) + + elif isinstance(val, ParamStruct): + builder.write_push_int(ret, 0) + writer.write_byte(ret, opcode.NEWSTRUCT) + writer.write_byte(ret, opcode.TOALTSTACK) + + for v in val.arr: + _write_code_param_script(ret, v) + writer.write_byte(ret, opcode.DUPFROMALTSTACK) + writer.write_byte(ret, opcode.SWAP) + writer.write_byte(ret, opcode.APPEND) + + writer.write_byte(ret, opcode.FROMALTSTACK) + + elif isinstance(val, list) and is_typed_list(val, ParamStruct): + builder.write_push_int(ret, 0) + writer.write_byte(ret, opcode.NEWSTRUCT) + writer.write_byte(ret, opcode.TOALTSTACK) + + for s in val: + _write_code_param_script(ret, s) + + writer.write_byte(ret, opcode.FROMALTSTACK) + builder.write_push_int(ret, len(val)) + writer.write_byte(ret, opcode.PACK) + + elif isinstance(val, list): + print("array") + _write_native_code_script(ret, val) + builder.write_push_int(ret, len(val)) + writer.write_byte(ret, opcode.PACK) + + else: + raise wire.DataError("Invalid param type") + + +def _write_code_param_script(ret: bytearray, obj) -> None: + """ + Writes native code param script from supplied data + """ + if isinstance(obj, str): + builder.write_push_bytes(ret, unhexlify(obj)) + + elif isinstance(obj, (bytes, bytearray)): + builder.write_push_bytes(ret, obj) + + elif isinstance(obj, bool): + builder.write_push_bool(ret, obj) + + elif isinstance(obj, int): + builder.write_push_int(ret, obj) + + elif isinstance(obj, ParamStruct): + for v in obj.arr: + _write_code_param_script(ret, v) + writer.write_byte(ret, opcode.DUPFROMALTSTACK) + writer.write_byte(ret, opcode.SWAP) + writer.write_byte(ret, opcode.APPEND) + else: + raise wire.DataError("Invalid param type") + + +def is_typed_list(arr: list, t) -> bool: + """ + Tests if list contains only object of specified types + """ + for a in arr: + if not isinstance(a, t): + return False + + return True diff --git a/core/src/apps/ontology/sc/opcode.py b/core/src/apps/ontology/sc/opcode.py new file mode 100644 index 000000000..4cfea6fae --- /dev/null +++ b/core/src/apps/ontology/sc/opcode.py @@ -0,0 +1,31 @@ +# OpCode constants +# An empty array of bytes is pushed onto the stack. +PUSH0 = 0x00 +PUSHBYTES75 = 0x4B +# The next byte contains the number of bytes to be pushed onto the stack. +PUSHDATA1 = 0x4C +# The next two bytes contain the number of bytes to be pushed onto the stack. +PUSHDATA2 = 0x4D +# The next four bytes contain the number of bytes to be pushed onto the stack. +PUSHDATA4 = 0x4E +# The number -1 is pushed onto the stack. +PUSHM1 = 0x4F +# The number 1 is pushed onto the stack. +PUSH1 = 0x51 + +# Flow control +SYSCALL = 0x68 +DUPFROMALTSTACK = 0x6A + +# Stack +# Puts the input onto the top of the alt stack. Removes it from the main stack. +TOALTSTACK = 0x6B +# Puts the input onto the top of the main stack. Removes it from the alt stack. +FROMALTSTACK = 0x6C +# The top two items on the stack are swapped. +SWAP = 0x7C + +# Array +PACK = 0xC1 +NEWSTRUCT = 0xC6 +APPEND = 0xC8 diff --git a/core/src/apps/ontology/serialize.py b/core/src/apps/ontology/serialize.py new file mode 100644 index 000000000..979b20b57 --- /dev/null +++ b/core/src/apps/ontology/serialize.py @@ -0,0 +1,104 @@ +from trezor.messages import OntologyAsset +from trezor.messages.OntologyOntIdAddAttributes import OntologyOntIdAddAttributes +from trezor.messages.OntologyOntIdRegister import OntologyOntIdRegister +from trezor.messages.OntologyTransaction import OntologyTransaction +from trezor.messages.OntologyTransfer import OntologyTransfer +from trezor.messages.OntologyTxAttribute import OntologyTxAttribute +from trezor.messages.OntologyWithdrawOng import OntologyWithdrawOng + +from . import const as Const, writer +from .helpers import get_bytes_from_address +from .sc.native_builder import ParamStruct, build_native_call + + +def serialize_tx(tx: OntologyTransaction, payload: bytes, hw) -> None: + writer.write_byte(hw, tx.version) + writer.write_byte(hw, tx.type) + writer.write_uint32(hw, tx.nonce) + writer.write_uint64(hw, tx.gas_price) + writer.write_uint64(hw, tx.gas_limit) + payer = get_bytes_from_address(tx.payer) + writer.write_bytes(hw, payer) + + writer.write_bytes(hw, payload) + + attributes = tx.tx_attributes + writer.write_varint(hw, len(attributes)) + + if attributes is not None: + for attribute in attributes: + _serialize_tx_attribute(hw, attribute) + + +def serialize_transfer(transfer: OntologyTransfer) -> bytes: + from_address = get_bytes_from_address(transfer.from_address) + to_address = get_bytes_from_address(transfer.to_address) + amount = transfer.amount + contract = "" + + if transfer.asset == OntologyAsset.ONT: + contract = Const.ONT_CONTRACT + else: + contract = Const.ONG_CONTRACT + + struct = ParamStruct([from_address, to_address, amount]) + native_call = build_native_call("transfer", [[struct]], contract) + + # 9 is the maximum possible length of the uvarint prefix + ret = bytearray() + writer.write_bytes_with_length(ret, native_call) + return bytes(ret) + + +def serialize_withdraw_ong(withdraw_ong: OntologyWithdrawOng) -> bytes: + from_address = get_bytes_from_address(withdraw_ong.from_address) + to_address = get_bytes_from_address(withdraw_ong.to_address) + amount = withdraw_ong.amount + + struct = ParamStruct([from_address, Const.ONT_CONTRACT, to_address, amount]) + native_call = build_native_call("transferFrom", [struct], Const.ONG_CONTRACT) + + ret = bytearray() + writer.write_bytes_with_length(ret, native_call) + return bytes(ret) + + +def serialize_ont_id_register(register: OntologyOntIdRegister) -> bytes: + ont_id = register.ont_id.encode() + + struct = ParamStruct([ont_id, register.public_key]) + native_call = build_native_call( + "regIDWithPublicKey", [struct], Const.ONTID_CONTRACT + ) + + ret = bytearray() + writer.write_bytes_with_length(ret, native_call) + return bytes(ret) + + +def serialize_ont_id_add_attributes(add: OntologyOntIdAddAttributes) -> bytes: + ont_id = add.ont_id.encode() + attributes = add.ont_id_attributes + + arguments = [ont_id, len(attributes)] + + for attribute in attributes: + arguments.append(attribute.key.encode()) + arguments.append(attribute.type.encode()) + arguments.append(attribute.value.encode()) + + arguments.append(add.public_key) + + struct = ParamStruct(arguments) + native_call = build_native_call("addAttributes", [struct], Const.ONTID_CONTRACT) + + ret = bytearray() + writer.write_bytes_with_length(ret, native_call) + return bytes(ret) + + +def _serialize_tx_attribute(ret: bytearray, attribute: OntologyTxAttribute) -> None: + writer.write_byte(ret, attribute.usage) + + if attribute.data is not None: + writer.write_bytes_with_length(ret, attribute.data) diff --git a/core/src/apps/ontology/sign_tx.py b/core/src/apps/ontology/sign_tx.py new file mode 100644 index 000000000..03ed2af30 --- /dev/null +++ b/core/src/apps/ontology/sign_tx.py @@ -0,0 +1,125 @@ +from trezor import wire +from trezor.crypto.curve import nist256p1 +from trezor.crypto.hashlib import sha256 +from trezor.messages import OntologyAsset +from trezor.messages.OntologySignedTx import OntologySignedTx +from trezor.messages.OntologySignTx import OntologySignTx +from trezor.utils import HashWriter + +from .helpers import CURVE, validate_full_path +from .layout import ( + require_confirm_ont_id_add_attributes, + require_confirm_ont_id_register, + require_confirm_transfer_ong, + require_confirm_transfer_ont, + require_confirm_withdraw_ong, +) +from .serialize import ( + serialize_ont_id_add_attributes, + serialize_ont_id_register, + serialize_transfer, + serialize_tx, + serialize_withdraw_ong, +) + +from apps.common import paths + + +async def sign_tx(ctx, msg: OntologySignTx, keychain) -> OntologySignedTx: + await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) + + if msg.transfer: + return await sign_transfer(ctx, msg, keychain) + elif msg.withdraw_ong: + return await sign_withdraw_ong(ctx, msg, keychain) + elif msg.ont_id_register: + return await sign_ont_id_register(ctx, msg, keychain) + elif msg.ont_id_add_attributes: + return await sign_ont_id_add_attributes(ctx, msg, keychain) + + +async def sign(raw_data: bytes, private_key: bytes) -> bytes: + """ + Creates signature for data + """ + data_hash = sha256(sha256(raw_data).digest()).digest() + + signature = nist256p1.sign(private_key, data_hash, False) + signature = b"\x01" + signature[1:65] # first byte of transaction is 0x01 + return signature + + +async def sign_transfer(ctx, msg: OntologySignTx, keychain) -> OntologySignedTx: + if msg.transaction.type == 0xD1: + if msg.transfer.asset == OntologyAsset.ONT: + await require_confirm_transfer_ont( + ctx, msg.transfer.to_address, msg.transfer.amount + ) + if msg.transfer.asset == OntologyAsset.ONG: + await require_confirm_transfer_ong( + ctx, msg.transfer.to_address, msg.transfer.amount + ) + else: + raise wire.DataError("Invalid transaction type") + + node = keychain.derive(msg.address_n, CURVE) + hw = HashWriter(sha256()) + serialized_payload = serialize_transfer(msg.transfer) + serialize_tx(msg.transaction, serialized_payload, hw) + signature = await sign(hw.get_digest(), node.private_key()) + + return OntologySignedTx(signature=signature, payload=serialized_payload) + + +async def sign_withdraw_ong(ctx, msg: OntologySignTx, keychain) -> OntologySignedTx: + if msg.transaction.type == 0xD1: + await require_confirm_withdraw_ong(ctx, msg.withdraw_ong.amount) + else: + raise wire.DataError("Invalid transaction type") + + node = keychain.derive(msg.address_n, CURVE) + hw = HashWriter(sha256()) + serialized_payload = serialize_withdraw_ong(msg.withdraw_ong) + serialize_tx(msg.transaction, serialized_payload, hw) + signature = await sign(hw.get_digest(), node.private_key()) + + return OntologySignedTx(signature=signature, payload=serialized_payload) + + +async def sign_ont_id_register(ctx, msg: OntologySignTx, keychain) -> OntologySignedTx: + if msg.transaction.type == 0xD1: + await require_confirm_ont_id_register( + ctx, msg.ont_id_register.ont_id, msg.ont_id_register.public_key + ) + else: + raise wire.DataError("Invalid transaction type") + + node = keychain.derive(msg.address_n, CURVE) + hw = HashWriter(sha256()) + serialized_payload = serialize_ont_id_register(msg.ont_id_register) + serialize_tx(msg.transaction, serialized_payload, hw) + signature = await sign(hw.get_digest(), node.private_key()) + + return OntologySignedTx(signature=signature, payload=serialized_payload) + + +async def sign_ont_id_add_attributes( + ctx, msg: OntologySignTx, keychain +) -> OntologySignedTx: + if msg.transaction.type == 0xD1: + await require_confirm_ont_id_add_attributes( + ctx, + msg.ont_id_add_attributes.ont_id, + msg.ont_id_add_attributes.public_key, + msg.ont_id_add_attributes.ont_id_attributes, + ) + else: + raise wire.DataError("Invalid transaction type") + + node = keychain.derive(msg.address_n, CURVE) + hw = HashWriter(sha256()) + serialized_payload = serialize_ont_id_add_attributes(msg.ont_id_add_attributes) + serialize_tx(msg.transaction, serialized_payload, hw) + signature = await sign(hw.get_digest(), node.private_key()) + + return OntologySignedTx(signature=signature, payload=serialized_payload) diff --git a/core/src/apps/ontology/writer.py b/core/src/apps/ontology/writer.py new file mode 100644 index 000000000..4af5ed8d5 --- /dev/null +++ b/core/src/apps/ontology/writer.py @@ -0,0 +1,22 @@ +from apps.common.writers import ( # noqa: F401 + write_bytes, + write_uint16_le as write_uint16, + write_uint32_le as write_uint32, + write_uint64_le as write_uint64, +) +from apps.wallet.sign_tx.writers import write_varint + + +def write_byte(w: bytearray, n: int) -> None: + """ + Writes one byte (8bit) + """ + w.append(n & 0xFF) + + +def write_bytes_with_length(w: bytearray, buf: bytes) -> None: + """ + Writes arbitrary byte sequence prepended with the length using variable length integer + """ + write_varint(w, len(buf)) + write_bytes(w, buf) diff --git a/core/src/main.py b/core/src/main.py index 956460094..1215dca09 100644 --- a/core/src/main.py +++ b/core/src/main.py @@ -34,6 +34,7 @@ def _boot_apps() -> None: import apps.eos import apps.binance import apps.webauthn + import apps.ontology if __debug__: import apps.debug @@ -54,6 +55,7 @@ def _boot_apps() -> None: apps.eos.boot() apps.binance.boot() apps.webauthn.boot() + apps.ontology.boot() if __debug__: apps.debug.boot() diff --git a/core/src/trezor/messages/Capability.py b/core/src/trezor/messages/Capability.py index ae1c5abb5..2518f3b07 100644 --- a/core/src/trezor/messages/Capability.py +++ b/core/src/trezor/messages/Capability.py @@ -23,3 +23,5 @@ if not utils.BITCOIN_ONLY: U2F = 14 # type: Literal[14] Shamir = 15 # type: Literal[15] ShamirGroups = 16 # type: Literal[16] +if not utils.BITCOIN_ONLY: + Ontology = 17 # type: Literal[17] diff --git a/core/src/trezor/messages/Features.py b/core/src/trezor/messages/Features.py index 17586cc24..131c2a7dc 100644 --- a/core/src/trezor/messages/Features.py +++ b/core/src/trezor/messages/Features.py @@ -6,7 +6,7 @@ if __debug__: try: from typing import Dict, List # noqa: F401 from typing_extensions import Literal # noqa: F401 - EnumTypeCapability = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + EnumTypeCapability = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] EnumTypeBackupType = Literal[0, 1, 2] except ImportError: pass @@ -116,7 +116,7 @@ class Features(p.MessageType): 27: ('unfinished_backup', p.BoolType, 0), 28: ('no_backup', p.BoolType, 0), 29: ('recovery_mode', p.BoolType, 0), - 30: ('capabilities', p.EnumType("Capability", (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)), p.FLAG_REPEATED), + 30: ('capabilities', p.EnumType("Capability", (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)), p.FLAG_REPEATED), 31: ('backup_type', p.EnumType("BackupType", (0, 1, 2)), 0), 32: ('sd_card_present', p.BoolType, 0), 33: ('sd_protection', p.BoolType, 0), diff --git a/core/src/trezor/messages/MessageType.py b/core/src/trezor/messages/MessageType.py index f9994d83f..d2c462444 100644 --- a/core/src/trezor/messages/MessageType.py +++ b/core/src/trezor/messages/MessageType.py @@ -130,6 +130,12 @@ if not utils.BITCOIN_ONLY: CardanoAddress = 308 # type: Literal[308] CardanoTxAck = 309 # type: Literal[309] CardanoSignedTx = 310 # type: Literal[310] + OntologyGetAddress = 350 # type: Literal[350] + OntologyAddress = 351 # type: Literal[351] + OntologyGetPublicKey = 352 # type: Literal[352] + OntologyPublicKey = 353 # type: Literal[353] + OntologySignTx = 354 # type: Literal[354] + OntologySignedTx = 355 # type: Literal[355] RippleGetAddress = 400 # type: Literal[400] RippleAddress = 401 # type: Literal[401] RippleSignTx = 402 # type: Literal[402] diff --git a/core/src/trezor/messages/OntologyAddress.py b/core/src/trezor/messages/OntologyAddress.py new file mode 100644 index 000000000..61f325eaa --- /dev/null +++ b/core/src/trezor/messages/OntologyAddress.py @@ -0,0 +1,26 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyAddress(p.MessageType): + MESSAGE_WIRE_TYPE = 351 + + def __init__( + self, + address: str = None, + ) -> None: + self.address = address + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address', p.UnicodeType, 0), + } diff --git a/core/src/trezor/messages/OntologyAsset.py b/core/src/trezor/messages/OntologyAsset.py new file mode 100644 index 000000000..df1744f88 --- /dev/null +++ b/core/src/trezor/messages/OntologyAsset.py @@ -0,0 +1,7 @@ +# Automatically generated by pb2py +# fmt: off +if False: + from typing_extensions import Literal + +ONT = 1 # type: Literal[1] +ONG = 2 # type: Literal[2] diff --git a/core/src/trezor/messages/OntologyGetAddress.py b/core/src/trezor/messages/OntologyGetAddress.py new file mode 100644 index 000000000..93a876138 --- /dev/null +++ b/core/src/trezor/messages/OntologyGetAddress.py @@ -0,0 +1,29 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyGetAddress(p.MessageType): + MESSAGE_WIRE_TYPE = 350 + + def __init__( + self, + address_n: List[int] = None, + show_display: bool = None, + ) -> None: + self.address_n = address_n if address_n is not None else [] + self.show_display = show_display + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('show_display', p.BoolType, 0), + } diff --git a/core/src/trezor/messages/OntologyGetPublicKey.py b/core/src/trezor/messages/OntologyGetPublicKey.py new file mode 100644 index 000000000..804119f4a --- /dev/null +++ b/core/src/trezor/messages/OntologyGetPublicKey.py @@ -0,0 +1,29 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyGetPublicKey(p.MessageType): + MESSAGE_WIRE_TYPE = 352 + + def __init__( + self, + address_n: List[int] = None, + show_display: bool = None, + ) -> None: + self.address_n = address_n if address_n is not None else [] + self.show_display = show_display + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('show_display', p.BoolType, 0), + } diff --git a/core/src/trezor/messages/OntologyOntIdAddAttributes.py b/core/src/trezor/messages/OntologyOntIdAddAttributes.py new file mode 100644 index 000000000..6e2d4b35d --- /dev/null +++ b/core/src/trezor/messages/OntologyOntIdAddAttributes.py @@ -0,0 +1,33 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +from .OntologyOntIdAttribute import OntologyOntIdAttribute + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyOntIdAddAttributes(p.MessageType): + + def __init__( + self, + ont_id: str = None, + public_key: bytes = None, + ont_id_attributes: List[OntologyOntIdAttribute] = None, + ) -> None: + self.ont_id = ont_id + self.public_key = public_key + self.ont_id_attributes = ont_id_attributes if ont_id_attributes is not None else [] + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('ont_id', p.UnicodeType, 0), + 2: ('public_key', p.BytesType, 0), + 3: ('ont_id_attributes', OntologyOntIdAttribute, p.FLAG_REPEATED), + } diff --git a/core/src/trezor/messages/OntologyOntIdAttribute.py b/core/src/trezor/messages/OntologyOntIdAttribute.py new file mode 100644 index 000000000..fa05ae6ae --- /dev/null +++ b/core/src/trezor/messages/OntologyOntIdAttribute.py @@ -0,0 +1,31 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyOntIdAttribute(p.MessageType): + + def __init__( + self, + key: str = None, + type: str = None, + value: str = None, + ) -> None: + self.key = key + self.type = type + self.value = value + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('key', p.UnicodeType, 0), + 2: ('type', p.UnicodeType, 0), + 3: ('value', p.UnicodeType, 0), + } diff --git a/core/src/trezor/messages/OntologyOntIdRegister.py b/core/src/trezor/messages/OntologyOntIdRegister.py new file mode 100644 index 000000000..f91342a57 --- /dev/null +++ b/core/src/trezor/messages/OntologyOntIdRegister.py @@ -0,0 +1,28 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyOntIdRegister(p.MessageType): + + def __init__( + self, + ont_id: str = None, + public_key: bytes = None, + ) -> None: + self.ont_id = ont_id + self.public_key = public_key + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('ont_id', p.UnicodeType, 0), + 2: ('public_key', p.BytesType, 0), + } diff --git a/core/src/trezor/messages/OntologyPublicKey.py b/core/src/trezor/messages/OntologyPublicKey.py new file mode 100644 index 000000000..3b39a3dde --- /dev/null +++ b/core/src/trezor/messages/OntologyPublicKey.py @@ -0,0 +1,26 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyPublicKey(p.MessageType): + MESSAGE_WIRE_TYPE = 353 + + def __init__( + self, + public_key: bytes = None, + ) -> None: + self.public_key = public_key + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('public_key', p.BytesType, 0), + } diff --git a/core/src/trezor/messages/OntologySignTx.py b/core/src/trezor/messages/OntologySignTx.py new file mode 100644 index 000000000..0276beade --- /dev/null +++ b/core/src/trezor/messages/OntologySignTx.py @@ -0,0 +1,47 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +from .OntologyOntIdAddAttributes import OntologyOntIdAddAttributes +from .OntologyOntIdRegister import OntologyOntIdRegister +from .OntologyTransaction import OntologyTransaction +from .OntologyTransfer import OntologyTransfer +from .OntologyWithdrawOng import OntologyWithdrawOng + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologySignTx(p.MessageType): + MESSAGE_WIRE_TYPE = 354 + + def __init__( + self, + address_n: List[int] = None, + transaction: OntologyTransaction = None, + transfer: OntologyTransfer = None, + withdraw_ong: OntologyWithdrawOng = None, + ont_id_register: OntologyOntIdRegister = None, + ont_id_add_attributes: OntologyOntIdAddAttributes = None, + ) -> None: + self.address_n = address_n if address_n is not None else [] + self.transaction = transaction + self.transfer = transfer + self.withdraw_ong = withdraw_ong + self.ont_id_register = ont_id_register + self.ont_id_add_attributes = ont_id_add_attributes + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('transaction', OntologyTransaction, 0), + 3: ('transfer', OntologyTransfer, 0), + 4: ('withdraw_ong', OntologyWithdrawOng, 0), + 5: ('ont_id_register', OntologyOntIdRegister, 0), + 6: ('ont_id_add_attributes', OntologyOntIdAddAttributes, 0), + } diff --git a/core/src/trezor/messages/OntologySignedTx.py b/core/src/trezor/messages/OntologySignedTx.py new file mode 100644 index 000000000..bb75c9800 --- /dev/null +++ b/core/src/trezor/messages/OntologySignedTx.py @@ -0,0 +1,29 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologySignedTx(p.MessageType): + MESSAGE_WIRE_TYPE = 355 + + def __init__( + self, + signature: bytes = None, + payload: bytes = None, + ) -> None: + self.signature = signature + self.payload = payload + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('signature', p.BytesType, 0), + 2: ('payload', p.BytesType, 0), + } diff --git a/core/src/trezor/messages/OntologyTransaction.py b/core/src/trezor/messages/OntologyTransaction.py new file mode 100644 index 000000000..813524ee9 --- /dev/null +++ b/core/src/trezor/messages/OntologyTransaction.py @@ -0,0 +1,45 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +from .OntologyTxAttribute import OntologyTxAttribute + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyTransaction(p.MessageType): + + def __init__( + self, + version: int = None, + type: int = None, + nonce: int = None, + gas_price: int = None, + gas_limit: int = None, + payer: str = None, + tx_attributes: List[OntologyTxAttribute] = None, + ) -> None: + self.version = version + self.type = type + self.nonce = nonce + self.gas_price = gas_price + self.gas_limit = gas_limit + self.payer = payer + self.tx_attributes = tx_attributes if tx_attributes is not None else [] + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('version', p.UVarintType, 0), + 2: ('type', p.UVarintType, 0), + 3: ('nonce', p.UVarintType, 0), + 4: ('gas_price', p.UVarintType, 0), + 5: ('gas_limit', p.UVarintType, 0), + 6: ('payer', p.UnicodeType, 0), + 7: ('tx_attributes', OntologyTxAttribute, p.FLAG_REPEATED), + } diff --git a/core/src/trezor/messages/OntologyTransfer.py b/core/src/trezor/messages/OntologyTransfer.py new file mode 100644 index 000000000..39425e3bd --- /dev/null +++ b/core/src/trezor/messages/OntologyTransfer.py @@ -0,0 +1,35 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + EnumTypeOntologyAsset = Literal[1, 2] + except ImportError: + pass + + +class OntologyTransfer(p.MessageType): + + def __init__( + self, + asset: EnumTypeOntologyAsset = None, + amount: int = None, + from_address: str = None, + to_address: str = None, + ) -> None: + self.asset = asset + self.amount = amount + self.from_address = from_address + self.to_address = to_address + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('asset', p.EnumType("OntologyAsset", (1, 2)), 0), + 2: ('amount', p.UVarintType, 0), + 3: ('from_address', p.UnicodeType, 0), + 4: ('to_address', p.UnicodeType, 0), + } diff --git a/core/src/trezor/messages/OntologyTxAttribute.py b/core/src/trezor/messages/OntologyTxAttribute.py new file mode 100644 index 000000000..cdb027010 --- /dev/null +++ b/core/src/trezor/messages/OntologyTxAttribute.py @@ -0,0 +1,28 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyTxAttribute(p.MessageType): + + def __init__( + self, + usage: int = None, + data: bytes = None, + ) -> None: + self.usage = usage + self.data = data + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('usage', p.UVarintType, 0), + 2: ('data', p.BytesType, 0), + } diff --git a/core/src/trezor/messages/OntologyWithdrawOng.py b/core/src/trezor/messages/OntologyWithdrawOng.py new file mode 100644 index 000000000..19bd3faf1 --- /dev/null +++ b/core/src/trezor/messages/OntologyWithdrawOng.py @@ -0,0 +1,31 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyWithdrawOng(p.MessageType): + + def __init__( + self, + amount: int = None, + from_address: str = None, + to_address: str = None, + ) -> None: + self.amount = amount + self.from_address = from_address + self.to_address = to_address + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('amount', p.UVarintType, 0), + 2: ('from_address', p.UnicodeType, 0), + 3: ('to_address', p.UnicodeType, 0), + } diff --git a/python/src/trezorlib/cli/ontology.py b/python/src/trezorlib/cli/ontology.py new file mode 100644 index 000000000..6e5efbd35 --- /dev/null +++ b/python/src/trezorlib/cli/ontology.py @@ -0,0 +1,193 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2019 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import json + +import click + +from .. import messages, ontology, protobuf, tools + +PATH_HELP = "BIP-32 path to key, e.g. m/44'/888'/0'/0/0" + + +@click.group(name="ontology") +def cli(): + """Ontology commands.""" + + +@cli.command(help="Get Ontology address for specified path.") +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.option("-d", "--show-display", is_flag=True) +@click.pass_obj +def get_address(connect, address, show_display): + client = connect() + address_n = tools.parse_path(address) + return ontology.get_address(client, address_n, show_display) + + +@cli.command(help="Get Ontology public key for specified path.") +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.option("-d", "--show-display", is_flag=True) +@click.pass_obj +def get_public_key(connect, address, show_display): + client = connect() + address_n = tools.parse_path(address) + result = ontology.get_public_key(client, address_n, show_display) + + return result.public_key.hex() + + +@cli.command(help="Sign Ontology transfer.") +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.option( + "-t", + "--transaction", + type=click.File("r"), + default="-", + help="Transaction in JSON format", +) +@click.option( + "-r", + "--transfer", + type=click.File("r"), + default="-", + help="Transfer in JSON format", +) +@click.pass_obj +def sign_transfer(connect, address, transaction, transfer): + client = connect() + address_n = tools.parse_path(address) + transaction = protobuf.dict_to_proto( + messages.OntologyTransaction, json.load(transaction) + ) + transfer = protobuf.dict_to_proto(messages.OntologyTransfer, json.load(transfer)) + + result = ontology.sign_transfer(client, address_n, transaction, transfer) + + output = {"payload": result.payload.hex(), "signature": result.signature.hex()} + + return output + + +@cli.command(help="Sign Ontology withdraw Ong.") +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.option( + "-t", + "--transaction", + type=click.File("r"), + default="-", + help="Transaction in JSON format", +) +@click.option( + "-w", + "--withdraw_ong", + type=click.File("r"), + default="-", + help="Withdrawal in JSON format", +) +@click.pass_obj +def sign_withdraw_ong(connect, address, transaction, withdraw_ong): + client = connect() + address_n = tools.parse_path(address) + transaction = protobuf.dict_to_proto( + messages.OntologyTransaction, json.load(transaction) + ) + withdraw_ong = protobuf.dict_to_proto( + messages.OntologyWithdrawOng, json.load(withdraw_ong) + ) + + result = ontology.sign_withdrawal(client, address_n, transaction, withdraw_ong) + + output = {"payload": result.payload.hex(), "signature": result.signature.hex()} + + return output + + +@cli.command(help="Sign Ontology ONT ID Registration.") +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.option( + "-t", + "--transaction", + type=click.File("r"), + default="-", + help="Transaction in JSON format", +) +@click.option( + "-r", + "--register", + type=click.File("r"), + default="-", + help="Register in JSON format", +) +@click.argument("transaction") +@click.argument("ont_id_register") +@click.pass_obj +def sign_ont_id_register(connect, address, transaction, register): + client = connect() + address_n = tools.parse_path(address) + transaction = protobuf.dict_to_proto( + messages.OntologyTransaction, json.load(transaction) + ) + ont_id_register = protobuf.dict_to_proto( + messages.OntologyOntIdRegister, json.load(register) + ) + + result = ontology.sign_register(client, address_n, transaction, ont_id_register) + + output = {"payload": result.payload.hex(), "signature": result.signature.hex()} + + return output + + +@cli.command(help="Sign Ontology ONT ID Attributes adding.") +@click.option( + "-n", + "--address", + required=True, + help="BIP-32 path to signing key, e.g. m/44'/888'/0'/0/0", +) +@click.option( + "-t", + "--transaction", + type=click.File("r"), + required=True, + default="-", + help="Transaction in JSON format", +) +@click.option( + "-a", + "--add_attribute", + type=click.File("r"), + required=True, + default="-", + help="Add attributes in JSON format", +) +@click.pass_obj +def sign_ont_id_add_attributes(connect, address, transaction, add_attribute): + client = connect() + address_n = tools.parse_path(address) + transaction = protobuf.dict_to_proto( + messages.OntologyTransaction, json.load(transaction) + ) + ont_id_add_attributes = protobuf.dict_to_proto( + messages.OntologyOntIdAddAttributes, json.load(add_attribute) + ) + result = ontology.sign_add_attr( + client, address_n, transaction, ont_id_add_attributes + ) + output = {"payload": result.payload.hex(), "signature": result.signature.hex()} + + return output diff --git a/python/src/trezorlib/cli/trezorctl.py b/python/src/trezorlib/cli/trezorctl.py index 2db84a42c..1fbd81d48 100755 --- a/python/src/trezorlib/cli/trezorctl.py +++ b/python/src/trezorlib/cli/trezorctl.py @@ -39,6 +39,7 @@ from . import ( lisk, monero, nem, + ontology, ripple, settings, stellar, @@ -266,6 +267,7 @@ cli.add_command(fido.cli) cli.add_command(lisk.cli) cli.add_command(monero.cli) cli.add_command(nem.cli) +cli.add_command(ontology.cli) cli.add_command(ripple.cli) cli.add_command(settings.cli) cli.add_command(stellar.cli) diff --git a/python/src/trezorlib/messages/Capability.py b/python/src/trezorlib/messages/Capability.py index 4a4f6d93f..50512bd09 100644 --- a/python/src/trezorlib/messages/Capability.py +++ b/python/src/trezorlib/messages/Capability.py @@ -19,3 +19,4 @@ Tezos = 13 # type: Literal[13] U2F = 14 # type: Literal[14] Shamir = 15 # type: Literal[15] ShamirGroups = 16 # type: Literal[16] +Ontology = 17 # type: Literal[17] diff --git a/python/src/trezorlib/messages/Features.py b/python/src/trezorlib/messages/Features.py index 23720aa22..4b8bdea59 100644 --- a/python/src/trezorlib/messages/Features.py +++ b/python/src/trezorlib/messages/Features.py @@ -6,7 +6,7 @@ if __debug__: try: from typing import Dict, List # noqa: F401 from typing_extensions import Literal # noqa: F401 - EnumTypeCapability = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + EnumTypeCapability = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] EnumTypeBackupType = Literal[0, 1, 2] except ImportError: pass @@ -116,7 +116,7 @@ class Features(p.MessageType): 27: ('unfinished_backup', p.BoolType, 0), 28: ('no_backup', p.BoolType, 0), 29: ('recovery_mode', p.BoolType, 0), - 30: ('capabilities', p.EnumType("Capability", (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)), p.FLAG_REPEATED), + 30: ('capabilities', p.EnumType("Capability", (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)), p.FLAG_REPEATED), 31: ('backup_type', p.EnumType("BackupType", (0, 1, 2)), 0), 32: ('sd_card_present', p.BoolType, 0), 33: ('sd_protection', p.BoolType, 0), diff --git a/python/src/trezorlib/messages/MessageType.py b/python/src/trezorlib/messages/MessageType.py index 0d8a6baf9..14ccb0bef 100644 --- a/python/src/trezorlib/messages/MessageType.py +++ b/python/src/trezorlib/messages/MessageType.py @@ -127,6 +127,12 @@ CardanoGetAddress = 307 # type: Literal[307] CardanoAddress = 308 # type: Literal[308] CardanoTxAck = 309 # type: Literal[309] CardanoSignedTx = 310 # type: Literal[310] +OntologyGetAddress = 350 # type: Literal[350] +OntologyAddress = 351 # type: Literal[351] +OntologyGetPublicKey = 352 # type: Literal[352] +OntologyPublicKey = 353 # type: Literal[353] +OntologySignTx = 354 # type: Literal[354] +OntologySignedTx = 355 # type: Literal[355] RippleGetAddress = 400 # type: Literal[400] RippleAddress = 401 # type: Literal[401] RippleSignTx = 402 # type: Literal[402] diff --git a/python/src/trezorlib/messages/OntologyAddress.py b/python/src/trezorlib/messages/OntologyAddress.py new file mode 100644 index 000000000..cf6632c42 --- /dev/null +++ b/python/src/trezorlib/messages/OntologyAddress.py @@ -0,0 +1,26 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyAddress(p.MessageType): + MESSAGE_WIRE_TYPE = 351 + + def __init__( + self, + address: str = None, + ) -> None: + self.address = address + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address', p.UnicodeType, 0), + } diff --git a/python/src/trezorlib/messages/OntologyAsset.py b/python/src/trezorlib/messages/OntologyAsset.py new file mode 100644 index 000000000..df1744f88 --- /dev/null +++ b/python/src/trezorlib/messages/OntologyAsset.py @@ -0,0 +1,7 @@ +# Automatically generated by pb2py +# fmt: off +if False: + from typing_extensions import Literal + +ONT = 1 # type: Literal[1] +ONG = 2 # type: Literal[2] diff --git a/python/src/trezorlib/messages/OntologyGetAddress.py b/python/src/trezorlib/messages/OntologyGetAddress.py new file mode 100644 index 000000000..e7e6624f6 --- /dev/null +++ b/python/src/trezorlib/messages/OntologyGetAddress.py @@ -0,0 +1,29 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyGetAddress(p.MessageType): + MESSAGE_WIRE_TYPE = 350 + + def __init__( + self, + address_n: List[int] = None, + show_display: bool = None, + ) -> None: + self.address_n = address_n if address_n is not None else [] + self.show_display = show_display + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('show_display', p.BoolType, 0), + } diff --git a/python/src/trezorlib/messages/OntologyGetPublicKey.py b/python/src/trezorlib/messages/OntologyGetPublicKey.py new file mode 100644 index 000000000..e2f8c6129 --- /dev/null +++ b/python/src/trezorlib/messages/OntologyGetPublicKey.py @@ -0,0 +1,29 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyGetPublicKey(p.MessageType): + MESSAGE_WIRE_TYPE = 352 + + def __init__( + self, + address_n: List[int] = None, + show_display: bool = None, + ) -> None: + self.address_n = address_n if address_n is not None else [] + self.show_display = show_display + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('show_display', p.BoolType, 0), + } diff --git a/python/src/trezorlib/messages/OntologyOntIdAddAttributes.py b/python/src/trezorlib/messages/OntologyOntIdAddAttributes.py new file mode 100644 index 000000000..529b0a83d --- /dev/null +++ b/python/src/trezorlib/messages/OntologyOntIdAddAttributes.py @@ -0,0 +1,33 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +from .OntologyOntIdAttribute import OntologyOntIdAttribute + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyOntIdAddAttributes(p.MessageType): + + def __init__( + self, + ont_id: str = None, + public_key: bytes = None, + ont_id_attributes: List[OntologyOntIdAttribute] = None, + ) -> None: + self.ont_id = ont_id + self.public_key = public_key + self.ont_id_attributes = ont_id_attributes if ont_id_attributes is not None else [] + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('ont_id', p.UnicodeType, 0), + 2: ('public_key', p.BytesType, 0), + 3: ('ont_id_attributes', OntologyOntIdAttribute, p.FLAG_REPEATED), + } diff --git a/python/src/trezorlib/messages/OntologyOntIdAttribute.py b/python/src/trezorlib/messages/OntologyOntIdAttribute.py new file mode 100644 index 000000000..f2af15af0 --- /dev/null +++ b/python/src/trezorlib/messages/OntologyOntIdAttribute.py @@ -0,0 +1,31 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyOntIdAttribute(p.MessageType): + + def __init__( + self, + key: str = None, + type: str = None, + value: str = None, + ) -> None: + self.key = key + self.type = type + self.value = value + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('key', p.UnicodeType, 0), + 2: ('type', p.UnicodeType, 0), + 3: ('value', p.UnicodeType, 0), + } diff --git a/python/src/trezorlib/messages/OntologyOntIdRegister.py b/python/src/trezorlib/messages/OntologyOntIdRegister.py new file mode 100644 index 000000000..9bc2d3c85 --- /dev/null +++ b/python/src/trezorlib/messages/OntologyOntIdRegister.py @@ -0,0 +1,28 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyOntIdRegister(p.MessageType): + + def __init__( + self, + ont_id: str = None, + public_key: bytes = None, + ) -> None: + self.ont_id = ont_id + self.public_key = public_key + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('ont_id', p.UnicodeType, 0), + 2: ('public_key', p.BytesType, 0), + } diff --git a/python/src/trezorlib/messages/OntologyPublicKey.py b/python/src/trezorlib/messages/OntologyPublicKey.py new file mode 100644 index 000000000..deaa4f56f --- /dev/null +++ b/python/src/trezorlib/messages/OntologyPublicKey.py @@ -0,0 +1,26 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyPublicKey(p.MessageType): + MESSAGE_WIRE_TYPE = 353 + + def __init__( + self, + public_key: bytes = None, + ) -> None: + self.public_key = public_key + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('public_key', p.BytesType, 0), + } diff --git a/python/src/trezorlib/messages/OntologySignTx.py b/python/src/trezorlib/messages/OntologySignTx.py new file mode 100644 index 000000000..c2fb3e34e --- /dev/null +++ b/python/src/trezorlib/messages/OntologySignTx.py @@ -0,0 +1,47 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +from .OntologyOntIdAddAttributes import OntologyOntIdAddAttributes +from .OntologyOntIdRegister import OntologyOntIdRegister +from .OntologyTransaction import OntologyTransaction +from .OntologyTransfer import OntologyTransfer +from .OntologyWithdrawOng import OntologyWithdrawOng + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologySignTx(p.MessageType): + MESSAGE_WIRE_TYPE = 354 + + def __init__( + self, + address_n: List[int] = None, + transaction: OntologyTransaction = None, + transfer: OntologyTransfer = None, + withdraw_ong: OntologyWithdrawOng = None, + ont_id_register: OntologyOntIdRegister = None, + ont_id_add_attributes: OntologyOntIdAddAttributes = None, + ) -> None: + self.address_n = address_n if address_n is not None else [] + self.transaction = transaction + self.transfer = transfer + self.withdraw_ong = withdraw_ong + self.ont_id_register = ont_id_register + self.ont_id_add_attributes = ont_id_add_attributes + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('transaction', OntologyTransaction, 0), + 3: ('transfer', OntologyTransfer, 0), + 4: ('withdraw_ong', OntologyWithdrawOng, 0), + 5: ('ont_id_register', OntologyOntIdRegister, 0), + 6: ('ont_id_add_attributes', OntologyOntIdAddAttributes, 0), + } diff --git a/python/src/trezorlib/messages/OntologySignedTx.py b/python/src/trezorlib/messages/OntologySignedTx.py new file mode 100644 index 000000000..811d5afaf --- /dev/null +++ b/python/src/trezorlib/messages/OntologySignedTx.py @@ -0,0 +1,29 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologySignedTx(p.MessageType): + MESSAGE_WIRE_TYPE = 355 + + def __init__( + self, + signature: bytes = None, + payload: bytes = None, + ) -> None: + self.signature = signature + self.payload = payload + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('signature', p.BytesType, 0), + 2: ('payload', p.BytesType, 0), + } diff --git a/python/src/trezorlib/messages/OntologyTransaction.py b/python/src/trezorlib/messages/OntologyTransaction.py new file mode 100644 index 000000000..531abaf6d --- /dev/null +++ b/python/src/trezorlib/messages/OntologyTransaction.py @@ -0,0 +1,45 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +from .OntologyTxAttribute import OntologyTxAttribute + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyTransaction(p.MessageType): + + def __init__( + self, + version: int = None, + type: int = None, + nonce: int = None, + gas_price: int = None, + gas_limit: int = None, + payer: str = None, + tx_attributes: List[OntologyTxAttribute] = None, + ) -> None: + self.version = version + self.type = type + self.nonce = nonce + self.gas_price = gas_price + self.gas_limit = gas_limit + self.payer = payer + self.tx_attributes = tx_attributes if tx_attributes is not None else [] + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('version', p.UVarintType, 0), + 2: ('type', p.UVarintType, 0), + 3: ('nonce', p.UVarintType, 0), + 4: ('gas_price', p.UVarintType, 0), + 5: ('gas_limit', p.UVarintType, 0), + 6: ('payer', p.UnicodeType, 0), + 7: ('tx_attributes', OntologyTxAttribute, p.FLAG_REPEATED), + } diff --git a/python/src/trezorlib/messages/OntologyTransfer.py b/python/src/trezorlib/messages/OntologyTransfer.py new file mode 100644 index 000000000..4da669d9d --- /dev/null +++ b/python/src/trezorlib/messages/OntologyTransfer.py @@ -0,0 +1,35 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + EnumTypeOntologyAsset = Literal[1, 2] + except ImportError: + pass + + +class OntologyTransfer(p.MessageType): + + def __init__( + self, + asset: EnumTypeOntologyAsset = None, + amount: int = None, + from_address: str = None, + to_address: str = None, + ) -> None: + self.asset = asset + self.amount = amount + self.from_address = from_address + self.to_address = to_address + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('asset', p.EnumType("OntologyAsset", (1, 2)), 0), + 2: ('amount', p.UVarintType, 0), + 3: ('from_address', p.UnicodeType, 0), + 4: ('to_address', p.UnicodeType, 0), + } diff --git a/python/src/trezorlib/messages/OntologyTxAttribute.py b/python/src/trezorlib/messages/OntologyTxAttribute.py new file mode 100644 index 000000000..a527e5bb6 --- /dev/null +++ b/python/src/trezorlib/messages/OntologyTxAttribute.py @@ -0,0 +1,28 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyTxAttribute(p.MessageType): + + def __init__( + self, + usage: int = None, + data: bytes = None, + ) -> None: + self.usage = usage + self.data = data + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('usage', p.UVarintType, 0), + 2: ('data', p.BytesType, 0), + } diff --git a/python/src/trezorlib/messages/OntologyWithdrawOng.py b/python/src/trezorlib/messages/OntologyWithdrawOng.py new file mode 100644 index 000000000..7925eacf3 --- /dev/null +++ b/python/src/trezorlib/messages/OntologyWithdrawOng.py @@ -0,0 +1,31 @@ +# Automatically generated by pb2py +# fmt: off +from .. import protobuf as p + +if __debug__: + try: + from typing import Dict, List # noqa: F401 + from typing_extensions import Literal # noqa: F401 + except ImportError: + pass + + +class OntologyWithdrawOng(p.MessageType): + + def __init__( + self, + amount: int = None, + from_address: str = None, + to_address: str = None, + ) -> None: + self.amount = amount + self.from_address = from_address + self.to_address = to_address + + @classmethod + def get_fields(cls) -> Dict: + return { + 1: ('amount', p.UVarintType, 0), + 2: ('from_address', p.UnicodeType, 0), + 3: ('to_address', p.UnicodeType, 0), + } diff --git a/python/src/trezorlib/messages/__init__.py b/python/src/trezorlib/messages/__init__.py index e217ee0ad..446cd5c85 100644 --- a/python/src/trezorlib/messages/__init__.py +++ b/python/src/trezorlib/messages/__init__.py @@ -190,6 +190,19 @@ from .NEMSignedTx import NEMSignedTx from .NEMTransactionCommon import NEMTransactionCommon from .NEMTransfer import NEMTransfer from .NextU2FCounter import NextU2FCounter +from .OntologyAddress import OntologyAddress +from .OntologyGetAddress import OntologyGetAddress +from .OntologyGetPublicKey import OntologyGetPublicKey +from .OntologyOntIdAddAttributes import OntologyOntIdAddAttributes +from .OntologyOntIdAttribute import OntologyOntIdAttribute +from .OntologyOntIdRegister import OntologyOntIdRegister +from .OntologyPublicKey import OntologyPublicKey +from .OntologySignTx import OntologySignTx +from .OntologySignedTx import OntologySignedTx +from .OntologyTransaction import OntologyTransaction +from .OntologyTransfer import OntologyTransfer +from .OntologyTxAttribute import OntologyTxAttribute +from .OntologyWithdrawOng import OntologyWithdrawOng from .PassphraseAck import PassphraseAck from .PassphraseRequest import PassphraseRequest from .PassphraseStateAck import PassphraseStateAck @@ -277,6 +290,7 @@ from . import NEMImportanceTransferMode from . import NEMModificationType from . import NEMMosaicLevy from . import NEMSupplyChangeType +from . import OntologyAsset from . import OutputScriptType from . import PassphraseSourceType from . import PinMatrixRequestType diff --git a/python/src/trezorlib/ontology.py b/python/src/trezorlib/ontology.py new file mode 100644 index 000000000..8a3a7d972 --- /dev/null +++ b/python/src/trezorlib/ontology.py @@ -0,0 +1,66 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +from . import messages +from .tools import expect + +# +# Ontology functions +# + + +@expect(messages.OntologyAddress, field="address") +def get_address(client, address_n, show_display=False): + return client.call( + messages.OntologyGetAddress(address_n=address_n, show_display=show_display) + ) + + +@expect(messages.OntologyPublicKey) +def get_public_key(client, address_n, show_display=False): + return client.call( + messages.OntologyGetPublicKey(address_n=address_n, show_display=show_display) + ) + + +@expect(messages.OntologySignedTx) +def sign_transfer(client, address_n, t, tr): + return client.call( + messages.OntologySignTx(address_n=address_n, transaction=t, transfer=tr) + ) + + +@expect(messages.OntologySignedTx) +def sign_withdrawal(client, address_n, t, w): + return client.call( + messages.OntologySignTx(address_n=address_n, transaction=t, withdraw_ong=w) + ) + + +@expect(messages.OntologySignedTx) +def sign_register(client, address_n, t, r): + return client.call( + messages.OntologySignTx(address_n=address_n, transaction=t, ont_id_register=r) + ) + + +@expect(messages.OntologySignedTx) +def sign_add_attr(client, address_n, t, a): + return client.call( + messages.OntologySignTx( + address_n=address_n, transaction=t, ont_id_add_attributes=a + ) + ) diff --git a/python/trezorlib/coins.json b/python/trezorlib/coins.json new file mode 100644 index 000000000..b10f9f5a1 --- /dev/null +++ b/python/trezorlib/coins.json @@ -0,0 +1 @@ +[{"address_type": 0, "address_type_p2sh": 5, "bech32_prefix": "bc", "bip115": false, "bitcore": [], "blockbook": ["https://btc1.trezor.io", "https://btc2.trezor.io", "https://btc3.trezor.io", "https://btc4.trezor.io", "https://btc5.trezor.io"], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Bitcoin", "coin_name": "Bitcoin", "coin_shortcut": "BTC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/bitcoin/bitcoin", "hash_genesis_block": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", "key": "bitcoin:BTC", "maintainer": "Pavol Rusnak ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Bitcoin", "segwit": true, "shortcut": "BTC", "signed_message_header": "Bitcoin Signed Message:\n", "slip44": 0, "support": {"connect": true, "trezor1": "1.5.2", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "bitcoin", "website": "https://bitcoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 111, "address_type_p2sh": 196, "bech32_prefix": "tb", "bip115": false, "bitcore": [], "blockbook": ["https://tbtc1.trezor.io", "https://tbtc2.trezor.io"], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Testnet", "coin_name": "Testnet", "coin_shortcut": "TEST", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/bitcoin/bitcoin", "hash_genesis_block": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943", "key": "bitcoin:TEST", "maintainer": "Pavol Rusnak ", "max_address_length": 34, "maxfee_kb": 10000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Testnet", "segwit": true, "shortcut": "TEST", "signed_message_header": "Bitcoin Signed Message:\n", "slip44": 1, "support": {"connect": true, "trezor1": "1.5.2", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "bitcoin", "website": "https://bitcoin.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_p2sh": 71979618}, {"address_type": 53, "address_type_p2sh": 55, "bech32_prefix": "acm", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Actinium", "coin_name": "Actinium", "coin_shortcut": "ACM", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 1000}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/Actinium-project/Actinium", "hash_genesis_block": "28d77872e23714562f49a1be792c276623c1bbe3fdcf21b6035cfde78b00b824", "key": "bitcoin:ACM", "maintainer": "Harris Brakmic ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 100000, "name": "Actinium", "segwit": true, "shortcut": "ACM", "signed_message_header": "Actinium Signed Message:\n", "slip44": 228, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.10", "webwallet": true}, "uri_prefix": "actinium", "website": "https://actinium.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 55, "address_type_p2sh": 16, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Axe", "coin_name": "Axe", "coin_shortcut": "AXE", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/axerunners/axe", "hash_genesis_block": "00000c33631ca6f2f61368991ce2dc03306b5bb50bf7cede5cfbba6db38e52e6", "key": "bitcoin:AXE", "maintainer": "Kirill Orlov ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 1000, "name": "Axe", "segwit": false, "shortcut": "AXE", "signed_message_header": "DarkCoin Signed Message:\n", "slip44": 4242, "support": {"connect": true, "trezor1": "1.7.3", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "axe", "website": "https://axerunners.com", "xprv_magic": 50221816, "xpub_magic": 50221772, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 25, "address_type_p2sh": 85, "bech32_prefix": "bm", "bip115": false, "bitcore": [], "blockbook": ["https://bellcoin-blockbook.ilmango.work", "https://bell.blockbook.ovh"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Bellcoin", "coin_name": "Bellcoin", "coin_shortcut": "BELL", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 20}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/bellcoin-org/bellcoin", "hash_genesis_block": "000008f3b6bd10c2d03b06674a006b8d9731f6cb58179ef1eee008cee2209603", "key": "bitcoin:BELL", "maintainer": "ilmango-doge ", "max_address_length": 34, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Bellcoin", "segwit": true, "shortcut": "BELL", "signed_message_header": "Bellcoin Signed Message:\n", "slip44": 25252, "support": {"connect": false, "trezor1": "1.8.2", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "bellcoin", "website": "https://bellcoin.web4u.jp", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 230, "address_type_p2sh": 235, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "BitCash", "coin_name": "BitCash", "coin_shortcut": "BITC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/WillyTheCat/BitCash", "hash_genesis_block": "7d57d87ff3c15a521530af60edee1887fba9c193eb518face926785c4cd8f4f1", "key": "bitcoin:BITC", "maintainer": "Christian Kassler ", "max_address_length": 53, "maxfee_kb": 30000000, "min_address_length": 52, "minfee_kb": 1000, "name": "BitCash", "segwit": false, "shortcut": "BITC", "signed_message_header": "Bitcash Signed Message:\n", "slip44": 230, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.10", "webwallet": false}, "uri_prefix": "bitcash", "website": "https://www.choosebitcash.com", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 81, "address_type_p2sh": 5, "bech32_prefix": "bz", "bip115": false, "bitcore": ["https://insight.bitzeny.jp", "https://zeny.insight.monaco-ex.org"], "blockbook": ["https://zny.blockbook.ovh"], "blocktime_seconds": 90, "cashaddr_prefix": null, "coin_label": "BitZeny", "coin_name": "BitZeny", "coin_shortcut": "ZNY", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 20}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/BitzenyCoreDevelopers/bitzeny", "hash_genesis_block": "000009f7e55e9e3b4781e22bd87a7cfa4acada9e4340d43ca738bf4e9fb8f5ce", "key": "bitcoin:ZNY", "maintainer": "y-chan ", "max_address_length": 34, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 1000, "name": "BitZeny", "segwit": true, "shortcut": "ZNY", "signed_message_header": "BitZeny Signed Message:\n", "slip44": 123, "support": {"connect": false, "trezor1": "1.8.2", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "bitzeny", "website": "https://bitzeny.tech", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 25, "address_type_p2sh": 5, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 300, "cashaddr_prefix": null, "coin_label": "Bitcloud", "coin_name": "Bitcloud", "coin_shortcut": "BTDX", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/LIMXTEC/Bitcloud", "hash_genesis_block": "000002d56463941c20eae5cb474cc805b646515d18bc7dc222a0885b206eadb0", "key": "bitcoin:BTDX", "maintainer": "limxdev ", "max_address_length": 34, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 10000, "name": "Bitcloud", "segwit": false, "shortcut": "BTDX", "signed_message_header": "Diamond Signed Message:\n", "slip44": 218, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.10", "webwallet": false}, "uri_prefix": "bitcloud", "website": "https://bit-cloud.info", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 0, "address_type_p2sh": 5, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://bch1.trezor.io", "https://bch2.trezor.io", "https://bch3.trezor.io", "https://bch4.trezor.io", "https://bch5.trezor.io"], "blocktime_seconds": 600, "cashaddr_prefix": "bitcoincash", "coin_label": "Bitcoin Cash", "coin_name": "Bcash", "coin_shortcut": "BCH", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 546, "force_bip143": true, "fork_id": 0, "github": "https://github.com/Bitcoin-ABC/bitcoin-abc", "hash_genesis_block": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", "key": "bitcoin:BCH", "maintainer": "Jochen Hoenicke ", "max_address_length": 34, "maxfee_kb": 500000, "min_address_length": 27, "minfee_kb": 1000, "name": "Bitcoin Cash", "segwit": false, "shortcut": "BCH", "signed_message_header": "Bitcoin Signed Message:\n", "slip44": 145, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": true}, "uri_prefix": "bitcoincash", "website": "https://www.bitcoincash.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 111, "address_type_p2sh": 196, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 600, "cashaddr_prefix": "bchtest", "coin_label": "Bitcoin Cash Testnet", "coin_name": "Bcash Testnet", "coin_shortcut": "TBCH", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": true, "fork_id": 0, "github": "https://github.com/Bitcoin-ABC/bitcoin-abc", "hash_genesis_block": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943", "key": "bitcoin:TBCH", "maintainer": "Jochen Hoenicke ", "max_address_length": 34, "maxfee_kb": 10000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Bitcoin Cash Testnet", "segwit": false, "shortcut": "TBCH", "signed_message_header": "Bitcoin Signed Message:\n", "slip44": 1, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": false}, "uri_prefix": "bitcoincash", "website": "https://www.bitcoincash.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 38, "address_type_p2sh": 23, "bech32_prefix": "btg", "bip115": false, "bitcore": [], "blockbook": ["https://btg1.trezor.io", "https://btg2.trezor.io", "https://btg3.trezor.io", "https://btg4.trezor.io", "https://btg5.trezor.io"], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Bitcoin Gold", "coin_name": "Bgold", "coin_shortcut": "BTG", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 546, "force_bip143": true, "fork_id": 79, "github": "https://github.com/BTCGPU/BTCGPU", "hash_genesis_block": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", "key": "bitcoin:BTG", "maintainer": "Saleem Rashid ", "max_address_length": 34, "maxfee_kb": 500000, "min_address_length": 27, "minfee_kb": 1000, "name": "Bitcoin Gold", "segwit": true, "shortcut": "BTG", "signed_message_header": "Bitcoin Gold Signed Message:\n", "slip44": 156, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": true}, "uri_prefix": "bitcoingold", "website": "https://bitcoingold.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 111, "address_type_p2sh": 196, "bech32_prefix": "tbtg", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Bitcoin Gold Testnet", "coin_name": "Bgold Testnet", "coin_shortcut": "TBTG", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 546, "force_bip143": true, "fork_id": 79, "github": "https://github.com/BTCGPU/BTCGPU", "hash_genesis_block": "00000000e0781ebe24b91eedc293adfea2f557b53ec379e78959de3853e6f9f6", "key": "bitcoin:TBTG", "maintainer": "The Bitcoin Gold Developers ", "max_address_length": 34, "maxfee_kb": 500000, "min_address_length": 27, "minfee_kb": 1000, "name": "Bitcoin Gold Testnet", "segwit": true, "shortcut": "TBTG", "signed_message_header": "Bitcoin Gold Signed Message:\n", "slip44": 156, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "bitcoingold", "website": "https://bitcoingold.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_p2sh": 71979618}, {"address_type": 4901, "address_type_p2sh": 5039, "bech32_prefix": null, "bip115": false, "bitcore": ["https://explorer.btcprivate.org"], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Bitcoin Private", "coin_name": "Bprivate", "coin_shortcut": "BTCP", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": 42, "github": "https://github.com/BTCPrivate/BitcoinPrivate", "hash_genesis_block": "0007104ccda289427919efc39dc9e4d499804b7bebc22df55f8b834301260602", "key": "bitcoin:BTCP", "maintainer": "Chris Sulmone ", "max_address_length": 95, "maxfee_kb": 1000000, "min_address_length": 35, "minfee_kb": 1000, "name": "Bitcoin Private", "segwit": false, "shortcut": "BTCP", "signed_message_header": "BitcoinPrivate Signed Message:\n", "slip44": 183, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": true}, "uri_prefix": "bitcoinprivate", "website": "https://btcprivate.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 61, "address_type_p2sh": 123, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://blockbook1.bitcoinrh.org", "https://blockbook2.bitcoinrh.org"], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Bitcoin Rhodium", "coin_name": "Brhodium", "coin_shortcut": "XRC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://gitlab.com/bitcoinrh/BRhodiumNode", "hash_genesis_block": "baff5bfd9dc43fb672d003ec20fd21428f9282ca46bfa1730d73e1f2c75f5fdd", "key": "bitcoin:XRC", "maintainer": "baff5b ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Bitcoin Rhodium", "segwit": false, "shortcut": "XRC", "signed_message_header": "BitCoin Rhodium Signed Message:\n", "slip44": 10291, "support": {"connect": false, "trezor1": "soon", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "bitcoin-rhodium", "website": "https://www.bitcoinrh.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 3, "address_type_p2sh": 125, "bech32_prefix": "btx", "bip115": false, "bitcore": ["https://insight.bitcore.cc"], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Bitcore", "coin_name": "Bitcore", "coin_shortcut": "BTX", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Low": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/LIMXTEC/BitCore", "hash_genesis_block": "604148281e5c4b7f2487e5d03cd60d8e6f69411d613f6448034508cea52e9574", "key": "bitcoin:BTX", "maintainer": "limxdev ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Bitcore", "segwit": true, "shortcut": "BTX", "signed_message_header": "BitCore Signed Message:\n", "slip44": 160, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "bitcore", "website": "https://bitcore.cc", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 102, "address_type_p2sh": 5, "bech32_prefix": "bsd", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 200, "cashaddr_prefix": null, "coin_label": "Bitsend", "coin_name": "Bitsend", "coin_shortcut": "BSD", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/LIMXTEC/BitSend", "hash_genesis_block": "0000012e1b8843ac9ce8c18603658eaf8895f99d3f5e7e1b7b1686f35e3c087a", "key": "bitcoin:BSD", "maintainer": "limxdev ", "max_address_length": 34, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 10000, "name": "Bitsend", "segwit": true, "shortcut": "BSD", "signed_message_header": "Bitsend Signed Message:\n", "slip44": 91, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.10", "webwallet": false}, "uri_prefix": "bitsend", "website": "https://bitsend.info", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 26, "address_type_p2sh": 5, "bech32_prefix": "bst", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "BlockStamp", "coin_name": "BlockStamp", "coin_shortcut": "BST", "consensus_branch_id": null, "cooldown": 1000, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "duplicate": true, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/BlockStamp/bst", "hash_genesis_block": "8000000049a2e26b0185be50b4b8ed58b707c8893762959f0b1673641cae1828", "key": "bitcoin:BST", "maintainer": "Krzysztof Kuchta ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "BlockStamp", "segwit": true, "shortcut": "BST", "signed_message_header": "BST Signed Message:\n", "slip44": 254, "support": {"connect": false, "trezor1": "soon", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "blockstamp", "website": "https://blockstamp.info", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 28, "address_type_p2sh": 35, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://blockbook.capricoin.org", "https://blockbook2.capricoin.org", "https://blockbook3.capricoin.org", "https://blockbook4.capricoin.org"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Capricoin", "coin_name": "Capricoin", "coin_shortcut": "CPC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 7, "High": 20, "Low": 1, "Normal": 14}, "duplicate": true, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/Capricoinofficial/Capricoin", "hash_genesis_block": "00000d23fa0fc52c90893adb1181c9ddffb6c797a3e41864b9a23aa2f2981fe3", "key": "bitcoin:CPC", "maintainer": "Tibor Arpas ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Capricoin", "segwit": false, "shortcut": "CPC", "signed_message_header": "Capricoin Signed Message:\n", "slip44": 289, "support": {"connect": true, "trezor1": false, "trezor2": "2.0.10", "webwallet": false}, "uri_prefix": "capricoin", "website": "https://capricoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 0, "address_type_p2sh": 28, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Crown", "coin_name": "Crown", "coin_shortcut": "CRW", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/Crowndev/crowncoin", "hash_genesis_block": "0000000085370d5e122f64f4ab19c68614ff3df78c8d13cb814fd7e69a1dc6da", "key": "bitcoin:CRW", "maintainer": "hypermist ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Crown", "segwit": false, "shortcut": "CRW", "signed_message_header": "Crown Signed Message:\n", "slip44": 72, "support": {"connect": false, "trezor1": false, "trezor2": false, "webwallet": false}, "uri_prefix": "crown", "website": "https://crown.tech", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 76, "address_type_p2sh": 16, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://dash1.trezor.io", "https://dash2.trezor.io", "https://dash3.trezor.io", "https://dash4.trezor.io", "https://dash5.trezor.io"], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Dash", "coin_name": "Dash", "coin_shortcut": "DASH", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/dashpay/dash", "hash_genesis_block": "00000ffd590b1485b3caadc19b22e6379c733355108f107a430458cdf3407ab6", "key": "bitcoin:DASH", "maintainer": "Karel Bilek ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 1000, "name": "Dash", "segwit": false, "shortcut": "DASH", "signed_message_header": "DarkCoin Signed Message:\n", "slip44": 5, "support": {"connect": true, "trezor1": "1.5.2", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "dash", "website": "https://www.dash.org", "xprv_magic": 50221816, "xpub_magic": 50221772, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 140, "address_type_p2sh": 19, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Dash Testnet", "coin_name": "Dash Testnet", "coin_shortcut": "tDASH", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/dashpay/dash", "hash_genesis_block": "00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c", "key": "bitcoin:tDASH", "maintainer": "Karel Bilek ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 10000, "name": "Dash Testnet", "segwit": false, "shortcut": "tDASH", "signed_message_header": "DarkCoin Signed Message:\n", "slip44": 1, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "dash", "website": "https://www.dash.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 1855, "address_type_p2sh": 1818, "bech32_prefix": null, "bip115": false, "bitcore": ["https://mainnet.decred.org"], "blockbook": [], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Decred", "coin_name": "Decred", "coin_shortcut": "DCR", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1_decred", "decred": true, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/decred/dcrd", "hash_genesis_block": "298e5cc3d985bfe7f81dc135f360abe089edd4396b86d2de66b0cef42b21d980", "key": "bitcoin:DCR", "maintainer": "Alex Yocom-Piatt ", "max_address_length": 35, "maxfee_kb": 1000000, "min_address_length": 35, "minfee_kb": 10000, "name": "Decred", "segwit": false, "shortcut": "DCR", "signed_message_header": "Decred Signed Message:\n", "slip44": 42, "support": {"connect": false, "trezor1": "1.6.2", "trezor2": "2.0.8", "webwallet": true}, "uri_prefix": "bitcoin", "website": "https://www.decred.org", "xprv_magic": 50177256, "xpub_magic": 50178342, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 3873, "address_type_p2sh": 3836, "bech32_prefix": null, "bip115": false, "bitcore": ["https://testnet.decred.org"], "blockbook": [], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Decred Testnet", "coin_name": "Decred Testnet", "coin_shortcut": "TDCR", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1_decred", "decred": true, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/decred/dcrd", "hash_genesis_block": "a649dce53918caf422e9c711c858837e08d626ecfcd198969b24f7b634a49bac", "key": "bitcoin:TDCR", "maintainer": "Saleem Rashid ", "max_address_length": 35, "maxfee_kb": 10000000, "min_address_length": 35, "minfee_kb": 1000, "name": "Decred Testnet", "segwit": false, "shortcut": "TDCR", "signed_message_header": "Decred Signed Message:\n", "slip44": 1, "support": {"connect": false, "trezor1": "1.6.2", "trezor2": "2.0.8", "webwallet": true}, "uri_prefix": "bitcoin", "website": "https://www.decred.org", "xprv_magic": 70615959, "xpub_magic": 70617041, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 30, "address_type_p2sh": 90, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 30, "cashaddr_prefix": null, "coin_label": "Denarius", "coin_name": "Denarius", "coin_shortcut": "DNR", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/carsenk/denarius", "hash_genesis_block": "00000d5dbbda01621cfc16bbc1f9bf3264d641a5dbf0de89fd0182c2c4828fcd", "key": "bitcoin:DNR", "maintainer": "carsenk ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 10000, "name": "Denarius", "segwit": false, "shortcut": "DNR", "signed_message_header": "Denarius Signed Message:\n", "slip44": 116, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "denarius", "website": "https://denarius.io", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 30, "address_type_p2sh": 63, "bech32_prefix": "dgb", "bip115": false, "bitcore": [], "blockbook": ["https://dgb1.trezor.io", "https://dgb2.trezor.io"], "blocktime_seconds": 15, "cashaddr_prefix": null, "coin_label": "DigiByte", "coin_name": "DigiByte", "coin_shortcut": "DGB", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/digibyte/digibyte", "hash_genesis_block": "7497ea1b465eb39f1c8f507bc877078fe016d6fcb6dfad3a64c98dcc6e1e8496", "key": "bitcoin:DGB", "maintainer": "DigiByte ", "max_address_length": 34, "maxfee_kb": 500000, "min_address_length": 27, "minfee_kb": 1000, "name": "DigiByte", "segwit": true, "shortcut": "DGB", "signed_message_header": "DigiByte Signed Message:\n", "slip44": 20, "support": {"connect": true, "trezor1": "1.6.3", "trezor2": "2.0.7", "webwallet": true}, "uri_prefix": "digibyte", "website": "https://digibyte.io", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 30, "address_type_p2sh": 22, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://doge1.trezor.io", "https://doge2.trezor.io", "https://doge3.trezor.io", "https://doge4.trezor.io", "https://doge5.trezor.io"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Dogecoin", "coin_name": "Dogecoin", "coin_shortcut": "DOGE", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 100000}, "dust_limit": 10000000, "force_bip143": false, "fork_id": null, "github": "https://github.com/dogecoin/dogecoin", "hash_genesis_block": "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691", "key": "bitcoin:DOGE", "maintainer": "Karel Bilek ", "max_address_length": 34, "maxfee_kb": 1000000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Dogecoin", "segwit": false, "shortcut": "DOGE", "signed_message_header": "Dogecoin Signed Message:\n", "slip44": 3, "support": {"connect": true, "trezor1": "1.5.2", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "dogecoin", "website": "https://dogecoin.com", "xprv_magic": 49988504, "xpub_magic": 49990397, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 235, "address_type_p2sh": 75, "bech32_prefix": "ert", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Elements", "coin_name": "Elements", "coin_shortcut": "ELEMENTS", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/ElementsProject/elements", "hash_genesis_block": "209577bda6bf4b5804bd46f8621580dd6d4e8bfa2d190e1c50e932492baca07d", "key": "bitcoin:ELEMENTS", "maintainer": "Roman Zeyde ", "max_address_length": 34, "maxfee_kb": 10000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Elements", "segwit": true, "shortcut": "ELEMENTS", "signed_message_header": "Bitcoin Signed Message:\n", "slip44": 1, "support": {"connect": false, "trezor1": null, "trezor2": null, "webwallet": false}, "uri_prefix": "elements", "website": "https://elementsproject.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_p2sh": 71979618}, {"address_type": 95, "address_type_p2sh": 36, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 180, "cashaddr_prefix": null, "coin_label": "FairCoin", "coin_name": "FairCoin", "coin_shortcut": "FAIR", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 800000}, "dust_limit": 500000, "force_bip143": false, "fork_id": 0, "github": "https://github.com/faircoin/faircoin", "hash_genesis_block": "beed44fa5e96150d95d56ebd5d2625781825a9407a5215dd7eda723373a0a1d7", "key": "bitcoin:FAIR", "maintainer": "Santi Nore\u00f1a ", "max_address_length": 34, "maxfee_kb": 10000000, "min_address_length": 27, "minfee_kb": 1000, "name": "FairCoin", "segwit": false, "shortcut": "FAIR", "signed_message_header": "FairCoin Signed Message:\n", "slip44": 298, "support": {"connect": false, "trezor1": "soon", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "faircoin", "website": "https://www.faircoin.world", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 14, "address_type_p2sh": 5, "bech32_prefix": "fc", "bip115": false, "bitcore": ["https://bitcore.feathercoin.com"], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Feathercoin", "coin_name": "Feathercoin", "coin_shortcut": "FTC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 1000}, "duplicate": true, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/FeatherCoin/Feathercoin", "hash_genesis_block": "12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2", "key": "bitcoin:FTC", "maintainer": "Lucas Betschart ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Feathercoin", "segwit": true, "shortcut": "FTC", "signed_message_header": "Feathercoin Signed Message:\n", "slip44": 8, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "feathercoin", "website": "https://feathercoin.com", "xprv_magic": 76077806, "xpub_magic": 76069926, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 35, "address_type_p2sh": 94, "bech32_prefix": "flo", "bip115": false, "bitcore": ["https://livenet.flocha.in"], "blockbook": [], "blocktime_seconds": 40, "cashaddr_prefix": null, "coin_label": "Flo", "coin_name": "Florincoin", "coin_shortcut": "FLO", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 1000}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/floblockchain/flo", "hash_genesis_block": "09c7781c9df90708e278c35d38ea5c9041d7ecfcdd1c56ba67274b7cff3e1cea", "key": "bitcoin:FLO", "maintainer": "Robert English ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 100000, "name": "Flo", "segwit": true, "shortcut": "FLO", "signed_message_header": "Florincoin Signed Message:\n", "slip44": 216, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "florincoin", "website": "https://flo.cash", "xprv_magic": 15264107, "xpub_magic": 1526049, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 28471030}, {"address_type": 36, "address_type_p2sh": 16, "bech32_prefix": "fc", "bip115": false, "bitcore": [], "blockbook": ["https://explorer.fujicoin.org"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Fujicoin", "coin_name": "Fujicoin", "coin_shortcut": "FJC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 20000, "High": 100000, "Low": 10000, "Normal": 50000}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/fujicoin/fujicoin", "hash_genesis_block": "adb6d9cfd74075e7f91608add4bd2a2ea636f70856183086842667a1597714a0", "key": "bitcoin:FJC", "maintainer": "motty ", "max_address_length": 34, "maxfee_kb": 1000000000, "min_address_length": 27, "minfee_kb": 10000000, "name": "Fujicoin", "segwit": true, "shortcut": "FJC", "signed_message_header": "FujiCoin Signed Message:\n", "slip44": 75, "support": {"connect": true, "trezor1": "1.6.1", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "fujicoin", "website": "https://fujicoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 38, "address_type_p2sh": 10, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://blockbook.gincoin.io"], "blocktime_seconds": 120, "cashaddr_prefix": null, "coin_label": "GIN", "coin_name": "Gincoin", "coin_shortcut": "GIN", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/gincoin-dev/gincoin-core", "hash_genesis_block": "00000cd6bde619b2c3b23ad2e384328a450a37fa28731debf748c3b17f91f97d", "key": "bitcoin:GIN", "maintainer": "Dragos Badea ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 1000, "name": "GIN", "segwit": false, "shortcut": "GIN", "signed_message_header": "DarkCoin Signed Message:\n", "slip44": 2000, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "gincoin", "website": "https://gincoin.io", "xprv_magic": 50221816, "xpub_magic": 50221772, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 38, "address_type_p2sh": 62, "bech32_prefix": "game", "bip115": false, "bitcore": [], "blockbook": ["https://blockbook.gamecredits.network"], "blocktime_seconds": 90, "cashaddr_prefix": null, "coin_label": "GameCredits", "coin_name": "GameCredits", "coin_shortcut": "GAME", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 1000}, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/gamecredits-project/gamecredits", "hash_genesis_block": "91ec5f25ee9a0ffa1af7d4da4db9a552228dd2dc77cdb15b738be4e1f55f30ee", "key": "bitcoin:GAME", "maintainer": "Samad Sajanlal ", "max_address_length": 34, "maxfee_kb": 5000000, "min_address_length": 27, "minfee_kb": 100000, "name": "GameCredits", "segwit": true, "shortcut": "GAME", "signed_message_header": "GameCredits Signed Message:\n", "slip44": 101, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "gamecredits", "website": "https://gamecredits.org", "xprv_magic": 27108450, "xpub_magic": 27106558, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 28471030}, {"address_type": 36, "address_type_p2sh": 5, "bech32_prefix": "grs", "bip115": false, "bitcore": ["https://groestlsight.groestlcoin.org", "https://grsblocks.com"], "blockbook": ["https://blockbook.groestlcoin.org"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Groestlcoin", "coin_name": "Groestlcoin", "coin_shortcut": "GRS", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1_groestl", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/Groestlcoin/groestlcoin", "hash_genesis_block": "00000ac5927c594d49cc0bdb81759d0da8297eb614683d3acb62f0703b639023", "key": "bitcoin:GRS", "maintainer": "Yura Pakhuchiy ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 1000, "name": "Groestlcoin", "segwit": true, "shortcut": "GRS", "signed_message_header": "GroestlCoin Signed Message:\n", "slip44": 17, "support": {"connect": false, "trezor1": "1.6.2", "trezor2": "2.0.8", "webwallet": true}, "uri_prefix": "groestlcoin", "website": "https://www.groestlcoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 111, "address_type_p2sh": 196, "bech32_prefix": "tgrs", "bip115": false, "bitcore": ["https://groestlsight-test.groestlcoin.org"], "blockbook": ["https://blockbook-test.groestlcoin.org"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Groestlcoin Testnet", "coin_name": "Groestlcoin Testnet", "coin_shortcut": "tGRS", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1_groestl", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/Groestlcoin/groestlcoin", "hash_genesis_block": "000000ffbb50fc9898cdd36ec163e6ba23230164c0052a28876255b7dcf2cd36", "key": "bitcoin:tGRS", "maintainer": "Yura Pakhuchiy ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 1000, "name": "Groestlcoin Testnet", "segwit": true, "shortcut": "tGRS", "signed_message_header": "GroestlCoin Signed Message:\n", "slip44": 1, "support": {"connect": false, "trezor1": "1.6.2", "trezor2": "2.0.8", "webwallet": true}, "uri_prefix": "groestlcoin", "website": "https://www.groestlcoin.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_p2sh": 71979618}, {"address_type": 8329, "address_type_p2sh": 8342, "bech32_prefix": null, "bip115": true, "bitcore": ["https://explorer.horizen.global"], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Horizen", "coin_name": "Horizen", "coin_shortcut": "ZEN", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/ZencashOfficial/zen", "hash_genesis_block": "0007104ccda289427919efc39dc9e4d499804b7bebc22df55f8b834301260602", "key": "bitcoin:ZEN", "maintainer": "Power_VANO ", "max_address_length": 95, "maxfee_kb": 2000000, "min_address_length": 35, "minfee_kb": 1000, "name": "Horizen", "segwit": false, "shortcut": "ZEN", "signed_message_header": "Zcash Signed Message:\n", "slip44": 121, "support": {"connect": true, "trezor1": false, "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "horizen", "website": "https://www.horizen.global", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 60, "address_type_p2sh": 85, "bech32_prefix": null, "bip115": false, "bitcore": ["https://api.kmd.dev"], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Komodo", "coin_name": "Komodo", "coin_shortcut": "KMD", "consensus_branch_id": {"1": 0, "2": 0, "3": 1537743641, "4": 1991772603}, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/komodoplatform/komodo", "hash_genesis_block": "027e3758c3a65b12aa1046462b486d0a63bfa1beae327897f56c5cfb7daaae71", "key": "bitcoin:KMD", "maintainer": "Kadan Stadelmann ", "max_address_length": 34, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Komodo", "segwit": false, "shortcut": "KMD", "signed_message_header": "Komodo Signed Message:\n", "slip44": 141, "support": {"connect": true, "trezor1": "1.8.0", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "komodo", "website": "https://komodoplatform.com", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 6198, "address_type_p2sh": 6203, "bech32_prefix": null, "bip115": false, "bitcore": ["https://insight.kotocoin.info"], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Koto", "coin_name": "Koto", "coin_shortcut": "KOTO", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/KotoDevelopers/koto", "hash_genesis_block": "6d424c350729ae633275d51dc3496e16cd1b1d195c164da00f39c499a2e9959e", "key": "bitcoin:KOTO", "maintainer": "WO ", "max_address_length": 95, "maxfee_kb": 1000000, "min_address_length": 35, "minfee_kb": 1000, "name": "Koto", "segwit": false, "shortcut": "KOTO", "signed_message_header": "Koto Signed Message:\n", "slip44": 510, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": true}, "uri_prefix": "koto", "website": "https://ko-to.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 48, "address_type_p2sh": 50, "bech32_prefix": "ltc", "bip115": false, "bitcore": [], "blockbook": ["https://ltc1.trezor.io", "https://ltc2.trezor.io", "https://ltc3.trezor.io", "https://ltc4.trezor.io", "https://ltc5.trezor.io"], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Litecoin", "coin_name": "Litecoin", "coin_shortcut": "LTC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 1000}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/litecoin-project/litecoin", "hash_genesis_block": "12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2", "key": "bitcoin:LTC", "maintainer": "Pavol Rusnak ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 100000, "name": "Litecoin", "segwit": true, "shortcut": "LTC", "signed_message_header": "Litecoin Signed Message:\n", "slip44": 2, "support": {"connect": true, "trezor1": "1.5.2", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "litecoin", "website": "https://litecoin.org", "xprv_magic": 27106558, "xpub_magic": 27108450, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 28471030}, {"address_type": 111, "address_type_p2sh": 58, "bech32_prefix": "tltc", "bip115": false, "bitcore": ["https://testnet.litecore.io"], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Litecoin Testnet", "coin_name": "Litecoin Testnet", "coin_shortcut": "tLTC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/litecoin-project/litecoin", "hash_genesis_block": "4966625a4b2851d9fdee139e56211a0d88575f59ed816ff5e6a63deb4e3e29a0", "key": "bitcoin:tLTC", "maintainer": "Pavol Rusnak ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Litecoin Testnet", "segwit": true, "shortcut": "tLTC", "signed_message_header": "Litecoin Signed Message:\n", "slip44": 1, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": true}, "uri_prefix": "litecoin", "website": "https://litecoin.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_p2sh": 71979618}, {"address_type": 50, "address_type_p2sh": 5, "bech32_prefix": "mec", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Megacoin", "coin_name": "Megacoin", "coin_shortcut": "MEC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Low": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/LIMXTEC/Megacoin", "hash_genesis_block": "7520788e2d99eec7cf6cf7315577e1268e177fff94cb0a7caf6a458ceeea9ac2", "key": "bitcoin:MEC", "maintainer": "limxdev ", "max_address_length": 34, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Megacoin", "segwit": true, "shortcut": "MEC", "signed_message_header": "MegaCoin Signed Message:\n", "slip44": 217, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.10", "webwallet": false}, "uri_prefix": "megacoin", "website": "https://www.megacoin.eu", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 50, "address_type_p2sh": 55, "bech32_prefix": "mona", "bip115": false, "bitcore": ["https://mona.chainsight.info", "https://insight.electrum-mona.org"], "blockbook": ["https://blockbook.electrum-mona.org"], "blocktime_seconds": 90, "cashaddr_prefix": null, "coin_label": "Monacoin", "coin_name": "Monacoin", "coin_shortcut": "MONA", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 1000}, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/monacoinproject/monacoin", "hash_genesis_block": "ff9f1c0116d19de7c9963845e129f9ed1bfc0b376eb54fd7afa42e0d418c8bb6", "key": "bitcoin:MONA", "maintainer": "cryptcoin-junkey ", "max_address_length": 34, "maxfee_kb": 5000000, "min_address_length": 27, "minfee_kb": 100000, "name": "Monacoin", "segwit": true, "shortcut": "MONA", "signed_message_header": "Monacoin Signed Message:\n", "slip44": 22, "support": {"connect": true, "trezor1": "1.6.0", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "monacoin", "website": "https://monacoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 16, "address_type_p2sh": 76, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://blockbook.monetaryunit.org"], "blocktime_seconds": 40, "cashaddr_prefix": null, "coin_label": "MonetaryUnit", "coin_name": "MonetaryUnit", "coin_shortcut": "MUE", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/muecoin/MUE", "hash_genesis_block": "0b58ed450b3819ca54ab0054c4d220ca4f887d21c9e55d2a333173adf76d987f", "key": "bitcoin:MUE", "maintainer": "Sotiris Blad ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 1000, "name": "MonetaryUnit", "segwit": false, "shortcut": "MUE", "signed_message_header": "MonetaryUnit Signed Message:\n", "slip44": 31, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "monetaryunit", "website": "https://www.monetaryunit.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 50, "address_type_p2sh": 9, "bech32_prefix": "my", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Myriad", "coin_name": "Myriad", "coin_shortcut": "XMY", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/myriadteam/myriadcoin", "hash_genesis_block": "00000ffde4c020b5938441a0ea3d314bf619eff0b38f32f78f7583cffa1ea485", "key": "bitcoin:XMY", "maintainer": "Adam Hickerson ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Myriad", "segwit": true, "shortcut": "XMY", "signed_message_header": "Myriadcoin Signed Message:\n", "slip44": 90, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "myriadcoin", "website": "https://www.myriadcoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 38, "address_type_p2sh": 53, "bech32_prefix": "nix", "bip115": false, "bitcore": ["https://blockchain.nixplatform.io"], "blockbook": [], "blocktime_seconds": 120, "cashaddr_prefix": null, "coin_label": "NIX", "coin_name": "NIX", "coin_shortcut": "NIX", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 1000}, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/nixplatform/nixcore", "hash_genesis_block": "dd28ad86def767c3cfc34267a950d871fc7462bc57ea4a929fc3596d9b598e41", "key": "bitcoin:NIX", "maintainer": "mattt21 ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 0, "name": "NIX", "segwit": true, "shortcut": "NIX", "signed_message_header": "NIX Signed Message:\n", "slip44": 400, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "nix", "website": "https://nixplatform.io", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 52, "address_type_p2sh": 5, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://nmc1.trezor.io", "https://nmc2.trezor.io"], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Namecoin", "coin_name": "Namecoin", "coin_shortcut": "NMC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 2940, "force_bip143": false, "fork_id": null, "github": "https://github.com/namecoin/namecoin-core", "hash_genesis_block": "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770", "key": "bitcoin:NMC", "maintainer": "Pavol Rusnak ", "max_address_length": 34, "maxfee_kb": 10000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Namecoin", "segwit": false, "shortcut": "NMC", "signed_message_header": "Namecoin Signed Message:\n", "slip44": 7, "support": {"connect": true, "trezor1": "1.5.2", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "namecoin", "website": "https://namecoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 30, "address_type_p2sh": 13, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://blockbook.pivx.link"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "PIVX", "coin_name": "PIVX", "coin_shortcut": "PIVX", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/PIVX-Project/PIVX", "hash_genesis_block": "0000041e482b9b9691d98eefb48473405c0b8ec31b76df3797c74a78680ef818", "key": "bitcoin:PIVX", "maintainer": "Random Zebra ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 100, "name": "PIVX", "segwit": false, "shortcut": "PIVX", "signed_message_header": "DarkNet Signed Message:\n", "slip44": 119, "support": {"connect": true, "trezor1": "1.8.0", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "pivx", "website": "https://pivx.org", "xprv_magic": 35729707, "xpub_magic": 36513075, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 139, "address_type_p2sh": 19, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://blockbook-testnet.pivx.link"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "PIVX Testnet", "coin_name": "PIVX Testnet", "coin_shortcut": "tPIVX", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/PIVX-Project/PIVX", "hash_genesis_block": "0000041e482b9b9691d98eefb48473405c0b8ec31b76df3797c74a78680ef818", "key": "bitcoin:tPIVX", "maintainer": "Random Zebra ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 100, "name": "PIVX Testnet", "segwit": false, "shortcut": "tPIVX", "signed_message_header": "DarkNet Signed Message:\n", "slip44": 1, "support": {"connect": true, "trezor1": "1.8.0", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "pivx", "website": "https://pivx.org", "xprv_magic": 981489719, "xpub_magic": 981492128, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 47, "address_type_p2sh": 22, "bech32_prefix": null, "bip115": false, "bitcore": ["https://live.pesetacoin.info"], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Pesetacoin", "coin_name": "Pesetacoin", "coin_shortcut": "PTC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "duplicate": true, "dust_limit": 10000000, "force_bip143": false, "fork_id": null, "github": "https://github.com/FundacionPesetacoin/PesetacoinCore", "hash_genesis_block": "edfe5830b53251bfff733600b1cd5c192e761c011b055f07924634818c906438", "key": "bitcoin:PTC", "maintainer": "Rw ", "max_address_length": 34, "maxfee_kb": 1000000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Pesetacoin", "segwit": false, "shortcut": "PTC", "signed_message_header": "Pesetacoin Signed Message:\n", "slip44": 109, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "pesetacoin", "website": "https://pesetacoin.info", "xprv_magic": 76079604, "xpub_magic": 76071982, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 55, "address_type_p2sh": 56, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://blockbook.polispay.org"], "blocktime_seconds": 120, "cashaddr_prefix": null, "coin_label": "Polis", "coin_name": "Polis", "coin_shortcut": "POLIS", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/polispay/polis", "hash_genesis_block": "000009701eb781a8113b1af1d814e2f060f6408a2c990db291bc5108a1345c1e", "key": "bitcoin:POLIS", "maintainer": "Cronos ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 1000, "name": "Polis", "segwit": false, "shortcut": "POLIS", "signed_message_header": "Polis Signed Message:\n", "slip44": 1997, "support": {"connect": false, "trezor1": "soon", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "polis", "website": "https://www.polispay.org", "xprv_magic": 65165637, "xpub_magic": 65166718, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 23, "address_type_p2sh": 83, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Primecoin", "coin_name": "Primecoin", "coin_shortcut": "XPM", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/primecoin/primecoin", "hash_genesis_block": "963d17ba4dc753138078a2f56afb3af9674e2546822badff26837db9a0152106", "key": "bitcoin:XPM", "maintainer": "James Skrowvedeht ", "max_address_length": 35, "maxfee_kb": 1000000, "min_address_length": 26, "minfee_kb": 1000, "name": "Primecoin", "segwit": false, "shortcut": "XPM", "signed_message_header": "Primecoin Signed Message:\n", "slip44": 24, "support": {"connect": true, "trezor1": "1.8.0", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "primecoin", "website": "https://primecoin.io", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 58, "address_type_p2sh": 50, "bech32_prefix": "qc", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 128, "cashaddr_prefix": null, "coin_label": "Qtum", "coin_name": "Qtum", "coin_shortcut": "QTUM", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 500, "High": 2000, "Low": 410, "Normal": 600}, "duplicate": true, "dust_limit": 218400, "force_bip143": false, "fork_id": null, "github": "https://github.com/qtumproject/qtum", "hash_genesis_block": "000075aef83cf2853580f8ae8ce6f8c3096cfa21d98334d6e3f95e5582ed986c", "key": "bitcoin:QTUM", "maintainer": "CodeFace ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 400000, "name": "Qtum", "segwit": true, "shortcut": "QTUM", "signed_message_header": "Qtum Signed Message:\n", "slip44": 2301, "support": {"connect": false, "trezor1": "1.8.1", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "qtum", "website": "https://qtum.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 120, "address_type_p2sh": 110, "bech32_prefix": "tq", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 128, "cashaddr_prefix": null, "coin_label": "Qtum Testnet", "coin_name": "Qtum Testnet", "coin_shortcut": "tQTUM", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 500, "High": 2000, "Low": 410, "Normal": 600}, "dust_limit": 218400, "force_bip143": false, "fork_id": null, "github": "https://github.com/qtumproject/qtum", "hash_genesis_block": "0000e803ee215c0684ca0d2f9220594d3f828617972aad66feb2ba51f5e14222", "key": "bitcoin:tQTUM", "maintainer": "CodeFace ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 400000, "name": "Qtum Testnet", "segwit": true, "shortcut": "tQTUM", "signed_message_header": "Qtum Signed Message:\n", "slip44": 1, "support": {"connect": false, "trezor1": "1.8.1", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "qtum", "website": "https://qtum.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_p2sh": 71979618}, {"address_type": 60, "address_type_p2sh": 122, "bech32_prefix": null, "bip115": false, "bitcore": ["https://ravencoin.network"], "blockbook": ["https://blockbook.ravencoin.org"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Ravencoin", "coin_name": "Ravencoin", "coin_shortcut": "RVN", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Low": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/RavenProject/Ravencoin", "hash_genesis_block": "0000006b444bc2f2ffe627be9d9e7e7a0730000870ef6eb6da46c8eae389df90", "key": "bitcoin:RVN", "maintainer": "Scotty ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Ravencoin", "segwit": false, "shortcut": "RVN", "signed_message_header": "Raven Signed Message:\n", "slip44": 175, "support": {"connect": true, "trezor1": "1.7.2", "trezor2": "2.0.10", "webwallet": true}, "uri_prefix": "raven", "website": "https://ravencoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 111, "address_type_p2sh": 196, "bech32_prefix": "bcrt", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Regtest", "coin_name": "Regtest", "coin_shortcut": "REGTEST", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/bitcoin/bitcoin", "hash_genesis_block": "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206", "key": "bitcoin:REGTEST", "maintainer": "Thomas Kerin ", "max_address_length": 34, "maxfee_kb": 10000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Regtest", "segwit": true, "shortcut": "REGTEST", "signed_message_header": "Bitcoin Signed Message:\n", "slip44": 1, "support": {"connect": false, "trezor1": "1.8.2", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "bitcoin", "website": "https://bitcoin.org", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": 73342198, "xpub_magic_segwit_p2sh": 71979618}, {"address_type": 25, "address_type_p2sh": 105, "bech32_prefix": null, "bip115": false, "bitcore": ["https://insight.ritocoin.org"], "blockbook": ["https://blockbook.ritocoin.org"], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Ritocoin", "coin_name": "Ritocoin", "coin_shortcut": "RITO", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Low": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/RitoProject", "hash_genesis_block": "00000075e344bdf1c0e433f453764b1830a7aa19b2a5213e707502a22b779c1b", "key": "bitcoin:RITO", "maintainer": "Scotty ", "max_address_length": 34, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Ritocoin", "segwit": false, "shortcut": "RITO", "signed_message_header": "Rito Signed Message:\n", "slip44": 19169, "support": {"connect": false, "trezor1": "1.8.2", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "rito", "website": "https://ritocoin.org", "xprv_magic": 87326380, "xpub_magic": 87353290, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 63, "address_type_p2sh": 18, "bech32_prefix": null, "bip115": false, "bitcore": ["https://insight.smartcash.cc"], "blockbook": [], "blocktime_seconds": 55, "cashaddr_prefix": null, "coin_label": "SmartCash", "coin_name": "SmartCash", "coin_shortcut": "SMART", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1_smart", "decred": false, "default_fee_b": {"Economy": 10, "High": 200, "Low": 1, "Normal": 100}, "duplicate": true, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/SmartCash/Core-Smart", "hash_genesis_block": "000007acc6970b812948d14ea5a0a13db0fdd07d5047c7e69101fa8b361e05a4", "key": "bitcoin:SMART", "maintainer": "Leandro Reinaux ", "max_address_length": 34, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 0, "name": "SmartCash", "segwit": false, "shortcut": "SMART", "signed_message_header": "SmartCash Signed Message:\n", "slip44": 224, "support": {"connect": false, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "smart", "website": "https://smartcash.cc", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 65, "address_type_p2sh": 21, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 55, "cashaddr_prefix": null, "coin_label": "SmartCash Testnet", "coin_name": "SmartCash Testnet", "coin_shortcut": "tSMART", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1_smart", "decred": false, "default_fee_b": {"Economy": 10, "High": 200, "Low": 1, "Normal": 100}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/SmartCash/Core-Smart", "hash_genesis_block": "0000027235b5679bcd28c90d03d4bf1a9ba4c07c4efcc1c87d6c68cce25e6e5d", "key": "bitcoin:tSMART", "maintainer": "Leandro Reinaux ", "max_address_length": 35, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 0, "name": "SmartCash Testnet", "segwit": false, "shortcut": "tSMART", "signed_message_header": "SmartCash Signed Message:\n", "slip44": 224, "support": {"connect": false, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": false}, "uri_prefix": "testsmart", "website": "https://smartcash.cc", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 76, "address_type_p2sh": 16, "bech32_prefix": "xc", "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 60, "cashaddr_prefix": null, "coin_label": "Stakenet", "coin_name": "Stakenet", "coin_shortcut": "XSN", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 70, "High": 200, "Low": 10, "Normal": 140}, "dust_limit": 1000, "force_bip143": false, "fork_id": null, "github": "https://github.com/X9Developers/XSN", "hash_genesis_block": "00000c822abdbb23e28f79a49d29b41429737c6c7e15df40d1b1f1b35907ae34", "key": "bitcoin:XSN", "maintainer": "Alexis Hernandez ", "max_address_length": 47, "maxfee_kb": 2000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Stakenet", "segwit": true, "shortcut": "XSN", "signed_message_header": "DarkCoin Signed Message:\n", "slip44": 199, "support": {"connect": true, "trezor1": "1.8.0", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "stakenet", "website": "https://stakenet.io", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 0, "address_type_p2sh": 5, "bech32_prefix": null, "bip115": false, "bitcore": ["https://insight.terracoin.io"], "blockbook": [], "blocktime_seconds": 120, "cashaddr_prefix": null, "coin_label": "Terracoin", "coin_name": "Terracoin", "coin_shortcut": "TRC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "duplicate": true, "dust_limit": 5460, "force_bip143": false, "fork_id": null, "github": "https://github.com/terracoin/terracoin", "hash_genesis_block": "00000000804bbc6a621a9dbb564ce469f492e1ccf2d70f8a6b241e26a277afa2", "key": "bitcoin:TRC", "maintainer": "The Terracoin Foundation ", "max_address_length": 34, "maxfee_kb": 100000, "min_address_length": 27, "minfee_kb": 10000, "name": "Terracoin", "segwit": false, "shortcut": "TRC", "signed_message_header": "DarkCoin Signed Message:\n", "slip44": 83, "support": {"connect": false, "trezor1": false, "trezor2": false, "webwallet": false}, "uri_prefix": "terracoin", "website": "https://terracoin.io", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 70, "address_type_p2sh": 50, "bech32_prefix": "vips", "bip115": false, "bitcore": ["https://insight.vipstarco.in"], "blockbook": ["https://vips.blockbook.japanesecoin-pool.work"], "blocktime_seconds": 120, "cashaddr_prefix": null, "coin_label": "VIPSTARCOIN", "coin_name": "VIPSTARCOIN", "coin_shortcut": "VIPS", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 500, "High": 2000, "Low": 410, "Normal": 600}, "dust_limit": 218400, "force_bip143": false, "fork_id": null, "github": "https://github.com/VIPSTARCOIN/VIPSTARCOIN", "hash_genesis_block": "0000d068e1d30f79fb64446137106be9c6ee69a6a722295c131506b1ee09b77c", "key": "bitcoin:VIPS", "maintainer": "y-chan ", "max_address_length": 36, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 400000, "name": "VIPSTARCOIN", "segwit": true, "shortcut": "VIPS", "signed_message_header": "VIPSTARCOIN Signed Message:\n", "slip44": 1919, "support": {"connect": false, "trezor1": "1.8.2", "trezor2": "2.1.1", "webwallet": false}, "uri_prefix": "vipstarcoin", "website": "https://vipstarcoin.jp", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 71, "address_type_p2sh": 5, "bech32_prefix": "vtc", "bip115": false, "bitcore": [], "blockbook": ["https://vtc1.trezor.io", "https://vtc2.trezor.io", "https://vtc3.trezor.io", "https://vtc4.trezor.io", "https://vtc5.trezor.io"], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Vertcoin", "coin_name": "Vertcoin", "coin_shortcut": "VTC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 1000}, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/vertcoin-project/vertcoin-core", "hash_genesis_block": "4d96a915f49d40b1e5c2844d1ee2dccb90013a990ccea12c492d22110489f0c4", "key": "bitcoin:VTC", "maintainer": "Jochen Hoenicke ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 100000, "name": "Vertcoin", "segwit": true, "shortcut": "VTC", "signed_message_header": "Vertcoin Signed Message:\n", "slip44": 28, "support": {"connect": true, "trezor1": "1.6.1", "trezor2": "2.0.5", "webwallet": true}, "uri_prefix": "vertcoin", "website": "https://vertcoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 71, "address_type_p2sh": 33, "bech32_prefix": "via", "bip115": false, "bitcore": ["https://explorer.viacoin.org"], "blockbook": [], "blocktime_seconds": 24, "cashaddr_prefix": null, "coin_label": "Viacoin", "coin_name": "Viacoin", "coin_shortcut": "VIA", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 7000, "High": 20000, "Low": 1000, "Normal": 14000}, "dust_limit": 54600, "force_bip143": false, "fork_id": null, "github": "https://github.com/viacoin", "hash_genesis_block": "4e9b54001f9976049830128ec0331515eaabe35a70970d79971da1539a400ba1", "key": "bitcoin:VIA", "maintainer": "romanornr ", "max_address_length": 34, "maxfee_kb": 40000000, "min_address_length": 27, "minfee_kb": 1000, "name": "Viacoin", "segwit": true, "shortcut": "VIA", "signed_message_header": "Viacoin Signed Message:\n", "slip44": 14, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": true}, "uri_prefix": "viacoin", "website": "https://viacoin.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": 78792518, "xpub_magic_segwit_p2sh": 77429938}, {"address_type": 7352, "address_type_p2sh": 7357, "bech32_prefix": null, "bip115": false, "bitcore": ["https://explorer.zcl.zeltrez.io"], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "ZClassic", "coin_name": "ZClassic", "coin_shortcut": "ZCL", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/ZclassicCommunity", "hash_genesis_block": "0007104ccda289427919efc39dc9e4d499804b7bebc22df55f8b834301260602", "key": "bitcoin:ZCL", "maintainer": "James Skrowvedeht ", "max_address_length": 95, "maxfee_kb": 1000000, "min_address_length": 35, "minfee_kb": 1000, "name": "ZClassic", "segwit": false, "shortcut": "ZCL", "signed_message_header": "Zcash Signed Message:\n", "slip44": 147, "support": {"connect": true, "trezor1": "1.8.0", "trezor2": "2.0.11", "webwallet": false}, "uri_prefix": "zclassic", "website": "https://zclassic.org", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 7352, "address_type_p2sh": 7357, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": ["https://zec1.trezor.io", "https://zec2.trezor.io", "https://zec3.trezor.io", "https://zec4.trezor.io", "https://zec5.trezor.io"], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Zcash", "coin_name": "Zcash", "coin_shortcut": "ZEC", "consensus_branch_id": {"1": 0, "2": 0, "3": 1537743641, "4": 1991772603}, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/zcash/zcash", "hash_genesis_block": "00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08", "key": "bitcoin:ZEC", "maintainer": "Pavol Rusnak ", "max_address_length": 95, "maxfee_kb": 1000000, "min_address_length": 35, "minfee_kb": 1000, "name": "Zcash", "segwit": false, "shortcut": "ZEC", "signed_message_header": "Zcash Signed Message:\n", "slip44": 133, "support": {"connect": true, "trezor1": "1.7.1", "trezor2": "2.0.8", "webwallet": true}, "uri_prefix": "zcash", "website": "https://z.cash", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 7461, "address_type_p2sh": 7354, "bech32_prefix": null, "bip115": false, "bitcore": ["https://explorer.testnet.z.cash"], "blockbook": [], "blocktime_seconds": 150, "cashaddr_prefix": null, "coin_label": "Zcash Testnet", "coin_name": "Zcash Testnet", "coin_shortcut": "TAZ", "consensus_branch_id": {"1": 0, "2": 0, "3": 1537743641, "4": 1991772603}, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Normal": 10}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/zcash/zcash", "hash_genesis_block": "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38", "key": "bitcoin:TAZ", "maintainer": "Pavol Rusnak ", "max_address_length": 95, "maxfee_kb": 10000000, "min_address_length": 35, "minfee_kb": 1000, "name": "Zcash Testnet", "segwit": false, "shortcut": "TAZ", "signed_message_header": "Zcash Signed Message:\n", "slip44": 1, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": true}, "uri_prefix": "zcash", "website": "https://z.cash", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 82, "address_type_p2sh": 7, "bech32_prefix": null, "bip115": false, "bitcore": ["https://insight.zcoin.io"], "blockbook": [], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Zcoin", "coin_name": "Zcoin", "coin_shortcut": "XZC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 10, "High": 200, "Low": 1, "Normal": 100}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/zcoinofficial/zcoin", "hash_genesis_block": "4381deb85b1b2c9843c222944b616d997516dcbd6a964e1eaf0def0830695233", "key": "bitcoin:XZC", "maintainer": "Yura Pakhuchiy ", "max_address_length": 34, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 0, "name": "Zcoin", "segwit": false, "shortcut": "XZC", "signed_message_header": "Zcoin Signed Message:\n", "slip44": 136, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": true}, "uri_prefix": "zcoin", "website": "https://zcoin.io", "xprv_magic": 76066276, "xpub_magic": 76067358, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}, {"address_type": 65, "address_type_p2sh": 178, "bech32_prefix": null, "bip115": false, "bitcore": [], "blockbook": [], "blocktime_seconds": 600, "cashaddr_prefix": null, "coin_label": "Zcoin Testnet", "coin_name": "Zcoin Testnet", "coin_shortcut": "tXZC", "consensus_branch_id": null, "cooldown": 100, "curve_name": "secp256k1", "decred": false, "default_fee_b": {"Economy": 10, "High": 200, "Low": 1, "Normal": 100}, "dust_limit": 546, "force_bip143": false, "fork_id": null, "github": "https://github.com/zcoinofficial/zcoin", "hash_genesis_block": "7ac038c193c2158c428c59f9ae0c02a07115141c6e9dc244ae96132e99b4e642", "key": "bitcoin:tXZC", "maintainer": "Yura Pakhuchiy ", "max_address_length": 35, "maxfee_kb": 1000000, "min_address_length": 27, "minfee_kb": 0, "name": "Zcoin Testnet", "segwit": false, "shortcut": "tXZC", "signed_message_header": "Zcoin Signed Message:\n", "slip44": 1, "support": {"connect": true, "trezor1": "1.6.2", "trezor2": "2.0.7", "webwallet": false}, "uri_prefix": "testzcoin", "website": "https://zcoin.io", "xprv_magic": 70615956, "xpub_magic": 70617039, "xpub_magic_segwit_native": null, "xpub_magic_segwit_p2sh": null}] diff --git a/tests/device_tests/test_msg_ontology_getaddress.py b/tests/device_tests/test_msg_ontology_getaddress.py new file mode 100644 index 000000000..8fc42d7ea --- /dev/null +++ b/tests/device_tests/test_msg_ontology_getaddress.py @@ -0,0 +1,41 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import pytest + +from trezorlib import ontology +from trezorlib.tools import parse_path + +from ..common import MNEMONIC12 + + +@pytest.mark.altcoin +@pytest.mark.ontology +@pytest.mark.skip_t1 +class TestMsgOntologyGetaddress: + @pytest.mark.setup_client(mnemonic=MNEMONIC12) + def test_ontology_get_ont_address(self, client): + assert ( + ontology.get_address(client, parse_path("m/44'/1024'/0'/0/0")) + == "ANzeepWmi9hoLBA3UiwVhUm7Eku196VUHk" + ) + + @pytest.mark.setup_client(mnemonic=MNEMONIC12) + def test_ontology_get_neo_address(self, client): + assert ( + ontology.get_address(client, parse_path("m/44'/888'/0'/0/0")) + == "AZEMburLePcdfqBFnVfdbsXKiBSnmtgFZr" + ) diff --git a/tests/device_tests/test_msg_ontology_sign_ont_id_add_attributes.py b/tests/device_tests/test_msg_ontology_sign_ont_id_add_attributes.py new file mode 100644 index 000000000..0c30c4658 --- /dev/null +++ b/tests/device_tests/test_msg_ontology_sign_ont_id_add_attributes.py @@ -0,0 +1,77 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import time + +import pytest + +from trezorlib import messages, ontology +from trezorlib.tools import parse_path + +from ..common import MNEMONIC12 + + +@pytest.mark.altcoin +@pytest.mark.ontology +@pytest.mark.skip_t1 +@pytest.mark.setup_client(mnemonic=MNEMONIC12) +def test_ontology_sign_ont_id_add_attributes(client): + with client: + client.set_input_flow(input_flow(client, num_pages=2)) + + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7F7F1CEB, + type=0xD1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + ont_id_add_attributes = messages.OntologyOntIdAddAttributes( + ont_id="did:ont:AGVn4NZNEQ7RawHTDxjaTjZ3R8h8q1aq9h", + public_key=bytes.fromhex( + "03a8269b0dad311d98195e76729bc57003348a315fd17b6bf4f90ba8b86735fa33" + ), + ont_id_attributes=[ + messages.OntologyOntIdAttribute( + key="firstName", type="json", value="John Sheppard" + ) + ], + ) + + signature = ontology.sign_add_attr( + client, parse_path("m/44'/1024'/0'/0/0"), transaction, ont_id_add_attributes + ) + + assert ( + signature.payload.hex() + == "bd00c66b2a6469643a6f6e743a4147566e344e5a4e455137526177485444786a61546a5a33523868387131617139686a7cc8516a7cc80966697273744e616d656a7cc8046a736f6e6a7cc80d4a6f686e2053686570706172646a7cc82103a8269b0dad311d98195e76729bc57003348a315fd17b6bf4f90ba8b86735fa336a7cc86c0d616464417474726962757465731400000000000000000000000000000000000000030068164f6e746f6c6f67792e4e61746976652e496e766f6b65" + ) + assert ( + signature.signature.hex() + == "01c256dc16d88685fd6652d69b808059f7ed30edadb0ccfe51802702b94b65500922f9ea80e0fd7b77b5c51515e3bc43a495b3e98fb3adb82a0ab5dd47169fcf4e" + ) + + +def input_flow(client, num_pages): + yield + time.sleep(1) + for _ in range(num_pages - 1): + client.debug.swipe_down() + time.sleep(1) + client.debug.press_yes() diff --git a/tests/device_tests/test_msg_ontology_sign_ont_id_register.py b/tests/device_tests/test_msg_ontology_sign_ont_id_register.py new file mode 100644 index 000000000..75d990f3a --- /dev/null +++ b/tests/device_tests/test_msg_ontology_sign_ont_id_register.py @@ -0,0 +1,70 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import time + +import pytest + +from trezorlib import messages, ontology +from trezorlib.tools import parse_path + +from ..common import MNEMONIC12 + + +@pytest.mark.altcoin +@pytest.mark.ontology +@pytest.mark.skip_t1 +@pytest.mark.skip(reason="bad testing data") +@pytest.mark.setup_client(mnemonic=MNEMONIC12) +def test_ontology_sign_ont_id_register(client): + client.set_input_flow(input_flow(client, num_pages=2)) + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7F7F1CEB, + type=0xD1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + ont_id_register = messages.OntologyOntIdRegister( + ont_id="did:ont:AGVn4NZNEQ7RawHTDxjaTjZ3R8h8q1aq9h", + public_key=bytes.fromhex( + "03a8269b0dad311d98195e76729bc57003348a315fd17b6bf4f90ba8b86735fa33" + ), + ) + + signature = ontology.sign_add_attr( + client, parse_path("m/44'/1024'/0'/0/0"), transaction, ont_id_register + ) + assert ( + signature.payload.hex() + == "9800c66b2a6469643a6f6e743a4147566e344e5a4e455137526177485444786a61546a5a33523868387131617139686a7cc82103a8269b0dad311d98195e76729bc57003348a315fd17b6bf4f90ba8b86735fa336a7cc86c127265674944576974685075626c69634b65791400000000000000000000000000000000000000030068164f6e746f6c6f67792e4e61746976652e496e766f6b65" + ) + assert ( + signature.signature.hex() + == "015d6abe231352d1ab32f0b0de0222cfb9a7a13f467a2bf8a369b61aa1f933dc3a6a2ba7831c8a15984fe0958d24cbca05d8e0736510c1734d773145ce3eac9e9b" + ) + + +def input_flow(client, num_pages): + yield + time.sleep(1) + for _ in range(num_pages - 1): + client.debug.swipe_down() + time.sleep(1) + client.debug.press_yes() diff --git a/tests/device_tests/test_msg_ontology_sign_tx.py b/tests/device_tests/test_msg_ontology_sign_tx.py new file mode 100644 index 000000000..4c73b7c4e --- /dev/null +++ b/tests/device_tests/test_msg_ontology_sign_tx.py @@ -0,0 +1,92 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import pytest + +from trezorlib import messages, ontology +from trezorlib.tools import parse_path + +from ..common import MNEMONIC12 + +ONTOLOGY_TEST_MNEMONIC = "immune annual decorate major humble surprise dismiss trend edit suit alert uncover release transfer suit torch small timber lock mind tomorrow north lend diet" + + +@pytest.mark.altcoin +@pytest.mark.ontology +@pytest.mark.skip_t1 +@pytest.mark.setup_client(mnemonic=MNEMONIC12) +def test_ontology_sign_transfer_ont(client): + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7F7F1CEB, + type=0xD1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + transfer = messages.OntologyTransfer( + asset=1, + amount=100, + from_address="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + to_address="AcyLq3tokVpkMBMLALVMWRdVJ83TTgBUwU", + ) + + signature = ontology.sign_transfer( + client, parse_path("m/44'/1024'/0'/0/0"), transaction, transfer + ) + assert ( + signature.payload.hex() + == "7900c66b140b045b101bc9fabaf181e251a38e76b73962111b6a7cc814e885e849e7f545ea84e8c555b86c70e4f751c4ec6a7cc80864000000000000006a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b65" + ) + assert ( + signature.signature.hex() + == "0102f9b0c43b2ed35aa89b0927a60e692cb8a74280c2da819a909150c8b3fd2b0b401806c97797fcc4b93d34f210ad01740cfd13b720a389a80f384c1f94fb749e" + ) + + +# original test didn't work, had to replace with data from +# https://github.com/ontio/ontology-ts-sdk/blob/master/test/transfer.sign.test.ts +@pytest.mark.altcoin +@pytest.mark.ontology +@pytest.mark.skip_t1 +@pytest.mark.setup_client(mnemonic=ONTOLOGY_TEST_MNEMONIC) +def test_ontology_sign_transfer_ong(client): + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7F7F1CEB, + type=0xD1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + transfer = messages.OntologyTransfer( + asset=2, + amount=12000000, + from_address="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + to_address="AcyLq3tokVpkMBMLALVMWRdVJ83TTgBUwU", + ) + + signature = ontology.sign_transfer( + client, parse_path("m/44'/888'/0'/0/0"), transaction, transfer + ) + assert ( + signature.signature.hex() + == "017da1b8268e1272d7471eef58fa0884108073c09d5efdae0143da5d281019682e5a1562f1d76484eb0379e3febe7025a958bb14855107b9ad26daec2fee0119f4" + ) diff --git a/tests/device_tests/test_msg_ontology_sign_withdraw_ong.py b/tests/device_tests/test_msg_ontology_sign_withdraw_ong.py new file mode 100644 index 000000000..1d661b9e5 --- /dev/null +++ b/tests/device_tests/test_msg_ontology_sign_withdraw_ong.py @@ -0,0 +1,56 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2018 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import pytest + +from trezorlib import messages, ontology +from trezorlib.tools import parse_path + +from ..common import MNEMONIC12 + + +@pytest.mark.altcoin +@pytest.mark.ontology +@pytest.mark.skip_t1 +@pytest.mark.setup_client(mnemonic=MNEMONIC12) +def test_ontology_sign_withdraw_ong(client): + transaction = messages.OntologyTransaction( + version=0x00, + nonce=0x7F7F1CEB, + type=0xD1, + gas_price=500, + gas_limit=30000, + payer="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + tx_attributes=[], + ) + + withdraw_ong = messages.OntologyWithdrawOng( + amount=12000000, + from_address="AGn8JFPGM5S4jkWhTC89Xtz1Y76sPz29Rc", + to_address="AcyLq3tokVpkMBMLALVMWRdVJ83TTgBUwU", + ) + + signature = ontology.sign_withdrawal( + client, parse_path("m/44'/1024'/0'/0/0"), transaction, withdraw_ong + ) + assert ( + signature.payload.hex() + == "9300c66b140b045b101bc9fabaf181e251a38e76b73962111b6a7cc81400000000000000000000000000000000000000016a7cc814e885e849e7f545ea84e8c555b86c70e4f751c4ec6a7cc808001bb700000000006a7cc86c0c7472616e7366657246726f6d1400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b65" + ) + assert ( + signature.signature.hex() + == "01a44355ac4549a021ecc571eb85ffb6ae4ff50cffc416ec55df40cad538fa55c64386167df2fb6b3fa9e698ebe265088839667b88da7e599ce7df679b0d5dfe60" + ) diff --git a/tools/build_protobuf b/tools/build_protobuf index 04bca9e26..336929e95 100755 --- a/tools/build_protobuf +++ b/tools/build_protobuf @@ -17,6 +17,7 @@ CORE_PROTOBUF_SOURCES="\ $PROTOB/messages-management.proto \ $PROTOB/messages-monero.proto \ $PROTOB/messages-nem.proto \ + $PROTOB/messages-ontology.proto \ $PROTOB/messages-ripple.proto \ $PROTOB/messages-stellar.proto \ $PROTOB/messages-tezos.proto \ @@ -73,12 +74,13 @@ do_rebuild() { sed -i "3ifrom trezor import utils\n" "$DESTDIR"/Capability.py sed -i "3ifrom trezor import utils\n" "$DESTDIR"/MessageType.py sed -i "/^EthereumGetPublicKey/iif not utils.BITCOIN_ONLY:" "$DESTDIR"/MessageType.py - for altcoin in Ethereum NEM Lisk Tezos Stellar Cardano Ripple Monero DebugMonero Eos Binance WebAuthn; do + for altcoin in Ethereum NEM Lisk Tezos Stellar Cardano Ripple Monero DebugMonero Eos Binance WebAuthn Ontology; do sed -i "s:^$altcoin: $altcoin:" "$DESTDIR"/Capability.py sed -i "s:^$altcoin: $altcoin:" "$DESTDIR"/MessageType.py done sed -i "/^Bitcoin_like/iif not utils.BITCOIN_ONLY:" "$DESTDIR"/Capability.py sed -i "/^EOS/iif not utils.BITCOIN_ONLY:" "$DESTDIR"/Capability.py + sed -i "/Ontology/iif not utils.BITCOIN_ONLY:" "$DESTDIR"/Capability.py for feature in Bitcoin_like EOS U2F; do sed -i "s:^$feature: $feature:" "$DESTDIR"/Capability.py done