From 50a960f62f26e3839a02ebd43a453dcb729890b9 Mon Sep 17 00:00:00 2001 From: Will O'Beirne Date: Fri, 27 Apr 2018 16:49:17 -0400 Subject: [PATCH] Check in progress --- common/index.tsx | 3 +- electron-app/main/window.ts | 1 + package.json | 37 ++++++++----------- shared/enclave/client/index.ts | 6 +++ .../enclave/server/handlers/getChainCode.ts | 7 ++++ shared/enclave/server/handlers/index.ts | 6 ++- shared/enclave/server/index.ts | 5 ++- shared/enclave/server/wallets/index.ts | 14 +++++++ shared/enclave/server/wallets/keepkey.ts | 9 +++++ shared/enclave/server/wallets/ledger.ts | 27 ++++++++++++++ shared/enclave/server/wallets/trezor.ts | 9 +++++ shared/enclave/types.ts | 35 +++++++++++++----- tsconfig.json | 3 +- webpack_config/webpack.electron-dev.js | 7 +++- webpack_config/webpack.electron-prod.js | 2 + 15 files changed, 134 insertions(+), 37 deletions(-) create mode 100644 shared/enclave/server/handlers/getChainCode.ts create mode 100644 shared/enclave/server/wallets/index.ts create mode 100644 shared/enclave/server/wallets/keepkey.ts create mode 100644 shared/enclave/server/wallets/ledger.ts create mode 100644 shared/enclave/server/wallets/trezor.ts diff --git a/common/index.tsx b/common/index.tsx index 62b8c90d..79eb5059 100644 --- a/common/index.tsx +++ b/common/index.tsx @@ -8,7 +8,7 @@ import { render } from 'react-dom'; import Root from './Root'; import { configuredStore } from './store'; import consoleAdvertisement from './utils/consoleAdvertisement'; -import { registerClient } from 'shared/enclave/client'; +import EnclaveAPI, { registerClient } from 'shared/enclave/client'; const appEl = document.getElementById('app'); @@ -28,4 +28,5 @@ if (process.env.NODE_ENV === 'production') { if (process.env.BUILD_ELECTRON) { registerClient(); + (window as any).EnclaveAPI = EnclaveAPI; } diff --git a/electron-app/main/window.ts b/electron-app/main/window.ts index 6af6309c..ff7460f6 100644 --- a/electron-app/main/window.ts +++ b/electron-app/main/window.ts @@ -4,6 +4,7 @@ import path from 'path'; import MENU from './menu'; import updater from './updater'; import { APP_TITLE } from '../constants'; +import ledger from 'shared/enclave/server/wallets/ledger'; const isDevelopment = process.env.NODE_ENV !== 'production'; // Cached reference, preventing recreations diff --git a/package.json b/package.json index 70b889c0..676ddfd0 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "npm": ">= 5.0.0" }, "dependencies": { + "@ledgerhq/hw-app-eth": "4.7.3", + "@ledgerhq/hw-transport-node-hid": "4.7.6", "@parity/qr-signer": "0.1.1", "babel-polyfill": "6.26.0", "bip39": "2.5.0", @@ -18,8 +20,7 @@ "classnames": "2.2.5", "electron-updater": "2.21.4", "ethereum-blockies-base64": "1.0.1", - "ethereumjs-abi": - "git://github.com/ethereumjs/ethereumjs-abi.git#09c3c48fd3bed143df7fa8f36f6f164205e23796", + "ethereumjs-abi": "git://github.com/ethereumjs/ethereumjs-abi.git#09c3c48fd3bed143df7fa8f36f6f164205e23796", "ethereumjs-tx": "1.3.4", "ethereumjs-util": "5.1.5", "ethereumjs-wallet": "0.6.0", @@ -92,6 +93,7 @@ "css-loader": "0.28.11", "electron": "1.8.4", "electron-builder": "20.8.1", + "electron-rebuild": "1.7.3", "empty": "0.10.1", "enzyme": "3.3.0", "enzyme-adapter-react-16": "1.1.1", @@ -109,6 +111,7 @@ "lint-staged": "7.0.4", "mini-css-extract-plugin": "0.4.0", "minimist": "1.2.0", + "node-hid": "0.7.2", "node-sass": "4.8.3", "nodemon": "1.17.3", "null-loader": "0.1.1", @@ -153,19 +156,13 @@ "prebuild": "check-node-version --package", "build:downloadable": "webpack --mode=production --config webpack_config/webpack.html.js", "prebuild:downloadable": "check-node-version --package", - "build:electron": - "webpack --config webpack_config/webpack.electron-prod.js && node webpack_config/buildElectron.js", - "build:electron:osx": - "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=osx node webpack_config/buildElectron.js", - "build:electron:windows": - "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=windows node webpack_config/buildElectron.js", - "build:electron:linux": - "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=linux node webpack_config/buildElectron.js", + "build:electron": "webpack --config webpack_config/webpack.electron-prod.js && node webpack_config/buildElectron.js", + "build:electron:osx": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=osx node webpack_config/buildElectron.js", + "build:electron:windows": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=windows node webpack_config/buildElectron.js", + "build:electron:linux": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=linux node webpack_config/buildElectron.js", "prebuild:electron": "check-node-version --package", - "jenkins:build:linux": - "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_LINUX node webpack_config/buildElectron.js", - "jenkins:build:mac": - "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_MAC node webpack_config/buildElectron.js", + "jenkins:build:linux": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_LINUX node webpack_config/buildElectron.js", + "jenkins:build:mac": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_MAC node webpack_config/buildElectron.js", "jenkins:upload": "node jenkins/upload", "test:coverage": "jest --config=jest_config/jest.config.json --coverage", "test": "jest --config=jest_config/jest.config.json", @@ -178,18 +175,16 @@ "predev": "check-node-version --package", "dev:https": "HTTPS=true node webpack_config/devServer.js", "predev:https": "check-node-version --package", - "dev:electron": - "concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true node webpack_config/devServer.js' 'webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'", + "dev:electron": "concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true node webpack_config/devServer.js' 'webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'", "tslint": "tslint --project . --exclude common/vendor/**/*", "tscheck": "tsc --noEmit", "start": "npm run dev", "precommit": "lint-staged", - "formatAll": - "find ./common/ -name '*.ts*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override", - "prettier:diff": - "prettier --write --config ./.prettierrc --list-different \"common/**/*.ts\" \"common/**/*.tsx\"", + "formatAll": "find ./common/ -name '*.ts*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override", + "prettier:diff": "prettier --write --config ./.prettierrc --list-different \"common/**/*.ts\" \"common/**/*.tsx\"", "prepush": "npm run tslint && npm run tscheck", - "update:tokens": "ts-node scripts/update-tokens" + "update:tokens": "ts-node scripts/update-tokens", + "postinstall": "electron-rebuild --force" }, "lint-staged": { "*.{ts,tsx}": [ diff --git a/shared/enclave/client/index.ts b/shared/enclave/client/index.ts index ea09ccd0..ffec29dd 100644 --- a/shared/enclave/client/index.ts +++ b/shared/enclave/client/index.ts @@ -3,6 +3,8 @@ import { EnclaveEvents, GetAddressesParams, GetAddressesResponse, + GetChainCodeParams, + GetChainCodeResponse, SignTransactionParams, SignTransactionResponse } from 'shared/enclave/types'; @@ -16,6 +18,10 @@ const api = { return makeRequestExpectingResponse(EnclaveEvents.GET_ADDRESSES, params); }, + getChainCode(params: GetChainCodeParams) { + return makeRequestExpectingResponse(EnclaveEvents.GET_CHAIN_CODE, params); + }, + signTransaction(params: SignTransactionParams) { return makeRequestExpectingResponse( EnclaveEvents.SIGN_TRANSACTION, diff --git a/shared/enclave/server/handlers/getChainCode.ts b/shared/enclave/server/handlers/getChainCode.ts new file mode 100644 index 00000000..f84bdc2e --- /dev/null +++ b/shared/enclave/server/handlers/getChainCode.ts @@ -0,0 +1,7 @@ +import { getWalletLib } from 'shared/enclave/server/wallets'; +import { GetChainCodeParams, GetChainCodeResponse } from 'shared/enclave/types'; + +export default async function(params: GetChainCodeParams): Promise { + const wallet = getWalletLib(params.walletType); + return wallet.getChainCode(params.dpath); +} diff --git a/shared/enclave/server/handlers/index.ts b/shared/enclave/server/handlers/index.ts index 94500a66..ba48f5c2 100644 --- a/shared/enclave/server/handlers/index.ts +++ b/shared/enclave/server/handlers/index.ts @@ -1,9 +1,13 @@ import getAddresses from './getAddresses'; +import getChainCode from './getChainCode'; import signTransaction from './signTransaction'; import { EnclaveEvents, EventParams, EventResponse } from 'shared/enclave/types'; -const handlers: { [key in EnclaveEvents]: (params: EventParams) => EventResponse } = { +const handlers: { + [key in EnclaveEvents]: (params: EventParams) => EventResponse | Promise +} = { [EnclaveEvents.GET_ADDRESSES]: getAddresses, + [EnclaveEvents.GET_CHAIN_CODE]: getChainCode, [EnclaveEvents.SIGN_TRANSACTION]: signTransaction }; diff --git a/shared/enclave/server/index.ts b/shared/enclave/server/index.ts index 04c3d7f9..3d4cb5c6 100644 --- a/shared/enclave/server/index.ts +++ b/shared/enclave/server/index.ts @@ -2,13 +2,14 @@ import handlers from './handlers'; import { isValidEventType } from 'shared/enclave/utils'; import { RpcRequest, RpcEventSuccess, RpcEventFailure, EventResponse } from 'shared/enclave/types'; -function processRequest(req: RpcRequest) { +async function processRequest(req: RpcRequest) { try { - const data = handlers[req.type](req.params); + const data = await handlers[req.type](req.params); if (data) { respondWithPayload(req, data); } } catch (err) { + console.error('Request', req.type, 'failed with error:', err); respondWithError(req, err.toString()); } } diff --git a/shared/enclave/server/wallets/index.ts b/shared/enclave/server/wallets/index.ts new file mode 100644 index 00000000..a7d3483f --- /dev/null +++ b/shared/enclave/server/wallets/index.ts @@ -0,0 +1,14 @@ +import Ledger from './ledger'; +import Trezor from './trezor'; +import KeepKey from './keepkey'; +import { WalletTypes, WalletLib } from 'shared/enclave/types'; + +export const wallets: { [key in WalletTypes]: WalletLib } = { + [WalletTypes.LEDGER]: Ledger, + [WalletTypes.TREZOR]: Trezor, + [WalletTypes.KEEPKEY]: KeepKey +}; + +export function getWalletLib(type: WalletTypes): WalletLib { + return wallets[type]; +} diff --git a/shared/enclave/server/wallets/keepkey.ts b/shared/enclave/server/wallets/keepkey.ts new file mode 100644 index 00000000..be83de82 --- /dev/null +++ b/shared/enclave/server/wallets/keepkey.ts @@ -0,0 +1,9 @@ +import { WalletLib } from 'shared/enclave/types'; + +const KeepKey: WalletLib = { + async getChainCode() { + throw new Error('Not yet supported'); + } +}; + +export default KeepKey; diff --git a/shared/enclave/server/wallets/ledger.ts b/shared/enclave/server/wallets/ledger.ts new file mode 100644 index 00000000..a93dfab8 --- /dev/null +++ b/shared/enclave/server/wallets/ledger.ts @@ -0,0 +1,27 @@ +import { WalletLib } from 'shared/enclave/types'; +import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'; +import LedgerEth from '@ledgerhq/hw-app-eth'; + +async function getEthApp() { + const transport = await TransportNodeHid.create(); + return new LedgerEth(transport); +} + +const Ledger: WalletLib = { + async getChainCode(dpath: string) { + const app = await getEthApp(); + try { + const res = await app.getAddress(dpath); + console.log(res); + return { + publicKey: res.publicKey, + chainCode: res.chainCode + }; + } catch (err) { + console.log('wtf', err); + throw new Error('test'); + } + } +}; + +export default Ledger; diff --git a/shared/enclave/server/wallets/trezor.ts b/shared/enclave/server/wallets/trezor.ts new file mode 100644 index 00000000..d744e8e4 --- /dev/null +++ b/shared/enclave/server/wallets/trezor.ts @@ -0,0 +1,9 @@ +import { WalletLib } from 'shared/enclave/types'; + +const Trezor: WalletLib = { + async getChainCode() { + throw new Error('Not yet supported'); + } +}; + +export default Trezor; diff --git a/shared/enclave/types.ts b/shared/enclave/types.ts index 3f5e7a61..b3b9220d 100644 --- a/shared/enclave/types.ts +++ b/shared/enclave/types.ts @@ -1,18 +1,36 @@ -// All enclave event types +// Enclave enums export enum EnclaveEvents { GET_ADDRESSES = 'get-addresses', + GET_CHAIN_CODE = 'get-chain-code', SIGN_TRANSACTION = 'sign-transaction' } +export enum WalletTypes { + LEDGER = 'ledger', + TREZOR = 'trezor', + KEEPKEY = 'keepkey' +} + // Get Addresses Request export interface GetAddressesParams { - walletType: string; + walletType: WalletTypes; } export interface GetAddressesResponse { addresses: string[]; } +// Get chain code request +export interface GetChainCodeParams { + walletType: WalletTypes; + dpath: string; +} + +export interface GetChainCodeResponse { + publicKey: string; + chainCode: string; +} + // Sign Transaction Request export interface SignTransactionParams { path: string; @@ -26,8 +44,8 @@ export interface SignTransactionResponse { } // All Requests & Responses -export type EventParams = GetAddressesParams | SignTransactionParams; -export type EventResponse = GetAddressesResponse | SignTransactionResponse; +export type EventParams = GetAddressesParams | GetChainCodeParams | SignTransactionParams; +export type EventResponse = GetAddressesResponse | GetChainCodeResponse | SignTransactionResponse; // RPC requests, responses & failures export interface RpcRequest { @@ -53,10 +71,7 @@ export interface RpcEventFailure { export type RpcEvent = RpcEventFailure | RpcEventSuccess; -// No clue -export interface RpcEventServer { - payload: T; - id: number; +// Wallet lib +export interface WalletLib { + getChainCode(dpath: string): Promise; } -export type RpcEventHandler = (event: EnclaveEvents, args: A) => R; -export type MatchingIdHandler = (event: string, args: A) => void; diff --git a/tsconfig.json b/tsconfig.json index f878f0a4..945ebb84 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,8 @@ "noEmitOnError": false, "noUnusedLocals": true, "noUnusedParameters": true, - "noImplicitAny": true + "noImplicitAny": true, + "noImplicitUseStrict": true }, "include": [ "./scripts", diff --git a/webpack_config/webpack.electron-dev.js b/webpack_config/webpack.electron-dev.js index 1ed40851..5e5da3c5 100644 --- a/webpack_config/webpack.electron-dev.js +++ b/webpack_config/webpack.electron-dev.js @@ -29,10 +29,15 @@ const electronConfig = { 'process.env.NODE_ENV': JSON.stringify('development') }), ], + externals: { + 'node-hid': 'commonjs node-hid', + '@ledger/hw-transport-hid': 'commonjs @ledger/hw-transport-hid' + }, node: { __dirname: false, __filename: false - } + }, + devtool: 'eval' }; module.exports = electronConfig; diff --git a/webpack_config/webpack.electron-prod.js b/webpack_config/webpack.electron-prod.js index a0343c6a..9bd8fc85 100644 --- a/webpack_config/webpack.electron-prod.js +++ b/webpack_config/webpack.electron-prod.js @@ -23,4 +23,6 @@ electronConfig.plugins = [ new DelayPlugin(500) ]; +electronConfig.devtool = undefined; + module.exports = [electronConfig, jsConfig];