From 0412030c1b2cc0e724dc2ccdb5d41a73cc9800d5 Mon Sep 17 00:00:00 2001
From: Empowerful <45366397+Empowerful@users.noreply.github.com>
Date: Thu, 25 Apr 2019 17:34:33 -0400
Subject: [PATCH] POC: Configure two Web Wallet applications (Root Document &
Main Process) to coexist.
---
.../package.json | 3 +-
.../src/IPC/Middleware.js | 70 +++
.../src/IPC/index.js | 29 ++
.../src/data/auth/actionTypes.js | 1 +
.../src/data/auth/actions.js | 6 +
.../src/data/auth/sagaRegister.js | 5 +-
.../src/data/auth/sagas.js | 27 +-
.../src/data/auth/sagas.spec.js | 8 +-
.../src/data/logs/reducers.js | 1 +
.../src/data/rootSaga.js | 13 +-
.../src/scenes/Settings/General/index.js | 4 -
.../src/store/index.js | 226 +++++----
.../webpack.config.dev.js | 16 +-
.../src/network/api/http.js | 3 +-
.../src/network/api/index.js | 3 +-
.../src/network/walletApi.js | 13 +-
.../src/redux/kvStore/eth/sagaRegister.js | 4 +-
.../src/redux/kvStore/eth/sagas.js | 38 +-
.../src/redux/kvStore/root/sagas.js | 51 +-
.../src/redux/kvStore/sagaRegister.js | 24 +-
.../src/redux/kvStore/sagas.js | 26 +-
.../src/redux/kvStore/xlm/sagaRegister.js | 4 +-
.../src/redux/kvStore/xlm/sagas.js | 24 +-
.../src/redux/rootSaga.js | 4 +-
.../blockchain-wallet-v4/src/redux/sagas.js | 6 +-
.../src/redux/wallet/actionTypes.js | 1 +
.../src/redux/wallet/actions.js | 3 +
.../src/redux/wallet/sagas.js | 9 +-
.../src/redux/walletSync/middleware.js | 108 ++---
.../src/redux/walletSync/middleware.spec.js | 14 +-
.../blockchain-wallet-v4/src/remote/index.js | 2 +
.../src/types/HDWallet.js | 16 +-
.../src/types/KVStoreEntry.js | 13 +-
.../src/types/Serializer.js | 4 +-
.../blockchain-wallet-v4/src/types/Wallet.js | 75 +--
.../blockchain-wallet-v4/src/utils/eth.js | 24 +-
.../blockchain-wallet-v4/src/utils/xlm.js | 6 +-
main-process/packages/web-microkernel | 1 +
main-process/yarn.lock | 11 +-
.../package.json | 3 +-
.../src/IPC/Exports.js | 61 +++
.../src/IPC/Middleware.js | 135 ++++++
.../src/IPC/index.js | 45 ++
.../src/data/auth/actionTypes.js | 1 +
.../src/data/auth/actions.js | 6 +
.../src/data/auth/sagaRegister.js | 1 +
.../src/data/auth/sagas.js | 61 +--
.../src/data/auth/sagas.spec.js | 8 +-
.../src/data/logs/reducers.js | 1 +
.../src/index.html | 88 +++-
.../src/layouts/Security/Header/index.js | 53 +++
.../src/layouts/Security/Header/index.spec.js | 14 +
.../src/layouts/Security/index.js | 114 +++++
.../AdvancedSecurity/PairingCode/index.js | 89 ++++
.../AdvancedSecurity/WalletId/index.js | 49 ++
.../SecurityCenter/AdvancedSecurity/index.js | 4 +
.../src/scenes/app.js | 3 +-
.../src/store/index.js | 231 +++++----
.../webpack.config.dev.js | 3 +-
.../src/redux/kvStore/root/sagas.js | 4 +-
.../src/redux/kvStore/xlm/sagas.js | 3 +-
.../__snapshots__/walletReducers.spec.js.snap | 86 ++++
.../src/redux/wallet/actionTypes.js | 1 +
.../src/redux/wallet/actions.js | 3 +
.../src/redux/wallet/reducers.js | 32 +-
.../src/redux/wallet/walletReducers.spec.js | 17 +
.../blockchain-wallet-v4/src/remote/index.js | 2 +
.../src/types/Serializer.js | 14 +-
packages/web-microkernel/package.json | 9 +
.../web-microkernel/src/RealmConnection.js | 441 ++++++++++++++++++
.../src/RealmConnection.test.js | 406 ++++++++++++++++
packages/web-microkernel/src/index.js | 4 +
packages/web-microkernel/src/lodash-es | 1 +
.../web-microkernel/src/multiplexRealm.js | 39 ++
.../src/multiplexRealm.test.js | 103 ++++
packages/web-microkernel/wallaby.js | 7 +
packages/web-microkernel/yarn.lock | 8 +
yarn.lock | 11 +-
78 files changed, 2486 insertions(+), 571 deletions(-)
create mode 100644 main-process/packages/blockchain-wallet-v4-frontend/src/IPC/Middleware.js
create mode 100644 main-process/packages/blockchain-wallet-v4-frontend/src/IPC/index.js
create mode 120000 main-process/packages/web-microkernel
create mode 100644 packages/blockchain-wallet-v4-frontend/src/IPC/Exports.js
create mode 100644 packages/blockchain-wallet-v4-frontend/src/IPC/Middleware.js
create mode 100644 packages/blockchain-wallet-v4-frontend/src/IPC/index.js
create mode 100644 packages/blockchain-wallet-v4-frontend/src/layouts/Security/Header/index.js
create mode 100644 packages/blockchain-wallet-v4-frontend/src/layouts/Security/Header/index.spec.js
create mode 100644 packages/blockchain-wallet-v4-frontend/src/layouts/Security/index.js
create mode 100644 packages/blockchain-wallet-v4-frontend/src/scenes/SecurityCenter/AdvancedSecurity/PairingCode/index.js
create mode 100644 packages/blockchain-wallet-v4-frontend/src/scenes/SecurityCenter/AdvancedSecurity/WalletId/index.js
create mode 100644 packages/blockchain-wallet-v4/src/redux/wallet/__snapshots__/walletReducers.spec.js.snap
create mode 100644 packages/web-microkernel/package.json
create mode 100644 packages/web-microkernel/src/RealmConnection.js
create mode 100644 packages/web-microkernel/src/RealmConnection.test.js
create mode 100644 packages/web-microkernel/src/index.js
create mode 120000 packages/web-microkernel/src/lodash-es
create mode 100644 packages/web-microkernel/src/multiplexRealm.js
create mode 100644 packages/web-microkernel/src/multiplexRealm.test.js
create mode 100644 packages/web-microkernel/wallaby.js
create mode 100644 packages/web-microkernel/yarn.lock
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/package.json b/main-process/packages/blockchain-wallet-v4-frontend/package.json
index 9aea4db5df6..f14c3edf4ef 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/package.json
+++ b/main-process/packages/blockchain-wallet-v4-frontend/package.json
@@ -84,10 +84,11 @@
},
"dependencies": {
"@blockchain-com/components": "5.1.1",
- "@ledgerhq/hw-app-eth": "4.26.0-beta.ebeb3540",
"@ledgerhq/hw-app-btc": "4.30.0",
+ "@ledgerhq/hw-app-eth": "4.26.0-beta.ebeb3540",
"@ledgerhq/hw-app-str": "4.26.0-beta.ebeb3540",
"@ledgerhq/hw-transport-u2f": "4.31.0",
+ "@nodeguy/channel": "0.6.5",
"awesome-phonenumber": "2.2.6",
"base-64": "0.1.0",
"bignumber.js": "8.0.1",
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/IPC/Middleware.js b/main-process/packages/blockchain-wallet-v4-frontend/src/IPC/Middleware.js
new file mode 100644
index 00000000000..c8c2f59befe
--- /dev/null
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/IPC/Middleware.js
@@ -0,0 +1,70 @@
+import * as router from 'connected-react-router'
+import * as R from 'ramda'
+
+import * as coreTypes from 'blockchain-wallet-v4/src/redux/actionTypes'
+import * as types from '../data/actionTypes'
+
+const alreadyForwarded = ({ meta }) => meta && meta.forwarded
+
+const dispatchToBoth = ({ rootDocumentDispatch, next }, action) => {
+ if (!alreadyForwarded(action)) {
+ rootDocumentDispatch(action)
+ }
+
+ next(action)
+}
+
+const dispatchToRootDocument = ({ rootDocumentDispatch }, action) => {
+ rootDocumentDispatch(action)
+}
+
+const tag = action => ({
+ ...action,
+ meta: { ...action.meta, forwarded: true }
+})
+
+const handlers = {
+ // This requires the GUID.
+ [coreTypes.data.misc.FETCH_LOGS]: dispatchToRootDocument,
+
+ // This requires the GUID.
+ [coreTypes.settings.FETCH_SETTINGS]: dispatchToRootDocument,
+
+ // Tell the Root Document to merge our wrapper with its own.
+ [coreTypes.wallet.MERGE_WRAPPER]: dispatchToRootDocument,
+
+ // Inform the root document about routing changes so that it can switch which
+ // application is displayed.
+ [router.LOCATION_CHANGE]: dispatchToBoth,
+
+ // Tell the root document to reload itself when we do.
+ [types.auth.LOGOUT]: dispatchToBoth,
+
+ // This requires the GUID.
+ [types.modules.settings.UPDATE_LANGUAGE]: dispatchToRootDocument
+}
+
+export default ({ actionsChannel, rootDocumentDispatch }) => store => {
+ // Now that we have access to the store, dispatch stored actions from the Root
+ // Document to it.
+ actionsChannel.forEach(store.dispatch)
+
+ return next => action => {
+ const { type } = action
+
+ const context = {
+ rootDocumentDispatch: R.pipe(
+ tag,
+ rootDocumentDispatch
+ ),
+ next,
+ store
+ }
+
+ if (type in handlers) {
+ return handlers[type](context, action)
+ } else {
+ return next(action)
+ }
+ }
+}
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/IPC/index.js b/main-process/packages/blockchain-wallet-v4-frontend/src/IPC/index.js
new file mode 100644
index 00000000000..1d566ee98ea
--- /dev/null
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/IPC/index.js
@@ -0,0 +1,29 @@
+import Channel from '@nodeguy/channel'
+
+import { serializer } from 'blockchain-wallet-v4/src/types'
+import Middleware from './Middleware'
+import * as kernel from '../../../web-microkernel/src'
+
+export default async ({ input, output, outputOrigin }) => {
+ // We need to export a function for dispatching actions from the Root
+ // Document before the store is created so use a channel to save them until
+ // the store is ready.
+ const actionsChannel = Channel()
+
+ const connection = await kernel.RealmConnection({
+ exports: { dispatch: actionsChannel.push },
+ input,
+ output,
+ outputOrigin,
+ reviver: serializer.reviver
+ })
+
+ return {
+ connection,
+
+ middleware: Middleware({
+ actionsChannel,
+ rootDocumentDispatch: connection.imports.dispatch
+ })
+ }
+}
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/actionTypes.js b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/actionTypes.js
index 0de985229bb..14e453788a5 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/actionTypes.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/actionTypes.js
@@ -3,6 +3,7 @@ export const DEAUTHORIZE_BROWSER = 'DEAUTHORIZE_BROWSER'
export const LOGIN = 'LOGIN'
export const LOGIN_FAILURE = 'LOGIN_FAILURE'
export const LOGIN_LOADING = 'LOGIN_LOADING'
+export const LOGIN_ROUTINE = 'LOGIN_ROUTINE'
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
export const LOGOUT = 'LOGOUT'
export const LOGOUT_CLEAR_REDUX_STORE = 'LOGOUT_CLEAR_REDUX_STORE'
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/actions.js b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/actions.js
index 63600f6a11a..f1845c62e56 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/actions.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/actions.js
@@ -7,6 +7,12 @@ export const login = (guid, password, code, sharedKey, mobileLogin) => ({
payload: { guid, password, code, sharedKey, mobileLogin }
})
export const loginLoading = () => ({ type: AT.LOGIN_LOADING })
+
+export const loginRoutine = (mobileLogin = false, firstLogin = false) => ({
+ type: AT.LOGIN_ROUTINE,
+ payload: { firstLogin, mobileLogin }
+})
+
export const loginSuccess = () => ({ type: AT.LOGIN_SUCCESS, payload: {} })
export const loginFailure = err => ({
type: AT.LOGIN_FAILURE,
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagaRegister.js b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagaRegister.js
index f45371a6e1a..2144c42a7e3 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagaRegister.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagaRegister.js
@@ -2,12 +2,13 @@ import { takeLatest } from 'redux-saga/effects'
import * as AT from './actionTypes'
import sagas from './sagas'
-export default ({ api, coreSagas }) => {
- const authSagas = sagas({ api, coreSagas })
+export default (...args) => {
+ const authSagas = sagas(...args)
return function* authSaga () {
yield takeLatest(AT.DEAUTHORIZE_BROWSER, authSagas.deauthorizeBrowser)
yield takeLatest(AT.LOGIN, authSagas.login)
+ yield takeLatest(AT.LOGIN_ROUTINE, authSagas.loginRoutineSaga)
yield takeLatest(AT.LOGOUT, authSagas.logout)
yield takeLatest(
AT.LOGOUT_CLEAR_REDUX_STORE,
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.js b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.js
index 9b8247a6e8c..2d556e15c6d 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.js
@@ -28,7 +28,7 @@ export const emailMismatch2faErrorMessage =
export const wrongCaptcha2faErrorMessage = 'Error: Captcha Code Incorrect'
export const wrongAuthCodeErrorMessage = 'Authentication code is incorrect'
-export default ({ api, coreSagas }) => {
+export default ({ api, coreSagas, rootDocument }) => {
const upgradeWallet = function*() {
try {
let password = yield call(promptForSecondPassword)
@@ -92,15 +92,18 @@ export default ({ api, coreSagas }) => {
if (userFlowSupported) yield put(actions.modules.profile.signIn())
}
- const loginRoutineSaga = function*(mobileLogin, firstLogin) {
+ const loginRoutineSaga = function*({ payload: { mobileLogin, firstLogin } }) {
try {
// If needed, the user should upgrade its wallet before being able to open the wallet
const isHdWallet = yield select(selectors.core.wallet.isHdWallet)
if (!isHdWallet) {
yield call(upgradeWalletSaga)
}
- yield put(actions.auth.authenticate())
- yield call(coreSagas.kvStore.root.fetchRoot, askSecondPasswordEnhancer)
+
+ yield call(coreSagas.kvStore.root.fetchRoot, {
+ askSecondPasswordEnhancer
+ })
+
// If there was no ethereum metadata kv store entry, we need to create one and that requires the second password.
yield call(
coreSagas.kvStore.ethereum.fetchMetadataEthereum,
@@ -118,19 +121,13 @@ export default ({ api, coreSagas }) => {
yield put(actions.middleware.webSocket.eth.startSocket())
yield put(actions.middleware.webSocket.xlm.startStreams())
yield put(actions.router.push('/home'))
- yield call(coreSagas.settings.fetchSettings)
+ yield put(actions.core.settings.fetchSettings())
yield call(coreSagas.data.xlm.fetchLedgerDetails)
yield call(coreSagas.data.xlm.fetchData)
yield call(authNabu)
yield call(upgradeAddressLabelsSaga)
yield put(actions.auth.loginSuccess())
yield put(actions.auth.startLogoutTimer())
- // store guid in cache for future logins
- const guid = yield select(selectors.core.wallet.getGuid)
- yield put(actions.cache.guidEntered(guid))
- // reset auth type and clear previous login form state
- yield put(actions.auth.setAuthType(0))
- yield put(actions.form.destroy('login'))
// set payload language to settings language
const language = yield select(selectors.preferences.getLanguage)
yield put(actions.modules.settings.updateLanguage(language))
@@ -222,7 +219,7 @@ export default ({ api, coreSagas }) => {
password,
code
})
- yield call(loginRoutineSaga, mobileLogin)
+ yield put(actions.auth.loginRoutine(mobileLogin))
} catch (error) {
const initialError = prop('initial_error', error)
const authRequired = prop('authorization_required', error)
@@ -245,7 +242,7 @@ export default ({ api, coreSagas }) => {
session,
password
})
- yield call(loginRoutineSaga, mobileLogin)
+ yield put(actions.auth.loginRoutine(mobileLogin))
} catch (error) {
if (error && error.auth_type > 0) {
yield put(actions.auth.setAuthType(error.auth_type))
@@ -334,7 +331,7 @@ export default ({ api, coreSagas }) => {
yield put(actions.alerts.displayInfo(C.CREATE_WALLET_INFO))
yield call(coreSagas.wallet.createWalletSaga, action.payload)
yield put(actions.alerts.displaySuccess(C.REGISTER_SUCCESS))
- yield call(loginRoutineSaga, false, true)
+ yield put(actions.auth.loginRoutine(false, true))
yield put(actions.auth.registerSuccess())
} catch (e) {
yield put(actions.auth.registerFailure())
@@ -349,7 +346,7 @@ export default ({ api, coreSagas }) => {
yield put(actions.alerts.displayInfo(C.RESTORE_WALLET_INFO))
yield call(coreSagas.wallet.restoreWalletSaga, action.payload)
yield put(actions.alerts.displaySuccess(C.RESTORE_SUCCESS))
- yield call(loginRoutineSaga, false, true)
+ yield put(actions.auth.loginRoutine(false, true))
yield put(actions.auth.restoreSuccess())
} catch (e) {
yield put(actions.auth.restoreFailure())
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.spec.js b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.spec.js
index b4d73337244..1d501d2dfea 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.spec.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.spec.js
@@ -93,11 +93,11 @@ describe('authSagas', () => {
})
})
- it('should call login routine', () => {
+ it('should put login routine', () => {
const { mobileLogin } = payload
saga
.next()
- .call(loginRoutineSaga, mobileLogin)
+ .put(actions.auth.loginRoutineSaga(mobileLogin))
.next()
.isDone()
})
@@ -185,9 +185,9 @@ describe('authSagas', () => {
})
})
- it('should call login routine', () => {
+ it('should put login routine', () => {
const { mobileLogin } = payload
- saga.next().call(loginRoutineSaga, mobileLogin)
+ saga.next().put(actions.auth.loginRoutineSaga(mobileLogin))
})
it('should follow 2FA flow on auth error', () => {
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/data/logs/reducers.js b/main-process/packages/blockchain-wallet-v4-frontend/src/data/logs/reducers.js
index c905bf90105..119fef2b1b3 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/data/logs/reducers.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/data/logs/reducers.js
@@ -18,6 +18,7 @@ const logger = (state = INITIAL_STATE, action) => {
switch (type) {
case AT.LOG_ERROR_MSG: {
+ console.error(payload)
return insert(0, createLog('ERROR', payload), state)
}
case AT.LOG_INFO_MSG: {
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/data/rootSaga.js b/main-process/packages/blockchain-wallet-v4-frontend/src/data/rootSaga.js
index 85c6d3f32ba..7d19e72edb2 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/data/rootSaga.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/data/rootSaga.js
@@ -59,23 +59,30 @@ export default function* rootSaga ({
btcSocket,
ethSocket,
ratesSocket,
+ rootDocument,
networks,
options
}) {
- const coreSagas = coreSagasFactory({ api, networks, options })
+
+ const coreSagas = coreSagasFactory({
+ api,
+ networks,
+ options,
+ rootDocument
+ })
yield all([
call(welcomeSaga),
fork(alerts),
fork(analytics({ api })),
- fork(auth({ api, coreSagas })),
+ fork(auth({ api, coreSagas, rootDocument })),
fork(components({ api, coreSagas, networks, options })),
fork(modules({ api, coreSagas, networks })),
fork(preferences()),
fork(goals({ api })),
fork(wallet({ coreSagas })),
fork(middleware({ api, bchSocket, btcSocket, ethSocket, ratesSocket })),
- fork(coreRootSagaFactory({ api, networks, options })),
+ fork(coreRootSagaFactory({ api, networks, options, rootDocument })),
fork(router()),
call(languageInitSaga)
])
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/scenes/Settings/General/index.js b/main-process/packages/blockchain-wallet-v4-frontend/src/scenes/Settings/General/index.js
index cfae104eab7..a41ae687195 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/scenes/Settings/General/index.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/scenes/Settings/General/index.js
@@ -5,10 +5,8 @@ import { FormattedMessage } from 'react-intl'
import { Banner, Text } from 'blockchain-info-components'
import About from './About'
-import PairingCode from './PairingCode'
import PrivacyPolicy from './PrivacyPolicy'
import TermsOfService from './TermsOfService'
-import WalletId from './WalletId'
const Wrapper = styled.section`
padding: 30px;
@@ -32,8 +30,6 @@ const General = () => {
/>
-
-
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/store/index.js b/main-process/packages/blockchain-wallet-v4-frontend/src/store/index.js
index 2efff0316cf..976baa3038b 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/src/store/index.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/src/store/index.js
@@ -18,6 +18,8 @@ import {
} from 'blockchain-wallet-v4/src/network'
import { serializer } from 'blockchain-wallet-v4/src/types'
import { actions, rootSaga, rootReducer, selectors } from 'data'
+import IPC from '../IPC'
+
import {
autoDisconnection,
streamingXlm,
@@ -27,8 +29,11 @@ import {
webSocketRates
} from '../middleware'
+import * as kernel from '../../../web-microkernel/src'
+
const devToolsConfig = {
maxAge: 1000,
+ name: `Main Process`,
serialize: serializer,
actionsBlacklist: [
// '@@redux-form/INITIALIZE',
@@ -43,118 +48,133 @@ const devToolsConfig = {
]
}
-const configureStore = () => {
+const configureStore = async () => {
const history = createHashHistory()
const sagaMiddleware = createSagaMiddleware()
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__(devToolsConfig)
: compose
- const walletPath = 'wallet.payload'
const kvStorePath = 'wallet.kvstore'
const isAuthenticated = selectors.auth.isAuthenticated
- return fetch('/Resources/wallet-options-v4.json')
- .then(res => res.json())
- .then(options => {
- const apiKey = '1770d5d9-bcea-4d28-ad21-6cbd5be018a8'
- // TODO: deprecate when wallet-options-v4 is updated on prod
- const socketUrl = head(options.domains.webSocket.split('/inv'))
- const horizonUrl = options.domains.horizon
- const btcSocket = new Socket({
- options,
- url: `${socketUrl}/inv`
- })
- const bchSocket = new Socket({
- options,
- url: `${socketUrl}/bch/inv`
- })
- const ethSocket = new Socket({
- options,
- url: `${socketUrl}/eth/inv`
- })
- const ratesSocket = new ApiSocket({
- options,
- url: `${socketUrl}/nabu-gateway/markets/quotes`,
- maxReconnects: 3
- })
- const xlmStreamingService = new HorizonStreamingService({
- url: horizonUrl
- })
- const getAuthCredentials = () =>
- selectors.modules.profile.getAuthCredentials(store.getState())
- const reauthenticate = () =>
- store.dispatch(actions.modules.profile.signIn())
- const networks = {
- btc: Bitcoin.networks[options.platforms.web.btc.config.network],
- bch: BitcoinCash.networks[options.platforms.web.btc.config.network],
- bsv: BitcoinCash.networks[options.platforms.web.btc.config.network],
- eth: options.platforms.web.eth.config.network,
- xlm: options.platforms.web.xlm.config.network
- }
- const api = createWalletApi({
- options,
- apiKey,
- getAuthCredentials,
- reauthenticate,
- networks
- })
- const persistWhitelist = ['session', 'preferences', 'cache']
-
- // TODO: remove getStoredStateMigrateV4 someday (at least a year from now)
- const store = createStore(
- connectRouter(history)(
- persistCombineReducers(
- {
- getStoredState: getStoredStateMigrateV4({
- whitelist: persistWhitelist
- }),
- key: 'root',
- storage,
- whitelist: persistWhitelist
- },
- rootReducer
- )
- ),
- composeEnhancers(
- applyMiddleware(
- sagaMiddleware,
- routerMiddleware(history),
- coreMiddleware.kvStore({ isAuthenticated, api, kvStorePath }),
- webSocketBtc(btcSocket),
- webSocketBch(bchSocket),
- webSocketEth(ethSocket),
- streamingXlm(xlmStreamingService, api),
- webSocketRates(ratesSocket),
- coreMiddleware.walletSync({ isAuthenticated, api, walletPath }),
- autoDisconnection()
- )
- )
+ const options = await (await fetch(
+ '/Resources/wallet-options-v4.json'
+ )).json()
+
+ const apiKey = '1770d5d9-bcea-4d28-ad21-6cbd5be018a8'
+ // TODO: deprecate when wallet-options-v4 is updated on prod
+ const socketUrl = head(options.domains.webSocket.split('/inv'))
+ const horizonUrl = options.domains.horizon
+ const btcSocket = new Socket({
+ options,
+ url: `${socketUrl}/inv`
+ })
+ const bchSocket = new Socket({
+ options,
+ url: `${socketUrl}/bch/inv`
+ })
+ const ethSocket = new Socket({
+ options,
+ url: `${socketUrl}/eth/inv`
+ })
+ const ratesSocket = new ApiSocket({
+ options,
+ url: `${socketUrl}/nabu-gateway/markets/quotes`,
+ maxReconnects: 3
+ })
+ const xlmStreamingService = new HorizonStreamingService({
+ url: horizonUrl
+ })
+
+ const {
+ connection: rootDocumentConnection,
+ middleware: IPCmiddleware
+ } = await IPC({
+ input: kernel.multiplexRealm({ tag: `realms` }, window),
+ output: kernel.multiplexRealm({ tag: `realms` }, window.parent),
+ outputOrigin: options.domains.rootDocument
+ })
+
+ rootDocumentConnection.addEventListener(`error`, console.error)
+
+ const getAuthCredentials = () =>
+ selectors.modules.profile.getAuthCredentials(store.getState())
+ const reauthenticate = () => store.dispatch(actions.modules.profile.signIn())
+ const networks = {
+ btc: Bitcoin.networks[options.platforms.web.btc.config.network],
+ bch: BitcoinCash.networks[options.platforms.web.btc.config.network],
+ bsv: BitcoinCash.networks[options.platforms.web.btc.config.network],
+ eth: options.platforms.web.eth.config.network,
+ xlm: options.platforms.web.xlm.config.network
+ }
+
+ const api = createWalletApi({
+ axiosAdapter: kernel.sanitizeFunction(rootDocumentConnection.imports.axios),
+ options,
+ apiKey,
+ getAuthCredentials,
+ reauthenticate,
+ networks
+ })
+
+ const persistWhitelist = ['session', 'preferences', 'cache']
+
+ // TODO: remove getStoredStateMigrateV4 someday (at least a year from now)
+ const store = createStore(
+ connectRouter(history)(
+ persistCombineReducers(
+ {
+ getStoredState: getStoredStateMigrateV4({
+ whitelist: persistWhitelist
+ }),
+ key: 'root',
+ storage,
+ whitelist: persistWhitelist
+ },
+ rootReducer
)
- const persistor = persistStore(store, null)
-
- sagaMiddleware.run(rootSaga, {
- api,
- bchSocket,
- btcSocket,
- ethSocket,
- ratesSocket,
- networks,
- options
- })
-
- // expose globals here
- window.createTestXlmAccounts = () => {
- store.dispatch(actions.core.data.xlm.createTestAccounts())
- }
-
- store.dispatch(actions.goals.defineGoals())
-
- return {
- store,
- history,
- persistor
- }
- })
+ ),
+ composeEnhancers(
+ applyMiddleware(
+ IPCmiddleware,
+ sagaMiddleware,
+ routerMiddleware(history),
+ coreMiddleware.kvStore({ isAuthenticated, api, kvStorePath }),
+ webSocketBtc(btcSocket),
+ webSocketBch(bchSocket),
+ webSocketEth(ethSocket),
+ streamingXlm(xlmStreamingService, api),
+ webSocketRates(ratesSocket),
+ coreMiddleware.walletSync({ isAuthenticated }),
+ autoDisconnection()
+ )
+ )
+ )
+ const persistor = persistStore(store, null)
+
+ sagaMiddleware.run(rootSaga, {
+ api,
+ bchSocket,
+ btcSocket,
+ ethSocket,
+ ratesSocket,
+ rootDocument: rootDocumentConnection.imports,
+ networks,
+ options
+ })
+
+ // expose globals here
+ window.createTestXlmAccounts = () => {
+ store.dispatch(actions.core.data.xlm.createTestAccounts())
+ }
+
+ store.dispatch(actions.goals.defineGoals())
+
+ return {
+ store,
+ history,
+ persistor
+ }
}
export default configureStore
diff --git a/main-process/packages/blockchain-wallet-v4-frontend/webpack.config.dev.js b/main-process/packages/blockchain-wallet-v4-frontend/webpack.config.dev.js
index ca6f7010fc1..1fa64945a99 100644
--- a/main-process/packages/blockchain-wallet-v4-frontend/webpack.config.dev.js
+++ b/main-process/packages/blockchain-wallet-v4-frontend/webpack.config.dev.js
@@ -17,13 +17,14 @@ const iSignThisDomain =
let envConfig = {}
let manifestCacheBust = new Date().getTime()
+const port = 8082
let sslEnabled = process.env.DISABLE_SSL
? false
: fs.existsSync(PATHS.sslConfig + '/key.pem') &&
fs.existsSync(PATHS.sslConfig + '/cert.pem')
let localhostUrl = sslEnabled
- ? 'https://localhost:8080'
- : 'http://localhost:8080'
+ ? `https://localhost:${port}`
+ : `http://localhost:${port}`
try {
envConfig = require(PATHS.envConfig + `/${process.env.NODE_ENV}` + '.js')
@@ -61,7 +62,7 @@ module.exports = {
app: [
'@babel/polyfill',
'react-hot-loader/patch',
- 'webpack-dev-server/client?http://localhost:8080',
+ `webpack-dev-server/client?http://localhost:${port}`,
'webpack/hot/only-dev-server',
PATHS.src + '/index.js'
]
@@ -192,7 +193,7 @@ module.exports = {
key: sslEnabled
? fs.readFileSync(PATHS.sslConfig + '/key.pem', 'utf8')
: '',
- port: 8080,
+ port,
hot: true,
historyApiFallback: true,
before(app) {
@@ -200,6 +201,7 @@ module.exports = {
// combine wallet options base with custom environment config
mockWalletOptions.domains = {
root: envConfig.ROOT_URL,
+ rootDocument: `http://localhost:8080`,
api: envConfig.API_DOMAIN,
webSocket: envConfig.WEB_SOCKET_URL,
walletHelper: envConfig.WALLET_HELPER_DOMAIN,
@@ -257,13 +259,13 @@ module.exports = {
"style-src 'self' 'unsafe-inline'",
`frame-src ${iSignThisDomain} ${envConfig.WALLET_HELPER_DOMAIN} ${
envConfig.ROOT_URL
- } https://magic.veriff.me https://localhost:8080 http://localhost:8080`,
+ } https://magic.veriff.me https://localhost:${port} http://localhost:${port}`,
`child-src ${iSignThisDomain} ${envConfig.WALLET_HELPER_DOMAIN} blob:`,
[
'connect-src',
"'self'",
- 'ws://localhost:8080',
- 'wss://localhost:8080',
+ `ws://localhost:${port}`,
+ `wss://localhost:${port}`,
'wss://api.ledgerwallet.com',
'wss://ws.testnet.blockchain.info/inv',
envConfig.WEB_SOCKET_URL,
diff --git a/main-process/packages/blockchain-wallet-v4/src/network/api/http.js b/main-process/packages/blockchain-wallet-v4/src/network/api/http.js
index 18238b40022..65ff8b28e37 100755
--- a/main-process/packages/blockchain-wallet-v4/src/network/api/http.js
+++ b/main-process/packages/blockchain-wallet-v4/src/network/api/http.js
@@ -5,7 +5,7 @@ import { prop, path, pathOr, merge } from 'ramda'
axios.defaults.withCredentials = false
axios.defaults.timeout = Infinity
-export default ({ apiKey }) => {
+export default ({ axiosAdapter, apiKey }) => {
const encodeData = (data, contentType) => {
const defaultData = {
api_code: apiKey,
@@ -39,6 +39,7 @@ export default ({ apiKey }) => {
...options
}) =>
axios({
+ adapter: axiosAdapter,
url: `${url}${endPoint}`,
method,
data: encodeData(data, contentType),
diff --git a/main-process/packages/blockchain-wallet-v4/src/network/api/index.js b/main-process/packages/blockchain-wallet-v4/src/network/api/index.js
index 92401c06321..34edbeefc6f 100755
--- a/main-process/packages/blockchain-wallet-v4/src/network/api/index.js
+++ b/main-process/packages/blockchain-wallet-v4/src/network/api/index.js
@@ -20,13 +20,14 @@ import httpService from './http'
import apiAuthorize from './apiAuthorize'
export default ({
+ axiosAdapter,
options,
apiKey,
getAuthCredentials,
reauthenticate,
networks
} = {}) => {
- const http = httpService({ apiKey })
+ const http = httpService({ axiosAdapter, apiKey })
const authorizedHttp = apiAuthorize(http, getAuthCredentials, reauthenticate)
const apiUrl = options.domains.api
const horizonUrl = options.domains.horizon
diff --git a/main-process/packages/blockchain-wallet-v4/src/network/walletApi.js b/main-process/packages/blockchain-wallet-v4/src/network/walletApi.js
index dd9b47fd49a..43e9cbdb657 100755
--- a/main-process/packages/blockchain-wallet-v4/src/network/walletApi.js
+++ b/main-process/packages/blockchain-wallet-v4/src/network/walletApi.js
@@ -20,18 +20,9 @@ import { futurizeP } from 'futurize'
import createApi from './api'
import * as Coin from '../coinSelection/coin.js'
-const createWalletApi = (
- { options, apiKey, getAuthCredentials, reauthenticate, networks } = {},
- returnType
-) => {
+const createWalletApi = (options, returnType) => {
// ////////////////////////////////////////////////////////////////
- const ApiPromise = createApi({
- options,
- apiKey,
- getAuthCredentials,
- reauthenticate,
- networks
- })
+ const ApiPromise = createApi(options)
const eitherToTask = e => e.fold(Task.rejected, Task.of)
const taskToPromise = t =>
new Promise((resolve, reject) => t.fork(reject, resolve))
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/eth/sagaRegister.js b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/eth/sagaRegister.js
index d92c6963c80..b282085e8d9 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/eth/sagaRegister.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/eth/sagaRegister.js
@@ -2,8 +2,8 @@ import { takeLatest } from 'redux-saga/effects'
import * as AT from './actionTypes'
import sagas from './sagas'
-export default ({ api, networks }) => {
- const kvStoreEthereumSagas = sagas({ api, networks })
+export default (...args) => {
+ const kvStoreEthereumSagas = sagas(...args)
return function* coreKvStoreEthSaga () {
yield takeLatest(
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/eth/sagas.js b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/eth/sagas.js
index a5409aa1ce3..828ce67666a 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/eth/sagas.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/eth/sagas.js
@@ -1,4 +1,4 @@
-import { head, prop, isNil, isEmpty } from 'ramda'
+import { curry, head, prop, isNil, isEmpty } from 'ramda'
import { call, put, select } from 'redux-saga/effects'
import { set } from 'ramda-lens'
import * as A from './actions'
@@ -7,28 +7,31 @@ import { KVStoreEntry } from '../../../types'
import { getMetadataXpriv } from '../root/selectors'
import { derivationMap, ETHEREUM } from '../config'
import * as eth from '../../../utils/eth'
-import { getMnemonic } from '../../wallet/selectors'
+
+import { getPbkdf2Iterations, getSharedKey } from '../../wallet/selectors'
import { callTask } from '../../../utils/functional'
-export default ({ api, networks } = {}) => {
- const deriveAccount = function*(password) {
- try {
- const obtainMnemonic = state => getMnemonic(state, password)
- const mnemonicT = yield select(obtainMnemonic)
- const mnemonic = yield callTask(mnemonicT)
- const defaultIndex = 0
- const addr = eth.deriveAddress(mnemonic, defaultIndex)
+export default ({ api, networks, rootDocument } = {}) => {
+ const deriveAccount = function*(secondPassword) {
+ const defaultIndex = 0
- return { defaultIndex, addr }
- } catch (e) {
- throw new Error(
- '[NOT IMPLEMENTED] MISSING_SECOND_PASSWORD in core.createEthereum saga'
- )
+ const credentials = {
+ iterations: yield select(getPbkdf2Iterations),
+ secondPassword,
+ sharedKey: yield select(getSharedKey)
}
+
+ const addr = yield call(eth.deriveAddress, {
+ deriveBIP32Key: curry(rootDocument.deriveBIP32Key)(credentials),
+ index: defaultIndex
+ })
+
+ return { defaultIndex, addr }
}
const createEthereum = function*({ kv, password }) {
const { defaultIndex, addr } = yield call(deriveAccount, password)
+
const ethereum = {
has_seen: true,
default_account_idx: defaultIndex,
@@ -50,7 +53,10 @@ export default ({ api, networks } = {}) => {
}
const transitionFromLegacy = function*({ newkv, password }) {
- const { defaultIndex, addr } = yield call(deriveAccount, password)
+ const { defaultIndex, addr } = yield call(
+ rootDocument.deriveEthereumAccount,
+ password
+ )
const defaultAccount = Map(newkv.value.ethereum.accounts[defaultIndex])
newkv.value.ethereum.legacy_account = defaultAccount.toJS()
newkv.value.ethereum.accounts[defaultIndex].addr = addr
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/root/sagas.js b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/root/sagas.js
index a9e32db86a0..3cfa11d0250 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/root/sagas.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/root/sagas.js
@@ -1,18 +1,13 @@
import { call, put, select } from 'redux-saga/effects'
import { prop, compose, isNil } from 'ramda'
import * as A from './actions'
-import BIP39 from 'bip39'
import { KVStoreEntry } from '../../../types'
-import {
- getMnemonic,
- getGuid,
- getMainPassword,
- getSharedKey
-} from '../../wallet/selectors'
+import { getPbkdf2Iterations, getSharedKey } from '../../wallet/selectors'
+
const taskToPromise = t =>
new Promise((resolve, reject) => t.fork(reject, resolve))
-export default ({ api, networks }) => {
+export default ({ api, networks, rootDocument }) => {
const callTask = function*(task) {
return yield call(
compose(
@@ -23,39 +18,39 @@ export default ({ api, networks }) => {
}
const createRoot = function*({ password }) {
try {
- const obtainMnemonic = state => getMnemonic(state, password)
- const mnemonicT = yield select(obtainMnemonic)
- const mnemonic = yield call(() => taskToPromise(mnemonicT))
- const seedHex = BIP39.mnemonicToEntropy(mnemonic)
- const getMetadataNode = compose(
- KVStoreEntry.deriveMetadataNode,
- KVStoreEntry.getMasterHDNode(networks.btc)
- )
- const metadataNode = getMetadataNode(seedHex)
- const metadata = metadataNode.toBase58()
+ const credentials = {
+ iterations: yield select(getPbkdf2Iterations),
+ secondPassword: password,
+ sharedKey: yield select(getSharedKey)
+ }
+
+ const metadata = yield call(rootDocument.deriveBIP32Key, credentials, {
+ network: networks.btc,
+ path: `m/${KVStoreEntry.metadataPurpose}'`
+ })
+
yield put(A.updateMetadataRoot({ metadata }))
} catch (e) {
throw new Error('create root Metadata :: Error decrypting mnemonic')
}
}
- const fetchRoot = function*(secondPasswordSagaEnhancer) {
+ const fetchRoot = function*({ askSecondPasswordEnhancer }) {
try {
- const guid = yield select(getGuid)
const sharedKey = yield select(getSharedKey)
- const mainPassword = yield select(getMainPassword)
yield put(A.fetchMetadataRootLoading())
- const kv = KVStoreEntry.fromCredentials(
- guid,
- sharedKey,
- mainPassword,
- networks.btc
- )
+ const entropy = yield call(rootDocument.credentialsEntropy, { sharedKey })
+
+ const kv = yield call(KVStoreEntry.fromEntropy, {
+ entropy,
+ network: networks.btc
+ })
+
const newkv = yield callTask(api.fetchKVStore(kv))
yield put(A.fetchMetadataRootSuccess(newkv))
if (isNil(prop('metadata', newkv.value))) {
// no metadata node saved
- const createRootenhanced = secondPasswordSagaEnhancer(createRoot)
+ const createRootenhanced = askSecondPasswordEnhancer(createRoot)
yield call(createRootenhanced, {})
}
} catch (e) {
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/sagaRegister.js b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/sagaRegister.js
index 98296907ae2..4f78afd342e 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/sagaRegister.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/sagaRegister.js
@@ -12,17 +12,17 @@ import contacts from './contacts/sagaRegister'
import lockbox from './lockbox/sagaRegister'
import userCredentials from './userCredentials/sagaRegister'
-export default ({ api, networks }) =>
+export default (...args) =>
function* coreKvStoreSaga () {
- yield fork(whatsNew({ api, networks }))
- yield fork(ethereum({ api, networks }))
- yield fork(bch({ api, networks }))
- yield fork(btc({ api, networks }))
- yield fork(bsv({ api, networks }))
- yield fork(xlm({ api, networks }))
- yield fork(shapeShift({ api, networks }))
- yield fork(buySell({ api, networks }))
- yield fork(contacts({ api, networks }))
- yield fork(lockbox({ api, networks }))
- yield fork(userCredentials({ api, networks }))
+ yield fork(whatsNew(...args))
+ yield fork(ethereum(...args))
+ yield fork(bch(...args))
+ yield fork(btc(...args))
+ yield fork(bsv(...args))
+ yield fork(xlm(...args))
+ yield fork(shapeShift(...args))
+ yield fork(buySell(...args))
+ yield fork(contacts(...args))
+ yield fork(lockbox(...args))
+ yield fork(userCredentials(...args))
}
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/sagas.js b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/sagas.js
index a307d9f2e16..39ceb26262c 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/sagas.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/sagas.js
@@ -11,17 +11,17 @@ import userCredentials from './userCredentials/sagas'
import shapeShift from './shapeShift/sagas'
import xlm from './xlm/sagas'
-export default ({ api, networks }) => ({
- bch: bch({ api, networks }),
- btc: btc({ api, networks }),
- bsv: bsv({ api, networks }),
- ethereum: eth({ api, networks }),
- root: root({ api, networks }),
- lockbox: lockbox({ api, networks }),
- buySell: buySell({ api, networks }),
- whatsNew: whatsNew({ api, networks }),
- contacts: contacts({ api, networks }),
- shapeShift: shapeShift({ api, networks }),
- userCredentials: userCredentials({ api, networks }),
- xlm: xlm({ api, networks })
+export default (...args) => ({
+ bch: bch(...args),
+ btc: btc(...args),
+ bsv: bsv(...args),
+ ethereum: eth(...args),
+ root: root(...args),
+ lockbox: lockbox(...args),
+ buySell: buySell(...args),
+ whatsNew: whatsNew(...args),
+ contacts: contacts(...args),
+ shapeShift: shapeShift(...args),
+ userCredentials: userCredentials(...args),
+ xlm: xlm(...args)
})
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/xlm/sagaRegister.js b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/xlm/sagaRegister.js
index d5915069376..142c9b337c6 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/xlm/sagaRegister.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/xlm/sagaRegister.js
@@ -2,8 +2,8 @@ import { takeLatest } from 'redux-saga/effects'
import * as AT from './actionTypes'
import sagas from './sagas'
-export default ({ api, networks }) => {
- const kvStoreXlmSagas = sagas({ api, networks })
+export default (...args) => {
+ const kvStoreXlmSagas = sagas(...args)
return function* coreKvStoreXlmSaga () {
yield takeLatest(AT.FETCH_METADATA_XLM, kvStoreXlmSagas.fetchMetadataXlm)
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/xlm/sagas.js b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/xlm/sagas.js
index 58369d9a087..632b107d70a 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/xlm/sagas.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/kvStore/xlm/sagas.js
@@ -1,20 +1,29 @@
import { call, put, select } from 'redux-saga/effects'
-import { isNil, isEmpty } from 'ramda'
+import { curry, isNil, isEmpty } from 'ramda'
import { set } from 'ramda-lens'
import * as A from './actions'
import { KVStoreEntry } from '../../../types'
import { getMetadataXpriv } from '../root/selectors'
import { derivationMap, XLM } from '../config'
-import { getMnemonic } from '../../wallet/selectors'
import { getKeyPair } from '../../../utils/xlm'
import { callTask } from '../../../utils/functional'
+import { getPbkdf2Iterations, getSharedKey } from '../../wallet/selectors'
-export default ({ api, networks } = {}) => {
+export default ({ api, networks, rootDocument } = {}) => {
const createXlm = function*({ kv, password }) {
try {
- const mnemonicT = yield select(getMnemonic, password)
- const mnemonic = yield callTask(mnemonicT)
- const keypair = getKeyPair(mnemonic)
+ const credentials = {
+ iterations: yield select(getPbkdf2Iterations),
+ secondPassword: password,
+ sharedKey: yield select(getSharedKey)
+ }
+
+ const keypair = yield call(getKeyPair, {
+ deriveSLIP10ed25519Key: curry(rootDocument.deriveSLIP10ed25519Key)(
+ credentials
+ )
+ })
+
const xlm = {
default_account_idx: 0,
accounts: [
@@ -43,7 +52,8 @@ export default ({ api, networks } = {}) => {
yield put(A.fetchMetadataXlmLoading())
const newkv = yield callTask(api.fetchKVStore(kv))
if (isNil(newkv.value) || isEmpty(newkv.value)) {
- yield call(secondPasswordSagaEnhancer(createXlm), { kv })
+ const newkv = yield call(secondPasswordSagaEnhancer(createXlm), { kv })
+ yield put(A.createMetadataXlm(newkv))
} else {
yield put(A.fetchMetadataXlmSuccess(newkv))
}
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/rootSaga.js b/main-process/packages/blockchain-wallet-v4/src/redux/rootSaga.js
index ccfc887e071..02f6f91105e 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/rootSaga.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/rootSaga.js
@@ -5,11 +5,11 @@ import walletOptions from './walletOptions/sagaRegister'
import settings from './settings/sagaRegister'
import wallet from './wallet/sagaRegister'
-export default ({ api, networks, options }) =>
+export default ({ api, networks, options, rootDocument }) =>
function* coreSaga () {
yield all([
fork(data({ api, options, networks })),
- fork(kvStore({ api, networks })),
+ fork(kvStore({ api, networks, rootDocument })),
fork(walletOptions({ api, options })),
fork(settings({ api })),
fork(wallet({ api, networks }))
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/sagas.js b/main-process/packages/blockchain-wallet-v4/src/redux/sagas.js
index 23a574f8cc0..6a882cd34bf 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/sagas.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/sagas.js
@@ -5,11 +5,11 @@ import walletOptions from './walletOptions/sagas'
import kvStore from './kvStore/sagas'
import payment from './payment/sagas'
-export default ({ api, networks, options }) => ({
+export default ({ api, networks, options, rootDocument }) => ({
data: data({ api, options, networks }),
settings: settings({ api }),
- wallet: wallet({ api, networks }),
+ wallet: wallet({ api, networks, rootDocument }),
walletOptions: walletOptions({ api }),
- kvStore: kvStore({ api, networks }),
+ kvStore: kvStore({ api, networks, rootDocument }),
payment: payment({ api, options })
})
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/wallet/actionTypes.js b/main-process/packages/blockchain-wallet-v4/src/redux/wallet/actionTypes.js
index 1e71c3b27bd..6211d6c7718 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/wallet/actionTypes.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/wallet/actionTypes.js
@@ -1,4 +1,5 @@
// setters
+export const MERGE_WRAPPER = '@CORE.MERGE_WRAPPER'
export const SET_WRAPPER = '@CORE.SET_WRAPPER'
export const REFRESH_WRAPPER = '@CORE.REFRESH_WRAPPER'
export const SET_MAIN_PASSWORD = '@CORE.SET_MAIN_PASSWORD'
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/wallet/actions.js b/main-process/packages/blockchain-wallet-v4/src/redux/wallet/actions.js
index 624d77e3563..4e6c7b28f3c 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/wallet/actions.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/wallet/actions.js
@@ -1,6 +1,9 @@
import * as T from './actionTypes'
// setters
+
+export const mergeWrapper = payload => ({ type: T.MERGE_WRAPPER, payload })
+
export const setWrapper = payload => ({
type: T.SET_WRAPPER,
payload: payload
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/wallet/sagas.js b/main-process/packages/blockchain-wallet-v4/src/redux/wallet/sagas.js
index efd085340c9..c30796e348e 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/wallet/sagas.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/wallet/sagas.js
@@ -30,7 +30,7 @@ import { generateMnemonic } from '../../walletCrypto'
const taskToPromise = t =>
new Promise((resolve, reject) => t.fork(reject, resolve))
-export default ({ api, networks }) => {
+export default ({ api, networks, rootDocument }) => {
const runTask = function*(task, setActionCreator) {
let result = yield call(
compose(
@@ -80,7 +80,12 @@ export default ({ api, networks }) => {
let wrapper = yield select(S.getWrapper)
let nextWrapper = Wrapper.traverseWallet(
Task.of,
- Wallet.newHDAccount(label, password, networks.btc),
+ Wallet.newHDAccount({
+ deriveBIP32Key: rootDocument.deriveBIP32Key,
+ label,
+ network: networks.btc,
+ secondPassword: password
+ }),
wrapper
)
yield call(runTask, nextWrapper, A.wallet.setWrapper)
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/walletSync/middleware.js b/main-process/packages/blockchain-wallet-v4/src/redux/walletSync/middleware.js
index 6e24f37c6ee..87c2e913bd3 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/walletSync/middleware.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/walletSync/middleware.js
@@ -1,9 +1,7 @@
-import { futurizeP } from 'futurize'
-import Task from 'data.task'
+import * as A from '../actions'
+
import {
compose,
- assoc,
- join,
curry,
range,
keysIn,
@@ -15,9 +13,8 @@ import {
} from 'ramda'
import { networks } from 'bitcoinjs-lib'
-import * as A from '../actions'
import * as T from '../actionTypes'
-import { Wrapper, Wallet, HDAccount } from '../../types'
+import { Wallet, HDAccount } from '../../types'
import * as selectors from '../selectors'
/**
@@ -93,81 +90,44 @@ export const getWalletAddresses = async (state, api) => {
return activeAddresses.concat(uniq(hdAddresses.concat(unusedAddresses)))
}
+export const shouldSync = ({
+ actionType,
+ newAuthenticated,
+ newWallet,
+ oldAuthenticated,
+ oldWallet
+}) =>
+ actionType === T.walletSync.FORCE_SYNC ||
+ (oldAuthenticated &&
+ newAuthenticated &&
+ actionType !== T.wallet.SET_PAYLOAD_CHECKSUM &&
+ actionType !== T.wallet.REFRESH_WRAPPER &&
+ // Easily know when to sync, because of ✨immutable✨ data
+ // the initial_state check could be done against full payload state
+ oldWallet !== newWallet)
+
/**
* Wallet sync middleware
* Calls sync on special conditions
*
* TODO: refactor to sagas, VERY painful to test/write mocks
*/
-const walletSync = ({
- isAuthenticated,
- api
-} = {}) => store => next => action => {
- const prevState = store.getState()
- const prevWallet = selectors.wallet.getWrapper(prevState)
- const wasAuth = isAuthenticated(prevState)
+const walletSync = ({ isAuthenticated } = {}) => store => next => action => {
+ const oldState = store.getState()
const result = next(action)
-
- const state = store.getState()
- const nextWallet = selectors.wallet.getWrapper(state)
- const syncPubKeys = selectors.wallet.shouldSyncPubKeys(state)
- const isAuth = isAuthenticated(state)
- const promiseToTask = futurizeP(Task)
-
- // Easily know when to sync, because of ✨immutable✨ data
- // the initial_state check could be done against full payload state
-
- const handleChecksum = encrypted => {
- const checksum = Wrapper.computeChecksum(encrypted)
- compose(
- store.dispatch,
- A.wallet.setPayloadChecksum
- )(checksum)
- return encrypted
- }
-
- const sync = async () => {
- let encryptedWallet = Wrapper.toEncJSON(nextWallet)
- if (syncPubKeys) {
- /**
- * To get notifications working you have to add list of lookahead addresses
- * For each of the wallet's accounts
- */
- try {
- const addresses = await getWalletAddresses(state, api)
- encryptedWallet = encryptedWallet.map(
- assoc('active', join('|', addresses))
- )
- } catch (error) {
- return store.dispatch(A.walletSync.syncError(error))
- }
- }
- return encryptedWallet
- .map(handleChecksum)
- .chain(promiseToTask(api.savePayload))
- .fork(
- compose(
- store.dispatch,
- A.walletSync.syncError
- ),
- compose(
- store.dispatch,
- A.walletSync.syncSuccess
- )
- )
- }
-
- switch (true) {
- case action.type === T.walletSync.FORCE_SYNC:
- case wasAuth &&
- isAuth &&
- action.type !== T.wallet.SET_PAYLOAD_CHECKSUM &&
- action.type !== T.wallet.REFRESH_WRAPPER &&
- prevWallet !== nextWallet:
- sync()
- break
- default:
- break
+ const newState = store.getState()
+ const newWallet = selectors.wallet.getWrapper(newState)
+
+ if (
+ shouldSync({
+ actionType: action.type,
+ newAuthenticated: isAuthenticated(newState),
+ newWallet,
+ oldAuthenticated: isAuthenticated(oldState),
+ oldWallet: selectors.wallet.getWrapper(oldState)
+ })
+ ) {
+ store.dispatch(A.wallet.mergeWrapper(newWallet))
}
return result
diff --git a/main-process/packages/blockchain-wallet-v4/src/redux/walletSync/middleware.spec.js b/main-process/packages/blockchain-wallet-v4/src/redux/walletSync/middleware.spec.js
index ac3655dbde9..0852d2de82f 100755
--- a/main-process/packages/blockchain-wallet-v4/src/redux/walletSync/middleware.spec.js
+++ b/main-process/packages/blockchain-wallet-v4/src/redux/walletSync/middleware.spec.js
@@ -1,7 +1,8 @@
import { map, range } from 'ramda'
import {
addressLookaheadCount,
- getHDAccountAddressPromises
+ getHDAccountAddressPromises,
+ shouldSync
} from './middleware'
import { getReceiveAddress } from '../../types/HDAccount'
import { getAccountXpub } from '../wallet/selectors'
@@ -80,3 +81,14 @@ describe('getHDAccountAddressPromises', () => {
return promise
})
})
+
+it(`shouldSync if the wallet changes`, () => {
+ expect(
+ shouldSync({
+ newAuthenticated: true,
+ newWallet: 43,
+ oldAuthenticated: true,
+ oldWallet: 42
+ })
+ ).toEqual(true)
+})
diff --git a/main-process/packages/blockchain-wallet-v4/src/remote/index.js b/main-process/packages/blockchain-wallet-v4/src/remote/index.js
index 845f7bcb156..5f45170cc5e 100755
--- a/main-process/packages/blockchain-wallet-v4/src/remote/index.js
+++ b/main-process/packages/blockchain-wallet-v4/src/remote/index.js
@@ -1,3 +1,5 @@
+// https://medium.com/@jaumepernas/your-data-is-loading-1425c6b76bf0
+
import { taggedSum } from 'daggy'
const Remote = taggedSum('Remote', {
diff --git a/main-process/packages/blockchain-wallet-v4/src/types/HDWallet.js b/main-process/packages/blockchain-wallet-v4/src/types/HDWallet.js
index d1581d73a57..218884f86eb 100755
--- a/main-process/packages/blockchain-wallet-v4/src/types/HDWallet.js
+++ b/main-process/packages/blockchain-wallet-v4/src/types/HDWallet.js
@@ -84,10 +84,20 @@ export const deriveAccountNodeAtIndex = (seedHex, index, network) => {
.deriveHardened(index)
}
-export const generateAccount = curry((index, label, network, seedHex) => {
- let node = deriveAccountNodeAtIndex(seedHex, index, network)
+export const generateAccount = async ({
+ deriveBIP32Key,
+ index,
+ label,
+ network
+}) => {
+ const key = await deriveBIP32Key({
+ network,
+ path: `m/44'/0'/${index}'`
+ })
+
+ const node = Bitcoin.HDNode.fromBase58(key)
return HDAccount.fromJS(HDAccount.js(label, node, null))
-})
+}
// encrypt :: Number -> String -> String -> HDWallet -> Task Error HDWallet
export const encrypt = curry((iterations, sharedKey, password, hdWallet) => {
diff --git a/main-process/packages/blockchain-wallet-v4/src/types/KVStoreEntry.js b/main-process/packages/blockchain-wallet-v4/src/types/KVStoreEntry.js
index 444c23365ce..e7dc97aedd4 100755
--- a/main-process/packages/blockchain-wallet-v4/src/types/KVStoreEntry.js
+++ b/main-process/packages/blockchain-wallet-v4/src/types/KVStoreEntry.js
@@ -61,13 +61,12 @@ export const fromKeys = (entryECKey, encKeyBuffer, typeId) => {
})
}
-export const fromCredentials = curry((guid, sharedKey, password, network) => {
- const entropy = crypto.sha256(Buffer.from(guid + sharedKey + password))
+export const fromEntropy = ({ entropy, network }) => {
const d = BigInteger.fromBuffer(entropy)
const key = new Bitcoin.ECPair(d, null, { network })
const enc = key.d.toBuffer(32)
return fromKeys(key, enc)
-})
+}
export const getMasterHDNode = curry((network, seedHex) => {
const mnemonic = BIP39.entropyToMnemonic(seedHex)
@@ -75,6 +74,14 @@ export const getMasterHDNode = curry((network, seedHex) => {
return Bitcoin.HDNode.fromSeedBuffer(masterhex, network)
})
+// BIP 43 purpose needs to be 31 bit or less. For lack of a BIP number
+// we take the first 31 bits of the SHA256 hash of a reverse domain.
+export const metadataPurpose =
+ crypto
+ .sha256('info.blockchain.metadata')
+ .slice(0, 4)
+ .readUInt32BE(0) & 0x7fffffff // 510742
+
export const deriveMetadataNode = masterHDNode => {
// BIP 43 purpose needs to be 31 bit or less. For lack of a BIP number
// we take the first 31 bits of the SHA256 hash of a reverse domain.
diff --git a/main-process/packages/blockchain-wallet-v4/src/types/Serializer.js b/main-process/packages/blockchain-wallet-v4/src/types/Serializer.js
index 2eb76750fc7..d872f9f8e05 100755
--- a/main-process/packages/blockchain-wallet-v4/src/types/Serializer.js
+++ b/main-process/packages/blockchain-wallet-v4/src/types/Serializer.js
@@ -30,7 +30,9 @@ const serializer = {
return value
},
reviver: function (key, value) {
- if (
+ if (value && value.type === 'Buffer') {
+ return Buffer.from(value.data)
+ } else if (
typeof value === 'object' &&
value !== null &&
'__serializedType__' in value
diff --git a/main-process/packages/blockchain-wallet-v4/src/types/Wallet.js b/main-process/packages/blockchain-wallet-v4/src/types/Wallet.js
index 489144cf36c..d68a6cae22f 100755
--- a/main-process/packages/blockchain-wallet-v4/src/types/Wallet.js
+++ b/main-process/packages/blockchain-wallet-v4/src/types/Wallet.js
@@ -6,18 +6,7 @@ import Maybe from 'data.maybe'
import Bitcoin from 'bitcoinjs-lib'
import memoize from 'fast-memoize'
import BIP39 from 'bip39'
-import {
- compose,
- curry,
- map,
- is,
- pipe,
- __,
- concat,
- split,
- isNil,
- flip
-} from 'ramda'
+import { compose, curry, map, is, pipe, __, concat, split, isNil } from 'ramda'
import { traversed, traverseOf, over, view, set } from 'ramda-lens'
import * as crypto from '../walletCrypto'
import { shift, shiftIProp } from './util'
@@ -293,29 +282,45 @@ export const newHDWallet = curry((mnemonic, password, wallet) => {
)
})
-// newHDAccount :: String -> String? -> Wallet -> Task Error Wallet
-export const newHDAccount = curry((label, password, network, wallet) => {
- let hdWallet = HDWalletList.selectHDWallet(selectHdWallets(wallet))
- let index = hdWallet.accounts.size
- let appendAccount = curry((w, account) => {
- let accountsLens = compose(
- hdWallets,
- HDWalletList.hdwallet,
- HDWallet.accounts
- )
- let accountWithIndex = set(HDAccount.index, index, account)
- return over(accountsLens, accounts => accounts.push(accountWithIndex), w)
- })
- return applyCipher(
- wallet,
- password,
- flip(crypto.decryptSecPass),
- hdWallet.seedHex
- )
- .map(HDWallet.generateAccount(index, label, network))
- .chain(applyCipher(wallet, password, HDAccount.encrypt))
- .map(appendAccount(wallet))
-})
+const promiseToTask = promise =>
+ new Task((reject, resolve) => promise.then(resolve, reject))
+
+// newHDAccount :: Object -> Wallet -> Task Error Wallet
+export const newHDAccount = curry(
+ ({ deriveBIP32Key, label, secondPassword, network }, wallet) => {
+ let hdWallet = HDWalletList.selectHDWallet(selectHdWallets(wallet))
+ let index = hdWallet.accounts.size
+ let appendAccount = curry((w, account) => {
+ let accountsLens = compose(
+ hdWallets,
+ HDWalletList.hdwallet,
+ HDWallet.accounts
+ )
+ let accountWithIndex = set(HDAccount.index, index, account)
+ return over(accountsLens, accounts => accounts.push(accountWithIndex), w)
+ })
+
+ const credentials = {
+ iterations: selectIterations(wallet),
+ secondPassword,
+ sharedKey: selectSharedKey(wallet)
+ }
+
+ return validateSecondPwd(Task.of, Task.rejected)(secondPassword, wallet)
+ .chain(() =>
+ promiseToTask(
+ HDWallet.generateAccount({
+ deriveBIP32Key: curry(deriveBIP32Key)(credentials),
+ index,
+ label,
+ network
+ })
+ )
+ )
+ .chain(applyCipher(wallet, secondPassword, HDAccount.encrypt))
+ .map(appendAccount(wallet))
+ }
+)
// setLegacyAddressLabel :: String -> String -> Wallet -> Wallet
export const setLegacyAddressLabel = curry((address, label, wallet) => {
diff --git a/main-process/packages/blockchain-wallet-v4/src/utils/eth.js b/main-process/packages/blockchain-wallet-v4/src/utils/eth.js
index c0523556df8..35e97961e4f 100755
--- a/main-process/packages/blockchain-wallet-v4/src/utils/eth.js
+++ b/main-process/packages/blockchain-wallet-v4/src/utils/eth.js
@@ -1,7 +1,5 @@
import * as Exchange from '../exchange'
import { prop, path } from 'ramda'
-import BIP39 from 'bip39'
-import Bitcoin from 'bitcoinjs-lib'
import EthHd from 'ethereumjs-wallet/hdkey'
import EthTx from 'ethereumjs-tx'
import EthUtil from 'ethereumjs-util'
@@ -13,19 +11,15 @@ import BigNumber from 'bignumber.js'
export const isValidAddress = address => /^0x[a-fA-F0-9]{40}$/.test(address)
/**
- * @param {string} mnemonic
+ * @param {function} deriveBIP32Key
* @param {integer} index
*/
-export const getPrivateKey = (mnemonic, index) => {
- const seed = BIP39.mnemonicToSeed(mnemonic)
- const account = Bitcoin.HDNode.fromSeedBuffer(seed)
- .deriveHardened(44)
- .deriveHardened(60)
- .deriveHardened(0)
- .derive(0)
- .derive(index)
- .toBase58()
- return EthHd.fromExtendedKey(account)
+export const getPrivateKey = async ({ deriveBIP32Key, index }) => {
+ const key = await deriveBIP32Key({
+ path: `m/44'/60'/0'/0/${index}`
+ })
+
+ return EthHd.fromExtendedKey(key)
.getWallet()
.getPrivateKey()
}
@@ -47,8 +41,8 @@ const deriveChildLegacy = (index, seed) => {
export const privateKeyToAddress = pk =>
EthUtil.toChecksumAddress(EthUtil.privateToAddress(pk).toString('hex'))
-export const deriveAddress = (mnemonic, index) =>
- privateKeyToAddress(getPrivateKey(mnemonic, index))
+export const deriveAddress = async (...args) =>
+ privateKeyToAddress(await getPrivateKey(...args))
export const deriveAddressFromXpub = xpub => {
const ethPublic = EthHd.fromExtendedKey(xpub)
diff --git a/main-process/packages/blockchain-wallet-v4/src/utils/xlm.js b/main-process/packages/blockchain-wallet-v4/src/utils/xlm.js
index 820dadc079e..34c13e7d3e5 100755
--- a/main-process/packages/blockchain-wallet-v4/src/utils/xlm.js
+++ b/main-process/packages/blockchain-wallet-v4/src/utils/xlm.js
@@ -48,9 +48,7 @@ export const decodeXlmURI = uri => {
return { address: destination, amount, memo, note: msg }
}
-export const getKeyPair = mnemonic => {
- const seed = BIP39.mnemonicToSeed(mnemonic)
- const seedHex = seed.toString('hex')
- const masterKey = ed25519.derivePath("m/44'/148'/0'", seedHex)
+export const getKeyPair = async ({ deriveSLIP10ed25519Key }) => {
+ const masterKey = await deriveSLIP10ed25519Key({ path: `m/44'/148'/0'` })
return StellarSdk.Keypair.fromRawEd25519Seed(masterKey.key)
}
diff --git a/main-process/packages/web-microkernel b/main-process/packages/web-microkernel
new file mode 120000
index 00000000000..0c8cc3cca44
--- /dev/null
+++ b/main-process/packages/web-microkernel
@@ -0,0 +1 @@
+../../packages/web-microkernel
\ No newline at end of file
diff --git a/main-process/yarn.lock b/main-process/yarn.lock
index b454ce5e8cc..91b3f70b20c 100644
--- a/main-process/yarn.lock
+++ b/main-process/yarn.lock
@@ -1419,6 +1419,13 @@
call-me-maybe "^1.0.1"
glob-to-regexp "^0.3.0"
+"@nodeguy/channel@0.6.5":
+ version "0.6.5"
+ resolved "https://registry.yarnpkg.com/@nodeguy/channel/-/channel-0.6.5.tgz#c04938f443bacaf8abcbe574ca7c12b802f07808"
+ integrity sha512-etReylPZCfyWU0dgs4qPAOCWPZ8bFaFNNYJTZ894FMZNHOastw6Bb2F42iDnfWBf0ONkDRWLzEt2YABn//iHZQ==
+ dependencies:
+ setimmediate "1.0.5"
+
"@nodelib/fs.stat@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz#54c5a964462be3d4d78af631363c18d6fa91ac26"
@@ -10122,7 +10129,7 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
-lodash-es@^4.17.10:
+lodash-es@4.17.11, lodash-es@^4.17.10:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0"
integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==
@@ -13874,7 +13881,7 @@ set-value@^2.0.0:
is-plain-object "^2.0.3"
split-string "^3.0.1"
-setimmediate@^1.0.4, setimmediate@^1.0.5:
+setimmediate@1.0.5, setimmediate@^1.0.4, setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
diff --git a/packages/blockchain-wallet-v4-frontend/package.json b/packages/blockchain-wallet-v4-frontend/package.json
index 9aea4db5df6..f14c3edf4ef 100644
--- a/packages/blockchain-wallet-v4-frontend/package.json
+++ b/packages/blockchain-wallet-v4-frontend/package.json
@@ -84,10 +84,11 @@
},
"dependencies": {
"@blockchain-com/components": "5.1.1",
- "@ledgerhq/hw-app-eth": "4.26.0-beta.ebeb3540",
"@ledgerhq/hw-app-btc": "4.30.0",
+ "@ledgerhq/hw-app-eth": "4.26.0-beta.ebeb3540",
"@ledgerhq/hw-app-str": "4.26.0-beta.ebeb3540",
"@ledgerhq/hw-transport-u2f": "4.31.0",
+ "@nodeguy/channel": "0.6.5",
"awesome-phonenumber": "2.2.6",
"base-64": "0.1.0",
"bignumber.js": "8.0.1",
diff --git a/packages/blockchain-wallet-v4-frontend/src/IPC/Exports.js b/packages/blockchain-wallet-v4-frontend/src/IPC/Exports.js
new file mode 100644
index 00000000000..442420b1ca7
--- /dev/null
+++ b/packages/blockchain-wallet-v4-frontend/src/IPC/Exports.js
@@ -0,0 +1,61 @@
+import axios from 'axios'
+import BIP39 from 'bip39'
+import Bitcoin from 'bitcoinjs-lib'
+import * as ed25519 from 'ed25519-hd-key'
+
+import {
+ getDefaultHDWallet,
+ getGuid,
+ getMainPassword
+} from 'blockchain-wallet-v4/src/redux/wallet/selectors'
+
+import { HDWallet } from 'blockchain-wallet-v4/src/types'
+import * as crypto from 'blockchain-wallet-v4/src/walletCrypto'
+import * as kernel from '../../../web-microkernel/src'
+
+export default ({ store }) => {
+ const credentialsEntropy = ({ sharedKey }) => {
+ const state = store.getState()
+ const guid = getGuid(state)
+ const password = getMainPassword(state)
+ return crypto.sha256(Buffer.from(guid + sharedKey + password))
+ }
+
+ const getSeed = ({ iterations, secondPassword, sharedKey }) => {
+ const state = store.getState()
+ const candidate = HDWallet.selectSeedHex(getDefaultHDWallet(state))
+
+ const entropy = secondPassword
+ ? crypto.decryptSecPass(sharedKey, iterations, secondPassword, candidate)
+ : candidate
+
+ return BIP39.mnemonicToSeed(BIP39.entropyToMnemonic(entropy))
+ }
+
+ const deriveBIP32Key = (
+ { iterations, secondPassword, sharedKey },
+ { network, path }
+ ) => {
+ const seed = getSeed({ iterations, secondPassword, sharedKey })
+
+ return Bitcoin.HDNode.fromSeedBuffer(seed, network)
+ .derivePath(path)
+ .toBase58()
+ }
+
+ const deriveSLIP10ed25519Key = (
+ { iterations, secondPassword, sharedKey },
+ { path }
+ ) => {
+ const seed = getSeed({ iterations, secondPassword, sharedKey })
+ return ed25519.derivePath(path, seed.toString(`hex`))
+ }
+
+ return {
+ axios: kernel.sanitizeFunction(axios),
+ credentialsEntropy,
+ deriveBIP32Key,
+ deriveSLIP10ed25519Key,
+ dispatch: store.dispatch
+ }
+}
diff --git a/packages/blockchain-wallet-v4-frontend/src/IPC/Middleware.js b/packages/blockchain-wallet-v4-frontend/src/IPC/Middleware.js
new file mode 100644
index 00000000000..0dffc86f10d
--- /dev/null
+++ b/packages/blockchain-wallet-v4-frontend/src/IPC/Middleware.js
@@ -0,0 +1,135 @@
+import * as router from 'connected-react-router'
+import * as R from 'ramda'
+
+import * as coreTypes from 'blockchain-wallet-v4/src/redux/actionTypes'
+import * as types from '../data/actionTypes'
+
+const alreadyForwarded = ({ meta }) => meta && meta.forwarded
+
+const tag = action => ({
+ ...action,
+ meta: { ...action.meta, forwarded: true }
+})
+
+const appElement = document.getElementById(`app`)
+const frameElement = document.getElementById(`frame`)
+const mainProcessElement = document.getElementById(`main-process`)
+
+const displayRootDocument = () => {
+ appElement.style.display = `block`
+ frameElement.style.borderColor = `lightgreen`
+ mainProcessElement.style.display = `none`
+}
+
+const displayMainProcess = () => {
+ appElement.style.display = `none`
+ frameElement.style.borderColor = `red`
+ mainProcessElement.style.display = `block`
+}
+
+const dispatchToBoth = ({ mainProcessDispatch, next }, action) => {
+ if (!alreadyForwarded(action)) {
+ mainProcessDispatch(action)
+ }
+
+ next(action)
+}
+
+const wrapperMask = {
+ password: undefined,
+ wallet: {
+ guid: undefined,
+ hd_wallets: [{ seedHex: undefined }]
+ }
+}
+
+const handlers = {
+ // This requires the GUID.
+ [coreTypes.data.misc.FETCH_LOGS_FAILURE]: dispatchToBoth,
+ [coreTypes.data.misc.FETCH_LOGS_LOADING]: dispatchToBoth,
+ [coreTypes.data.misc.FETCH_LOGS_SUCCESS]: dispatchToBoth,
+
+ // Dispatched by createRoot, which requires the mnemonic.
+ [coreTypes.kvStore.root.UPDATE_METADATA_ROOT]: dispatchToBoth,
+
+ // Dispatched by createXlm, which requires the mnemonic.
+ [coreTypes.kvStore.xlm.CREATE_METADATA_XLM]: dispatchToBoth,
+
+ // This requires the GUID.
+ [coreTypes.settings.FETCH_SETTINGS_FAILURE]: dispatchToBoth,
+ [coreTypes.settings.FETCH_SETTINGS_LOADING]: dispatchToBoth,
+ [coreTypes.settings.FETCH_SETTINGS_SUCCESS]: dispatchToBoth,
+
+ // Report failure of wallet synchronization.
+ [coreTypes.walletSync.SYNC_ERROR]: dispatchToBoth,
+
+ // Report success of wallet synchronization.
+ [coreTypes.walletSync.SYNC_SUCCESS]: dispatchToBoth,
+
+ // Proceed with the login routine after receiving the payload.
+ [types.auth.AUTHENTICATE]: dispatchToBoth,
+ [types.auth.LOGIN_ROUTINE]: dispatchToBoth
+}
+
+// Used to set the wrapper in /recover.
+
+handlers[coreTypes.wallet.REFRESH_WRAPPER] = (
+ { mainProcessDispatch, next },
+ action
+) => {
+ const redactedPayload = action.payload.mergeDeep(wrapperMask)
+ mainProcessDispatch({ ...action, payload: redactedPayload })
+ next(action)
+}
+
+// Send the wrapper to the Main Process after logging in.
+handlers[coreTypes.wallet.SET_WRAPPER] =
+ handlers[coreTypes.wallet.REFRESH_WRAPPER]
+
+export default ({ mainProcessDispatch, rootDocumentRoutes }) => {
+ const routeIsInRootDocument = pathname =>
+ rootDocumentRoutes.some(route => pathname.startsWith(route))
+
+ handlers[router.LOCATION_CHANGE] = (
+ { mainProcessDispatch, next, store },
+ action
+ ) => {
+ const { pathname } = action.payload.location
+
+ if (routeIsInRootDocument(pathname)) {
+ displayRootDocument()
+
+ const isNewLocationFromMainProcess =
+ alreadyForwarded(action) &&
+ pathname !== store.getState().router.location.pathname
+
+ const nextAction = isNewLocationFromMainProcess
+ ? router.replace(pathname)
+ : action
+
+ next(nextAction)
+ } else {
+ displayMainProcess()
+ mainProcessDispatch(action)
+ }
+ }
+
+ return store => next => action => {
+ const { type } = action
+
+ const context = {
+ mainProcessDispatch: R.pipe(
+ tag,
+ mainProcessDispatch
+ ),
+ next,
+ store
+ }
+
+ if (type in handlers) {
+ return handlers[type](context, action)
+ } else {
+ return next(action)
+ }
+ }
+}
diff --git a/packages/blockchain-wallet-v4-frontend/src/IPC/index.js b/packages/blockchain-wallet-v4-frontend/src/IPC/index.js
new file mode 100644
index 00000000000..6420d679761
--- /dev/null
+++ b/packages/blockchain-wallet-v4-frontend/src/IPC/index.js
@@ -0,0 +1,45 @@
+import Channel from '@nodeguy/channel'
+
+import { serializer } from 'blockchain-wallet-v4/src/types'
+import * as kernel from '../../../web-microkernel/src'
+
+import Exports from './Exports'
+import Middleware from './Middleware'
+
+const rootDocumentRoutes = [
+ `/help`,
+ `/login`,
+ `/recover`,
+ `/reminder`,
+ `/reset-2fa`,
+ `/security-center`,
+ `/signup`
+]
+
+export default () => {
+ // The middleware dispatches actions to the Main Process store but we need to
+ // provide the middleware before we can import the Main Process's `dispatch`
+ // method. Create a channel for buffering actions to be dispatched once it's
+ // available.
+ const mainProcessActionsChannel = Channel()
+
+ return {
+ Connection: async ({ input, output, outputOrigin, store }) => {
+ const connection = await kernel.RealmConnection({
+ exports: Exports({ store }),
+ input,
+ output,
+ outputOrigin,
+ reviver: serializer.reviver
+ })
+
+ mainProcessActionsChannel.forEach(connection.imports.dispatch)
+ return connection
+ },
+
+ middleware: Middleware({
+ mainProcessDispatch: mainProcessActionsChannel.push,
+ rootDocumentRoutes
+ })
+ }
+}
diff --git a/packages/blockchain-wallet-v4-frontend/src/data/auth/actionTypes.js b/packages/blockchain-wallet-v4-frontend/src/data/auth/actionTypes.js
index 0de985229bb..14e453788a5 100644
--- a/packages/blockchain-wallet-v4-frontend/src/data/auth/actionTypes.js
+++ b/packages/blockchain-wallet-v4-frontend/src/data/auth/actionTypes.js
@@ -3,6 +3,7 @@ export const DEAUTHORIZE_BROWSER = 'DEAUTHORIZE_BROWSER'
export const LOGIN = 'LOGIN'
export const LOGIN_FAILURE = 'LOGIN_FAILURE'
export const LOGIN_LOADING = 'LOGIN_LOADING'
+export const LOGIN_ROUTINE = 'LOGIN_ROUTINE'
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
export const LOGOUT = 'LOGOUT'
export const LOGOUT_CLEAR_REDUX_STORE = 'LOGOUT_CLEAR_REDUX_STORE'
diff --git a/packages/blockchain-wallet-v4-frontend/src/data/auth/actions.js b/packages/blockchain-wallet-v4-frontend/src/data/auth/actions.js
index 63600f6a11a..f1845c62e56 100644
--- a/packages/blockchain-wallet-v4-frontend/src/data/auth/actions.js
+++ b/packages/blockchain-wallet-v4-frontend/src/data/auth/actions.js
@@ -7,6 +7,12 @@ export const login = (guid, password, code, sharedKey, mobileLogin) => ({
payload: { guid, password, code, sharedKey, mobileLogin }
})
export const loginLoading = () => ({ type: AT.LOGIN_LOADING })
+
+export const loginRoutine = (mobileLogin = false, firstLogin = false) => ({
+ type: AT.LOGIN_ROUTINE,
+ payload: { firstLogin, mobileLogin }
+})
+
export const loginSuccess = () => ({ type: AT.LOGIN_SUCCESS, payload: {} })
export const loginFailure = err => ({
type: AT.LOGIN_FAILURE,
diff --git a/packages/blockchain-wallet-v4-frontend/src/data/auth/sagaRegister.js b/packages/blockchain-wallet-v4-frontend/src/data/auth/sagaRegister.js
index f45371a6e1a..0c0effd3a99 100644
--- a/packages/blockchain-wallet-v4-frontend/src/data/auth/sagaRegister.js
+++ b/packages/blockchain-wallet-v4-frontend/src/data/auth/sagaRegister.js
@@ -8,6 +8,7 @@ export default ({ api, coreSagas }) => {
return function* authSaga () {
yield takeLatest(AT.DEAUTHORIZE_BROWSER, authSagas.deauthorizeBrowser)
yield takeLatest(AT.LOGIN, authSagas.login)
+ yield takeLatest(AT.LOGIN_ROUTINE, authSagas.loginRoutineSaga)
yield takeLatest(AT.LOGOUT, authSagas.logout)
yield takeLatest(
AT.LOGOUT_CLEAR_REDUX_STORE,
diff --git a/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.js b/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.js
index 9b8247a6e8c..8d035bb493f 100644
--- a/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.js
+++ b/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.js
@@ -92,61 +92,15 @@ export default ({ api, coreSagas }) => {
if (userFlowSupported) yield put(actions.modules.profile.signIn())
}
- const loginRoutineSaga = function*(mobileLogin, firstLogin) {
+ const loginRoutineSaga = function*({ payload: { mobileLogin, firstLogin } }) {
try {
- // If needed, the user should upgrade its wallet before being able to open the wallet
- const isHdWallet = yield select(selectors.core.wallet.isHdWallet)
- if (!isHdWallet) {
- yield call(upgradeWalletSaga)
- }
yield put(actions.auth.authenticate())
- yield call(coreSagas.kvStore.root.fetchRoot, askSecondPasswordEnhancer)
- // If there was no ethereum metadata kv store entry, we need to create one and that requires the second password.
- yield call(
- coreSagas.kvStore.ethereum.fetchMetadataEthereum,
- askSecondPasswordEnhancer
- )
- yield call(
- coreSagas.kvStore.xlm.fetchMetadataXlm,
- askSecondPasswordEnhancer
- )
- yield call(coreSagas.kvStore.bch.fetchMetadataBch)
- yield call(coreSagas.kvStore.bsv.fetchMetadataBsv)
- yield call(coreSagas.kvStore.lockbox.fetchMetadataLockbox)
- yield put(actions.middleware.webSocket.bch.startSocket())
- yield put(actions.middleware.webSocket.btc.startSocket())
- yield put(actions.middleware.webSocket.eth.startSocket())
- yield put(actions.middleware.webSocket.xlm.startStreams())
- yield put(actions.router.push('/home'))
- yield call(coreSagas.settings.fetchSettings)
- yield call(coreSagas.data.xlm.fetchLedgerDetails)
- yield call(coreSagas.data.xlm.fetchData)
- yield call(authNabu)
- yield call(upgradeAddressLabelsSaga)
- yield put(actions.auth.loginSuccess())
- yield put(actions.auth.startLogoutTimer())
// store guid in cache for future logins
const guid = yield select(selectors.core.wallet.getGuid)
yield put(actions.cache.guidEntered(guid))
// reset auth type and clear previous login form state
yield put(actions.auth.setAuthType(0))
yield put(actions.form.destroy('login'))
- // set payload language to settings language
- const language = yield select(selectors.preferences.getLanguage)
- yield put(actions.modules.settings.updateLanguage(language))
- yield fork(transferEthSaga)
- // TODO @analytics.logEvent login flow
- // yield fork(reportStats, mobileLogin)
- yield put(actions.goals.saveGoal('welcome', { firstLogin }))
- yield put(actions.goals.saveGoal('swapUpgrade'))
- yield put(actions.goals.saveGoal('kyc'))
- yield put(actions.goals.runGoals())
- yield fork(checkDataErrors)
- yield put(actions.analytics.reportBalanceStats())
- yield fork(logoutRoutine, yield call(setLogoutEventListener))
- if (!firstLogin) {
- yield put(actions.alerts.displaySuccess(C.LOGIN_SUCCESS))
- }
} catch (e) {
yield put(
actions.logs.logErrorMessage(logLocation, 'loginRoutineSaga', e)
@@ -222,7 +176,7 @@ export default ({ api, coreSagas }) => {
password,
code
})
- yield call(loginRoutineSaga, mobileLogin)
+ yield put(actions.auth.loginRoutine(mobileLogin))
} catch (error) {
const initialError = prop('initial_error', error)
const authRequired = prop('authorization_required', error)
@@ -245,7 +199,7 @@ export default ({ api, coreSagas }) => {
session,
password
})
- yield call(loginRoutineSaga, mobileLogin)
+ yield put(actions.auth.loginRoutine(mobileLogin))
} catch (error) {
if (error && error.auth_type > 0) {
yield put(actions.auth.setAuthType(error.auth_type))
@@ -334,7 +288,12 @@ export default ({ api, coreSagas }) => {
yield put(actions.alerts.displayInfo(C.CREATE_WALLET_INFO))
yield call(coreSagas.wallet.createWalletSaga, action.payload)
yield put(actions.alerts.displaySuccess(C.REGISTER_SUCCESS))
- yield call(loginRoutineSaga, false, true)
+
+ // Temporarily send the whole wrapper over to the Main Process.
+ const wrapper = yield select(selectors.core.wallet.getWrapper)
+ yield put(actions.core.wallet.setWrapper(wrapper))
+
+ yield put(actions.auth.loginRoutine(false, true))
yield put(actions.auth.registerSuccess())
} catch (e) {
yield put(actions.auth.registerFailure())
@@ -349,7 +308,7 @@ export default ({ api, coreSagas }) => {
yield put(actions.alerts.displayInfo(C.RESTORE_WALLET_INFO))
yield call(coreSagas.wallet.restoreWalletSaga, action.payload)
yield put(actions.alerts.displaySuccess(C.RESTORE_SUCCESS))
- yield call(loginRoutineSaga, false, true)
+ yield put(actions.auth.loginRoutine(false, true))
yield put(actions.auth.restoreSuccess())
} catch (e) {
yield put(actions.auth.restoreFailure())
diff --git a/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.spec.js b/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.spec.js
index b4d73337244..1d501d2dfea 100644
--- a/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.spec.js
+++ b/packages/blockchain-wallet-v4-frontend/src/data/auth/sagas.spec.js
@@ -93,11 +93,11 @@ describe('authSagas', () => {
})
})
- it('should call login routine', () => {
+ it('should put login routine', () => {
const { mobileLogin } = payload
saga
.next()
- .call(loginRoutineSaga, mobileLogin)
+ .put(actions.auth.loginRoutineSaga(mobileLogin))
.next()
.isDone()
})
@@ -185,9 +185,9 @@ describe('authSagas', () => {
})
})
- it('should call login routine', () => {
+ it('should put login routine', () => {
const { mobileLogin } = payload
- saga.next().call(loginRoutineSaga, mobileLogin)
+ saga.next().put(actions.auth.loginRoutineSaga(mobileLogin))
})
it('should follow 2FA flow on auth error', () => {
diff --git a/packages/blockchain-wallet-v4-frontend/src/data/logs/reducers.js b/packages/blockchain-wallet-v4-frontend/src/data/logs/reducers.js
index c905bf90105..119fef2b1b3 100644
--- a/packages/blockchain-wallet-v4-frontend/src/data/logs/reducers.js
+++ b/packages/blockchain-wallet-v4-frontend/src/data/logs/reducers.js
@@ -18,6 +18,7 @@ const logger = (state = INITIAL_STATE, action) => {
switch (type) {
case AT.LOG_ERROR_MSG: {
+ console.error(payload)
return insert(0, createLog('ERROR', payload), state)
}
case AT.LOG_INFO_MSG: {
diff --git a/packages/blockchain-wallet-v4-frontend/src/index.html b/packages/blockchain-wallet-v4-frontend/src/index.html
index 7858507df44..10db155d202 100644
--- a/packages/blockchain-wallet-v4-frontend/src/index.html
+++ b/packages/blockchain-wallet-v4-frontend/src/index.html
@@ -1,27 +1,69 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Blockchain Wallet - Exchange Cryptocurrency
-
-
-
-
-
+
+
+
+
+
+