From c55bf21eaa17034240678ea98d54bc66c7c6da1d Mon Sep 17 00:00:00 2001 From: Empowerful <45366397+Empowerful@users.noreply.github.com> Date: Tue, 12 Mar 2019 10:25:14 -0400 Subject: [PATCH] POC: Configure two Web Wallet applications (Root Document & Main Process) to coexist. The Root Document handles wallet synchronization for both applications. --- .../package.json | 1 + .../src/data/logs/reducers.js | 1 + .../src/middleware/actionTypes.js | 1 + .../src/middleware/forwardActions.js | 11 + .../src/middleware/index.js | 2 + .../src/store/index.js | 288 +++++++----- .../webpack.config.dev.js | 16 +- .../src/network/api/http.js | 3 +- .../src/network/api/index.js | 3 +- .../src/network/walletApi.js | 13 +- .../src/redux/wallet/actionTypes.js | 1 + .../src/redux/wallet/actions.js | 4 + .../src/redux/wallet/sagas.js | 9 + .../src/redux/walletSync/middleware.js | 107 ++--- .../src/redux/walletSync/middleware.spec.js | 14 +- main-process/packages/web-microkernel | 1 + main-process/yarn.lock | 11 +- .../package.json | 1 + .../src/data/logs/reducers.js | 1 + .../src/index.html | 82 +++- .../src/middleware/actionTypes.js | 1 + .../src/middleware/forwardActions.js | 11 + .../src/middleware/index.js | 2 + .../src/store/index.js | 270 +++++++----- .../webpack.config.dev.js | 3 +- .../__snapshots__/walletReducers.spec.js.snap | 86 ++++ .../src/redux/wallet/actionTypes.js | 1 + .../src/redux/wallet/actions.js | 5 + .../src/redux/wallet/reducers.js | 36 +- .../src/redux/wallet/walletReducers.spec.js | 17 + .../src/types/Serializer.js | 10 +- packages/web-microkernel/package.json | 9 + packages/web-microkernel/src/Multiplexed.js | 39 ++ .../web-microkernel/src/Multiplexed.test.js | 103 +++++ .../web-microkernel/src/RealmConnection.js | 409 ++++++++++++++++++ .../src/RealmConnection.test.js | 390 +++++++++++++++++ packages/web-microkernel/src/index.js | 4 + packages/web-microkernel/src/lodash-es | 1 + packages/web-microkernel/wallaby.js | 7 + packages/web-microkernel/yarn.lock | 8 + yarn.lock | 11 +- 41 files changed, 1666 insertions(+), 327 deletions(-) create mode 100644 main-process/packages/blockchain-wallet-v4-frontend/src/middleware/actionTypes.js create mode 100644 main-process/packages/blockchain-wallet-v4-frontend/src/middleware/forwardActions.js create mode 120000 main-process/packages/web-microkernel create mode 100644 packages/blockchain-wallet-v4-frontend/src/middleware/actionTypes.js create mode 100644 packages/blockchain-wallet-v4-frontend/src/middleware/forwardActions.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/Multiplexed.js create mode 100644 packages/web-microkernel/src/Multiplexed.test.js 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/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..69f745e29b2 100644 --- a/main-process/packages/blockchain-wallet-v4-frontend/package.json +++ b/main-process/packages/blockchain-wallet-v4-frontend/package.json @@ -88,6 +88,7 @@ "@ledgerhq/hw-app-btc": "4.30.0", "@ledgerhq/hw-app-str": "4.26.0-beta.ebeb3540", "@ledgerhq/hw-transport-u2f": "4.31.0", + "@nodeguy/channel": "0.6.4", "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/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/middleware/actionTypes.js b/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/actionTypes.js new file mode 100644 index 00000000000..64a7efe5ecd --- /dev/null +++ b/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/actionTypes.js @@ -0,0 +1 @@ +export const FORWARD = `FORWARD` diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/forwardActions.js b/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/forwardActions.js new file mode 100644 index 00000000000..2018d400603 --- /dev/null +++ b/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/forwardActions.js @@ -0,0 +1,11 @@ +import { FORWARD } from './actionTypes' + +export default ({ forward, types }) => () => next => action => { + if (types.has(action.type)) { + forward(action) + } else if (action.type === FORWARD) { + forward(action.payload) + } + + return next(action) +} diff --git a/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/index.js b/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/index.js index 07c8c525f27..52d564ffca7 100644 --- a/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/index.js +++ b/main-process/packages/blockchain-wallet-v4-frontend/src/middleware/index.js @@ -1,4 +1,5 @@ import autoDisconnection from './autoDisconnection' +import forwardActions from './forwardActions' import webSocketBch from './webSocketBch' import webSocketBtc from './webSocketBtc' import webSocketEth from './webSocketEth' @@ -7,6 +8,7 @@ import streamingXlm from './streamingXlm' export { autoDisconnection, + forwardActions, streamingXlm, webSocketBch, webSocketBtc, 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..4267ce48a73 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 @@ -1,11 +1,14 @@ +import { AUTHENTICATE, LOGOUT } from '../data/auth/actionTypes' +import Channel from '@nodeguy/channel' import { createStore, applyMiddleware, compose } from 'redux' import createSagaMiddleware from 'redux-saga' import { persistStore, persistCombineReducers } from 'redux-persist' +import { RealmConnection, Multiplexed } from '../../../web-microkernel/src' import storage from 'redux-persist/lib/storage' import getStoredStateMigrateV4 from 'redux-persist/lib/integration/getStoredStateMigrateV4' import { createHashHistory } from 'history' import { connectRouter, routerMiddleware } from 'connected-react-router' -import { head } from 'ramda' +import { clone, head, omit } from 'ramda' import Bitcoin from 'bitcoinjs-lib' import BitcoinCash from 'bitcoinforksjs-lib' @@ -20,6 +23,7 @@ import { serializer } from 'blockchain-wallet-v4/src/types' import { actions, rootSaga, rootReducer, selectors } from 'data' import { autoDisconnection, + forwardActions, streamingXlm, webSocketBch, webSocketBtc, @@ -29,6 +33,7 @@ import { const devToolsConfig = { maxAge: 1000, + name: `Main Process`, serialize: serializer, actionsBlacklist: [ // '@@redux-form/INITIALIZE', @@ -43,118 +48,193 @@ 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 + }) + + // The store isn't available by the time we want to export its dispatch method + // so use a channel to hold pending actions. + const actionsChannel = Channel() + + const rootDocument = await RealmConnection({ + exports: { dispatch: actionsChannel.push }, + input: Multiplexed({ realm: window, tag: `realms` }), + output: Multiplexed({ realm: window.parent, tag: `realms` }), + outputOrigin: options.domains.rootDocument + }) + + rootDocument.addEventListener(`error`, event => { + const plainError = pick( + [`message`, `filename`, `lineno`, `colno`, `error`], + event + ) + + store.dispatch( + actions.logs.logErrorMessage(`store`, `realm connection`, plainError) + ) + }) + + 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 axiosAdapter = async config => { + try { + // `transformRequest`, `transformResponse`, `validateStatus` are all + // performed locally so there's no need to call them in the root document. + + const sanitizedConfig = omit( + [`adapter`, `transformRequest`, `transformResponse`, `validateStatus`], + config ) - 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 - } - }) + + const immutableResponse = await rootDocument.imports.axios( + sanitizedConfig + ) + + // Return a shallow clone because Axios wants to mutate it. + return { ...immutableResponse } + } catch (immutableException) { + // Create a mutable deep clone of the exception because Axios wants to + // mutate it. + + const plainObject = { ...immutableException } + + const mutableException = Object.assign( + Error(immutableException.message), + clone(plainObject) + ) + + throw mutableException + } + } + + const api = createWalletApi({ + axiosAdapter, + options, + apiKey, + getAuthCredentials, + reauthenticate, + networks + }) + + const persistWhitelist = ['session', 'preferences', 'cache'] + + // Forward the following action types to the root document. + // + // AUTHENTICATE: Enable the root document to synchronize the wallet. + // LOGOUT: Tell the root document to reload itself when we do. + const forwardActionTypes = new Set([AUTHENTICATE, LOGOUT]) + + // 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, + rootDocumentDispatch: rootDocument.imports.dispatch + }), + + autoDisconnection(), + + forwardActions({ + forward: rootDocument.imports.dispatch, + types: forwardActionTypes + }) + ) + ) + ) + const persistor = persistStore(store, null) + + sagaMiddleware.run(rootSaga, { + api, + bchSocket, + btcSocket, + ethSocket, + ratesSocket, + networks, + options + }) + + // Now that we have a store, dispatch pending and future actions from the + // channel. + actionsChannel.forEach(store.dispatch) + + // 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/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..0d5dee6ba97 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,10 @@ import * as T from './actionTypes' // setters +export const mergeWrapper = payload => ({ + type: T.MERGE_WRAPPER, + payload: 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..b3c3b63475e 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 @@ -1,3 +1,4 @@ +import { FORWARD } from '../../../../blockchain-wallet-v4-frontend/src/middleware/actionTypes' import { call, put, select } from 'redux-saga/effects' import BIP39 from 'bip39' import Bitcoin from 'bitcoinjs-lib' @@ -120,6 +121,14 @@ export default ({ api, networks }) => { code ) yield put(A.wallet.setWrapper(wrapper)) + + // Temporary: Forward the wrapper to the root document to initialize + // sensitive fields (seed, etc). This will be removed in the future when + // the root document handles login, etc. + yield put({ + type: FORWARD, + payload: A.wallet.setWrapper(wrapper) + }) } const upgradeToHd = function*({ password }) { 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..2c49a3c0ac3 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,5 @@ -import { futurizeP } from 'futurize' -import Task from 'data.task' import { compose, - assoc, - join, curry, range, keysIn, @@ -15,7 +11,6 @@ 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 * as selectors from '../selectors' @@ -93,6 +88,22 @@ 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 @@ -101,73 +112,29 @@ export const getWalletAddresses = async (state, api) => { */ const walletSync = ({ isAuthenticated, - api + rootDocumentDispatch } = {}) => store => next => action => { - const prevState = store.getState() - const prevWallet = selectors.wallet.getWrapper(prevState) - const wasAuth = isAuthenticated(prevState) + 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) + }) + ) { + rootDocumentDispatch({ + type: T.wallet.MERGE_WRAPPER, + + // Convert the wallet to JavaScript types so it can cross the realm + // boundary. + payload: Wrapper.toJS(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/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..696e059bd63 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.4": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@nodeguy/channel/-/channel-0.6.4.tgz#84d64b513d17c35b599e74e7a16101945f62ae43" + integrity sha512-TT4kEgcj8K9Gj7fzq1m/Nb/xoyTYEc5C27WZPBEebQXQoLeZtoytKi1pYZ6vU3tn/nkj1fkS1sLCJ5w5R/2JpA== + 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..69f745e29b2 100644 --- a/packages/blockchain-wallet-v4-frontend/package.json +++ b/packages/blockchain-wallet-v4-frontend/package.json @@ -88,6 +88,7 @@ "@ledgerhq/hw-app-btc": "4.30.0", "@ledgerhq/hw-app-str": "4.26.0-beta.ebeb3540", "@ledgerhq/hw-transport-u2f": "4.31.0", + "@nodeguy/channel": "0.6.4", "awesome-phonenumber": "2.2.6", "base-64": "0.1.0", "bignumber.js": "8.0.1", 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..26948bdcb14 100644 --- a/packages/blockchain-wallet-v4-frontend/src/index.html +++ b/packages/blockchain-wallet-v4-frontend/src/index.html @@ -1,27 +1,63 @@ -
- - - - - - - - - - - - - - - -