diff --git a/data/ProkeyCoinsInfo.json b/data/ProkeyCoinsInfo.json index 548f2cd..ce5d69a 100644 --- a/data/ProkeyCoinsInfo.json +++ b/data/ProkeyCoinsInfo.json @@ -35744,7 +35744,7 @@ "slip44": 144, "decimals": 6, "priority": 100, - "on_device": "Ripple Testnet", + "on_device": "Ripple", "test": true, "support": { "optimum": "1.10.1" @@ -35755,5 +35755,55 @@ ], "min_balance": 20000000 } + ], + "tron": [ + { + "name": "Tron", + "shortcut": "TRX", + "slip44": 195, + "decimals": 6, + "priority": 7, + "on_device": "Tron", + "test": false, + "support": { + "optimum": "1.10.0" + }, + "tx_url": "https://tronscan.org/#/transaction/{hash}", + "wallets": [ + "https://wallet.prokey.io" + ] + }, + { + "name": "Tron Shasta", + "shortcut": "Shasta", + "slip44": 195, + "decimals": 6, + "priority": 7, + "on_device": "Tron", + "test": true, + "support": { + "optimum": "1.10.0" + }, + "tx_url": "https://shasta.tronscan.org/#/transaction/{hash}", + "wallets": [ + "https://wallet.prokey.io" + ] + }, + { + "name": "Tron Nile", + "shortcut": "Nile", + "slip44": 195, + "decimals": 6, + "priority": 7, + "on_device": "Tron", + "test": true, + "support": { + "optimum": "1.10.0" + }, + "tx_url": "https://nile.tronscan.org/#/transaction/{hash}", + "wallets": [ + "https://wallet.prokey.io" + ] + } ] } \ No newline at end of file diff --git a/index.ts b/index.ts index 4590b17..845021d 100644 --- a/index.ts +++ b/index.ts @@ -3,6 +3,7 @@ * Copyright (C) Prokey.io * * Hadi Robati, hadi@prokey.io + * Ali Akbar Mohammadi * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +22,8 @@ export { Device } from './src/device/Device'; export { BitcoinCommands } from './src/device/BitcoinCommands'; export { EthereumCommands } from './src/device/EthereumCommands'; +export { TronCommands } from './src/device/TronCommands'; +export { RippleCommands } from './src/device/RippleCommands'; export { BitcoinWallet } from './src/wallet/BitcoinWallet'; export { EthereumWallet } from './src/wallet/EthereumWallet'; export { OmniWallet } from './src/wallet/OmniWallet'; diff --git a/protob/combined.proto.txt b/protob/combined.proto.txt index 6e1f0bb..b2911ed 100644 --- a/protob/combined.proto.txt +++ b/protob/combined.proto.txt @@ -1,6 +1,8 @@ syntax = "proto2"; import "google/protobuf/descriptor.proto.txt"; +// Sugar for easier handling in Java + /** * Request: Ask the device for a Binance address. * @start @@ -40,7 +42,7 @@ message BinancePublicKey { /** * Request: Starts the Binance transaction protocol flow. - * A transaction consists of these common fields and a series of BinanceMsg messages. + * A transaction consists of these common fields and a series of BinanceMsg messages. * These parts form a JSON structure (a string) in Trezor's memory, which is signed to produce a BinanceSignedTx. * @start * @next BinanceTxRequest @@ -67,6 +69,7 @@ message BinanceTxRequest { /** * Request: Ask the device to include a Binance transfer msg in the tx. + * @next BinanceTxRequest * @next BinanceSignedTx * @next Failure */ @@ -87,6 +90,7 @@ message BinanceTransferMsg { /** * Request: Ask the device to include a Binance order msg in the tx. + * @next BinanceTxRequest * @next BinanceSignedTx * @next Failure */ @@ -123,6 +127,7 @@ message BinanceOrderMsg { /** * Request: Ask the device to include a Binance cancel msg in the tx. + * @next BinanceTxRequest * @next BinanceSignedTx * @next Failure */ @@ -139,6 +144,7 @@ message BinanceCancelMsg { message BinanceSignedTx { optional bytes signature = 1; optional bytes public_key = 2; + optional string json = 3; } @@ -530,8 +536,6 @@ message CardanoSignedTx { optional bytes tx_body = 2; // serialised body of the signed transaction } -// Sugar for easier handling in Java - /** * Response: Success of the previous request * @end @@ -560,7 +564,6 @@ message Failure { Failure_NotEnoughFunds = 10; Failure_NotInitialized = 11; Failure_PinMismatch = 12; - Failure_WipeCodeMismatch = 13; Failure_FirmwareError = 99; } } @@ -572,6 +575,7 @@ message Failure { */ message ButtonRequest { optional ButtonRequestType code = 1; + optional string data = 2; /** * Type of button request */ @@ -591,9 +595,6 @@ message ButtonRequest { ButtonRequest_MnemonicInput = 13; ButtonRequest_PassphraseType = 14; ButtonRequest_UnknownDerivationPath = 15; - ButtonRequest_RecoveryHomepage = 16; - ButtonRequest_Success = 17; - ButtonRequest_Warning = 18; ButtonRequest_EnterPinOnDevice = 101; ButtonRequest_EnterNewPinOnDevice = 102; ButtonRequest_ReEnterNewPinOnDevice = 103; @@ -622,8 +623,6 @@ message PinMatrixRequest { PinMatrixRequestType_Current = 1; PinMatrixRequestType_NewFirst = 2; PinMatrixRequestType_NewSecond = 3; - PinMatrixRequestType_WipeCodeFirst = 4; - PinMatrixRequestType_WipeCodeSecond = 5; } } @@ -811,33 +810,12 @@ message CosiSignature { /** * Request: "Press" the button on the device * @start - * @next DebugLinkLayout + * @next Success */ message DebugLinkDecision { optional bool yes_no = 1; // true for "Confirm", false for "Cancel" - optional DebugSwipeDirection swipe = 2; // swipe direction + optional bool up_down = 2; // true for scroll up, false for scroll down optional string input = 3; // keyboard input - /** - * Structure representing swipe direction - */ - enum DebugSwipeDirection { - UP = 0; - DOWN = 1; - LEFT = 2; - RIGHT = 3; - } - - optional uint32 x = 4; // touch X coordinate - optional uint32 y = 5; // touch Y coordinate - optional bool wait = 6; // wait for layout change -} - -/** - * Response: Device text layout - * @end - */ -message DebugLinkLayout { - repeated string lines = 1; } /** @@ -846,9 +824,6 @@ message DebugLinkLayout { * @next DebugLinkState */ message DebugLinkGetState { - optional bool wait_word_list = 1; // Trezor T only - wait until mnemonic words are shown - optional bool wait_word_pos = 2; // Trezor T only - wait until reset word position is requested - optional bool wait_layout = 3; // wait until current layout changes } /** @@ -868,7 +843,6 @@ message DebugLinkState { optional uint32 recovery_word_pos = 10; // index of mnemonic word the device is expecting during RecoveryDevice workflow optional uint32 reset_word_pos = 11; // index of mnemonic word the device is expecting during ResetDevice workflow optional uint32 mnemonic_type = 12; // current mnemonic type (BIP-39/SLIP-39) - repeated string layout_lines = 13; // current layout text } /** @@ -1204,7 +1178,9 @@ message EosTxActionAck { * @end */ message EosSignedTx { - optional string signature = 1; // Computed signature + optional uint32 signature_v = 1; // Computed signature (recovery parameter, limited to 31 or 32) + optional bytes signature_r = 2; // Computed signature R component (256 bit) + optional bytes signature_s = 3; // Computed signature S component (256 bit) } // Sugar for easier handling in Java @@ -1475,14 +1451,6 @@ message LiskVerifyMessage { // Sugar for easier handling in Java -/** - * Type of the mnemonic backup given/received by the device during reset/recovery. - */ -enum BackupType { - Bip39 = 0; // also called "Single Backup", see BIP-0039 - Slip39_Basic = 1; // also called "Shamir Backup", see SLIP-0039 - Slip39_Advanced = 2; // also called "Super Shamir" or "Shamir with Groups", see SLIP-0039#two-level-scheme -} /** * Request: Reset device to default state and ask for device details @@ -1507,7 +1475,7 @@ message GetFeatures { * @end */ message Features { - optional string vendor = 1; // name of the manufacturer, e.g. "prokey.io" + optional string vendor = 1; // name of the manufacturer, e.g. "trezor.io" optional uint32 major_version = 2; // major version of the firmware/bootloader, e.g. 1 optional uint32 minor_version = 3; // minor version of the firmware/bootloader, e.g. 0 optional uint32 patch_version = 4; // patch version of the firmware/bootloader, e.g. 0 @@ -1534,34 +1502,10 @@ message Features { optional bytes fw_vendor_keys = 26; // reported firmware vendor keys (their hash) optional bool unfinished_backup = 27; // report unfinished backup (equals to Storage.unfinished_backup) optional bool no_backup = 28; // report no backup (equals to Storage.no_backup) - optional bool recovery_mode = 29; // is recovery mode in progress - repeated Capability capabilities = 30; // list of supported capabilities - enum Capability { - Capability_Bitcoin = 1; - Capability_Bitcoin_like = 2; // Altcoins based on the Bitcoin source code - Capability_Binance = 3; - Capability_Cardano = 4; - Capability_Crypto = 5; // generic crypto operations for GPG, SSH, etc. - Capability_EOS = 6; - Capability_Ethereum = 7; - Capability_Lisk = 8; - Capability_Monero = 9; - Capability_NEM = 10; - Capability_Ripple = 11; - Capability_Stellar = 12; - Capability_Tezos = 13; - Capability_U2F = 14; - Capability_Shamir = 15; - Capability_ShamirGroups = 16; - } - 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 - optional bool sd_protection = 33; // is SD Protect enabled - optional bool wipe_code_protection = 34; // is wipe code protection enabled - optional bool pin_on_device = 100; - optional uint32 view_major_version = 101; - optional uint32 view_minor_version = 102; - optional uint32 view_patch_version = 103; + optional bool pin_on_device = 100; // Support pin on device + optional uint32 view_major_version = 101; // major version of the firmware to show in UI + optional uint32 view_minor_version = 102; // minor version of the firmware to show in UI + optional uint32 view_patch_version = 103; // patch version of the firmware to show in UI } /** @@ -1616,34 +1560,6 @@ message ChangePin { optional bool remove = 1; // is PIN removal requested? } -/** - * Request: Starts workflow for setting/removing the wipe code - * @start - * @next Success - * @next Failure - */ -message ChangeWipeCode { - optional bool remove = 1; // is wipe code removal requested? -} - -/** - * Request: Starts workflow for enabling/regenerating/disabling SD card protection - * @start - * @next Success - * @next Failure - */ -message SdProtect { - optional SdProtectOperationType operation = 1; - /** - * Structure representing SD card protection operation - */ - enum SdProtectOperationType { - DISABLE = 0; - ENABLE = 1; - REFRESH = 2; - } -} - /** * Request: Test if the device is alive, device sends back the message in Success response * @start @@ -1698,15 +1614,14 @@ message WipeDevice { * @next Failure */ message LoadDevice { - repeated string mnemonics = 1; // seed encoded as mnemonic (12, 18 or 24 words for BIP39, 20 or 33 for SLIP39) + optional string mnemonic = 1; // seed encoded as BIP-39 mnemonic (12, 18 or 24 words) + optional HDNodeType node = 2; // BIP-32 node optional string pin = 3; // set PIN protection optional bool passphrase_protection = 4; // enable master node encryption using passphrase optional string language = 5 [default='english']; // device language optional string label = 6; // device label optional bool skip_checksum = 7; // do not test mnemonic for valid BIP-39 checksum optional uint32 u2f_counter = 8; // U2F counter - optional bool needs_backup = 9; // set "needs backup" flag - optional bool no_backup = 10; // indicate that no backup is going to be made } /** @@ -1716,16 +1631,15 @@ message LoadDevice { * @next Failure */ message ResetDevice { - optional bool display_random = 1; // display entropy generated by the device before asking for additional entropy - optional uint32 strength = 2 [default=256]; // strength of seed in bits - optional bool passphrase_protection = 3; // enable master node encryption using passphrase - optional bool pin_protection = 4; // enable PIN protection - optional string language = 5 [default='english']; // device language - optional string label = 6; // device label - optional uint32 u2f_counter = 7; // U2F counter - optional bool skip_backup = 8; // postpone seed backup to BackupDevice workflow - optional bool no_backup = 9; // indicate that no backup is going to be made - optional BackupType backup_type = 10 [default=Bip39]; // type of the mnemonic backup + optional bool display_random = 1; // display entropy generated by the device before asking for additional entropy + optional uint32 strength = 2 [default=256]; // strength of seed in bits + optional bool passphrase_protection = 3; // enable master node encryption using passphrase + optional bool pin_protection = 4; // enable PIN protection + optional string language = 5 [default='english']; // device language + optional string label = 6; // device label + optional uint32 u2f_counter = 7; // U2F counter + optional bool skip_backup = 8; // postpone seed backup to BackupDevice workflow + optional bool no_backup = 9; // indicate that no backup is going to be made } /** @@ -1816,23 +1730,7 @@ message WordAck { * @next Success */ message SetU2FCounter { - optional uint32 u2f_counter = 1; -} - -/** - * Request: Set U2F counter - * @start - * @next NextU2FCounter - */ -message GetNextU2FCounter { -} - -/** - * Request: Set U2F counter - * @end - */ -message NextU2FCounter { - optional uint32 u2f_counter = 1; + optional uint32 u2f_counter = 1; // counter } // Sugar for easier handling in Java @@ -1913,7 +1811,6 @@ message MoneroGetAddress { optional uint32 network_type = 3; // Main-net / testnet / stagenet optional uint32 account = 4; // Major subaddr index optional uint32 minor = 5; // Minor subaddr index - optional bytes payment_id = 6; // Payment ID for integrated address } /** @@ -2027,7 +1924,7 @@ message MoneroTransactionInputsPermutationAck { * @next MoneroTransactionInputViniAck */ message MoneroTransactionInputViniRequest { - optional MoneroTransactionSourceEntry src_entr = 1; + optional MoneroTransactionSourceEntry src_entr = 1; optional bytes vini = 2; // xmrtypes.TxinToKey optional bytes vini_hmac = 3; optional bytes pseudo_out = 4; @@ -2531,14 +2428,200 @@ message NEMDecryptedMessage { // Sugar for easier handling in Java +/** + * 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 transfer + * @start + * @next OntologySignedTransfer + */ +message OntologySignTransfer { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional OntologyTransaction transaction = 2; + optional OntologyTransfer transfer = 3; + /** + * Ontology Transfer + */ + 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; + } + } +} + +/** + * Response: Contains Ontology transfer signature + * @end + */ +message OntologySignedTransfer { + optional bytes signature = 1; + optional bytes payload = 2; +} + +/** + * Request: Ask device to sign Ontology ONG withdrawal + * @start + * @next OntologySignedWithdrawOng + */ +message OntologySignWithdrawOng { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional OntologyTransaction transaction = 2; + optional OntologyWithdrawOng withdraw_ong = 3; + /** + * Ontology ONG Withdrawal + */ + message OntologyWithdrawOng { + optional uint64 amount = 1; + optional string from_address = 2; + optional string to_address = 3; + } +} + +/** + * Response: Contains Ontology ONG withdrawal signature + * @end + */ +message OntologySignedWithdrawOng { + optional bytes signature = 1; + optional bytes payload = 2; +} + +/** + * Request: Ask device to sign Ontology ONT ID registration + * @start + * @next OntologySignedOntIdRegister + */ +message OntologySignOntIdRegister { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional OntologyTransaction transaction = 2; + optional OntologyOntIdRegister ont_id_register = 3; + /** + * Ontology ONT ID registration + */ + message OntologyOntIdRegister { + optional string ont_id = 1; + optional bytes public_key = 2; + } +} + +/** + * Response: Contains Ontology ONT ID registration signature + * @end + */ +message OntologySignedOntIdRegister { + optional bytes signature = 1; + optional bytes payload = 2; +} + +/** + * Request: Ask device to sign Ontology ONT ID attributes adding + * @start + * @next OntologySignedOntIdAddAttributes + */ +message OntologySignOntIdAddAttributes { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional OntologyTransaction transaction = 2; + optional OntologyOntIdAddAttributes ont_id_add_attributes = 3; + /** + * 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 ONT ID attributes adding signature + * @end + */ +message OntologySignedOntIdAddAttributes { + optional bytes signature = 1; + optional bytes payload = 2; +} + +// Sugar for easier handling in Java + /** * Request: Address at the specified index * @start * @next RippleAddress */ message RippleGetAddress { - repeated uint32 address_n = 1; // BIP-32 path. For compatibility with other wallets, must be m/44'/144'/index' - optional bool show_display = 2; // optionally show on display before sending the result + repeated uint32 address_n = 1; // BIP-32 path. For compatibility with other wallets, must be m/44'/144'/index' + optional bool show_display = 2; // optionally show on display before sending the result } /** @@ -2546,7 +2629,7 @@ message RippleGetAddress { * @end */ message RippleAddress { - optional string address = 1; // Address in Ripple format (base58 of a pubkey with checksum) + optional string address = 1; // Address in Ripple format (base58 of a pubkey with checksum) } /** @@ -2555,24 +2638,24 @@ message RippleAddress { * @next RippleSignedTx */ message RippleSignTx { - repeated uint32 address_n = 1; // BIP-32 path. For compatibility with other wallets, must be m/44'/144'/index' - optional uint64 fee = 2; // fee (in drops) for the transaction - optional uint32 flags = 3; // transaction flags - optional uint32 sequence = 4; // transaction sequence number - optional uint32 last_ledger_sequence = 5; // see https://developers.ripple.com/reliable-transaction-submission.html#lastledgersequence - optional RipplePayment payment = 6; // Payment transaction type - - /** - * Payment transaction type - * - simple A sends money to B - * - only a subset of fields is supported - * - see https://developers.ripple.com/payment.html - */ - message RipplePayment { - optional uint64 amount = 1; // only XRP is supported at the moment so this an integer - optional string destination = 2; // destination account address - optional uint32 destination_tag = 3; // destination tag to identify payments - } + repeated uint32 address_n = 1; // BIP-32 path. For compatibility with other wallets, must be m/44'/144'/index' + optional uint64 fee = 2; // fee (in drops) for the transaction + optional uint32 flags = 3; // transaction flags + optional uint32 sequence = 4; // transaction sequence number + optional uint32 last_ledger_sequence = 5; // see https://developers.ripple.com/reliable-transaction-submission.html#lastledgersequence + optional RipplePayment payment = 6; // Payment transaction type + + /** + * Payment transaction type + * - simple A sends money to B + * - only a subset of fields is supported + * - see https://developers.ripple.com/payment.html + */ + message RipplePayment { + optional uint64 amount = 1; // only XRP is supported at the moment so this an integer + optional string destination = 2; // destination account address + optional uint32 destination_tag = 3; // destination tag to identify payments + } } /** @@ -2580,8 +2663,8 @@ message RippleSignTx { * @end */ message RippleSignedTx { - optional bytes signature = 1; - optional bytes serialized_tx = 2; + optional bytes signature = 1; + optional bytes serialized_tx = 2; } // Sugar for easier handling in Java @@ -2874,7 +2957,7 @@ message TezosSignTx { * Structure representing information for reveal */ message TezosRevealOp { - optional bytes source = 7; + optional TezosContractID source = 1; optional uint64 fee = 2; optional uint64 counter = 3; optional uint64 gas_limit = 4; @@ -2885,7 +2968,7 @@ message TezosSignTx { * Structure representing information for transaction */ message TezosTransactionOp { - optional bytes source = 9; + optional TezosContractID source = 1; optional uint64 fee = 2; optional uint64 counter = 3; optional uint64 gas_limit = 4; @@ -2893,24 +2976,12 @@ message TezosSignTx { optional uint64 amount = 6; optional TezosContractID destination = 7; optional bytes parameters = 8; - optional TezosParametersManager parameters_manager = 10; - - message TezosParametersManager { - optional bytes set_delegate = 1; - optional bool cancel_delegate = 2; - optional TezosManagerTransfer transfer = 3; - - message TezosManagerTransfer { - optional TezosContractID destination = 1; - optional uint64 amount = 2; - } - } } /** * Structure representing information for origination */ message TezosOriginationOp { - optional bytes source = 12; + optional TezosContractID source = 1; optional uint64 fee = 2; optional uint64 counter = 3; optional uint64 gas_limit = 4; @@ -2926,7 +2997,7 @@ message TezosSignTx { * Structure representing information for delegation */ message TezosDelegationOp { - optional bytes source = 7; + optional TezosContractID source = 1; optional uint64 fee = 2; optional uint64 counter = 3; optional uint64 gas_limit = 4; @@ -2971,62 +3042,158 @@ message TezosSignedTx { // Sugar for easier handling in Java /** - * Request: List resident credentials + * Request: Ask device for Tron address corresponding to address_n path * @start - * @next WebAuthnCredentials + * @next TronAddress * @next Failure */ -message WebAuthnListResidentCredentials { +message TronGetAddress { + 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 } /** - * Request: Add resident credential - * @start - * @next Success - * @next Failure + * Response: Contains Tron address derived from device private seed + * @end */ -message WebAuthnAddResidentCredential { - optional bytes credential_id = 1; +message TronAddress { + optional string address = 1; // Tron address (base58) } /** - * Request: Remove resident credential + * Request: Ask device to sign Tron transaction * @start - * @next Success - * @next Failure + * @next TronSignedTx */ -message WebAuthnRemoveResidentCredential { - optional uint32 index = 1; -} +message TronSignTx { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + // Common part of transaction + optional uint64 timestamp = 2; // UTC timestamp + optional uint64 expiration = 3; // Transaction expiration + optional string block_id = 4; // Now block ID + optional TronContract contract = 6; // Contract messages + optional uint64 fee_limit = 8; // fee limit - -/** - * Response: Resident credential list - * @start - * @next end - */ -message WebAuthnCredentials { - repeated WebAuthnCredential credentials = 1; - message WebAuthnCredential { - optional uint32 index = 1; - optional bytes id = 2; - optional string rp_id = 3; - optional string rp_name = 4; - optional bytes user_id = 5; - optional string user_name = 6; - optional string user_display_name = 7; - optional uint32 creation_time = 8; - optional bool hmac_secret = 9; - optional bool use_sign_count = 10; + /*** + * Tron Contracts Messages + * + */ + message TronContract { + // Transfer TRX + message TronTransferContract { + optional string to_address = 1; // To address + optional uint64 amount = 2; // TRX amount in sun (10^-6) + } + // Transfer asset + message TronTransferAssetContract { + optional string asset_name = 1; // Asset name is token id in hex string ex: "31303030303031" + optional string to_address = 2; // To address + optional uint64 amount = 3; // Amount to transfer + } + // Vote witness + message TronVoteWitnessContract { + message TronVote { + optional string vote_address = 1; // Candidate Address + optional uint64 vote_count = 2; // Amount of votes + } + repeated TronVote votes = 1; // votes + } + // Issue Asset + message TronAssetIssueContract { + message TronFrozenSupply { + optional uint64 frozen_amount = 1; // Amount frozen + optional uint64 frozen_days = 2; // Days from issue date + } + optional string name = 2; // Asset name + optional string abbr = 3; // Asset abbreviation + optional uint64 total_supply = 4; // Total supply including frozen + repeated TronFrozenSupply frozen_supply = 5; // Frozen supply + optional uint32 trx_num = 6; // Amount of TRX (exchange ratio) + optional uint32 num = 7; // Amount of tokens (exchange ratio) + optional uint64 start_time = 8; // Negotiation start date and time + optional uint64 end_time = 9; // Negotiation end date and time + optional string description = 10; // Asset description + optional string url = 11; // Asset URL + } + // Participate in an asset + message TronParticipateAssetIssueContract { + optional string to_address = 1; // Asset issuer address + optional string asset_name = 2; // The name of target asset + optional uint64 amount = 3; // TRX amount in sun + } + enum TronResourceCode { + BANDWIDTH = 0x00; + ENERGY = 0x01; + } + // Freeze TRX balance + message TronFreezeBalanceContract { + optional uint64 frozen_balance = 1; // Amount to freeze + optional uint64 frozen_duration = 2; // (Optional)Freeze minimal duration in days currently 3 days is fixed on tron network + optional TronResourceCode resource = 3; // Freeze the balance to get bandwidth or energy + optional string receiver_address = 4; // (Optional)Energy or bandwidth receiver address + } + // Unfreeze TRX Balance + message TronUnfreezeBalanceContract { + optional TronResourceCode resource = 3; // Unfreeze the bandwidth or energy + optional string receiver_address = 4; // (Optional)Energy or bandwidth receiver address + } + // Unfreeze Asset Balance + message TronUnfreezeAssetContract { + } + // Withdraw witness balance + message TronWithdrawBalanceContract { + } + // Update Asset + message TronUpdateAssetContract { + optional string description = 1; // New description + optional string url = 2; // New URL + } + // Approval contract + message TronProposalApproveContract { + optional uint64 proposal_id = 1; // Proposal ID + optional bool is_add_approval = 2; // Add or remove approval + } + // Trigger smart contract + message TronTriggerSmartContract { + optional string contract_address = 1; // The contract address + optional int64 call_value = 2; // The amount of TRX to send to the contract when triggers. + optional bytes data = 3; // The parameters to trigger the contract. + optional int64 call_token_value = 4; // The amount of TRC-10 token to send to the contract when triggers. + optional int64 token_id = 5; // The id of the TRC-10 token to be sent to the contract. + } + // Transger TRC20 + message TronTransferTRC20 { // This is a helper function to ease sending of TRC20 tokens + optional string contract_address = 1; // The TRC20 contract address + optional string to_address = 2; // The to address + optional bytes amount = 3; // <=256 bit unsigned big endian + } + optional TronTransferContract transfer_contract = 1; + optional TronTransferAssetContract transfer_asset_contract = 2; + optional TronVoteWitnessContract vote_witness_contract = 3; + optional TronAssetIssueContract asset_issue_contract = 4; + optional TronParticipateAssetIssueContract participate_asset_issue_contract = 5; + optional TronFreezeBalanceContract freeze_balance_contract = 6; + optional TronUnfreezeBalanceContract unfreeze_balance_contract = 7; + optional TronWithdrawBalanceContract withdraw_balance_contract = 8; + optional TronUnfreezeAssetContract unfreeze_asset_contract = 9; + optional TronUpdateAssetContract update_asset_contract = 10; + optional TronProposalApproveContract proposal_approve_contract = 11; + optional TronTriggerSmartContract trigger_smart_contract = 12; + optional TronTransferTRC20 transfer_trc20 = 13; } } -message RebootDevice { - +/** + * Response: Contains Tron transaction signature + * @end + */ +message TronSignedTx { + optional bytes serialized_tx = 1; // Serialized transaction + optional bytes signature = 2; // transaction signature } /** - * Messages for Trezor communication + * Messages for TREZOR communication */ // Sugar for easier handling in Java @@ -3036,17 +3203,17 @@ message RebootDevice { * Options for specifying message direction and type of wire (normal/debug) */ extend google.protobuf.EnumValueOptions { - optional bool wire_in = 50002; // message can be transmitted via wire from PC to Trezor - optional bool wire_out = 50003; // message can be transmitted via wire from Trezor to PC - optional bool wire_debug_in = 50004; // message can be transmitted via debug wire from PC to Trezor - optional bool wire_debug_out = 50005; // message can be transmitted via debug wire from Trezor to PC - optional bool wire_tiny = 50006; // message is handled by Trezor when the USB stack is in tiny mode - optional bool wire_bootloader = 50007; // message is only handled by Trezor Bootloader - optional bool wire_no_fsm = 50008; // message is not handled by Trezor unless the USB stack is in tiny mode + optional bool wire_in = 50002; // message can be transmitted via wire from PC to TREZOR + optional bool wire_out = 50003; // message can be transmitted via wire from TREZOR to PC + optional bool wire_debug_in = 50004; // message can be transmitted via debug wire from PC to TREZOR + optional bool wire_debug_out = 50005; // message can be transmitted via debug wire from TREZOR to PC + optional bool wire_tiny = 50006; // message is handled by TREZOR when the USB stack is in tiny mode + optional bool wire_bootloader = 50007; // message is only handled by TREZOR Bootloader + optional bool wire_no_fsm = 50008; // message is not handled by TREZOR unless the USB stack is in tiny mode } /** - * Mapping between Trezor wire identifier (uint) and a protobuf message + * Mapping between TREZOR wire identifier (uint) and a protobuf message */ enum MessageType { @@ -3082,10 +3249,6 @@ enum MessageType { MessageType_WordAck = 47 [(wire_in) = true]; MessageType_GetFeatures = 55 [(wire_in) = true]; MessageType_SetU2FCounter = 63 [(wire_in) = true]; - MessageType_SdProtect = 79 [(wire_in) = true]; - MessageType_GetNextU2FCounter = 80 [(wire_in) = true]; - MessageType_NextU2FCounter = 81 [(wire_out) = true]; - MessageType_ChangeWipeCode = 82 [(wire_in) = true]; // Bootloader MessageType_FirmwareErase = 6 [(wire_in) = true, (wire_bootloader) = true]; @@ -3127,7 +3290,6 @@ enum MessageType { MessageType_DebugLinkMemory = 111 [(wire_debug_out) = true]; MessageType_DebugLinkMemoryWrite = 112 [(wire_debug_in) = true]; MessageType_DebugLinkFlashErase = 113 [(wire_debug_in) = true]; - MessageType_DebugLinkLayout = 9001 [(wire_debug_out) = true]; // Ethereum MessageType_EthereumGetPublicKey = 450 [(wire_in) = true]; @@ -3187,6 +3349,12 @@ enum MessageType { MessageType_StellarBumpSequenceOp = 221 [(wire_in) = true]; MessageType_StellarSignedTx = 230 [(wire_out) = true]; + // TRON + MessageType_TronGetAddress = 250 [(wire_in) = true]; + MessageType_TronAddress = 251 [(wire_out) = true]; + MessageType_TronSignTx = 252 [(wire_in) = true]; + MessageType_TronSignedTx = 253 [(wire_out) = true]; + // Cardano // dropped Sign/VerifyMessage ids 300-302 MessageType_CardanoSignTx = 303 [(wire_in) = true]; @@ -3198,11 +3366,25 @@ 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_OntologySignTransfer = 354 [(wire_in) = true]; + MessageType_OntologySignedTransfer = 355 [(wire_out) = true]; + MessageType_OntologySignWithdrawOng = 356 [(wire_in) = true]; + MessageType_OntologySignedWithdrawOng = 357 [(wire_out) = true]; + MessageType_OntologySignOntIdRegister = 358 [(wire_in) = true]; + MessageType_OntologySignedOntIdRegister = 359 [(wire_out) = true]; + MessageType_OntologySignOntIdAddAttributes = 360 [(wire_in) = true]; + MessageType_OntologySignedOntIdAddAttributes = 361 [(wire_out) = true]; + // Ripple MessageType_RippleGetAddress = 400 [(wire_in) = true]; MessageType_RippleAddress = 401 [(wire_out) = true]; MessageType_RippleSignTx = 402 [(wire_in) = true]; - MessageType_RippleSignedTx = 403 [(wire_in) = true]; + MessageType_RippleSignedTx = 403 [(wire_out) = true]; // Monero MessageType_MoneroTransactionInitRequest = 501 [(wire_out) = true]; @@ -3263,12 +3445,4 @@ enum MessageType { MessageType_BinanceOrderMsg = 707 [(wire_in) = true]; MessageType_BinanceCancelMsg = 708 [(wire_in) = true]; MessageType_BinanceSignedTx = 709 [(wire_out) = true]; - - // WebAuthn - MessageType_WebAuthnListResidentCredentials = 800 [(wire_in) = true]; - MessageType_WebAuthnCredentials = 801 [(wire_out) = true]; - MessageType_WebAuthnAddResidentCredential = 802 [(wire_in) = true]; - MessageType_WebAuthnRemoveResidentCredential = 803 [(wire_in) = true]; - - MessageType_RebootDevice = 65520 [(wire_in) = true]; } diff --git a/src/blockchain/servers/prokey/src/ripple/RippleModel.ts b/src/blockchain/servers/prokey/src/ripple/RippleModel.ts index 70bae23..75ccbf5 100644 --- a/src/blockchain/servers/prokey/src/ripple/RippleModel.ts +++ b/src/blockchain/servers/prokey/src/ripple/RippleModel.ts @@ -2,6 +2,8 @@ * This is part of PROKEY HARDWARE WALLET project * Copyright (C) Prokey.io * + * Ali Akbar Mohammadi + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or diff --git a/src/blockchain/servers/prokey/src/tron/TronBlockchain.ts b/src/blockchain/servers/prokey/src/tron/TronBlockchain.ts new file mode 100644 index 0000000..f377614 --- /dev/null +++ b/src/blockchain/servers/prokey/src/tron/TronBlockchain.ts @@ -0,0 +1,122 @@ +/* + * This is part of PROKEY HARDWARE WALLET project + * Copyright (C) Prokey.io + * + * Ali Akbar Mohammadi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { RequestAddressInfo } from '../../../../../models/GenericWalletModel'; +import { MyConsole } from '../../../../../utils/console'; +import { ProkeyBaseBlockChain } from '../ProkeyBaseBlockChain'; +import { TronAccountInfo, TronAccountResources, TronBlock, TronTransactionDataInfo, TronTrc20TransactionDataInfo } from './TronModel'; + +export class TronBlockchain extends ProkeyBaseBlockChain { + + public GetAddressInfo(reqAddresses: RequestAddressInfo | RequestAddressInfo[]) { + throw new Error('Method not needed here.'); + } + public GetTransactions(hash: string) { + throw new Error('Method not needed here.'); + } + public GetLatestTransactions(trs: any[], count: number, offset: number) { + throw new Error('Method not needed here.'); + } + + _coinName: string; + + constructor(coinName: string) { + super(); + this._coinName = coinName; + } + + public async GetAccountInfo(account: string): Promise { + try { + let r = await this.GetFromServer(`address/${this._coinName}/${account}`); + if (!r.success) { + MyConsole.Error("Tron get account info error: ", r); + return null; + } + return r.data[0]; + } catch (error) { + return null; + } + } + + // Get account resources the address must be in HEX format + public async GetAccountResources(account: string): Promise { + try { + let r = await this.GetFromServer(`address/resources/${this._coinName}/${account}`); + MyConsole.Info("Tron account resources: ", r); + if (r.freeNetUsed == undefined) { + r.freeNetUsed = 0; + } + if (r.EnergyLimit == undefined) { + r.EnergyLimit = 0; + } + if (r.EnergyUsed == undefined) { + r.EnergyUsed = 0; + } + if (r.tronPowerLimit == undefined) { + r.tronPowerLimit = 0; + } + if (r.tronPowerUsed == undefined) { + r.tronPowerUsed = 0; + } + if (r.NetLimit == undefined) { + r.NetLimit = 0; + } + if (r.NetUsed == undefined) { + r.NetUsed = 0; + } + return r; + } catch (error) { + MyConsole.Error("Tron get account resources error: ", error); + return null; + } + } + + public async GetAccountTransactions(account: string, limit: number = 10): Promise> { + let r = await this.GetFromServer(`address/transactions/${this._coinName}/${account}/${limit}`); + if (!r.success) { + MyConsole.Error("Tron get account transactions error: ", r); + return []; + } + MyConsole.Info("tron transactions: ", r.data); + return r.data; + } + + public async GetAccountTrc20Transactions(account: string, limit: number = 10): Promise> { + let r = await this.GetFromServer(`address/trc20/transactions/${this._coinName}/${account}/${limit}`); + if (!r.success) { + MyConsole.Error("Tron get account TRC20 transactions error: ", r); + return []; + } + return r.data; + } + + public async GetNowBlock(): Promise { + return await this.GetFromServer(`block/getblockcount/${this._coinName}`); + } + + public async GetLatestBlock(count: number): Promise { + return await this.GetFromServer(`block/GetLatest/${this._coinName}/${count}`); + } + + public async BroadCastTransaction(data: string): Promise { + return await this.GetFromServer(`Transaction/send/${this._coinName}/${data}`); + } + +} \ No newline at end of file diff --git a/src/blockchain/servers/prokey/src/tron/TronModel.ts b/src/blockchain/servers/prokey/src/tron/TronModel.ts new file mode 100644 index 0000000..1bce012 --- /dev/null +++ b/src/blockchain/servers/prokey/src/tron/TronModel.ts @@ -0,0 +1,135 @@ +/* + * This is part of PROKEY HARDWARE WALLET project + * Copyright (C) Prokey.io + * + * Ali Akbar Mohammadi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +export type TronAccountInfo = { + latest_opration_time: number; + account_resource?: { + frozen_balance_for_energy?: { + frozen_balance: number, + expire_time: number + }; + delegated_frozen_balance_for_energy?: number; + }; + address: string; + balance: number; + create_time: number; + trc20?: Array; // TRC20 balances + latest_consume_free_time: number; + frozen?: Array<{ + frozen_balance: number, + expire_time: number + }>; + delegated_frozen_balance_for_bandwidth?: number; +} + +export type TronAccountResources = { + freeNetUsed: number; // Free bandwidth used + freeNetLimit: number; // Total free bandwidth + NetLimit: number; // Total bandwidth obtained by freezing + NetUsed: number; // Total bandwidth used + TotalNetLimit: number; // Total bandwidth can be obtained by freezing + TotalNetWeight: number; // Total TRX frozen for bandwidth + tronPowerUsed: number; // TRON power used + tronPowerLimit: number; // TRON Power(vote) + EnergyLimit: number; // Total energy obtained by freezing + EnergyUsed: number; // Total energy obtained by freezing + TotalEnergyLimit: number; // Total energy can be obtained by freezing + TotalEnergyWeight: number; // Total TRX frozen for energy} +} + +export type TronContractRet = { + contractRet: string; + fee: number; +} + +export type TronContractTransfer = { + amount: number; + owner_address: string; + to_address: string; +} + +export type TronContractParameter = { + value: TronContractTransfer | any; + type: string; +} + +export type TronContract = { + parameter: TronContractParameter; + type: string; +} + +export type TronTransactionRawData = { + contract: Array; + ref_block_bytes: string; + ref_block_hash: string; + expiration: number; + timestamp: number; +} + +export type TronTransactionDataInfo = { + ret: Array; + signature: Array; + txID: string; + raw_data_hex: string; + net_usage: number; + net_fee: number; + energy_usage: number; + blockNumber: number; + block_timestamp: number; + energy_fee: number; + energy_usage_total: number; + raw_data: TronTransactionRawData; +} + +export type TronTokenInfo = { + symbol: string; + address: string; + decimals: number; + name: string; +} + +export type TronTrc20TransactionDataInfo = { + transaction_id: string; + token_info: TronTokenInfo; + block_timestamp: number; + from: string; + to: string; + type: string; + value: string; +} + +export type TronBlockHeaderRawData = { + number: number; + txTrieRoot: string; + witness_address: string; + parentHash: string; + version: number; + timestamp: number; +} + +export type TronBlockHeader = { + raw_data: TronBlockHeaderRawData; + witness_signature: string; +} + +export type TronBlock = { + blockID: string; + block_header: TronBlockHeader; +} \ No newline at end of file diff --git a/src/coins/CoinInfo.ts b/src/coins/CoinInfo.ts index 499cb59..1bb4fa1 100644 --- a/src/coins/CoinInfo.ts +++ b/src/coins/CoinInfo.ts @@ -41,6 +41,7 @@ export enum CoinBaseType { NEM, OMNI, Ripple, + Tron, Stellar, OTHERS } @@ -90,6 +91,9 @@ export class CoinInfo { case CoinBaseType.Ripple: c = ProkeyCoinInfoModel.ripple; break; + case CoinBaseType.Tron: + c = ProkeyCoinInfoModel.tron; + break; } let f = coinName.toLowerCase(); @@ -131,6 +135,10 @@ export class CoinInfo { ci.id = `ripple_${ci.shortcut}`; break; + case CoinBaseType.Tron: + ci = c.find(obj => obj.name.toLowerCase() == f || obj.shortcut.toLowerCase() == f); + ci.id = `tron_${ci.shortcut}`; + break; default: ci = c.find(obj => obj.name.toLowerCase() == f || obj.shortcut.toLowerCase() == f); @@ -224,6 +232,18 @@ export class CoinInfo { } }); + //! For all Tron base coins + ProkeyCoinInfoModel.tron.forEach(tron => { + //! Check the version + if(compareVersions(firmwareVersion, tron.support.optimum) >= 0) { + list.push({ + ...tron, + coinBaseType: CoinBaseType.Tron, + id: `tron_${tron.shortcut}`, + }) + } + }); + //! Sort the list by Priority list.sort((a, b) => { if (a.priority > b.priority) diff --git a/src/device/BaseCommands.ts b/src/device/BaseCommands.ts new file mode 100644 index 0000000..89b305a --- /dev/null +++ b/src/device/BaseCommands.ts @@ -0,0 +1,191 @@ +/* + * This is part of PROKEY HARDWARE WALLET project + * Copyright (C) Prokey.io + * + * Ali Akbar Mohammadi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { Device } from "./Device"; +import * as PathUtil from '../utils/pathUtils'; +import * as Utility from '../utils/utils'; +import { GeneralErrors } from "../models/GeneralResponse"; +import { MessageSignature, PublicKey, Success } from "../models/Prokey"; + +export abstract class BaseCommands { + + /** + * Get coin address from device + * @param device Prokey device instance + * @param path BIP path + * @param showOnProkey true means show the address on device display + */ + protected async GetAddressBase( + deviceCommand: string, + returnType: string, + device: Device, + path: Array | string, + showOnProkey?: boolean): Promise { + if (device == null || path == null) { + return Promise.reject({ success: false, errorCode: GeneralErrors.INVALID_PARAM }); + } + + let showDisplay = (showOnProkey == null) ? true : showOnProkey; + + // convert path to array of num + let address_n: Array; + if (typeof path == "string") { + try { + address_n = PathUtil.getHDPath(path); + } + catch (e) { + return Promise.reject({ success: false, errorCode: GeneralErrors.PATH_NOT_VALID }); + } + + } else { + address_n = path; + } + + let param = { + address_n: address_n, + show_display: showDisplay, + } + + return await device.SendMessage(deviceCommand, param, returnType); + } + + /** + * Get List of addresses, This function is useful in account discovery + * @param device the prokey device instance + * @param paths list of paths to retrive the addresses + */ + protected async GetAddressesBase( + deviceCommand: string, + returnType: string, + device: Device, + paths: Array>): Promise> { + + if (device == null || paths == null) { + return Promise.reject({ + success: false, + errorCode: GeneralErrors.INVALID_PARAM + }); + } + + let lstAddress = new Array(); + lstAddress.length = paths.length; + + paths.forEach(async (path) => { + lstAddress.push(await this.GetAddressBase( + deviceCommand, + returnType, + device, + path, + false)); + }); + + return lstAddress; + } + + /** + * Get Public key + * @param device The prokey device + * @param path BIP path + * @param showOnProkey true means show the public key on prokey display + */ + protected async GetPublicKeyBase(device: Device, + path: Array | string, + showOnProkey?: boolean): Promise { + + if (device == null || path == null) { + return Promise.reject({ success: false, errorCode: GeneralErrors.INVALID_PARAM }); + } + + let showDisplay = (showOnProkey == null) ? true : showOnProkey; + + // convert path to array of num + let address_n: Array; + if (typeof path == "string") { + try { + address_n = PathUtil.getHDPath(path); + } + catch (e) { + return Promise.reject({ success: false, errorCode: GeneralErrors.PATH_NOT_VALID }); + } + + } else { + address_n = path; + } + + let param = { + address_n: address_n, + show_display: showDisplay, + } + + return await device.SendMessage('GetPublicKey', param, 'PublicKey'); + } + + /** + * Sign Message + * @param device Prokey device instance + * @param address_n array of BIP32/44 Path + * @param message message to be signed + * @param coin coin name + */ + protected async SignMessageBase( + device: Device, + address_n: Array, + message: Uint8Array, + coin: string): Promise { + + let scriptType = PathUtil.GetScriptType(address_n); + + let res = await device.SendMessage('SignMessage', { + address_n: address_n, + message: message, + coin_name: coin, + script_type: scriptType, + }, 'MessageSignature'); + + if (res.signature) { + res.signature = Utility.ByteArrayToHexString(res.signature); + } + + return res; + } + + /** + * Verify Message + * @param device Prokey device instance + * @param address address + * @param message message + * @param signature signature data + * @param coinName coin name + */ + public async VerifyMessageBase( + device: Device, + address: string, + message: Uint8Array, + signature: Uint8Array, + coinName: string): Promise { + + return await device.SendMessage('VerifyMessage', { + address: address, + signature: signature, + message: message, + coin_name: coinName, + }, 'Success'); + } +} \ No newline at end of file diff --git a/src/device/Device.ts b/src/device/Device.ts index 9ef8ec7..599777b 100644 --- a/src/device/Device.ts +++ b/src/device/Device.ts @@ -399,10 +399,14 @@ export class Device { if (res.type === 'Failure') { + res.success = false; console.log("Failure", res); if(this._eventEmitters.emit('OnFailure', res.payload) == false){ MyConsole.Warning('DeviceCommands::OnReceiveDataFromBridge->OnFailure has no listener'); } + if(this._sendMessageReject) { + this._sendMessageReject(res); + } } else if (res.type === 'ButtonRequest') { diff --git a/src/device/ICoinCommand.ts b/src/device/ICoinCommand.ts index a76af11..549fc19 100644 --- a/src/device/ICoinCommand.ts +++ b/src/device/ICoinCommand.ts @@ -22,7 +22,7 @@ import { Device } from './Device'; import * as ProkeyResponses from '../models/Prokey'; import { BitcoinTx } from '../models/BitcoinTx'; import { EthereumTx } from '../models/EthereumTx'; -import { RippleTransaction } from '../models/Responses-V6'; +import { RippleTransaction } from '../models/Prokey'; export interface ICoinCommands { GetAddress( @@ -64,7 +64,8 @@ export interface ICoinCommands { device: Device, transaction:BitcoinTx | EthereumTx | - RippleTransaction, + RippleTransaction | + ProkeyResponses.TronTransaction, ): Promise; + ProkeyResponses.RippleSignedTx | + ProkeyResponses.TronSignedTx>; SignMessage( device: Device, diff --git a/src/device/RippleCommands.ts b/src/device/RippleCommands.ts index 1de374b..0e08a1c 100644 --- a/src/device/RippleCommands.ts +++ b/src/device/RippleCommands.ts @@ -27,23 +27,31 @@ import { RippleCoinInfoModel } from '../models/CoinInfoModel'; import { CoinInfo, CoinBaseType } from '../coins/CoinInfo'; import { Device } from './Device'; import { GeneralResponse, GeneralErrors } from '../models/GeneralResponse'; -import { RippleTransaction } from '../models/Responses-V6'; -import { RippleAddress } from '../models/Prokey'; +import { RippleTransaction } from '../models/Prokey'; import { MyConsole } from '../utils/console'; import { validateParams } from '../utils/paramsValidator'; +import { BaseCommands } from './BaseCommands'; -export class RippleCommands implements ICoinCommands { +export class RippleCommands extends BaseCommands implements ICoinCommands { private _coinInfo: RippleCoinInfoModel; _failedSignHandler: any; constructor(coinName: string) { + super(); this._coinInfo = CoinInfo.Get(coinName, CoinBaseType.Ripple); if (this._coinInfo == null) { throw new Error(`Cannot load CoinInfo for ${coinName}`); } } + /** + * Get Coin Info + */ + public GetCoinInfo(): RippleCoinInfoModel { + return this._coinInfo; + } + /** * Get Bitcoin/Litecoin and etc address * @param device Prokey device instance @@ -51,32 +59,7 @@ export class RippleCommands implements ICoinCommands { * @param showOnProkey true means show the address on device display */ public async GetAddress(device: Device, path: Array, showOnProkey?: boolean): Promise { - if (device == null || path == null) { - return Promise.reject({ success: false, errorCode: GeneralErrors.INVALID_PARAM }); - } - - let showDisplay = (showOnProkey == null) ? true : showOnProkey; - - // convert path to array of num - let address_n: Array; - if (typeof path == "string") { - try { - address_n = PathUtil.getHDPath(path); - } - catch (e) { - return Promise.reject({ success: false, errorCode: GeneralErrors.PATH_NOT_VALID }); - } - - } else { - address_n = path; - } - - let param = { - address_n: address_n, - show_display: showDisplay, - } - - return await device.SendMessage('RippleGetAddress', param, 'RippleAddress'); + return await this.GetAddressBase('RippleGetAddress', 'RippleAddress', device, path, showOnProkey); } /** @@ -85,43 +68,7 @@ export class RippleCommands implements ICoinCommands { * @param paths list of paths to retrive the addresses */ public async GetAddresses(device: Device, paths: Array>): Promise> { - if (device == null || paths == null) { - return Promise.reject({ - success: false, - errorCode: GeneralErrors.INVALID_PARAM - }); - } - - let lstAddress = new Array(); - - paths.forEach(async (path) => { - let pn: Array; - if (typeof path == "string") { - try { - pn = PathUtil.getHDPath(path); - } - catch (e) { - return Promise.reject({ success: false, errorCode: GeneralErrors.PATH_NOT_VALID }); - } - } - else { - pn = path; - } - - let param = { - address_n: pn, - show_display: false, - } - - try { - let address = await device.SendMessage('RippleGetAddress', param, 'RippleAddress'); - lstAddress.push(address); - } catch (e) { - Promise.reject(e); - } - }); - - return lstAddress; + return await this.GetAddressesBase('RippleGetAddress', 'RippleAddress', device, paths); } /** @@ -134,32 +81,7 @@ export class RippleCommands implements ICoinCommands { path: Array | string, showOnProkey?: boolean): Promise { - if (device == null || path == null) { - return Promise.reject({ success: false, errorCode: GeneralErrors.INVALID_PARAM }); - } - - let showDisplay = (showOnProkey == null) ? true : showOnProkey; - - // convert path to array of num - let address_n: Array; - if (typeof path == "string") { - try { - address_n = PathUtil.getHDPath(path); - } - catch (e) { - return Promise.reject({ success: false, errorCode: GeneralErrors.PATH_NOT_VALID }); - } - - } else { - address_n = path; - } - - let param = { - address_n: address_n, - show_display: showDisplay, - } - - return await device.SendMessage('GetPublicKey', param, 'PublicKey'); + return await this.GetPublicKeyBase(device, path, showOnProkey); } /** @@ -248,20 +170,7 @@ export class RippleCommands implements ICoinCommands { message: Uint8Array, coin?: string): Promise { - let scriptType = PathUtil.GetScriptType(address_n); - - let res = await device.SendMessage('SignMessage', { - address_n: address_n, - message: message, - coin_name: coin || 'Ripple', - script_type: scriptType, - }, 'MessageSignature'); - - if (res.signature) { - res.signature = Utility.ByteArrayToHexString(res.signature); - } - - return res; + return await this.SignMessageBase(device, address_n, message, coin || 'Ripple'); } /** @@ -279,12 +188,7 @@ export class RippleCommands implements ICoinCommands { signature: Uint8Array, coinName: string): Promise { - return await device.SendMessage('VerifyMessage', { - address: address, - signature: signature, - message: message, - coin_name: coinName || 'Ripple', - }, 'Success'); + return await this.VerifyMessageBase(device, address, message, signature, coinName || 'Ripple'); } } \ No newline at end of file diff --git a/src/device/TronCommands.ts b/src/device/TronCommands.ts new file mode 100644 index 0000000..69d9fa5 --- /dev/null +++ b/src/device/TronCommands.ts @@ -0,0 +1,69 @@ +/* + * This is part of PROKEY HARDWARE WALLET project + * Copyright (C) Prokey.io + * + * Hadi Robati, hadi@prokey.io + * Ali Akbar Mohammadi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { CoinBaseType, CoinInfo } from "../coins/CoinInfo"; +import { TronCoinInfoModel } from "../models/CoinInfoModel"; +import { TronTransaction, PublicKey, RippleSignedTx, MessageSignature, Success, TronAddress, TronSignedTx } from "../models/Prokey"; +import { BaseCommands } from "./BaseCommands"; +import { Device } from "./Device"; +import { ICoinCommands } from "./ICoinCommand"; + +export class TronCommands extends BaseCommands implements ICoinCommands { + + private _coinInfo: TronCoinInfoModel; + + public constructor(coinName: string) + { + super(); + this._coinInfo = CoinInfo.Get(coinName, CoinBaseType.Tron); + if (this._coinInfo == null) { + throw new Error(`Cannot load CoinInfo for ${coinName}`); + } + } + + GetCoinInfo(): TronCoinInfoModel { + return this._coinInfo; + } + + public async GetAddress(device: Device, path: Array, showOnProkey?: boolean): Promise { + return await this.GetAddressBase('TronGetAddress', 'TronAddress', device, path, showOnProkey); + } + + public async GetAddresses(device: Device, paths: Array>): Promise { + return await this.GetAddressesBase('TronGetAddress', 'TronAddress', device, paths); + } + + public async GetPublicKey(device: Device, path: string | number[], showOnProkey?: boolean): Promise { + return this.GetPublicKeyBase(device, path, showOnProkey); + } + + public async SignTransaction(device: Device, transaction: TronTransaction): Promise { + return await device.SendMessage("TronSignTx", transaction, "TronSignedTx"); + } + + public async SignMessage(device: Device, path: number[], message: Uint8Array, coinName?: string): Promise { + return await this.SignMessageBase(device, path, message, coinName || 'TRON'); + } + + public async VerifyMessage(device: Device, address: string, message: Uint8Array, signature: Uint8Array, coinName?: string): Promise { + return await this.VerifyMessageBase(device, address, message, signature, coinName || 'TRON'); + } +} diff --git a/src/models/CoinInfoModel.ts b/src/models/CoinInfoModel.ts index 85841d8..2387683 100644 --- a/src/models/CoinInfoModel.ts +++ b/src/models/CoinInfoModel.ts @@ -27,6 +27,7 @@ export type GeneralCoinInfoModel = BitcoinBaseCoinInfoModel | Erc20BaseCoinInfoModel | RippleCoinInfoModel | OmniCoinInfoModel | + TronCoinInfoModel | MiscCoinInfoModel; export interface BaseCoinInfoModel { @@ -131,3 +132,14 @@ export interface RippleCoinInfoModel extends BaseCoinInfoModel { priority: number, min_balance: number, } + +export interface TronCoinInfoModel extends BaseCoinInfoModel { + name: string, + shortcut: string, + slip44: number, + decimals: number, + on_device: string, + support: ProkeySupport; + test?: boolean, + tx_url: string, +} \ No newline at end of file diff --git a/src/models/Prokey.ts b/src/models/Prokey.ts index 2580c04..85fb22b 100644 --- a/src/models/Prokey.ts +++ b/src/models/Prokey.ts @@ -18,6 +18,8 @@ * along with this program. If not, see . */ +const Long = require('../protobuf/long'); + export type CipheredKeyValue = { value: string, } @@ -305,7 +307,7 @@ export type SignTxInfo = { inputs_cnt: number, outputs_cnt: number, extra_data_len?: number, - timestamp:number, + timestamp: number, version_group_id: number, }; @@ -735,6 +737,15 @@ export type RippleSignedTx = { serialized_tx: string, } +// TRON types +export type TronAddress = { + address: string, +} + +export type TronSignedTx = { + serialized_tx: string, +} + // EOS types export type EosPublicKey = { wif_public_key: string, @@ -1033,3 +1044,40 @@ export type LoadDeviceFlags = { skip_checksum?: boolean, u2f_counter?: number, } + +// Tron types +export enum TronResourceCode { + BANDWIDTH = 0x00, + ENERGY = 0x01 +} + +export type TronFreezeBalance = { + frozen_balance: number; // Amount to freeze + frozen_duration?: number; // (Optional)Freeze minimal duration in days currently 3 days is fixed on tron network + resource: TronResourceCode; // Freeze the balance to get bandwidth or energy + receiver_address?: string; // (Optional)Energy or bandwidth receiver address +} + +// Unfreeze TRX Balance +export type TronUnfreezeBalance = { + resource: TronResourceCode; // Unfreeze the bandwidth or energy + receiver_address: string; // (Optional)Energy or bandwidth receiver address +} + + +export type TronTransaction = { + address_n: Array; + timestamp: number; // UTC timestamp + expiration?: number; // Transaction expiration + block_id: string; // Now block ID + fee_limit?: number; // Max fee in TRX when calling contract address + contract: { + // Transfer TRX + transfer_contract?: { + to_address: string; // To address + amount: number; // TRX amount in sun (10^-6) + }; + // Freeze TRX balance + freeze_balance_contract?: TronFreezeBalance; + } +} diff --git a/src/models/Responses-V6.ts b/src/models/Responses-V6.ts deleted file mode 100644 index e2ca89d..0000000 --- a/src/models/Responses-V6.ts +++ /dev/null @@ -1,637 +0,0 @@ -/* - * This is part of PROKEY HARDWARE WALLET project - * Copyright (C) Prokey.io - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ - -export interface Address { - address: string; - path: Array; - serializedPath: string; -} - -export interface CipheredKeyValue { - value: string; -} - -export interface Success { - -} - -export interface Coininterface { - coin_name: string; - coin_shortcut: string; - address_interface: number; - maxfee_kb: number; - address_interface_p2sh: number; -} - -export interface Features { - vendor: string; - major_version: number; - minor_version: number; - patch_version: number; - bootloader_mode: boolean; - device_id: string; - pin_protection: boolean; - passphrase_protection: boolean; - language: string; - label: string; - coins: Coininterface[]; - initialized: boolean; - revision: string; - bootloader_hash: string; - imported: boolean; - pin_cached: boolean; - passphrase_cached: boolean; - state?: string; - needs_backup?: boolean; - firmware_present?: boolean; -} - -export interface ResetDeviceSettings { - display_random?: boolean; - strength?: number; - passphrase_protection?: boolean; - pin_protection?: boolean; - language?: string; - label?: string; - u2f_counter?: number; - skip_backup?: boolean; -} - -export interface HDPrivNode { - depth: number; - fingerprint: number; - child_num: number; - chain_code: string; - private_key: string; -} - -export interface HDPubNode { - depth: number; - fingerprint: number; - child_num: number; - chain_code: string; - public_key: string; -} - -export type HDNode = HDPubNode | HDPrivNode; - -export interface LoadDeviceSettings { - pin?: string; - passphrase_protection?: boolean; - language?: string; - label?: string; - skip_checksum?: boolean; - - mnemonic?: string; - node?: HDNode; - payload?: string; // will be converted - - u2f_counter?: number; -} - -export interface RecoverDeviceSettings { - word_count?: number; - passphrase_protection?: boolean; - pin_protection?: boolean; - language?: string; - label?: string; - enforce_wordlist?: boolean; - interface?: number; - u2f_counter?: number; -} - -export interface ApplySettings { - language?: string; - label?: string; - use_passphrase?: boolean; - homescreen?: string; -} - -export interface MessageSignature { - address: string; - signature: string; -} - -export interface MultisigRedeemScriptinterface { - pubkeys: Array<{ node: string; address_n: Array }>; - signatures: Array; - m?: number; -} - -export interface TransactionInput { - address_n?: Array; - prev_hash: string; - prev_index: number; - script_sig?: string; - sequence?: number; - script_interface?: 'SPENDADDRESS' | 'SPENDMULTISIG' | 'EXTERNAL' | 'SPENDWITNESS' | 'SPENDP2SHWITNESS'; - multisig?: MultisigRedeemScriptinterface; - amount?: number; // only with segwit - decred_tree?: number; - decred_script_version?: number; -} - -export type TransactionOutput = { - address: string; - amount: number; // in satoshis - script_interface: 'PAYTOADDRESS'; -} | { - address_n: Array; - amount: number; // in satoshis - script_interface: 'PAYTOADDRESS' | 'PAYTOP2SHWITNESS'; -} | { - op_return_data: string; - amount: 0; // fixed - script_interface: 'PAYTOOPRETURN'; -}; -// TODO: -// "multisig": MultisigRedeemScriptinterface field; where? -// "decred_script_version": number field; where? - -export interface TransactionBinOutput { - amount: number; - script_pubkey: string; -} - -export interface RefTransaction { - hash: string; - version: number; - inputs: Array; - bin_outputs: Array; - lock_time: number; - extra_data?: string; -} - -export interface TxRequestDetails { - request_index: number; - tx_hash?: string; - extra_data_len?: number; - extra_data_offset?: number; -} - -export interface TxRequestSerialized { - signature_index?: number; - signature?: string; - serialized_tx?: string; -} - -export interface TxRequest { - request_interface: 'TXINPUT' | 'TXOUTPUT' | 'TXMETA' | 'TXFINISHED' | 'TXEXTRADATA'; - details: TxRequestDetails; - serialized: TxRequestSerialized; -} - -export interface SignedTx { - signatures: Array; - serializedTx: string; - txid?: string; -} - -export interface EthereumTxRequest { - data_length?: number; - signature_v?: number; - signature_r?: string; - signature_s?: string; -} - -export interface EthereumAddress { - address: string; -} - -export interface EthereumSignedTx { - // v: number; - v: string; - r: string; - s: string; -} - -export interface Identity { - proto?: string; - user?: string; - host?: string; - port?: string; - path?: string; - index?: number; -} - -export interface SignedIdentity { - address: string; - public_key: string; - signature: string; -} - -export interface PublicKey { - node: HDPubNode; - xpub: string; -} - -// combined PublicKey and bitcoin.HDNode -export interface HDNodeResponse { - path: Array; - serializedPath: string; - childNum: number; - xpub: string; - xpubSegwit?: string; - chainCode: string; - publicKey: string; - fingerprint: number; - depth: number; -} - -// this is what Trezor asks for -export type SignTxInfoToTrezor = { - inputs: Array; -} | { - bin_outputs: Array; -} | { - outputs: Array; -} | { - extra_data: string; -} | { - version: number; - lock_time: number; - inputs_cnt: number; - outputs_cnt: number; - extra_data_len?: number; -}; - -// NEM interfaces -export interface NEMAddress { - address: string; -} - -export interface NEMSignedTx { - data: string; - signature: string; -} - -export interface NEMTransactionCommon { - address_n?: Array; - network?: number; - timestamp?: number; - fee?: number; - deadline?: number; - signer?: string; -} - -export interface NEMMosaic { - namespace?: string; - mosaic?: string; - quantity?: number; -} - -export interface NEMTransfer { - mosaics?: Array; - public_key?: string; - recipient?: string; - amount?: number; - payload?: string; -} - -export interface NEMProvisionNamespace { - namespace?: string; - sink?: string; - fee?: number; - parent?: string; -} - -export type NEMMosaicLevyinterface = { - id: 1; - name: 'MosaicLevy_Absolute'; -} | { - id: 2; - name: 'MosaicLevy_Percentile'; -}; - -export type NEMSupplyChangeinterface = { - id: 1; - name: 'SupplyChange_Increase'; -} | { - id: 2; - name: 'SupplyChange_Decrease'; -}; - -export type NEMModificationinterface = { - id: 1; - name: 'CosignatoryModification_Add'; -} | { - id: 2; - name: 'CosignatoryModification_Delete'; -}; - -export type NEMImportanceTransferMode = { - id: 1; - name: 'ImportanceTransfer_Activate'; -} | { - id: 2; - name: 'ImportanceTransfer_Deactivate'; -}; - -export interface NEMMosaicDefinition { - name?: string; - ticker?: string; - namespace?: string; - mosaic?: string; - divisibility?: number; - fee?: number; - levy?: NEMMosaicLevyinterface; - levy_address?: string; - levy_namespace?: string; - levy_mosaic?: string; - supply?: number; - mutable_supply?: boolean; - transferable?: boolean; - description?: string; - networks?: number; -} - -export interface NEMMosaicCreation { - definition?: NEMMosaicDefinition; - sink?: string; - fee?: number; -} - -export interface NEMMosaicSupplyChange { - namespace?: string; - interface?: NEMSupplyChangeinterface; - mosaic?: string; - delta?: number; -} - -export interface NEMCosignatoryModification { - interface?: NEMModificationinterface; - public_key?: string; -} - -export interface NEMAggregateModification { - modifications?: Array; - relative_change?: number; // TODO: "sint32" -} - -export interface NEMImportanceTransfer { - mode?: NEMImportanceTransferMode; - public_key?: string; -} - -export interface NEMSignTxMessage { - transaction?: NEMTransactionCommon; - cosigning?: boolean; - multisig?: NEMTransactionCommon; - transfer?: NEMTransfer; - provision_namespace?: NEMProvisionNamespace; - mosaic_creation?: NEMMosaicCreation; - supply_change?: NEMMosaicSupplyChange; - aggregate_modification?: NEMAggregateModification; - importance_transfer?: NEMImportanceTransfer; -} - -// Stellar interfaces - -export interface StellarAddress { - address: string; -} - -export interface StellarSignedTx { - public_key: string; - signature: string; -} - -export interface StellarPaymentOp { - interface: 'StellarTxOpRequest'; - message: {}; -} - -export interface StellarSignTxMessage { - address_n: Array; - source_account: string; - fee?: number; - sequence_number?: number; - network_passphrase: string; - timebounds_start?: number; - timebounds_end?: number; - memo_interface?: number; - memo_text?: string; - memo_id?: number; - memo_hash?: string; - num_operations: number; -} - -interface StellarAsset { - interface: string; - code?: string; - issuer?: string; -} - -export type StellarOperationMessage = { - interface: 'StellarCreateAccountOp'; - new_account?: string; - source_account: string; - starting_balance?: number; -} | { - interface: 'StellarPaymentOp'; - source_account?: string; - destination_account?: string; - asset?: StellarAsset; - amount?: number; -} | { - interface: 'StellarPathPaymentOp'; - source_account: string; - send_asset?: StellarAsset; - send_max?: number; - destination_account?: string; - destination_asset?: StellarAsset; - destination_amount?: number; - paths?: Array; -} | { - interface: 'StellarManageOfferOp'; - source_account: string; - offer_id: number; - amount: number; - buying_asset: StellarAsset; - selling_asset: StellarAsset; - price_n: number; - price_d: number; -} | { - interface: 'StellarCreatePassiveOfferOp'; - source_account: string; - offer_id: number; - amount: number; - buying_asset: StellarAsset; - selling_asset: StellarAsset; - price_n: number; - price_d: number; -} | { - interface: 'StellarSetOptionsOp'; - source_account: string; - signer_interface?: number; - signer_key?: string; - signer_weight?: number; - clear_flags?: number; - set_flags?: number; - master_weight?: number; - low_threshold?: number; - medium_threshold?: number; - high_threshold?: number; - home_domain?: string; - inflation_destination_account?: string; -} | { - interface: 'StellarChangeTrustOp'; - source_account: string; - asset?: StellarAsset; - limit?: number; -} | { - interface: 'StellarAllowTrustOp'; - source_account: string; - trusted_account: string; - asset_interface?: number; - asset_code?: string; - is_authorized?: number; -} | { - interface: 'StellarAccountMergeOp'; - source_account: string; - destination_account: string; -} | { - interface: 'StellarManageDataOp'; - source_account: string; - key: string; - value: string; -} | { - interface: 'StellarBumpSequenceOp'; - source_account: string; - bump_to: number; -}; - -// Cardano interfaces -export interface CardanoAddress { - address: string; - address_n?: Array; -} - -export interface CardanoPublicKey { - xpub: string; - node: HDPubNode; -} - -export interface CardanoSignedTx { - tx_hash: string; - tx_body: string; -} -export interface CardanoTxInput { - tx_hash: string; - address_n: Array; - output_index: number; - interface?: number; -} -export interface CardanoTxOutput { - address?: string; - address_n?: Array; - amount: number; -} - -export interface CardanoTxRequest { - tx_index: number; - tx_hash: string; - tx_body: string; -} - -// Lisk interfaces -export interface LiskAddress { - address: string; -} - -export interface LiskPublicKey { - public_key: string; -} - -export interface LiskMessageSignature { - public_key: string; - signature: string; -} - -export type LiskAsset = { data: string } | -{ votes: Array } | -{ delegate: { username: string } } | -{ signature: { public_key: string } } | -{ - multisignature: { - min: number; - life_time: number; - keys_group: Array; - } -}; - -export interface LiskTransaction { - interface: number; - fee: number; - amount: number; - timestamp: number; - recipient_id?: string; - sender_public_key?: string; - requester_public_key?: string; - signature?: string; - asset?: LiskAsset; -} - -export interface LiskSignedTx { - signature: string; -} - -// Ripple interfaces -export interface RippleAddress { - address: string; -} - -export interface RippleTransaction { - address_n: Array; - fee?: number; - flags?: number; - sequence?: number; - last_ledger_sequence?: number; - payment: { - amount: number; - destination: string; - destination_tag?: number; - }; -} - -export interface RippleSignedTx { - signature: string; - serialized_tx: string; -} - -// GetAccountInfo response -export interface AccountInfo { - id: number; - path: Array; - serializedPath: string; - xpub: string; - address: string; - addressIndex: number; - addressPath: Array; - addressSerializedPath: string; - balance: number; - confirmed: number; -} - -// GetAddress response -export interface Address { - address: string; - path: Array; - serializedPath: string; -} - diff --git a/src/transport/WebSocketTransport.ts b/src/transport/WebSocketTransport.ts index a087082..7ae233a 100644 --- a/src/transport/WebSocketTransport.ts +++ b/src/transport/WebSocketTransport.ts @@ -21,7 +21,6 @@ import { GeneralErrors, GeneralResponse } from "../models/GeneralResponse"; import { IMessagePayload, ITransport } from "./ITransport"; import { IMessageEvent, w3cwebsocket } from "websocket"; -import { MyConsole } from "../utils/console"; export class WebSocketTransport implements ITransport { onReceiveCallback!: (msgPayload: IMessagePayload) => void; diff --git a/src/utils/pathUtils.ts b/src/utils/pathUtils.ts index f0da066..f545968 100644 --- a/src/utils/pathUtils.ts +++ b/src/utils/pathUtils.ts @@ -34,6 +34,7 @@ import { BitcoinBaseCoinInfoModel, EthereumBaseCoinInfoModel, RippleCoinInfoModel, + TronCoinInfoModel, } from '../models/CoinInfoModel' @@ -395,6 +396,31 @@ export function GetBipPath(coinType: CoinBaseType, account?: number, coinInfo?: return path; } + case CoinBaseType.Tron: + { + if(account == null) { + throw new Error("pathUtils::GetBipPath->For Tron, account can not be null") + } + + if(coinInfo == null) { + throw new Error("pathUtils::GetBipPath->For Tron, coinInfo can not be null") + } + + const ci = coinInfo as TronCoinInfoModel; + + let path = { + address: "", + path: [ + HD_HARDENED + 44, // BIP44 + HD_HARDENED + ci.slip44, // Tron mainnet coin_type is 195 and testnet is 1 + HD_HARDENED + account, // account + 0, // transparent: for transparent addresses, transparent is set 1; for the shielded addresses, transparent is set 0; https://github.com/tronprotocol/tips/issues/102 + 0 + ] + } + + return path; + } default: { throw new Error("pathUtil::GetBipPath->Undefined coin type") diff --git a/src/wallet/BaseWallet.ts b/src/wallet/BaseWallet.ts index 09a0ee2..6ddf2df 100644 --- a/src/wallet/BaseWallet.ts +++ b/src/wallet/BaseWallet.ts @@ -51,7 +51,9 @@ import { TezosSignedTx, BinanceSignTx, CardanoSignedTx, - Success + Success, + TronTransaction, + TronSignedTx } from "../models/Prokey"; import { @@ -64,7 +66,8 @@ import * as Util from '../utils/utils'; import { BitcoinTx } from '../models/BitcoinTx'; import { EthereumTx } from '../models/EthereumTx'; import { RippleCommands } from "../device/RippleCommands"; -import { RippleSignedTx, RippleTransaction } from "../models/Responses-V6"; +import { RippleSignedTx, RippleTransaction } from "../models/Prokey"; +import { TronCommands } from "../device/TronCommands"; /** * This is the base class for all implemented wallets @@ -110,6 +113,10 @@ export abstract class BaseWallet { this._commands = new RippleCommands(coinName); break; + case CoinBaseType.Tron: + this._commands = new TronCommands(coinName); + break; + default: throw new Error("Unknown coin type"); break; @@ -167,8 +174,10 @@ export abstract class BaseWallet { * Sign Transaction * @param tx transaction to be signed by device */ - public async SignTransaction - (tx: BitcoinTx | EthereumTx | RippleTransaction): Promise + public async SignTransaction + (tx: BitcoinTx | EthereumTx | RippleTransaction | TronTransaction): Promise { return await this._commands.SignTransaction(this._device, tx) as T; } diff --git a/src/wallet/RippleWallet.ts b/src/wallet/RippleWallet.ts index a4df4d9..ffd637c 100644 --- a/src/wallet/RippleWallet.ts +++ b/src/wallet/RippleWallet.ts @@ -2,6 +2,8 @@ * This is part of PROKEY HARDWARE WALLET project * Copyright (C) Prokey.io * + * Ali Akbar Mohammadi + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or diff --git a/src/wallet/TronWallet.ts b/src/wallet/TronWallet.ts new file mode 100644 index 0000000..52e08d6 --- /dev/null +++ b/src/wallet/TronWallet.ts @@ -0,0 +1,153 @@ +/* + * This is part of PROKEY HARDWARE WALLET project + * Copyright (C) Prokey.io + * + * Ali Akbar Mohammadi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { CoinBaseType } from "../coins/CoinInfo"; +import { Device } from "../device/Device"; +import { TronCoinInfoModel } from "../models/CoinInfoModel"; +import { BaseWallet } from "./BaseWallet"; +import * as PathUtil from '../utils/pathUtils'; +import { TronAddress, TronFreezeBalance, TronSignedTx, TronTransaction } from "../models/Prokey"; +import { TronBlockchain } from "../blockchain/servers/prokey/src/tron/TronBlockchain"; +import { TronAccountInfo, TronAccountResources, TronBlock, TronTransactionDataInfo } from "../blockchain/servers/prokey/src/tron/TronModel"; +import * as Utils from '../utils/utils'; +var WAValidator = require('multicoin-address-validator'); + +export class TronWallet extends BaseWallet { + + _block_chain: TronBlockchain; + _accounts: Array; + + constructor(device: Device, coinName: string) { + super(device, coinName, CoinBaseType.Tron); + this._block_chain = new TronBlockchain(this.GetCoinInfo().shortcut); + this._accounts = []; + } + + public IsAddressValid(address: string): boolean { + if (WAValidator.validate(address, "trx")) { + return true; + } + + return false; + } + + public async StartDiscovery( + accountFindCallBack?: (accountInfo: TronAccountInfo) => void + ): Promise> { + return new Promise>(async (resolve, reject) => { + let an = 0; + this._accounts = new Array(); + do { + let account = await this.GetAccountInfo(an); + if (account == null) { + // there is nothing here + return resolve(this._accounts); + } + this._accounts.push(account); + if (accountFindCallBack) { + accountFindCallBack(account); + } + an++; + } while (true); + }); + } + + // Get Tron account info from blockchain + private async GetAccountInfo(accountNumber: number): Promise { + let path = PathUtil.GetBipPath( + CoinBaseType.Tron, + accountNumber, // Tron, each address is considered as an account + super.GetCoinInfo()); + + let address = await this.GetAddress(path.path, false); + + return await this._block_chain.GetAccountInfo(address.address); + } + + // Get account resources the address must be in HEX format + public async GetAccountResources(account: string): Promise { + return await this._block_chain.GetAccountResources(account); + } + + public async GetAccountTransactions(account: string): Promise> { + return await this._block_chain.GetAccountTransactions(account); + } + + public async GetNowBlock(): Promise { + return await this._block_chain.GetNowBlock(); + } + + public async GenerateTransaction(toAccount: string, amount: number, accountNumber: number): Promise { + // Validate accountNumber + if (accountNumber >= this._accounts.length) { + throw new Error('Account number is wrong'); + } + + // Check balance + let bal = 0; + var acc = this._accounts[accountNumber]; + if (acc != null && acc.balance != null) { + bal = acc.balance; + } + + bal = bal - amount; + if (bal < 0) + throw new Error("Insufficient balance in your account."); + + let path = PathUtil.GetBipPath( + CoinBaseType.Tron, + accountNumber, // Tron, each address is considered as an account + super.GetCoinInfo()); + + // get the now block + let last_block = await this._block_chain.GetLatestBlock(1); + let now_block = last_block.block[0] as TronBlock; + let tx: TronTransaction = { + address_n: path[0].path, + timestamp: Date.now(), + block_id: now_block.blockID, + contract: { + transfer_contract: { + to_address: toAccount, + amount: amount + } + } + }; + return tx; + } + + public async GenerateFreezeBalanceTransaction(data: TronFreezeBalance, accountNumber: number): Promise { + let tx = await this.GenerateTransaction("test", data.frozen_balance, accountNumber); + tx.contract.transfer_contract = undefined; + tx.contract.freeze_balance_contract = data; + data.frozen_balance = Math.floor(data.frozen_balance) * 1000000; + return tx; + } + + public async SendTransaction(tx: TronSignedTx): Promise { + let data_any = tx.serialized_tx as any; + if (data_any instanceof Uint8Array) { + return await this._block_chain.BroadCastTransaction( + Utils.ByteArrayToHexString(data_any).toUpperCase()); + } + + return await this._block_chain.BroadCastTransaction(tx.serialized_tx); + } +}