From 1d09e8a9e5b54c100a9b60ebb325ff4794bff23b Mon Sep 17 00:00:00 2001 From: Juan Cortes Ross Date: Thu, 31 Jan 2019 13:54:50 +0100 Subject: [PATCH 1/2] Add experimental option to export through ws --- README.md | 1 + package.json | 3 ++- src/components/SettingsPage/sections/Export.js | 10 ++++++++++ src/config/constants.js | 1 + yarn.lock | 8 ++++---- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d2632b44..6da07799 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ SKIP_GENUINE=1 SKIP_ONBOARDING=1 SHOW_LEGACY_NEW_ACCOUNT=1 HIGHLIGHT_I18N=1 +EXPERIMENTAL_WS_EXPORT=0 ## constants GET_CALLS_TIMEOUT=30000 diff --git a/package.json b/package.json index e6d49112..3e1d23db 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "i18next": "^11.2.2", "i18next-node-fs-backend": "^1.0.0", "invariant": "^2.2.4", + "ip": "^1.1.5", "jsqr": "^1.1.1", "lodash": "^4.17.5", "lru-cache": "^4.1.3", @@ -111,7 +112,7 @@ "winston": "^3.0.0", "winston-transport": "^4.2.0", "write-file-atomic": "^2.3.0", - "ws": "^5.1.1", + "ws": "^6.1.3", "zxcvbn": "^4.4.2" }, "devDependencies": { diff --git a/src/components/SettingsPage/sections/Export.js b/src/components/SettingsPage/sections/Export.js index 17932932..28cafdbc 100644 --- a/src/components/SettingsPage/sections/Export.js +++ b/src/components/SettingsPage/sections/Export.js @@ -8,6 +8,7 @@ import type { T } from 'types/common' import TrackPage from 'analytics/TrackPage' import styled from 'styled-components' import { SettingsSection as Section, SettingsSectionHeader as Header } from '../SettingsSection' +import { EXPERIMENTAL_WS_EXPORT } from '../../../config/constants' import IconShare from '../../../icons/Share' import Button from '../../base/Button' import Modal, { ModalBody, ModalContent, ModalFooter, ModalTitle } from '../../base/Modal' @@ -15,6 +16,7 @@ import Box from '../../base/Box' import QRCodeExporter from '../../QRCodeExporter' import { BulletRow } from '../../Onboarding/helperComponents' import Text from '../../base/Text' +import SocketExport from '../SocketExport' const BulletRowIcon = styled(Box).attrs({ ff: 'Rubik|Regular', @@ -139,6 +141,14 @@ class SectionExport extends PureComponent { } /> + {EXPERIMENTAL_WS_EXPORT && ( +
} + title="Experimental websocket local export ⚡" + desc="Generate a pairing code and use it on Ledger Live Mobile" + renderRight={} + /> + )} ) diff --git a/src/config/constants.js b/src/config/constants.js index 8f43698d..04fe9796 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -81,6 +81,7 @@ export const SHOW_LEGACY_NEW_ACCOUNT = boolFromEnv('SHOW_LEGACY_NEW_ACCOUNT') export const SHOW_MOCK_HSMWARNINGS = boolFromEnv('SHOW_MOCK_HSMWARNINGS') export const HIGHLIGHT_I18N = boolFromEnv('HIGHLIGHT_I18N') export const DISABLE_ACTIVITY_INDICATORS = boolFromEnv('DISABLE_ACTIVITY_INDICATORS') +export const EXPERIMENTAL_WS_EXPORT = boolFromEnv('EXPERIMENTAL_WS_EXPORT') export const EXPERIMENTAL_CENTER_MODAL = boolFromEnv('EXPERIMENTAL_CENTER_MODAL') export const EXPERIMENTAL_FIRMWARE_UPDATE = boolFromEnv('EXPERIMENTAL_FIRMWARE_UPDATE') export const EXPERIMENTAL_HTTP_ON_RENDERER = boolFromEnv('EXPERIMENTAL_HTTP_ON_RENDERER') diff --git a/yarn.lock b/yarn.lock index f5c6e48d..85745d44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16809,10 +16809,10 @@ ws@^4.0.0: async-limiter "~1.0.0" safe-buffer "~5.1.0" -ws@^5.1.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.1.tgz#37827a0ba772d072a843c3615b0ad38bcdb354eb" - integrity sha512-2NkHdPKjDBj3CHdnAGNpmlliryKqF+n9MYXX7/wsVC4yqYocKreKNjydPDvT3wShAZnndlM0RytEfTALCDvz7A== +ws@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.3.tgz#d2d2e5f0e3c700ef2de89080ebc0ac6e1bf3a72d" + integrity sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg== dependencies: async-limiter "~1.0.0" From 1422366ad5308f4e1f7601ef08f97e2be1201c58 Mon Sep 17 00:00:00 2001 From: Juan Cortes Ross Date: Thu, 31 Jan 2019 17:28:03 +0100 Subject: [PATCH 2/2] Missing component --- src/components/SettingsPage/SocketExport.js | 94 +++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/components/SettingsPage/SocketExport.js diff --git a/src/components/SettingsPage/SocketExport.js b/src/components/SettingsPage/SocketExport.js new file mode 100644 index 00000000..34b81c5b --- /dev/null +++ b/src/components/SettingsPage/SocketExport.js @@ -0,0 +1,94 @@ +// @flow + +import React, { PureComponent } from 'react' +import WebSocket from 'ws' +import IP from 'ip' +import { createStructuredSelector } from 'reselect' +import { activeAccountsSelector } from 'reducers/accounts' +import { exportSettingsSelector } from 'reducers/settings' +import { encode } from '@ledgerhq/live-common/lib/cross' +import connect from 'react-redux/es/connect/connect' +import Button from '../base/Button' +import QRCode from '../base/QRCode' + +type Props = { + accounts: *, + settings: *, +} + +type State = { + active: boolean, +} + +const mapStateToProps = createStructuredSelector({ + accounts: activeAccountsSelector, + settings: exportSettingsSelector, +}) + +class SocketExport extends PureComponent { + state = { + active: false, + } + + componentWillMount() { + this.resetServer() + } + + componentDidUpdate() { + if (!this.state.active) return + if (!this.server) { + this.resetServer() + } + } + + componentWillUnmount() { + if (this.server) this.server.close() + } + + resetServer = () => { + this.server = new WebSocket.Server({ port: 1234 }) + + const { accounts, settings } = this.props + + const data = encode({ + accounts, + settings, + exporterName: 'desktop', + exporterVersion: __APP_VERSION__, + }) + + // Secret handshake to avoid intruders + this.secret = Math.random() + .toString(36) + .slice(2) + + if (this.server) { + this.server.on('connection', ws => { + ws.on('message', message => { + if (message === this.secret) { + ws.send(data) + ws.close() + this.setState({ active: false }) + this.server = undefined + } + }) + }) + } + } + + secret: string + server: * + canvas = React.createRef() + + render() { + return this.state.active ? ( + + ) : ( + + ) + } +} + +export default connect(mapStateToProps)(SocketExport)