From b2671536cb01dff10fb00f351f770f3d7113d3e2 Mon Sep 17 00:00:00 2001 From: Rhys Bartels-Waller Date: Thu, 30 Jul 2020 02:51:22 +1000 Subject: [PATCH] [DDW-254] Address validation upgrade to use cardano-addresses CLI via IPC (#2041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP address introspection via IPC, using cardano-addresses CLI tool executued as a child process * Address introspection via IPC, using cardano-addresses CLI tool executed in a child process * Hoist stake_reference to AddressBase * [DDW-254] Integrate latest cardano js and update address validation logic - updates * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * cardano-address: add to daedalus bridge (cherry picked from commit e6efdaed72da551f7efd4cbcecec6aeb8928d7f0) * add cardanoAddressBin and autocompletions to shell (cherry picked from commit f47fa5085fffc9c18657a36b23ddff63838cc48c) * daedalus-bridge: fix errors copying files that exist * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW] Bump cardano-wallet revision * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - updates * cardano-wallet: bump for new cardano-address fix * Reject non validation errors from introspect-address onReceieve. This does not include client-side handling * Bump cardano-wallet rev * Bump cardano-wallet rev via nix-shell -A devops --run "niv update cardano-wallet * Wallet row fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * fix(address-introspection): refactor to spawning a child process Rather than the single line `exec`, this uses Node.js cross-platform API to pass stdin * fix(address-introspection): bump cardano-wallet * fix(address-introspection): bump cardano-wallet, switch to spawnSync removes the async complexity to produce a reliable and easy to reason about implementation. Given the nature of the operation, this is a fair use of the subprocess executor. Also adds renderer error handler to catch and log non-validation errors * fix(address-introspection): Check for empty buffer to detect error, generalise to user error matching * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * Bump cardano-wallet * Add cardano-address to the Jormungandr bridge * fix(address-introspection): Shelley testnet address network discrimination removal * [DDW-254] Integrate latest cardano js and update address validation logic - changelog * [DDW-254] Integrate latest cardano js and update address validation logic - fixes for Shelley * [DDW-254] Integrate latest cardano js and update address validation logic - fixes for Shelley testnet * [DDW-254] Integrate latest cardano js and update address validation logic - fixes for Shelley testnet * Apply valid mainnet_candidate_4 network ID to "Shelley Testnet" * [DDW-254] Integrate latest cardano js and update address validation logic - fixes for Shelley testnet * Bump cardano-wallet * DDW-296 Deadalus review fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Integrate latest cardano js and update address validation logic - fixes * [DDW-254] Prevent connecting issue report while blokchaing verification is is progress * DDW-296 Deadalus review fixes * Run translation manager * Fix flow error * Fix flow issues * Fix explorer URLs on Mainnet * BUmp Daedalus version * [DDW-254] Fix flow issue Co-authored-by: Aleksandar Djordjevic Co-authored-by: Samuel Leathers Co-authored-by: Nikola Glumac Co-authored-by: Tomislav Horaček --- CHANGELOG.md | 3 +- nix/jormungandr-bridge.nix | 3 +- nix/sources.json | 6 +- package.json | 2 +- source/common/ipc/api.js | 11 ++++ .../types/address-introspection.types.js | 54 ++++++++++++++++ source/common/types/cardano-node.types.js | 18 ++++++ source/main/cardano/utils.js | 4 +- source/main/config.js | 3 - source/main/ipc/index.js | 2 + source/main/ipc/introspect-address.js | 35 +++++++++++ .../syncing-connecting/SyncingConnecting.js | 13 ++-- .../app/components/wallet/WalletSendForm.js | 2 +- .../app/i18n/locales/defaultMessages.json | 6 +- source/renderer/app/i18n/locales/en-US.json | 1 - source/renderer/app/i18n/locales/ja-JP.json | 1 - source/renderer/app/ipc/introspect-address.js | 13 ++++ source/renderer/app/stores/WalletsStore.js | 62 ++++++++++++++----- source/renderer/app/utils/network.js | 8 ++- .../wallets/_utils/defaultWalletProps.js | 1 + 20 files changed, 208 insertions(+), 40 deletions(-) create mode 100644 source/common/types/address-introspection.types.js create mode 100644 source/main/ipc/introspect-address.js create mode 100644 source/renderer/app/ipc/introspect-address.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fb2834d99..e3388371b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ Changelog ========= -## vNext +## 2.0.0 ### Features +- Implemented address validation ([PR 2041](https://github.com/input-output-hk/daedalus/pull/2041)) - Show block replay progress ([PR 2110](https://github.com/input-output-hk/daedalus/pull/2110)) ### Fixes diff --git a/nix/jormungandr-bridge.nix b/nix/jormungandr-bridge.nix index d64c511363..fd0f5f82b1 100644 --- a/nix/jormungandr-bridge.nix +++ b/nix/jormungandr-bridge.nix @@ -1,4 +1,4 @@ -{ target, pkgs, cardano-wallet, cardano-shell, sources, jormungandrLib }: +{ target, pkgs, cardano-wallet, cardano-shell, sources, jormungandrLib, cardano-address }: let commonLib = import ../lib.nix {}; @@ -14,6 +14,7 @@ in pkgs.runCommandCC "daedalus-bridge" { cp -f ${cardano-wallet.haskellPackages.cardano-wallet-jormungandr.components.exes.cardano-wallet-jormungandr}/bin/* . cp -f ${cardano-shell.haskellPackages.cardano-launcher.components.exes.cardano-launcher}/bin/cardano-launcher* . cp -f ${cardano-wallet.jormungandr}/bin/* . + cp -f ${cardano-address}/bin/cardano-address* . echo ${cardano-wallet.version} > $out/version diff --git a/nix/sources.json b/nix/sources.json index cb05abb4e6..463c876eba 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -29,10 +29,10 @@ "homepage": null, "owner": "input-output-hk", "repo": "cardano-wallet", - "rev": "ca96c435df4ea4f2aefc98a75ae668d5f709da56", - "sha256": "1mnnlg1x3y9cf3sqmxpqjdiwlay58pdci4cjxfvlwlyqqlsy5d1i", + "rev": "1c7041e5d396dad6871d3fb9d9989cdb2fb32b1a", + "sha256": "144iyq4nlsbvbbaaajqaz6gz96xmmmvmz1qbjirbxih3jmfyyqny", "type": "tarball", - "url": "https://github.com/input-output-hk/cardano-wallet/archive/ca96c435df4ea4f2aefc98a75ae668d5f709da56.tar.gz", + "url": "https://github.com/input-output-hk/cardano-wallet/archive/1c7041e5d396dad6871d3fb9d9989cdb2fb32b1a.tar.gz", "url_template": "https://github.com///archive/.tar.gz", "version": "v2020-04-28" }, diff --git a/package.json b/package.json index aae4fea5a2..84a88b9aad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "daedalus", "productName": "Daedalus", - "version": "2.0.0-RC1", + "version": "2.0.0", "description": "Cryptocurrency Wallet", "main": "./dist/main/index.js", "scripts": { diff --git a/source/common/ipc/api.js b/source/common/ipc/api.js index d024bf8d68..6bd6d82d8d 100644 --- a/source/common/ipc/api.js +++ b/source/common/ipc/api.js @@ -38,6 +38,10 @@ import type { ResumeDownloadResponse, } from '../types/downloadManager.types'; import type { StoreMessage } from '../types/electron-store.types'; +import type { + IntrospectAddressRequest, + IntrospectAddressResponse, +} from '../types/address-introspection.types'; /** * ======================= IPC CHANNELS API ========================= @@ -321,6 +325,13 @@ export const RESUME_DOWNLOAD = 'RESUME_DOWNLOAD'; export type ResumeDownloadRendererRequest = ResumeDownloadRequest; export type ResumeDownloadMainResponse = ResumeDownloadResponse | void; +/** + * Channel for introspecting an address + */ +export const INTROSPECT_ADDRESS_CHANNEL = 'INTROSPECT_ADDRESS_CHANNEL'; +export type IntrospectAddressRendererRequest = IntrospectAddressRequest; +export type IntrospectAddressMainResponse = IntrospectAddressResponse; + /** * Channel for checking block replay progress */ diff --git a/source/common/types/address-introspection.types.js b/source/common/types/address-introspection.types.js new file mode 100644 index 0000000000..80b45568dd --- /dev/null +++ b/source/common/types/address-introspection.types.js @@ -0,0 +1,54 @@ +// @flow + +export type IntrospectAddressRequest = { + input: string, +}; + +export type AddressStyle = 'Byron' | 'Icarus' | 'Jormungandr' | 'Shelley'; + +export type ChainPointer = { + slot_num: number, + transaction_index: number, + output_index: number, +}; + +export type AddressBase = { + address_style: AddressStyle, + network_tag: number | null, + stake_reference: 'none' | 'by pointer' | 'by value', +}; + +export type ByronAddress = AddressBase & { + address_root: string, + derivation_path: string, +}; + +export type IcarusAddress = AddressBase & { + address_root: string, +}; + +export type JormungandrAddress = AddressBase & { + address_type: 'single' | 'group' | 'account' | 'multisig', + account_key?: string, + merkle_root?: string, + spending_key?: string, + stake_key?: string, +}; + +export type ShelleyAddress = AddressBase & { + pointer?: ChainPointer, + script_hash?: string, + spending_key_hash?: string, + stake_key_hash?: string, + stake_script_hash?: string, +}; + +export type IntrospectAddressResponse = + | { + introspection: + | ByronAddress + | IcarusAddress + | JormungandrAddress + | ShelleyAddress, + } + | 'Invalid'; diff --git a/source/common/types/cardano-node.types.js b/source/common/types/cardano-node.types.js index be832c027f..6ff255d174 100644 --- a/source/common/types/cardano-node.types.js +++ b/source/common/types/cardano-node.types.js @@ -134,3 +134,21 @@ export type CardanoStatus = { cardanoNodePID: number, cardanoWalletPID: number, }; + +// Cardano Mainet network magic +export const MAINNET_MAGIC = [1, null]; + +// Cardano Byron Testnet network magic +export const TESTNET_MAGIC = [1097911063, 0]; + +// Cardano Byron Testnet network magic +export const ITN_MAGIC = 1; + +// Cardano Mainnet Candidate during the Shelley HF +export const SHELLEY_TESTNET_NETWORK_ID = [1, null]; + +// Cardano Staging network magic +export const STAGING_MAGIC = 633343913; + +// Cardano Selfnode network magic +export const SELFNODE_MAGIC = 459045235; diff --git a/source/main/cardano/utils.js b/source/main/cardano/utils.js index b21f356612..0972dcb7d9 100644 --- a/source/main/cardano/utils.js +++ b/source/main/cardano/utils.js @@ -4,7 +4,6 @@ import path from 'path'; import { BrowserWindow, dialog } from 'electron'; import { spawnSync } from 'child_process'; import { logger } from '../utils/logging'; -import { TESTNET_MAGIC } from '../config'; import { getTranslation } from '../utils/getTranslation'; import ensureDirectoryExists from '../utils/ensureDirectoryExists'; import type { LauncherConfig } from '../config'; @@ -20,6 +19,7 @@ import { CardanoProcessNameOptions, CardanoNodeImplementationOptions, NetworkNameOptions, + TESTNET_MAGIC, } from '../../common/types/cardano-node.types'; export type Process = { @@ -231,7 +231,7 @@ export const exportWallets = async ( // Cluster flags if (cluster === 'testnet') { - exportWalletsBinFlags.push('--testnet', TESTNET_MAGIC); + exportWalletsBinFlags.push('--testnet', TESTNET_MAGIC.toString()); } else { exportWalletsBinFlags.push('--mainnet'); } diff --git a/source/main/config.js b/source/main/config.js index 5d5f0e7595..4cd3279efd 100644 --- a/source/main/config.js +++ b/source/main/config.js @@ -172,6 +172,3 @@ export const STAKE_POOL_REGISTRY_URL = { qa: 'https://explorer.qa.jormungandr-testnet.iohkdev.io/stakepool-registry/registry.zip', }; - -// Cardano Byron Testnet network magic -export const TESTNET_MAGIC = '1097911063'; diff --git a/source/main/ipc/index.js b/source/main/ipc/index.js index 66b77548f2..ae9aa29662 100644 --- a/source/main/ipc/index.js +++ b/source/main/ipc/index.js @@ -15,6 +15,7 @@ import { handlePaperWalletRequests } from './generatePaperWalletChannel'; import { handleAddressPDFRequests } from './generateAddressPDFChannel'; import { handleRewardsCsvRequests } from './generateRewardsCsvChannel'; import { handleFileDialogRequests } from './show-file-dialog-channels'; +import { handleAddressIntrospectionRequests } from './introspect-address'; import { openExternalUrlChannel } from './open-external-url'; import { openLocalDirectoryChannel } from './open-local-directory'; @@ -31,6 +32,7 @@ export default (window: BrowserWindow) => { handleAddressPDFRequests(); handleRewardsCsvRequests(); handleFileDialogRequests(window); + handleAddressIntrospectionRequests(); // eslint-disable-next-line no-unused-expressions openExternalUrlChannel; // eslint-disable-next-line no-unused-expressions diff --git a/source/main/ipc/introspect-address.js b/source/main/ipc/introspect-address.js new file mode 100644 index 0000000000..7d4d832c77 --- /dev/null +++ b/source/main/ipc/introspect-address.js @@ -0,0 +1,35 @@ +// @flow +import { spawnSync } from 'child_process'; +import { MainIpcChannel } from './lib/MainIpcChannel'; +import { INTROSPECT_ADDRESS_CHANNEL } from '../../common/ipc/api'; +import type { + IntrospectAddressRendererRequest, + IntrospectAddressMainResponse, +} from '../../common/ipc/api'; + +// IpcChannel + +export const introspectAddressChannel: MainIpcChannel< + IntrospectAddressRendererRequest, + IntrospectAddressMainResponse +> = new MainIpcChannel(INTROSPECT_ADDRESS_CHANNEL); + +export const handleAddressIntrospectionRequests = () => { + introspectAddressChannel.onReceive( + ({ input }: IntrospectAddressRendererRequest) => + new Promise((resolve, reject) => { + const { stdout, stderr } = spawnSync( + 'cardano-address', + ['address', 'inspect'], + { input } + ); + if (stderr.toString() !== '') { + if (stderr.toString().match(/user error/g) !== null) { + return resolve('Invalid'); + } + return reject(new Error(stderr.toString())); + } + return resolve({ introspection: JSON.parse(stdout.toString()) }); + }) + ); +}; diff --git a/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.js b/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.js index 5bb0b0c831..45da17ad0b 100644 --- a/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.js +++ b/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.js @@ -145,14 +145,17 @@ export default class SyncingConnecting extends Component { isNewAppVersionAvailable, isIncentivizedTestnet, forceConnectivityIssue, + isVerifyingBlockchain, } = this.props; const { connectingTime } = this.state; const canReportConnectingIssue = - isSyncProgressStalling || - forceConnectivityIssue || - (!isConnected && - (connectingTime >= REPORT_ISSUE_TIME_TRIGGER || - cardanoNodeState === CardanoNodeStates.UNRECOVERABLE)); + !isVerifyingBlockchain && + (isSyncProgressStalling || + forceConnectivityIssue || + (!isConnected && + (connectingTime >= REPORT_ISSUE_TIME_TRIGGER || + cardanoNodeState === CardanoNodeStates.UNRECOVERABLE))); + if (isFlight || isIncentivizedTestnet || global.isShelleyTestnet) { return canReportConnectingIssue; } diff --git a/source/renderer/app/components/wallet/WalletSendForm.js b/source/renderer/app/components/wallet/WalletSendForm.js index 721b773da5..a0b316fb32 100755 --- a/source/renderer/app/components/wallet/WalletSendForm.js +++ b/source/renderer/app/components/wallet/WalletSendForm.js @@ -190,7 +190,7 @@ export default class WalletSendForm extends Component { const amountField = form.$('amount'); const amountValue = amountField.value.toString(); const isAmountValid = amountField.isValid; - const isValidAddress = this.props.addressValidator(value); + const isValidAddress = await this.props.addressValidator(value); if (isValidAddress && isAmountValid) { await this._calculateTransactionFee(value, amountValue); } else { diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index 0203c3d341..b1b838777e 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -1082,7 +1082,7 @@ "description": "Message \"Verifying the blockchain (65% complete) ...\" on the loading screen.", "end": { "column": 3, - "line": 73 + "line": 74 }, "file": "source/renderer/app/components/loading/syncing-connecting/SyncingConnectingStatus.js", "id": "loading.screen.verifyingBlockchainMessage", @@ -8368,7 +8368,7 @@ } }, { - "defaultMessage": "!!!Please enter your {expectedWordCount}-word wallet recovery phrase. Make sure you enter the words in the correct order.", + "defaultMessage": "!!!Please enter your wallet recovery phrase. Make sure you enter the words in the correct order.", "description": "Label for the recoveryPhraseStep2Description on wallet settings.", "end": { "column": 3, @@ -13912,7 +13912,7 @@ "line": 78 }, "file": "storybook/stories/common/Widgets.stories.js", - "id": "appUpdate.manualUpdateOverlay.button.label", + "id": "manualUpdate.button.label", "start": { "column": 22, "line": 74 diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json index 9bf41a8bec..c1abe0fd80 100755 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -17,7 +17,6 @@ "api.errors.invalidAddress": "Please enter a valid address.", "api.errors.nothingToMigrate": "Funds cannot be transferred from this wallet because it contains some unspent transaction outputs (UTXOs), with amounts of ada that are too small to be migrated.", "api.errors.utxoTooSmall": "Invalid transaction.", - "appUpdate.manualUpdateOverlay.button.label": "!!!Follow instructions and manually update", "automaticUpdate.accept.button.label": "Restart Daedalus and Update", "automaticUpdate.description1": "You are currently running Daedalus {currentAppVersion} and {nextUpdateVersion} is available.", "automaticUpdate.description2": "Would you like to install the update? If choose to postpone, the update will be installed automatically on the next Daedalus launch.", diff --git a/source/renderer/app/i18n/locales/ja-JP.json b/source/renderer/app/i18n/locales/ja-JP.json index ac0efcffa9..c9a9cef915 100755 --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -17,7 +17,6 @@ "api.errors.invalidAddress": "有効なアドレスを入力してください。", "api.errors.nothingToMigrate": "このウォレットに保有されている未使用トランザクションアウトプット(UTXO)の一部に、移行するために十分なADAが入っていないため、このウォレットから資金を移すことはできません。", "api.errors.utxoTooSmall": "無効なトランザクションです。", - "appUpdate.manualUpdateOverlay.button.label": "!!!Follow instructions and manually update", "automaticUpdate.accept.button.label": "Daedalusを再起動して更新する", "automaticUpdate.description1": "現在実行中のDaedalus {currentAppVersion}{nextUpdateVersion}に更新できます。", "automaticUpdate.description2": "今すぐ最新版をインストールしますか。更新を保留すると、Daedalusを次回起動する時に自動的にインストールされます。", diff --git a/source/renderer/app/ipc/introspect-address.js b/source/renderer/app/ipc/introspect-address.js new file mode 100644 index 0000000000..28a4fa8c1a --- /dev/null +++ b/source/renderer/app/ipc/introspect-address.js @@ -0,0 +1,13 @@ +// @flow +import { RendererIpcChannel } from './lib/RendererIpcChannel'; +import { INTROSPECT_ADDRESS_CHANNEL } from '../../../common/ipc/api'; +import type { + IntrospectAddressRendererRequest, + IntrospectAddressMainResponse, +} from '../../../common/ipc/api'; + +// IpcChannel +export const introspectAddressChannel: RendererIpcChannel< + IntrospectAddressMainResponse, + IntrospectAddressRendererRequest +> = new RendererIpcChannel(INTROSPECT_ADDRESS_CHANNEL); diff --git a/source/renderer/app/stores/WalletsStore.js b/source/renderer/app/stores/WalletsStore.js index e9324c926e..ac9fae6b6f 100644 --- a/source/renderer/app/stores/WalletsStore.js +++ b/source/renderer/app/stores/WalletsStore.js @@ -1,10 +1,7 @@ // @flow import { observable, action, computed, runInAction, flow } from 'mobx'; -import { get, find, findIndex, isEqual } from 'lodash'; +import { get, find, findIndex, isEqual, includes } from 'lodash'; import { BigNumber } from 'bignumber.js'; -import { Address } from 'cardano-js'; -import { AddressGroup } from 'cardano-js/dist/Address/AddressGroup'; -import { ChainSettings } from 'cardano-js/dist/ChainSettings'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; import Wallet, { WalletSyncStateStatuses } from '../domains/Wallet'; @@ -17,6 +14,7 @@ import { paperWalletPdfGenerator } from '../utils/paperWalletPdfGenerator'; import { addressPDFGenerator } from '../utils/addressPDFGenerator'; import { downloadRewardsCsv } from '../utils/rewardsCsvGenerator'; import { buildRoute, matchRoute } from '../utils/routing'; +import { logger } from '../utils/logging'; import { ROUTES } from '../routes-config'; import { formattedWalletAmount } from '../utils/formatters'; import { @@ -44,6 +42,17 @@ import type { TransferFundsCalculateFeeRequest, TransferFundsRequest, } from '../api/wallets/types'; +import { introspectAddressChannel } from '../ipc/introspect-address.js'; +import type { AddressStyle } from '../../../common/types/address-introspection.types'; +import { + TESTNET_MAGIC, + SELFNODE_MAGIC, + STAGING_MAGIC, + SHELLEY_TESTNET_NETWORK_ID, + ITN_MAGIC, + MAINNET_MAGIC, +} from '../../../common/types/cardano-node.types'; + /* eslint-disable consistent-return */ /** @@ -873,21 +882,40 @@ export default class WalletsStore extends Store { }); }; - isValidAddress = (address: string) => { - const { app } = this.stores; - const { isMainnet, isStaging, isSelfnode } = app.environment; - if (global.isShelleyTestnet) return true; - const addressGroup = global.isIncentivizedTestnet - ? AddressGroup.jormungandr - : AddressGroup.byron; - const chainSettings = - isMainnet || isStaging ? ChainSettings.mainnet : ChainSettings.testnet; + isValidAddress = async (address: string) => { + const { isIncentivizedTestnet, isShelleyTestnet } = global; + const { isMainnet, isSelfnode, isStaging, isTestnet } = this.environment; + let expectedNetworkTag: ?Array | ?number; + let validAddressStyles: AddressStyle[] = ['Byron', 'Icarus', 'Shelley']; + if (isMainnet) { + expectedNetworkTag = MAINNET_MAGIC; + } else if (isStaging) { + expectedNetworkTag = STAGING_MAGIC; + } else if (isIncentivizedTestnet) { + expectedNetworkTag = ITN_MAGIC; + validAddressStyles = ['Jormungandr']; + } else if (isTestnet) { + expectedNetworkTag = TESTNET_MAGIC; + } else if (isShelleyTestnet) { + expectedNetworkTag = SHELLEY_TESTNET_NETWORK_ID; + } else if (isSelfnode) { + expectedNetworkTag = SELFNODE_MAGIC; + } else { + throw new Error('Unexpected environment'); + } try { - return isSelfnode - ? true // Selfnode address validation is missing in cardano-js - : Address.Util.isAddress(address, chainSettings, addressGroup); + const response = await introspectAddressChannel.send({ input: address }); + if (response === 'Invalid') { + return false; + } + return ( + validAddressStyles.includes(response.introspection.address_style) && + ((Array.isArray(expectedNetworkTag) && + includes(expectedNetworkTag, response.introspection.network_tag)) || + expectedNetworkTag === response.introspection.network_tag) + ); } catch (error) { - return false; + logger.error(error); } }; diff --git a/source/renderer/app/utils/network.js b/source/renderer/app/utils/network.js index 934728b27d..d382884885 100644 --- a/source/renderer/app/utils/network.js +++ b/source/renderer/app/utils/network.js @@ -69,6 +69,7 @@ export const getNetworkExplorerUrl = ( ): string => { const protocol = network === MAINNET || + network === TESTNET || network === DEVELOPMENT || network === ITN_REWARDS_V1 || network === SHELLEY_TESTNET @@ -89,7 +90,12 @@ export const getNetworkExplorerUrlByType = ( let localePrefix = ''; let typeValue = type; - if (network === ITN_REWARDS_V1 || network === SHELLEY_TESTNET) { + if ( + network === MAINNET || + network === TESTNET || + network === ITN_REWARDS_V1 || + network === SHELLEY_TESTNET + ) { localePrefix = `/${currentLocale.substr(0, 2)}`; if (type === 'address') { queryStringPrefix = '?address='; diff --git a/storybook/stories/wallets/_utils/defaultWalletProps.js b/storybook/stories/wallets/_utils/defaultWalletProps.js index 1fb8551f0b..28e274f0a5 100644 --- a/storybook/stories/wallets/_utils/defaultWalletProps.js +++ b/storybook/stories/wallets/_utils/defaultWalletProps.js @@ -30,6 +30,7 @@ export const defaultProps = { onClose={action('onClose')} onContinue={action('onContinue')} wordCount={number('wordCount', 15)} + expectedWordCount={15} /> ), walletRecoveryPhraseStep3Container: (